-
Notifications
You must be signed in to change notification settings - Fork 101
/
txbuilder.go
263 lines (242 loc) · 8.03 KB
/
txbuilder.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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
package types
import (
"errors"
"fmt"
"strings"
"github.com/pokt-network/pocket-core/crypto"
crkeys "github.com/pokt-network/pocket-core/crypto/keys"
sdk "github.com/pokt-network/pocket-core/types"
"github.com/tendermint/tendermint/libs/rand"
)
// TxBuilder implements a transaction context created in SDK modules.
type TxBuilder struct {
txEncoder sdk.TxEncoder
txDecoder sdk.TxDecoder
keybase crkeys.Keybase
chainID string
memo string
fees sdk.Coins
}
// NewTxBuilder returns a new initialized TxBuilder.
func NewTxBuilder(txEncoder sdk.TxEncoder, txDecoder sdk.TxDecoder, chainID, memo string, fees sdk.Coins) TxBuilder {
return TxBuilder{
txEncoder: txEncoder,
txDecoder: txDecoder,
keybase: nil,
chainID: chainID,
memo: memo,
fees: fees,
}
}
// TxEncoder returns the transaction encoder
func (bldr TxBuilder) TxEncoder() sdk.TxEncoder { return bldr.txEncoder }
// TxEncoder returns the transaction encoder
func (bldr TxBuilder) TxDecoder() sdk.TxDecoder { return bldr.txDecoder }
// Keybase returns the keybase
func (bldr TxBuilder) Keybase() crkeys.Keybase { return bldr.keybase }
// ChainID returns the chain id
func (bldr TxBuilder) ChainID() string { return bldr.chainID }
// Memo returns the memo message
func (bldr TxBuilder) Memo() string { return bldr.memo }
// Fees returns the fees for the transaction
func (bldr TxBuilder) Fees() sdk.Coins { return bldr.fees }
// WithTxEncoder returns a copy of the context with an updated codec.
func (bldr TxBuilder) WithTxEncoder(txEncoder sdk.TxEncoder) TxBuilder {
bldr.txEncoder = txEncoder
return bldr
}
// WithChainID returns a copy of the context with an updated chainID.
func (bldr TxBuilder) WithChainID(chainID string) TxBuilder {
bldr.chainID = chainID
return bldr
}
// WithFees returns a copy of the context with an updated fee.
func (bldr TxBuilder) WithFees(fees string) TxBuilder {
parsedFees, err := sdk.ParseCoins(fees)
if err != nil {
fmt.Println(fmt.Errorf("error adding fees to the tx builder: " + err.Error()))
return bldr
}
bldr.fees = parsedFees
return bldr
}
// WithKeybase returns a copy of the context with updated keybase.
func (bldr TxBuilder) WithKeybase(keybase crkeys.Keybase) TxBuilder {
bldr.keybase = keybase
return bldr
}
// WithMemo returns a copy of the context with an updated memo.
func (bldr TxBuilder) WithMemo(memo string) TxBuilder {
bldr.memo = strings.TrimSpace(memo)
return bldr
}
// BuildAndSign builds a single message to be signed, and signs a transaction
// with the built message given a address, private key, and a set of messages.
func (bldr TxBuilder) BuildAndSign(address sdk.Address, privateKey crypto.PrivateKey, msg sdk.ProtoMsg, legacyCodec bool) ([]byte, error) {
if bldr.chainID == "" {
return nil, errors.New("cant build and sign transaciton: the chainID is empty")
}
entropy := rand.Int64()
bytesToSign, err := StdSignBytes(bldr.chainID, entropy, bldr.fees, msg, bldr.memo)
if err != nil {
return nil, err
}
sigBytes, err := privateKey.Sign(bytesToSign)
if err != nil {
return nil, err
}
sig := StdSignature{
Signature: sigBytes,
PublicKey: privateKey.PublicKey(),
}
if legacyCodec {
return bldr.txEncoder(NewTx(msg, bldr.fees, sig, bldr.memo, entropy), 0)
}
return bldr.txEncoder(NewTx(msg, bldr.fees, sig, bldr.memo, entropy), -1)
}
// BuildAndSignWithEntropyForTesting signs a given message with a given
// private key and entropy.
// This is for testing use only. Use BuildAndSign for production use.
func (bldr TxBuilder) BuildAndSignWithEntropyForTesting(
privateKey crypto.PrivateKey,
msg sdk.ProtoMsg,
entropy int64,
) ([]byte, error) {
if bldr.chainID == "" {
return nil, errors.New("cant build and sign transaciton: the chainID is empty")
}
bytesToSign, err := StdSignBytes(bldr.chainID, entropy, bldr.fees, msg, bldr.memo)
if err != nil {
return nil, err
}
sigBytes, err := privateKey.Sign(bytesToSign)
if err != nil {
return nil, err
}
sig := StdSignature{
Signature: sigBytes,
PublicKey: privateKey.PublicKey(),
}
return bldr.txEncoder(NewTx(msg, bldr.fees, sig, bldr.memo, entropy), -1)
}
// BuildAndSignWithKeyBase builds a single message to be signed, and signs a transaction
// with the built message given a address, passphrase, and a set of messages.
func (bldr TxBuilder) BuildAndSignWithKeyBase(address sdk.Address, passphrase string, msg sdk.ProtoMsg, legacyCodec bool) ([]byte, error) {
if bldr.keybase == nil {
return nil, errors.New("cant build and sign transaciton: the keybase is nil")
}
if bldr.chainID == "" {
return nil, errors.New("cant build and sign transaciton: the chainID is empty")
}
entropy := rand.Int64()
bytesToSign, err := StdSignBytes(bldr.chainID, entropy, bldr.fees, msg, bldr.memo)
if err != nil {
return nil, err
}
sigBytes, pk, err := bldr.keybase.Sign(address, passphrase, bytesToSign)
if err != nil {
return nil, err
}
sig := StdSignature{
Signature: sigBytes,
PublicKey: pk,
}
if legacyCodec {
return bldr.txEncoder(NewTx(msg, bldr.fees, sig, bldr.memo, entropy), 0)
}
return bldr.txEncoder(NewTx(msg, bldr.fees, sig, bldr.memo, entropy), -1)
}
func (bldr TxBuilder) SignMultisigTransaction(address sdk.Address, keys []crypto.PublicKey, passphrase string, txBytes []byte, legacyCodec bool) (signedTx []byte, err error) {
if bldr.keybase == nil {
return nil, errors.New("cant build and sign transaciton: the keybase is nil")
}
if bldr.chainID == "" {
return nil, errors.New("cant build and sign transaciton: the chainID is empty")
}
t := sdk.Tx(nil)
// decode the transaction
if legacyCodec {
t, err = bldr.txDecoder(txBytes, 0)
if err != nil {
return nil, err
}
} else {
t, err = bldr.txDecoder(txBytes, -1)
if err != nil {
return nil, err
}
}
tx := t.(StdTx)
// get the sign bytes from the transaction
bytesToSign, err := StdSignBytes(bldr.chainID, tx.GetEntropy(), tx.GetFee(), tx.GetMsg(), tx.GetMemo())
if err != nil {
return nil, err
}
sigBytes, pubKey, err := bldr.keybase.Sign(address, passphrase, bytesToSign)
if err != nil {
return nil, err
}
// sign using multisignature sturcture
var ms = crypto.MultiSig(crypto.MultiSignature{})
if tx.GetSignature().GetSignature() == nil || len(tx.GetSignature().GetSignature()) == 0 {
ms = ms.NewMultiSignature()
} else {
ms = ms.Unmarshal(tx.GetSignature().GetSignature())
}
if len(keys) != 0 {
ms, err = ms.AddSignature(sigBytes, pubKey, keys)
if err != nil {
return nil, err
}
} else {
ms = ms.AddSignatureByIndex(sigBytes, len(ms.Signatures()))
}
sig := StdSignature{
PublicKey: tx.Signature.PublicKey,
Signature: ms.Marshal(),
}
// replace the old multi-signature with the new multi-signature (containing the additional signature)
tx, err = tx.WithSignature(sig)
if err != nil {
return nil, err
}
if legacyCodec {
return bldr.TxEncoder()(tx, 0)
}
// encode using the standard encoder
return bldr.TxEncoder()(tx, -1)
}
func (bldr TxBuilder) BuildAndSignMultisigTransaction(address sdk.Address, publicKey crypto.PublicKeyMultiSig, m sdk.ProtoMsg, passphrase string, fees int64, legacyCodec bool) (signedTx []byte, err error) {
if bldr.keybase == nil {
return nil, errors.New("cant build and sign transaciton: the keybase is nil")
}
if bldr.chainID == "" {
return nil, errors.New("cant build and sign transaciton: the chainID is empty")
}
// bulid the transaction from scratch
entropy := rand.Int64()
fee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultStakeDenom, sdk.NewInt(fees)))
signBz, err := StdSignBytes(bldr.chainID, entropy, fee, m, bldr.memo)
if err != nil {
return nil, err
}
sigBytes, _, err := bldr.keybase.Sign(address, passphrase, signBz)
if err != nil {
return nil, err
}
// sign using multisignature structure
var ms = crypto.MultiSig(crypto.MultiSignature{})
ms = ms.NewMultiSignature()
ms = ms.AddSignatureByIndex(sigBytes, 0)
sig := StdSignature{
PublicKey: publicKey,
Signature: ms.Marshal(),
}
// create a new standard transaction object
tx := NewTx(m, fee, sig, "", entropy)
// encode it using the default encoder
if legacyCodec {
return bldr.TxEncoder()(tx, 0)
}
return bldr.TxEncoder()(tx, -1)
}