-
Notifications
You must be signed in to change notification settings - Fork 0
/
tx-provider.go
138 lines (114 loc) · 3.93 KB
/
tx-provider.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
package tx
import (
"context"
"github.com/tessellated-io/pickaxe/crypto"
"github.com/tessellated-io/pickaxe/log"
"github.com/cosmos/cosmos-sdk/client"
cosmostx "github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
)
type TxProvider interface {
ProvideTx(ctx context.Context, gasPrice, gasFactor float64, messages []sdk.Msg, metadata *SigningMetadata) ([]byte, int64, error)
}
// txProvider is the default implementation of the Signer interface
type txProvider struct {
bytesSigner crypto.BytesSigner
feeDenom string
memo string
logger *log.Logger
simulationManager SimulationManager
txConfig client.TxConfig
txFactory cosmostx.Factory
}
// Assert type conformance
var _ TxProvider = (*txProvider)(nil)
func NewTxProvider(bytesSigner crypto.BytesSigner, chainID, feeDenom, memo string, logger *log.Logger, simulationManager SimulationManager, txConfig client.TxConfig) (TxProvider, error) {
txFactory := cosmostx.Factory{}.WithChainID(chainID).WithTxConfig(txConfig)
return &txProvider{
bytesSigner: bytesSigner,
feeDenom: feeDenom,
memo: memo,
logger: logger,
simulationManager: simulationManager,
txConfig: txConfig,
txFactory: txFactory,
}, nil
}
// Signer Interface
// Sign returns the set of messages, encoded with metadata, and includes a valid signature.
// It also includes the gas that was desired. This API is kinda nuts, but I can't find a sane way around it.
func (txp *txProvider) ProvideTx(ctx context.Context, gasPrice, gasFactor float64, messages []sdk.Msg, metadata *SigningMetadata) ([]byte, int64, error) {
txp.logger.Debug().Str("chain_id", metadata.chainID).Str("account", metadata.address).Uint64("sequence", metadata.sequence).Uint64("account_number", metadata.accountNumber).Msg("preparing to sign transaction")
// Build a transaction
txb, err := txp.txFactory.BuildUnsignedTx(messages...)
if err != nil {
return nil, 0, err
}
txb.SetMemo(txp.memo)
signatureProto := signing.SignatureV2{
PubKey: txp.bytesSigner.GetPublicKey(),
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
Signature: nil,
},
Sequence: metadata.Sequence(),
}
err = txb.SetSignatures(signatureProto)
if err != nil {
return nil, 0, err
}
// Simulate the tx
simulationResult, err := txp.simulationManager.SimulateTx(ctx, txb.GetTx(), gasFactor)
if err != nil {
return nil, 0, err
}
txp.logger.Debug().Int64("gas_units", simulationResult.GasRecommendation).Msg("simulated gas")
txb.SetGasLimit(uint64(simulationResult.GasRecommendation))
fee := []sdk.Coin{
{
Denom: txp.feeDenom,
Amount: sdk.NewInt(int64(gasPrice*float64(simulationResult.GasRecommendation)) + 1),
},
}
txb.SetFeeAmount(fee)
// Shim metadata into the format Cosmos SDK wants
signerData := authsigning.SignerData{
ChainID: metadata.ChainID(),
Sequence: metadata.Sequence(),
AccountNumber: metadata.AccountNumber(),
}
// Encode to bytes to sign
signMode := signing.SignMode_SIGN_MODE_DIRECT
unsignedTxBytes, err := txp.txConfig.SignModeHandler().GetSignBytes(signMode, signerData, txb.GetTx())
if err != nil {
return nil, 0, err
}
// Sign the bytes
signatureBytes, err := txp.bytesSigner.SignBytes(unsignedTxBytes)
if err != nil {
return nil, 0, err
}
// Reconstruct the signature proto
signatureData := &signing.SingleSignatureData{
SignMode: signMode,
Signature: signatureBytes,
}
signatureProto = signing.SignatureV2{
PubKey: txp.bytesSigner.GetPublicKey(),
Data: signatureData,
Sequence: metadata.Sequence(),
}
err = txb.SetSignatures(signatureProto)
if err != nil {
return []byte{}, 0, err
}
// Encode to bytes
encoder := txp.txConfig.TxEncoder()
txBytes, err := encoder(txb.GetTx())
if err != nil {
return nil, 0, err
}
return txBytes, simulationResult.GasRecommendation, nil
}