github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/flow/transaction.go (about)

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