github.com/koko1123/flow-go-1@v0.29.6/model/flow/transaction.go (about) 1 package flow 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/koko1123/flow-go-1/model/fingerprint" 8 "github.com/onflow/flow-go/crypto" 9 "github.com/onflow/flow-go/crypto/hash" 10 ) 11 12 // TransactionBody includes the main contents of a transaction 13 type TransactionBody struct { 14 15 // A reference to a previous block 16 // A transaction is expired after specific number of blocks (defined by network) counting from this block 17 // for example, if block reference is pointing to a block with height of X and network limit is 10, 18 // a block with x+10 height is the last block that is allowed to include this transaction. 19 // user can adjust this reference to older blocks if he/she wants to make tx expire faster 20 ReferenceBlockID Identifier 21 22 // the transaction script as UTF-8 encoded Cadence source code 23 Script []byte 24 25 // arguments passed to the Cadence transaction 26 Arguments [][]byte 27 28 // Max amount of computation which is allowed to be done during this transaction 29 GasLimit uint64 30 31 // Account key used to propose the transaction 32 ProposalKey ProposalKey 33 34 // Account that pays for this transaction fees 35 Payer Address 36 37 // A ordered (ascending) list of addresses that scripts will touch their assets (including payer address) 38 // Accounts listed here all have to provide signatures 39 // Each account might provide multiple signatures (sum of weight should be at least 1) 40 // If code touches accounts that is not listed here, tx fails 41 Authorizers []Address 42 43 // List of account signatures excluding signature of the payer account 44 PayloadSignatures []TransactionSignature 45 46 // payer signature over the envelope (payload + payload signatures) 47 EnvelopeSignatures []TransactionSignature 48 } 49 50 // NewTransactionBody initializes and returns an empty transaction body 51 func NewTransactionBody() *TransactionBody { 52 return &TransactionBody{} 53 } 54 55 func (tb TransactionBody) Fingerprint() []byte { 56 return fingerprint.Fingerprint(struct { 57 Payload interface{} 58 PayloadSignatures interface{} 59 EnvelopeSignatures interface{} 60 }{ 61 Payload: tb.payloadCanonicalForm(), 62 PayloadSignatures: signaturesList(tb.PayloadSignatures).canonicalForm(), 63 EnvelopeSignatures: signaturesList(tb.EnvelopeSignatures).canonicalForm(), 64 }) 65 } 66 67 func (tb TransactionBody) ByteSize() uint { 68 size := 0 69 size += len(tb.ReferenceBlockID) 70 size += len(tb.Script) 71 for _, arg := range tb.Arguments { 72 size += len(arg) 73 } 74 size += 8 // gas size 75 size += tb.ProposalKey.ByteSize() 76 size += AddressLength // payer address 77 size += len(tb.Authorizers) * AddressLength // Authorizers 78 for _, s := range tb.PayloadSignatures { 79 size += s.ByteSize() 80 } 81 for _, s := range tb.EnvelopeSignatures { 82 size += s.ByteSize() 83 } 84 return uint(size) 85 } 86 87 // InclusionEffort returns the inclusion effort of the transaction 88 func (tb TransactionBody) InclusionEffort() uint64 { 89 // Hardcoded inclusion effort (of 1.0 UFix). 90 // Eventually this will be dynamic and will depend on the transaction properties 91 inclusionEffort := uint64(100_000_000) 92 return inclusionEffort 93 } 94 95 func (tb TransactionBody) ID() Identifier { 96 return MakeID(tb) 97 } 98 99 func (tb TransactionBody) Checksum() Identifier { 100 return MakeID(tb) 101 } 102 103 // SetScript sets the Cadence script for this transaction. 104 func (tb *TransactionBody) SetScript(script []byte) *TransactionBody { 105 tb.Script = script 106 return tb 107 } 108 109 // SetArguments sets the Cadence arguments list for this transaction. 110 func (tb *TransactionBody) SetArguments(args [][]byte) *TransactionBody { 111 tb.Arguments = args 112 return tb 113 } 114 115 // AddArgument adds an argument to the Cadence arguments list for this transaction. 116 func (tb *TransactionBody) AddArgument(arg []byte) *TransactionBody { 117 tb.Arguments = append(tb.Arguments, arg) 118 return tb 119 } 120 121 // SetReferenceBlockID sets the reference block ID for this transaction. 122 func (tb *TransactionBody) SetReferenceBlockID(blockID Identifier) *TransactionBody { 123 tb.ReferenceBlockID = blockID 124 return tb 125 } 126 127 // SetGasLimit sets the gas limit for this transaction. 128 func (tb *TransactionBody) SetGasLimit(limit uint64) *TransactionBody { 129 tb.GasLimit = limit 130 return tb 131 } 132 133 // SetProposalKey sets the proposal key and sequence number for this transaction. 134 // 135 // The first two arguments specify the account key to be used, and the last argument is the sequence 136 // number being declared. 137 func (tb *TransactionBody) SetProposalKey(address Address, keyID uint64, sequenceNum uint64) *TransactionBody { 138 proposalKey := ProposalKey{ 139 Address: address, 140 KeyIndex: keyID, 141 SequenceNumber: sequenceNum, 142 } 143 tb.ProposalKey = proposalKey 144 return tb 145 } 146 147 // SetPayer sets the payer account for this transaction. 148 func (tb *TransactionBody) SetPayer(address Address) *TransactionBody { 149 tb.Payer = address 150 return tb 151 } 152 153 // AddAuthorizer adds an authorizer account to this transaction. 154 func (tb *TransactionBody) AddAuthorizer(address Address) *TransactionBody { 155 tb.Authorizers = append(tb.Authorizers, address) 156 return tb 157 } 158 159 // Transaction is the smallest unit of task. 160 type Transaction struct { 161 TransactionBody 162 Status TransactionStatus 163 Events []Event 164 ComputationSpent uint64 165 StartState StateCommitment 166 EndState StateCommitment 167 } 168 169 // MissingFields checks if a transaction is missing any required fields and returns those that are missing. 170 func (tb *TransactionBody) MissingFields() []string { 171 // Required fields are Script, ReferenceBlockID, Payer 172 missingFields := make([]string, 0) 173 174 if len(tb.Script) == 0 { 175 missingFields = append(missingFields, TransactionFieldScript.String()) 176 } 177 178 if tb.ReferenceBlockID == ZeroID { 179 missingFields = append(missingFields, TransactionFieldRefBlockID.String()) 180 } 181 182 if tb.Payer == EmptyAddress { 183 missingFields = append(missingFields, TransactionFieldPayer.String()) 184 } 185 186 return missingFields 187 } 188 189 // signerList returns a list of unique accounts required to sign this transaction. 190 // 191 // The list is returned in the following order: 192 // 1. PROPOSER 193 // 2. PAYER 194 // 2. AUTHORIZERS (in insertion order) 195 // 196 // The only exception to the above ordering is for deduplication; if the same account 197 // is used in multiple signing roles, only the first occurrence is included in the list. 198 func (tb *TransactionBody) signerList() []Address { 199 signers := make([]Address, 0) 200 seen := make(map[Address]struct{}) 201 202 var addSigner = func(address Address) { 203 _, ok := seen[address] 204 if ok { 205 return 206 } 207 208 signers = append(signers, address) 209 seen[address] = struct{}{} 210 } 211 212 if tb.ProposalKey.Address != EmptyAddress { 213 addSigner(tb.ProposalKey.Address) 214 } 215 216 if tb.Payer != EmptyAddress { 217 addSigner(tb.Payer) 218 } 219 220 for _, authorizer := range tb.Authorizers { 221 addSigner(authorizer) 222 } 223 224 return signers 225 } 226 227 // signerMap returns a mapping from address to signer index. 228 func (tb *TransactionBody) signerMap() map[Address]int { 229 signers := make(map[Address]int) 230 231 for i, signer := range tb.signerList() { 232 signers[signer] = i 233 } 234 235 return signers 236 } 237 238 // SignPayload signs the transaction payload (TransactionDomainTag + payload) with the specified account key using the default transaction domain tag. 239 // 240 // The resulting signature is combined with the account address and key ID before 241 // being added to the transaction. 242 // 243 // This function returns an error if the signature cannot be generated. 244 func (tb *TransactionBody) SignPayload( 245 address Address, 246 keyID uint64, 247 privateKey crypto.PrivateKey, 248 hasher hash.Hasher, 249 ) error { 250 sig, err := tb.Sign(tb.PayloadMessage(), privateKey, hasher) 251 252 if err != nil { 253 return fmt.Errorf("failed to sign transaction payload with given key: %w", err) 254 } 255 256 tb.AddPayloadSignature(address, keyID, sig) 257 258 return nil 259 } 260 261 // SignEnvelope signs the full transaction (TransactionDomainTag + payload + payload signatures) with the specified account key using the default transaction domain tag. 262 // 263 // The resulting signature is combined with the account address and key ID before 264 // being added to the transaction. 265 // 266 // This function returns an error if the signature cannot be generated. 267 func (tb *TransactionBody) SignEnvelope( 268 address Address, 269 keyID uint64, 270 privateKey crypto.PrivateKey, 271 hasher hash.Hasher, 272 ) error { 273 sig, err := tb.Sign(tb.EnvelopeMessage(), privateKey, hasher) 274 275 if err != nil { 276 return fmt.Errorf("failed to sign transaction envelope with given key: %w", err) 277 } 278 279 tb.AddEnvelopeSignature(address, keyID, sig) 280 281 return nil 282 } 283 284 // Sign signs the data (transaction_tag + message) with the specified private key 285 // and hasher. 286 // 287 // This function returns an error if: 288 // - crypto.InvalidInputsError if the private key cannot sign with the given hasher 289 // - other error if an unexpected error occurs 290 func (tb *TransactionBody) Sign( 291 message []byte, 292 privateKey crypto.PrivateKey, 293 hasher hash.Hasher, 294 ) ([]byte, error) { 295 message = append(TransactionDomainTag[:], message...) 296 sig, err := privateKey.Sign(message, hasher) 297 if err != nil { 298 return nil, fmt.Errorf("failed to sign message with given key: %w", err) 299 } 300 301 return sig, nil 302 } 303 304 // AddPayloadSignature adds a payload signature to the transaction for the given address and key ID. 305 func (tb *TransactionBody) AddPayloadSignature(address Address, keyID uint64, sig []byte) *TransactionBody { 306 s := tb.createSignature(address, keyID, sig) 307 308 tb.PayloadSignatures = append(tb.PayloadSignatures, s) 309 sort.Slice(tb.PayloadSignatures, compareSignatures(tb.PayloadSignatures)) 310 311 return tb 312 } 313 314 // AddEnvelopeSignature adds an envelope signature to the transaction for the given address and key ID. 315 func (tb *TransactionBody) AddEnvelopeSignature(address Address, keyID uint64, sig []byte) *TransactionBody { 316 s := tb.createSignature(address, keyID, sig) 317 318 tb.EnvelopeSignatures = append(tb.EnvelopeSignatures, s) 319 sort.Slice(tb.EnvelopeSignatures, compareSignatures(tb.EnvelopeSignatures)) 320 321 return tb 322 } 323 324 func (tb *TransactionBody) createSignature(address Address, keyID uint64, sig []byte) TransactionSignature { 325 signerIndex, signerExists := tb.signerMap()[address] 326 if !signerExists { 327 signerIndex = -1 328 } 329 330 return TransactionSignature{ 331 Address: address, 332 SignerIndex: signerIndex, 333 KeyIndex: keyID, 334 Signature: sig, 335 } 336 } 337 338 func (tb *TransactionBody) PayloadMessage() []byte { 339 return fingerprint.Fingerprint(tb.payloadCanonicalForm()) 340 } 341 342 func (tb *TransactionBody) payloadCanonicalForm() interface{} { 343 authorizers := make([][]byte, len(tb.Authorizers)) 344 for i, auth := range tb.Authorizers { 345 authorizers[i] = auth.Bytes() 346 } 347 348 return struct { 349 Script []byte 350 Arguments [][]byte 351 ReferenceBlockID []byte 352 GasLimit uint64 353 ProposalKeyAddress []byte 354 ProposalKeyID uint64 355 ProposalKeySequenceNumber uint64 356 Payer []byte 357 Authorizers [][]byte 358 }{ 359 Script: tb.Script, 360 Arguments: tb.Arguments, 361 ReferenceBlockID: tb.ReferenceBlockID[:], 362 GasLimit: tb.GasLimit, 363 ProposalKeyAddress: tb.ProposalKey.Address.Bytes(), 364 ProposalKeyID: tb.ProposalKey.KeyIndex, 365 ProposalKeySequenceNumber: tb.ProposalKey.SequenceNumber, 366 Payer: tb.Payer.Bytes(), 367 Authorizers: authorizers, 368 } 369 } 370 371 // EnvelopeMessage returns the signable message for transaction envelope. 372 // 373 // This message is only signed by the payer account. 374 func (tb *TransactionBody) EnvelopeMessage() []byte { 375 return fingerprint.Fingerprint(tb.envelopeCanonicalForm()) 376 } 377 378 func (tb *TransactionBody) envelopeCanonicalForm() interface{} { 379 return struct { 380 Payload interface{} 381 PayloadSignatures interface{} 382 }{ 383 tb.payloadCanonicalForm(), 384 signaturesList(tb.PayloadSignatures).canonicalForm(), 385 } 386 } 387 388 func (tx *Transaction) PayloadMessage() []byte { 389 return fingerprint.Fingerprint(tx.TransactionBody.payloadCanonicalForm()) 390 } 391 392 // Checksum provides a cryptographic commitment for a chunk content 393 func (tx *Transaction) Checksum() Identifier { 394 return MakeID(tx) 395 } 396 397 func (tx *Transaction) String() string { 398 return fmt.Sprintf("Transaction %v submitted by %v (block %v)", 399 tx.ID(), tx.Payer.Hex(), tx.ReferenceBlockID) 400 } 401 402 // TransactionStatus represents the status of a transaction. 403 type TransactionStatus int 404 405 const ( 406 // TransactionStatusUnknown indicates that the transaction status is not known. 407 TransactionStatusUnknown TransactionStatus = iota 408 // TransactionStatusPending is the status of a pending transaction. 409 TransactionStatusPending 410 // TransactionStatusFinalized is the status of a finalized transaction. 411 TransactionStatusFinalized 412 // TransactionStatusExecuted is the status of an executed transaction. 413 TransactionStatusExecuted 414 // TransactionStatusSealed is the status of a sealed transaction. 415 TransactionStatusSealed 416 // TransactionStatusExpired is the status of an expired transaction. 417 TransactionStatusExpired 418 ) 419 420 // String returns the string representation of a transaction status. 421 func (s TransactionStatus) String() string { 422 return [...]string{"UNKNOWN", "PENDING", "FINALIZED", "EXECUTED", "SEALED", "EXPIRED"}[s] 423 } 424 425 // TransactionField represents a required transaction field. 426 type TransactionField int 427 428 const ( 429 TransactionFieldUnknown TransactionField = iota 430 TransactionFieldScript 431 TransactionFieldRefBlockID 432 TransactionFieldPayer 433 ) 434 435 // String returns the string representation of a transaction field. 436 func (f TransactionField) String() string { 437 return [...]string{"Unknown", "Script", "ReferenceBlockID", "Payer"}[f] 438 } 439 440 // A ProposalKey is the key that specifies the proposal key and sequence number for a transaction. 441 type ProposalKey struct { 442 Address Address 443 KeyIndex uint64 444 SequenceNumber uint64 445 } 446 447 // ByteSize returns the byte size of the proposal key 448 func (p ProposalKey) ByteSize() int { 449 keyIDLen := 8 450 sequenceNumberLen := 8 451 return len(p.Address) + keyIDLen + sequenceNumberLen 452 } 453 454 // A TransactionSignature is a signature associated with a specific account key. 455 type TransactionSignature struct { 456 Address Address 457 SignerIndex int 458 KeyIndex uint64 459 Signature []byte 460 } 461 462 // String returns the string representation of a transaction signature. 463 func (s TransactionSignature) String() string { 464 return fmt.Sprintf("Address: %s. SignerIndex: %d. KeyID: %d. Signature: %s", 465 s.Address, s.SignerIndex, s.KeyIndex, s.Signature) 466 } 467 468 // ByteSize returns the byte size of the transaction signature 469 func (s TransactionSignature) ByteSize() int { 470 signerIndexLen := 8 471 keyIDLen := 8 472 return len(s.Address) + signerIndexLen + keyIDLen + len(s.Signature) 473 } 474 475 func (s TransactionSignature) Fingerprint() []byte { 476 return fingerprint.Fingerprint(s.canonicalForm()) 477 } 478 479 func (s TransactionSignature) canonicalForm() interface{} { 480 return struct { 481 SignerIndex uint 482 KeyID uint 483 Signature []byte 484 }{ 485 SignerIndex: uint(s.SignerIndex), // int is not RLP-serializable 486 KeyID: uint(s.KeyIndex), // int is not RLP-serializable 487 Signature: s.Signature, 488 } 489 } 490 491 func compareSignatures(signatures []TransactionSignature) func(i, j int) bool { 492 return func(i, j int) bool { 493 sigA := signatures[i] 494 sigB := signatures[j] 495 496 if sigA.SignerIndex == sigB.SignerIndex { 497 return sigA.KeyIndex < sigB.KeyIndex 498 } 499 500 return sigA.SignerIndex < sigB.SignerIndex 501 } 502 } 503 504 type signaturesList []TransactionSignature 505 506 func (s signaturesList) canonicalForm() interface{} { 507 signatures := make([]interface{}, len(s)) 508 509 for i, signature := range s { 510 signatures[i] = signature.canonicalForm() 511 } 512 513 return signatures 514 }