diff --git a/account/account.go b/account/account.go index 2dc00bc2..75c289c9 100644 --- a/account/account.go +++ b/account/account.go @@ -5,6 +5,7 @@ import ( "errors" "time" + "github.com/NethermindEth/juno/core/crypto" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/starknet.go/curve" "github.com/NethermindEth/starknet.go/hash" @@ -30,7 +31,7 @@ var ( type AccountInterface interface { Sign(ctx context.Context, msg *felt.Felt) ([]*felt.Felt, error) TransactionHashInvoke(invokeTxn rpc.InvokeTxnType) (*felt.Felt, error) - TransactionHashDeployAccount(tx rpc.DeployAccountTxn, contractAddress *felt.Felt) (*felt.Felt, error) + TransactionHashDeployAccount(tx rpc.DeployAccountType, contractAddress *felt.Felt) (*felt.Felt, error) TransactionHashDeclare(tx rpc.DeclareTxnType) (*felt.Felt, error) SignInvokeTransaction(ctx context.Context, tx *rpc.InvokeTxnV1) error SignDeployAccountTransaction(ctx context.Context, tx *rpc.DeployAccountTxn, precomputeAddress *felt.Felt) error @@ -171,37 +172,65 @@ func (account *Account) SignDeclareTransaction(ctx context.Context, tx *rpc.Decl // Returns: // - *felt.Felt: the calculated transaction hash // - error: an error if any -func (account *Account) TransactionHashDeployAccount(tx rpc.DeployAccountTxn, contractAddress *felt.Felt) (*felt.Felt, error) { +func (account *Account) TransactionHashDeployAccount(tx rpc.DeployAccountType, contractAddress *felt.Felt) (*felt.Felt, error) { // https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/transactions/#deploy_account_transaction + switch txn := tx.(type) { + case rpc.DeployAccountTxn: + calldata := []*felt.Felt{txn.ClassHash, txn.ContractAddressSalt} + calldata = append(calldata, txn.ConstructorCalldata...) + calldataHash, err := hash.ComputeHashOnElementsFelt(calldata) + if err != nil { + return nil, err + } - // There is only version 1 of deployAccount txn - if tx.Version != rpc.TransactionV1 { - return nil, ErrTxnTypeUnSupported - } - calldata := []*felt.Felt{tx.ClassHash, tx.ContractAddressSalt} - calldata = append(calldata, tx.ConstructorCalldata...) - calldataHash, err := hash.ComputeHashOnElementsFelt(calldata) - if err != nil { - return nil, err - } + versionFelt, err := new(felt.Felt).SetString(string(txn.Version)) + if err != nil { + return nil, err + } - versionFelt, err := new(felt.Felt).SetString(string(tx.Version)) - if err != nil { - return nil, err - } + // https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/transactions/#deploy_account_hash_calculation + return hash.CalculateTransactionHashCommon( + PREFIX_DEPLOY_ACCOUNT, + versionFelt, + contractAddress, + &felt.Zero, + calldataHash, + txn.MaxFee, + account.ChainId, + []*felt.Felt{txn.Nonce}, + ) + case rpc.DeployAccountTxnV3: + if txn.Version == "" || txn.ResourceBounds == (rpc.ResourceBoundsMapping{}) || txn.Nonce == nil || txn.PayMasterData == nil { + return nil, ErrNotAllParametersSet + } + calldata := []*felt.Felt{txn.ClassHash, txn.ContractAddressSalt} + calldata = append(calldata, txn.ConstructorCalldata...) - // https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/transactions/#deploy_account_hash_calculation - return hash.CalculateTransactionHashCommon( - PREFIX_DEPLOY_ACCOUNT, - versionFelt, - contractAddress, - &felt.Zero, - calldataHash, - tx.MaxFee, - account.ChainId, - []*felt.Felt{tx.Nonce}, - ) + txnVersionFelt, err := new(felt.Felt).SetString(string(txn.Version)) + if err != nil { + return nil, err + } + DAUint64, err := dataAvailabilityMode(txn.FeeMode, txn.NonceDataMode) + if err != nil { + return nil, err + } + // https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/transactions/#deploy_account_hash_calculation + return crypto.PoseidonArray( + PREFIX_DEPLOY_ACCOUNT, + txnVersionFelt, + contractAddress, + tipAndResourcesHash(txn.Tip.Impl().Uint64(), txn.ResourceBounds), + crypto.PoseidonArray(txn.PayMasterData...), + account.ChainId, + txn.Nonce, + new(felt.Felt).SetUint64(DAUint64), + crypto.PoseidonArray(txn.ConstructorCalldata...), + txn.ClassHash, + txn.ContractAddressSalt, + ), nil + } + return nil, ErrTxnTypeUnSupported } // TransactionHashInvoke calculates the transaction hash for the given invoke transaction. @@ -270,10 +299,56 @@ func (account *Account) TransactionHashInvoke(tx rpc.InvokeTxnType) (*felt.Felt, account.ChainId, []*felt.Felt{txn.Nonce}, ) + case rpc.InvokeTxnV3: + // https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-8.md#protocol-changes + if txn.Version == "" || txn.ResourceBounds == (rpc.ResourceBoundsMapping{}) || len(txn.Calldata) == 0 || txn.Nonce == nil || txn.SenderAddress == nil || txn.PayMasterData == nil || txn.AccountDeploymentData == nil { + return nil, ErrNotAllParametersSet + } + + txnVersionFelt, err := new(felt.Felt).SetString(string(txn.Version)) + if err != nil { + return nil, err + } + DAUint64, err := dataAvailabilityMode(txn.FeeMode, txn.NonceDataMode) + if err != nil { + return nil, err + } + + return crypto.PoseidonArray( + PREFIX_TRANSACTION, + txnVersionFelt, + txn.SenderAddress, + tipAndResourcesHash(txn.Tip.Impl().Uint64(), txn.ResourceBounds), + crypto.PoseidonArray(txn.PayMasterData...), + account.ChainId, + txn.Nonce, + new(felt.Felt).SetUint64(DAUint64), + crypto.PoseidonArray(txn.AccountDeploymentData...), + crypto.PoseidonArray(txn.Calldata...), + ), nil } return nil, ErrTxnTypeUnSupported } +func tipAndResourcesHash(tip uint64, resourceBounds rpc.ResourceBoundsMapping) *felt.Felt { + l1Bounds := new(felt.Felt).SetBytes(resourceBounds.L1Gas.Bytes(rpc.ResourceL1Gas)) + l2Bounds := new(felt.Felt).SetBytes(resourceBounds.L2Gas.Bytes(rpc.ResourceL2Gas)) + return crypto.PoseidonArray(new(felt.Felt).SetUint64(tip), l1Bounds, l2Bounds) +} + +func dataAvailabilityMode(feeDAMode, nonceDAMode rpc.DataAvailabilityMode) (uint64, error) { + const dataAvailabilityModeBits = 32 + fee64, err := feeDAMode.UInt64() + if err != nil { + return 0, err + } + nonce64, err := nonceDAMode.UInt64() + if err != nil { + return 0, err + } + return fee64 + nonce64<