/
tx.go
164 lines (131 loc) · 4.45 KB
/
tx.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package compatibility
import (
"fmt"
"io/ioutil"
"net/http"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
)
// prepareFactory ensures the account defined by ctx.GetFromAddress() exists and
// if the account number and/or the account sequence number are zero (not set),
// they will be queried for and set on the provided Factory. A new Factory with
// the updated fields will be returned.
// Deprecated: This function as made private in the Cosmos SDK, however it seems we have custom uses cases that still require this function. Please find alternative.
func PrepareFactory(clientCtx client.Context, txf tx.Factory) (tx.Factory, error) {
from := clientCtx.GetFromAddress()
if err := txf.AccountRetriever().EnsureExists(clientCtx, from); err != nil {
return txf, err
}
initNum, initSeq := txf.AccountNumber(), txf.Sequence()
if initNum == 0 || initSeq == 0 {
num, seq, err := txf.AccountRetriever().GetAccountNumberSequence(clientCtx, from)
if err != nil {
return txf, err
}
if initNum == 0 {
txf = txf.WithAccountNumber(num)
}
if initSeq == 0 {
txf = txf.WithSequence(seq)
}
}
return txf, nil
}
type BroadcastReq struct {
Tx legacytx.StdTx `json:"tx" yaml:"tx"`
Mode string `json:"mode" yaml:"mode"`
}
// CopyTx copies a Tx to a new TxBuilder, allowing conversion between
// different transaction formats. If ignoreSignatureError is true, copying will continue
// tx even if the signature cannot be set in the target builder resulting in an unsigned tx.
func CopyTx(tx signing.Tx, builder client.TxBuilder, ignoreSignatureError bool) error {
err := builder.SetMsgs(tx.GetMsgs()...)
if err != nil {
return err
}
sigs, err := tx.GetSignaturesV2()
if err != nil {
return err
}
err = builder.SetSignatures(sigs...)
if err != nil {
if ignoreSignatureError {
// we call SetSignatures() agan with no args to clear any signatures in case the
// previous call to SetSignatures() had any partial side-effects
_ = builder.SetSignatures()
} else {
return err
}
}
builder.SetMemo(tx.GetMemo())
builder.SetFeeAmount(tx.GetFee())
builder.SetGasLimit(tx.GetGas())
builder.SetTimeoutHeight(tx.GetTimeoutHeight())
return nil
}
func ConvertTxToStdTx(codec *codec.LegacyAmino, tx signing.Tx) (legacytx.StdTx, error) {
if stdTx, ok := tx.(legacytx.StdTx); ok {
return stdTx, nil
}
aminoTxConfig := legacytx.StdTxConfig{Cdc: codec}
builder := aminoTxConfig.NewTxBuilder()
err := CopyTx(tx, builder, true)
if err != nil {
return legacytx.StdTx{}, err
}
stdTx, ok := builder.GetTx().(legacytx.StdTx)
if !ok {
return legacytx.StdTx{}, fmt.Errorf("expected %T, got %+v", legacytx.StdTx{}, builder.GetTx())
}
return stdTx, nil
}
func ConvertAndEncodeStdTx(txConfig client.TxConfig, stdTx legacytx.StdTx) ([]byte, error) {
builder := txConfig.NewTxBuilder()
var theTx sdk.Tx
// check if we need a StdTx anyway, in that case don't copy
if _, ok := builder.GetTx().(legacytx.StdTx); ok {
theTx = stdTx
} else {
err := CopyTx(stdTx, builder, false)
if err != nil {
return nil, err
}
theTx = builder.GetTx()
}
return txConfig.TxEncoder()(theTx)
}
func BroadcastTxRequest(clientCtx client.Context) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req BroadcastReq
body, err := ioutil.ReadAll(r.Body)
if rest.CheckBadRequestError(w, err) {
return
}
// NOTE: amino is used intentionally here, don't migrate it!
err = clientCtx.LegacyAmino.UnmarshalJSON(body, &req)
if err != nil {
err := fmt.Errorf("this transaction cannot be broadcasted via legacy REST endpoints, because it does not support" +
" Amino serialization. Please either use CLI, gRPC, gRPC-gateway, or directly query the Tendermint RPC" +
" endpoint to broadcast this transaction. The new REST endpoint (via gRPC-gateway) is POST /cosmos/tx/v1beta1/txs",
)
if rest.CheckBadRequestError(w, err) {
return
}
}
txBytes, err := ConvertAndEncodeStdTx(clientCtx.TxConfig, req.Tx)
if rest.CheckInternalServerError(w, err) {
return
}
clientCtx = clientCtx.WithBroadcastMode(req.Mode)
res, err := clientCtx.BroadcastTx(txBytes)
if rest.CheckInternalServerError(w, err) {
return
}
rest.PostProcessResponseBare(w, clientCtx, res)
}
}