Skip to content

Commit

Permalink
rpcv06 implement v3 transactions (#462)
Browse files Browse the repository at this point in the history
* rpcv06 implement v3 transactions

* update v3-txs to rpc1

* invokeV3 transaction hash

* Add transaction hash functions for deploy, declare v3

* Add DeclareTxHash test - wip

* wip

* wip - test deployAccount

* test - integration wip

* test - declareTxnHashV3 works

* test - invokeTxnHashV3 works

* test - deployAcntTxnHashV3 works

* Add erroneous test - no test data for v3 testnet

* fix v3 type fields

* Fix minor test

* Update rpc/types_transaction.go

Co-authored-by: Josh Klopfenstein <git@joshklop.com>

* address comments

* use mocks to test TxHash instead of requiring integration endpoint

* update transactionHash test to use mocks and not testnet

---------

Co-authored-by: Josh Klopfenstein <git@joshklop.com>
  • Loading branch information
rianhughes and joshklop authored Nov 24, 2023
1 parent 6c244ac commit a94ff83
Show file tree
Hide file tree
Showing 11 changed files with 652 additions and 228 deletions.
174 changes: 139 additions & 35 deletions account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<<dataAvailabilityModeBits, nil
}

// TransactionHashDeclare calculates the transaction hash for declaring a transaction type.
//
// Parameters:
Expand Down Expand Up @@ -342,6 +417,35 @@ func (account *Account) TransactionHashDeclare(tx rpc.DeclareTxnType) (*felt.Fel
account.ChainId,
[]*felt.Felt{txn.Nonce, txn.CompiledClassHash},
)
case rpc.DeclareTxnV3:
// https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-8.md#protocol-changes
if txn.Version == "" || txn.ResourceBounds == (rpc.ResourceBoundsMapping{}) || txn.Nonce == nil || txn.SenderAddress == nil || txn.PayMasterData == nil || txn.AccountDeploymentData == nil ||
txn.ClassHash == nil || txn.CompiledClassHash == 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_DECLARE,
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...),
txn.ClassHash,
txn.CompiledClassHash,
), nil
}

return nil, ErrTxnTypeUnSupported
Expand Down Expand Up @@ -473,7 +577,7 @@ func (account *Account) BlockNumber(ctx context.Context) (uint64, error) {
// - blockID: The rpc.BlockID object representing the block.
// Returns:
// - uint64: the number of transactions in the block
// - error: an error, if any
// - error: an error, if any
func (account *Account) BlockTransactionCount(ctx context.Context, blockID rpc.BlockID) (uint64, error) {
return account.provider.BlockTransactionCount(ctx, blockID)
}
Expand Down Expand Up @@ -530,12 +634,12 @@ func (account *Account) ChainID(ctx context.Context) (string, error) {
//
// Parameters:
// - ctx: The context.Context
// - blockID: The rpc.BlockID
// - blockID: The rpc.BlockID
// - classHash: The `*felt.Felt`
// Returns:
// - *rpc.ClassOutput: The rpc.ClassOutput (the class output could be a DeprecatedContractClass
// - *rpc.ClassOutput: The rpc.ClassOutput (the class output could be a DeprecatedContractClass
// or just a Contract class depending on the contract version)
// - error: An error if any occurred.
// - error: An error if any occurred.
func (account *Account) Class(ctx context.Context, blockID rpc.BlockID, classHash *felt.Felt) (rpc.ClassOutput, error) {
return account.provider.Class(ctx, blockID, classHash)
}
Expand All @@ -546,9 +650,9 @@ func (account *Account) Class(ctx context.Context, blockID rpc.BlockID, classHas
// - blockID: The rpc.BlockID object representing the block ID.
// - contractAddress: The felt.Felt object representing the contract address.
// Returns:
// - *rpc.ClassOutput: The rpc.ClassOutput object (the class output could be a DeprecatedContractClass
// - *rpc.ClassOutput: The rpc.ClassOutput object (the class output could be a DeprecatedContractClass
// or just a Contract class depending on the contract version)
// - error: An error if any occurred.
// - error: An error if any occurred.
func (account *Account) ClassAt(ctx context.Context, blockID rpc.BlockID, contractAddress *felt.Felt) (rpc.ClassOutput, error) {
return account.provider.ClassAt(ctx, blockID, contractAddress)
}
Expand Down Expand Up @@ -618,7 +722,7 @@ func (account *Account) Nonce(ctx context.Context, blockID rpc.BlockID, contract
}

// SimulateTransactions simulates transactions using the provided context
// Parameters:
// Parameters:
// - ctx: The context.Context object
// - blockID: The rpc.BlockID object for the block referencing the state or call the transactions are on
// - txns: The slice of rpc.Transaction objects representing the transactions to simulate
Expand Down Expand Up @@ -707,7 +811,7 @@ func (account *Account) TransactionReceipt(ctx context.Context, transactionHash
// Parameters:
// - ctx: The context.Context object for the request.
// - transactionHash: The transaction hash for which the transaction trace is to be retrieved.
// Returns:
// Returns:
// - rpc.TxnTrace: The rpc.TxnTrace object representing the transaction trace, and an error if any.
func (account *Account) TraceTransaction(ctx context.Context, transactionHash *felt.Felt) (rpc.TxnTrace, error) {
return account.provider.TraceTransaction(ctx, transactionHash)
Expand Down
Loading

0 comments on commit a94ff83

Please sign in to comment.