-
Notifications
You must be signed in to change notification settings - Fork 2
/
broadcast.go
127 lines (102 loc) · 3.19 KB
/
broadcast.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package sacco
//go:generate stringer -type=TxMode
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strconv"
"github.com/cosmos/cosmos-sdk/codec"
)
// TxMode identifies when an LCD should replies to a client
// after a transaction broadcast.
type TxMode string
// String implements the stringer interface for TxMode.
func (txm TxMode) String() string {
return string(txm)
}
const (
// ModeAsync waits for the tx to pass/fail CheckTx
ModeAsync TxMode = "async"
// ModeSync doesn't wait for pass/fail CheckTx and send and return tx immediately
ModeSync TxMode = "sync"
// ModeBlock waits for the tx to pass/fail CheckTx, DeliverTx, and be committed in a block (not recommended, slow)
ModeBlock TxMode = "block"
)
// broadcastTx broadcasts a tx to the Cosmos LCD identified by lcdEndpoint.
func broadcastTx(tx SignedTransactionPayload, lcdEndpoint string, txMode TxMode) (string, error) {
endpoint := fmt.Sprintf("%s/txs", lcdEndpoint)
// assemble a tx transaction
txBody := TxBody{
Tx: tx,
Mode: txMode.String(),
}
cdc := codec.New()
requestBody, err := cdc.MarshalJSON(txBody)
if err != nil {
return "", err
}
// send tx to lcdEndpoint
resp, err := http.Post(endpoint, "application/json", bytes.NewBuffer(requestBody))
if err != nil {
return "", err
}
defer resp.Body.Close()
// we had a serious problem with the LCD request,
// decode the json error and return its value to caller
if resp.StatusCode != http.StatusOK {
var jerr Error
jd := json.NewDecoder(resp.Body)
err := jd.Decode(&jerr)
if err != nil {
return "", fmt.Errorf("could not process error json decoding: %w", err)
}
return "", fmt.Errorf("error while processing tx send request: %s", jerr.Error)
}
// deserialize LCD response into a cosmos TxResponse
var txr TxResponse
jdec := json.NewDecoder(resp.Body)
err = jdec.Decode(&txr)
if err != nil {
return "", fmt.Errorf("could not deserialize cosmos txresponse from lcd: %w", err)
}
if txr.Code != 0 {
return "", fmt.Errorf(
"codespace %s: %s, code %d",
txr.Codespace,
txr.RawLog,
txr.Code,
)
}
return txr.TxHash, nil
}
// SignAndBroadcast signs tx and broadcast it to the LCD specified by lcdEndpoint.
func (w *Wallet) SignAndBroadcast(tx TransactionPayload, lcdEndpoint string, txMode TxMode) (string, error) {
// get network (chain) name
nodeInfo, err := getNodeInfo(lcdEndpoint)
if err != nil {
return "", fmt.Errorf("could not get LCD node informations: %w", err)
}
// get account sequence and account number
accountData, err := getAccountData(lcdEndpoint, w.Address)
if err != nil {
return "", fmt.Errorf("could not get Account informations for address %s: %w", w.Address, err)
}
// sign transaction
signedTx, err := w.Sign(
tx,
nodeInfo.Info.Network,
strconv.FormatInt(accountData.Result.Value.AccountNumber, 10),
strconv.FormatInt(accountData.Result.Value.Sequence, 10),
)
if err != nil {
return "", fmt.Errorf("could not sign transaction: %w", err)
}
// broadcast transaction to the LCD
txHash, err := broadcastTx(signedTx, lcdEndpoint, txMode)
if err != nil {
return "", fmt.Errorf("could not broadcast transaction to the Cosmos network: %w", err)
}
// return transaction hash!
return txHash, nil
}