-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathtx.go
More file actions
231 lines (196 loc) · 7.02 KB
/
tx.go
File metadata and controls
231 lines (196 loc) · 7.02 KB
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
package crosschain
import (
"encoding/json"
"reflect"
"github.com/cordialsys/crosschain/call"
)
// TxInput is input data to a tx. Depending on the blockchain it can include nonce, recent block hash, account id, ...
type TxInput interface {
GetDriver() Driver
TxInputConflicts
TxInputGasFeeMultiplier
TxInputGetMaxPossibleFee
}
// For chains/transactions that can benefit from knowing the timestamp
type TxInputWithUnix interface {
SetUnix(int64)
}
// For transactions that come with their own payload, like Calls.
// This may change how conflict resolution works, like on Solana.
type TxInputWithCall interface {
SetCall(call TxCallPayload) error
}
type TxInputGasFeeMultiplier interface {
SetGasFeePriority(priority GasFeePriority) error
}
type TxInputGetMaxPossibleFee interface {
// Get the maximum possible fee that could occur for this transaction.
// This is used to guard against "griefing" attacks where a user is charged way more than they intended.
// The contract address may be "" when the fee is for the native asset, as is often the case..
//
// Note: The caller/user should check this after TxInput has been populated with all other fields, as they can influence
// what the ultimate max fee is.
GetFeeLimit() (AmountBlockchain, ContractAddress)
// Marker to indicate that the gas estimation included in a TxInput
// is accurate to what will actually be paid.
//
// For instance, if the chain is blindly setting a high gas price "limit", and in
// reality the user will only pay a fraction of that, then this should return false.
//
// If the fee is actually accurate, then we can rely on it for inclusive fee spending behavior.
// The user can opt to "send max balance" and we'll deduct the fee estimation from the current balance
// for the transaction amount. We want to know if this is accurate or we can avoid:
// - Leaving a high leftover balance
// - Transaction reverting due to fee-estimate being too low
IsFeeLimitAccurate() bool
}
// This interface determines whether if different tx inputs conflict with one another.
type TxInputConflicts interface {
// Test independence of two tx-inputs, assuming the same address is used.
// Examples:
// - using the same nonce or sequence is NOT independent
// - spending the same resources or utxo's is NOT independent
// - solana (using recent_block_hash) is pretty much always independent
// This is used to determine if a transaction needs to be queued or if it can be immediately signed & broadcasted.
IndependentOf(other TxInput) (independent bool)
// Test if tx-input could possibly result in a "double-send" given the history of past attempts.
// A double send is a user re-signing their transaction (to overcome a timeout or use new fees), but then risk multiple transactions
// landing on chain. A valid re-sign should only occur if it's only possible for one transaction to land out of the total set of attempts.
// Examples:
// - Solana typically has no conflicts, but need to ensure (a) new blockhash is used, and (b) sufficient time has passed
// to be sure a double send won't occur (return `true`).
// - If tx-inputs are not independent (spending same resources), then typically double-sends are impossible (and should return `true` here).
// - If there exists tx-inputs that are fully independent (and not timed out), then a double-send is possible and this should return false.
SafeFromDoubleSend(previousAttempts TxInput) (safe bool)
}
func IsTypeOf(input TxInput, validTypes ...any) bool {
if input == nil {
return false
}
for _, validType := range validTypes {
if reflect.TypeOf(input) == reflect.TypeOf(validType) {
return true
}
}
return false
}
type TxInputEnvelope struct {
Type Driver `json:"type"`
}
func NewTxInputEnvelope(envType Driver) *TxInputEnvelope {
return &TxInputEnvelope{
Type: envType,
}
}
type TxVariantInput interface {
TxInput
GetVariant() TxVariantInputType
}
// Markers for each type of Variant Tx
type MultiTransferInput interface {
TxVariantInput
MultiTransfer()
}
type StakeTxInput interface {
TxVariantInput
Staking()
}
type UnstakeTxInput interface {
TxVariantInput
Unstaking()
}
type WithdrawTxInput interface {
TxVariantInput
Withdrawing()
}
type CallTxInput interface {
TxVariantInput
Calling()
}
// TxStatus is the status of a tx on chain, currently success or failure.
type TxStatus uint8
// TxStatus values
const (
TxStatusSuccess TxStatus = 0
TxStatusFailure TxStatus = 1
)
// TxHash is a tx hash or id
type TxHash string
// TxSignature is a tx signature
type TxSignature []byte
// NewTxSignatures creates a new array of TxSignature, useful to cast [][]byte into []TxSignature
func NewTxSignatures(data [][]byte) []TxSignature {
ret := make([]TxSignature, len(data))
for i, sig := range data {
ret[i] = TxSignature(sig)
}
return ret
}
type SignatureRequest struct {
// Required: The payload to sign
Payload []byte
// May be optionally set, if different from the from-address.
Signer Address
}
func NewSignatureRequest(payload []byte, signerMaybe ...Address) *SignatureRequest {
signer := Address("")
if len(signerMaybe) > 0 {
signer = signerMaybe[0]
}
return &SignatureRequest{
Payload: payload,
Signer: signer,
}
}
type SignatureResponse struct {
// Signaure of the payload
Signature TxSignature
// Address + public key of the signer
PublicKey []byte
Address Address
}
// Tx is a transaction
type Tx interface {
Hash() TxHash
Sighashes() ([]*SignatureRequest, error)
SetSignatures(...*SignatureResponse) error
Serialize() ([]byte, error)
}
type TxWithMetadata interface {
// If the client SubmitTx needs additional metadata, this can be used to define it.
GetMetadata() ([]byte, bool, error)
}
type TxLegacyGetSignatures interface {
// Replaced by TxWithMetadata.GetMetadata()
GetSignatures() []TxSignature
}
type TxAdditionalSighashes interface {
// This is available in case a transaction needs to make signatures-over-signatures.
// This should return any _remaining_ signatures requests left to fill.
// The caller will always call .SetSignatures() after this with all of the signature made so far.
AdditionalSighashes() ([]*SignatureRequest, error)
}
type TxCall interface {
Tx
// Set transaction input. This may not be needed, but could be used to adjust:
// * fees
// * other dynamic chain information not included in the origin Call message
SetInput(input CallTxInput) error
// List of addresses that may be needed to sign
SigningAddresses() []Address
// List of 3rd party contract addresses that this Call resource interacts with
// (omit native system contracts)
ContractAddresses() []ContractAddress
// Get original serialized message that Call was constructed with
GetMsg() json.RawMessage
// Get the method of the call
GetMethod() call.Method
// Indicate if it's possible & safe to retry the call transaction.
// Meaning, can we re-sign the transaction with the a new input?
IsRetryable() (ok bool, reason string)
// Used if the tx-input needs to be updated retro-actively, like on Solana.
GetPayload() (TxCallPayload, bool)
}
type TxCallPayload interface {
IsTxCallPayload()
}