Skip to content

Commit 8572874

Browse files
committed
HTTP wallet example
1 parent 985f555 commit 8572874

File tree

3 files changed

+250
-0
lines changed

3 files changed

+250
-0
lines changed

docs/examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Here, you will find a number of common usage examples for the go-sdk.
4848
- [Create Signature](./create_signature/) - Create a digital signature.
4949
- [Create HMAC](./create_hmac/) - Create an HMAC.
5050
- [Get Public Key](./get_public_key/) - Retrieve a public key from the wallet.
51+
- [HTTP Wallet](./http_wallet/) - Interact with a wallet using JSON over HTTP.
5152

5253
## Additional Example Documents
5354
- [Converting Transactions from go-bt](./GO_BT.md)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# HTTP Wallet Example
2+
3+
This example demonstrates how to use the `substrates.HTTPWalletJSON` to interact with a wallet service over HTTP. It specifically shows how to make a `CreateAction` call to a mock HTTP server that simulates a wallet backend.
4+
5+
## Overview
6+
7+
The `substrates.HTTPWalletJSON` provides a client implementation for wallet operations that are exposed via an HTTP JSON API. This example focuses on the `CreateAction` method, which is typically used to initiate the creation of a transaction.
8+
9+
The example includes:
10+
1. Setting up a mock HTTP server to handle wallet API requests.
11+
2. Initializing an `HTTPWalletJSON` client to communicate with the mock server.
12+
3. Preparing `CreateActionArgs` with transaction details.
13+
4. Calling the `CreateAction` method on the HTTP wallet client.
14+
5. Processing the `CreateActionResult` received from the mock server.
15+
16+
## Code Walkthrough
17+
18+
### 1. Mock HTTP Server Setup
19+
20+
To simulate a backend wallet service, a mock HTTP server is created using `net/http/httptest`. This server will respond to the `/createAction` endpoint.
21+
22+
```go
23+
// Mock server setup
24+
mux := http.NewServeMux()
25+
26+
// Mock CreateAction
27+
var mockCreateActionReference = []byte("mock-tx-reference-123")
28+
mockCreateActionTxId, _ := chainhash.NewHashFromHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
29+
mux.HandleFunc("/createAction", func(w http.ResponseWriter, r *http.Request) {
30+
var args wallet.CreateActionArgs
31+
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
32+
http.Error(w, "Failed to decode request for createAction", http.StatusBadRequest)
33+
return
34+
}
35+
fmt.Printf("Mock Server: Received CreateAction with Description: %s\n", args.Description)
36+
37+
// Use an anonymous struct for the JSON response to send Txid as a string
38+
jsonResponse := struct {
39+
Txid string `json:"txid"`
40+
SignableTransaction *wallet.SignableTransaction `json:"signableTransaction,omitempty"`
41+
}{
42+
Txid: mockCreateActionTxId.String(), // Marshal as hex string
43+
SignableTransaction: &wallet.SignableTransaction{
44+
Reference: mockCreateActionReference,
45+
},
46+
}
47+
writeJSONResponse(w, jsonResponse)
48+
})
49+
50+
server := httptest.NewServer(mux)
51+
defer server.Close()
52+
53+
// Helper to write JSON responses for the mock server
54+
func writeJSONResponse(w http.ResponseWriter, data interface{}) {
55+
w.Header().Set("Content-Type", "application/json")
56+
if err := json.NewEncoder(w).Encode(data); err != nil {
57+
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
58+
}
59+
}
60+
```
61+
The mock server listens for POST requests on `/createAction`. When a request is received, it decodes the `wallet.CreateActionArgs`, prints the description, and sends back a mocked `CreateActionResult` containing a `Txid` and a `SignableTransaction` with a reference.
62+
63+
### 2. Initialize HTTPWalletJSON Client
64+
65+
An `HTTPWalletJSON` client is initialized with the URL of the mock server.
66+
67+
```go
68+
originator := "my-app-example"
69+
// Use the mock server's URL
70+
baseURL := server.URL
71+
httpClient := server.Client()
72+
73+
w := substrates.NewHTTPWalletJSON(originator, baseURL, httpClient)
74+
ctx := context.Background()
75+
```
76+
The `originator` string is an identifier for the application making the request. The `httpClient` from the `httptest.Server` is used to ensure requests go to the mock server.
77+
78+
### 3. Prepare and Call CreateAction
79+
80+
Arguments for `CreateAction` are prepared, including a description, output details (amount and locking script), and labels.
81+
82+
```go
83+
// --- CreateAction ---
84+
fmt.Println("\n--- CreateAction ---")
85+
// Create a P2PKH script for the output
86+
mockToAddress, err := script.NewAddressFromString("mfZQtGMnf2aP17fF3a9TzWMRw2NXp25hN2") // Using a valid testnet address format
87+
if err != nil {
88+
fmt.Printf("Error creating mock address: %v\n", err)
89+
return
90+
}
91+
p2pkhScript, _ := p2pkh.Lock(mockToAddress)
92+
93+
createActionArgs := wallet.CreateActionArgs{
94+
Description: "Test transaction from example",
95+
Outputs: []wallet.CreateActionOutput{
96+
{
97+
Satoshis: 1000, // Amount in satoshis
98+
LockingScript: p2pkhScript.Bytes(),
99+
},
100+
},
101+
Labels: []string{"test", "example"},
102+
}
103+
createActionResult, err := w.CreateAction(ctx, createActionArgs)
104+
if err != nil {
105+
fmt.Printf("Error creating action: %v\n", err)
106+
}
107+
var currentActionReference []byte
108+
if createActionResult != nil {
109+
fmt.Printf("CreateAction Result - TxID: %s\n", createActionResult.Txid.String())
110+
if createActionResult.SignableTransaction != nil {
111+
currentActionReference = createActionResult.SignableTransaction.Reference
112+
fmt.Printf("CreateAction Result - Reference: %s\n", string(currentActionReference))
113+
} else {
114+
fmt.Println("CreateAction Result - No SignableTransaction returned by mock.")
115+
}
116+
} else {
117+
fmt.Println("CreateAction failed or mock returned nil.")
118+
}
119+
```
120+
A P2PKH (Pay-to-Public-Key-Hash) script is created for the transaction output. The `CreateAction` method is then called on the `HTTPWalletJSON` client. The result, including the transaction ID and any signable transaction reference, is printed.
121+
122+
## Running the Example
123+
124+
To run this example:
125+
126+
```bash
127+
cd go-sdk/docs/examples/http_wallet
128+
go mod tidy
129+
go run http_wallet.go
130+
```
131+
132+
This will start the mock server, send a `CreateAction` request to it, and print the results to the console.
133+
134+
## Key Concepts
135+
136+
- **`substrates.HTTPWalletJSON`**: A client for interacting with a wallet service that exposes a JSON HTTP API. It implements the `wallet.Wallet` interface.
137+
- **Mock Server**: The example uses `net/http/httptest` to create a mock HTTP server that simulates the behavior of a real wallet backend for the `/createAction` endpoint. This is useful for testing client integrations without needing a live wallet service.
138+
- **`wallet.CreateActionArgs`**: This struct encapsulates the parameters needed to create a transaction, such as outputs (amount and script), description, and labels.
139+
- **`wallet.CreateActionResult`**: This struct represents the response from a `CreateAction` call, typically including the `Txid` of the created transaction and, if applicable, a `SignableTransaction` object which might contain a reference for further signing steps.
140+
- **Originator**: An identifier for the client application making the wallet requests.
141+
142+
## Additional Resources
143+
144+
- [go-sdk `wallet/substrates` package documentation](https://pkg.go.dev/github.com/bsv-blockchain/go-sdk/wallet/substrates)
145+
- [go-sdk `wallet` package documentation](https://pkg.go.dev/github.com/bsv-blockchain/go-sdk/wallet)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"net/http/httptest"
9+
10+
"github.com/bsv-blockchain/go-sdk/transaction/template/p2pkh"
11+
12+
"github.com/bsv-blockchain/go-sdk/chainhash"
13+
"github.com/bsv-blockchain/go-sdk/script"
14+
"github.com/bsv-blockchain/go-sdk/wallet"
15+
"github.com/bsv-blockchain/go-sdk/wallet/substrates"
16+
)
17+
18+
// Helper to write JSON responses for the mock server
19+
func writeJSONResponse(w http.ResponseWriter, data interface{}) {
20+
w.Header().Set("Content-Type", "application/json")
21+
if err := json.NewEncoder(w).Encode(data); err != nil {
22+
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
23+
}
24+
}
25+
26+
func main() {
27+
originator := "my-app-example"
28+
29+
// Mock server setup
30+
mux := http.NewServeMux()
31+
32+
// Mock CreateAction
33+
var mockCreateActionReference = []byte("mock-tx-reference-123")
34+
mockCreateActionTxId, _ := chainhash.NewHashFromHex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
35+
mux.HandleFunc("/createAction", func(w http.ResponseWriter, r *http.Request) {
36+
var args wallet.CreateActionArgs
37+
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
38+
http.Error(w, "Failed to decode request for createAction", http.StatusBadRequest)
39+
return
40+
}
41+
fmt.Printf("Mock Server: Received CreateAction with Description: %s\n", args.Description)
42+
43+
// Use an anonymous struct for the JSON response to send Txid as a string
44+
jsonResponse := struct {
45+
Txid string `json:"txid"`
46+
SignableTransaction *wallet.SignableTransaction `json:"signableTransaction,omitempty"`
47+
// Add other fields from CreateActionResult if the client expects them (e.g., Tx, NoSendChange)
48+
}{
49+
Txid: mockCreateActionTxId.String(), // Marshal as hex string
50+
SignableTransaction: &wallet.SignableTransaction{
51+
Reference: mockCreateActionReference,
52+
},
53+
}
54+
writeJSONResponse(w, jsonResponse)
55+
})
56+
57+
server := httptest.NewServer(mux)
58+
defer server.Close()
59+
60+
// Use the mock server's URL
61+
baseURL := server.URL
62+
httpClient := server.Client()
63+
64+
w := substrates.NewHTTPWalletJSON(originator, baseURL, httpClient)
65+
ctx := context.Background()
66+
67+
// --- CreateAction ---
68+
fmt.Println("\n--- CreateAction ---")
69+
// Create a P2PKH script for the output
70+
mockToAddress, err := script.NewAddressFromString("mfZQtGMnf2aP17fF3a9TzWMRw2NXp25hN2") // Using a valid testnet address format
71+
if err != nil {
72+
fmt.Printf("Error creating mock address: %v\n", err)
73+
return
74+
}
75+
p2pkhScript, _ := p2pkh.Lock(mockToAddress)
76+
77+
createActionArgs := wallet.CreateActionArgs{
78+
Description: "Test transaction from example",
79+
Outputs: []wallet.CreateActionOutput{
80+
{
81+
Satoshis: 1000, // Amount in satoshis
82+
LockingScript: p2pkhScript.Bytes(),
83+
},
84+
},
85+
Labels: []string{"test", "example"},
86+
}
87+
createActionResult, err := w.CreateAction(ctx, createActionArgs)
88+
if err != nil {
89+
fmt.Printf("Error creating action: %v\n", err)
90+
}
91+
var currentActionReference []byte
92+
if createActionResult != nil {
93+
fmt.Printf("CreateAction Result - TxID: %s\n", createActionResult.Txid.String())
94+
if createActionResult.SignableTransaction != nil {
95+
currentActionReference = createActionResult.SignableTransaction.Reference
96+
fmt.Printf("CreateAction Result - Reference: %s\n", string(currentActionReference))
97+
} else {
98+
fmt.Println("CreateAction Result - No SignableTransaction returned by mock.")
99+
}
100+
} else {
101+
fmt.Println("CreateAction failed or mock returned nil.")
102+
}
103+
104+
}

0 commit comments

Comments
 (0)