Skip to content

Commit

Permalink
Implement Hash calculations for v3 transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
omerfirmak committed Nov 22, 2023
1 parent 84cdc57 commit 04afcfe
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 5 deletions.
72 changes: 69 additions & 3 deletions core/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ type ResourceBounds struct {
MaxPricePerUnit *felt.Felt
}

func (rb ResourceBounds) Bytes(resource Resource) []byte {
const eight = 8
maxAmountBytes := make([]byte, eight)
binary.BigEndian.PutUint64(maxAmountBytes, rb.MaxAmount)
maxPriceBytes := rb.MaxPricePerUnit.Bytes()
return utils.Flatten(
[]byte{0},
[]byte(resource.String()),
maxAmountBytes,
maxPriceBytes[16:], // Last 128 bits.
)
}

type Event struct {
Data []*felt.Felt
From *felt.Felt
Expand Down Expand Up @@ -420,11 +433,35 @@ func invokeTransactionHash(i *InvokeTransaction, n utils.Network) (*felt.Felt, e
n.ChainID(),
i.Nonce,
), nil
case i.Version.Is(3):
return crypto.PoseidonArray(
invokeFelt,
i.Version.AsFelt(),
i.SenderAddress,
tipAndResourcesHash(i.Tip, i.ResourceBounds),
crypto.PoseidonArray(i.PaymasterData...),
n.ChainID(),
i.Nonce,
new(felt.Felt).SetUint64(dataAvailabilityMode(i.FeeDAMode, i.NonceDAMode)),
crypto.PoseidonArray(i.AccountDeploymentData...),
crypto.PoseidonArray(i.CallData...),
), nil
default:
return nil, errInvalidTransactionVersion(i, i.Version)
}
}

func tipAndResourcesHash(tip uint64, resourceBounds map[Resource]ResourceBounds) *felt.Felt {
l1Bounds := new(felt.Felt).SetBytes(resourceBounds[ResourceL1Gas].Bytes(ResourceL1Gas))
l2Bounds := new(felt.Felt).SetBytes(resourceBounds[ResourceL2Gas].Bytes(ResourceL2Gas))
return crypto.PoseidonArray(new(felt.Felt).SetUint64(tip), l1Bounds, l2Bounds)
}

func dataAvailabilityMode(feeDAMode, nonceDAMode DataAvailabilityMode) uint64 {
const dataAvailabilityModeBits = 32
return uint64(feeDAMode) + uint64(nonceDAMode)<<dataAvailabilityModeBits
}

func declareTransactionHash(d *DeclareTransaction, n utils.Network) (*felt.Felt, error) {
switch {
case d.Version.Is(0):
Expand Down Expand Up @@ -453,7 +490,20 @@ func declareTransactionHash(d *DeclareTransaction, n utils.Network) (*felt.Felt,
d.Nonce,
d.CompiledClassHash,
), nil

case d.Version.Is(3):
return crypto.PoseidonArray(
declareFelt,
d.Version.AsFelt(),
d.SenderAddress,
tipAndResourcesHash(d.Tip, d.ResourceBounds),
crypto.PoseidonArray(d.PaymasterData...),
n.ChainID(),
d.Nonce,
new(felt.Felt).SetUint64(dataAvailabilityMode(d.FeeDAMode, d.NonceDAMode)),
crypto.PoseidonArray(d.AccountDeploymentData...),
d.ClassHash,
d.CompiledClassHash,
), nil
default:
return nil, errInvalidTransactionVersion(d, d.Version)
}
Expand Down Expand Up @@ -486,7 +536,8 @@ func deployAccountTransactionHash(d *DeployAccountTransaction, n utils.Network)
callData := []*felt.Felt{d.ClassHash, d.ContractAddressSalt}
callData = append(callData, d.ConstructorCallData...)
// There is no version 0 for deploy account
if d.Version.Is(1) {
switch {
case d.Version.Is(1):
return crypto.PedersenArray(
deployAccountFelt,
d.Version.AsFelt(),
Expand All @@ -497,8 +548,23 @@ func deployAccountTransactionHash(d *DeployAccountTransaction, n utils.Network)
n.ChainID(),
d.Nonce,
), nil
case d.Version.Is(3):
return crypto.PoseidonArray(
deployAccountFelt,
d.Version.AsFelt(),
d.ContractAddress,
tipAndResourcesHash(d.Tip, d.ResourceBounds),
crypto.PoseidonArray(d.PaymasterData...),
n.ChainID(),
d.Nonce,
new(felt.Felt).SetUint64(dataAvailabilityMode(d.FeeDAMode, d.NonceDAMode)),
crypto.PoseidonArray(d.ConstructorCallData...),
d.ClassHash,
d.ContractAddressSalt,
), nil
default:
return nil, errInvalidTransactionVersion(d, d.Version)
}
return nil, errInvalidTransactionVersion(d, d.Version)
}

func VerifyTransactions(txs []Transaction, n utils.Network, protocolVersion string) error {
Expand Down
61 changes: 59 additions & 2 deletions core/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func TestVerifyTransactionHash(t *testing.T) {
t.Run("contains bad transaction", func(t *testing.T) {
badTxn0 := new(core.DeclareTransaction)
*badTxn0 = *txn0.(*core.DeclareTransaction)
badTxn0.Version = new(core.TransactionVersion).SetUint64(3)
badTxn0.Version = new(core.TransactionVersion).SetUint64(18)

badTxn1 := new(core.L1HandlerTransaction)
*badTxn1 = *txn3.(*core.L1HandlerTransaction)
Expand All @@ -182,7 +182,7 @@ func TestVerifyTransactionHash(t *testing.T) {
}{
*badTxn0.Hash(): {
name: "Declare - error if transaction hash calculation failed",
wantErr: fmt.Errorf("cannot calculate transaction hash of Transaction %v, reason: %w", badTxn0.Hash().String(), errors.New("invalid Transaction (type: *core.DeclareTransaction) version: 0x3")),
wantErr: fmt.Errorf("cannot calculate transaction hash of Transaction %v, reason: %w", badTxn0.Hash().String(), errors.New("invalid Transaction (type: *core.DeclareTransaction) version: 0x12")),
txn: badTxn0,
},
*badTxn1.Hash(): {
Expand Down Expand Up @@ -211,6 +211,63 @@ func TestVerifyTransactionHash(t *testing.T) {
})
}

func TestTransactionV3Hash(t *testing.T) {
network := utils.Integration
gw := adaptfeeder.New(feeder.NewTestClient(t, network))
ctx := context.Background()

// Test data was obtained by playing with Papyrus's implementation before v0.13 was released on integration.
tests := map[string]struct {
tx func(hash *felt.Felt) core.Transaction
want *felt.Felt
}{
"invoke": {
// https://external.integration.starknet.io/feeder_gateway/get_transaction?transactionHash=0x49728601e0bb2f48ce506b0cbd9c0e2a9e50d95858aa41463f46386dca489fd
tx: func(hash *felt.Felt) core.Transaction {
tx, err := gw.Transaction(ctx, hash)
require.NoError(t, err)
invoke, ok := tx.(*core.InvokeTransaction)
require.True(t, ok)
invoke.TransactionHash = nil
return invoke
},
want: utils.HexToFelt(t, "0x49728601e0bb2f48ce506b0cbd9c0e2a9e50d95858aa41463f46386dca489fd"),
},
// https://external.integration.starknet.io/feeder_gateway/get_transaction?transactionHash=0x41d1f5206ef58a443e7d3d1ca073171ec25fa75313394318fc83a074a6631c3
"declare": {
tx: func(hash *felt.Felt) core.Transaction {
tx, err := gw.Transaction(ctx, hash)
require.NoError(t, err)
declare, ok := tx.(*core.DeclareTransaction)
require.True(t, ok)
declare.TransactionHash = nil
return declare
},
want: utils.HexToFelt(t, "0x41d1f5206ef58a443e7d3d1ca073171ec25fa75313394318fc83a074a6631c3"),
},
// https://external.integration.starknet.io/feeder_gateway/get_transaction?transactionHash=0x29fd7881f14380842414cdfdd8d6c0b1f2174f8916edcfeb1ede1eb26ac3ef0
"deployAccount": {
tx: func(hash *felt.Felt) core.Transaction {
tx, err := gw.Transaction(ctx, hash)
require.NoError(t, err)
deployAccount, ok := tx.(*core.DeployAccountTransaction)
require.True(t, ok)
deployAccount.TransactionHash = nil
return deployAccount
},
want: utils.HexToFelt(t, "0x29fd7881f14380842414cdfdd8d6c0b1f2174f8916edcfeb1ede1eb26ac3ef0"),
},
}

for description, test := range tests {
t.Run(description, func(t *testing.T) {
got, err := core.TransactionHash(test.tx(test.want), network)
require.NoError(t, err)
require.Equal(t, test.want, got)
})
}
}

func TestTransactionVersi(t *testing.T) {
f := utils.HexToFelt(t, "0x100000000000000000000000000000002")
v := (*core.TransactionVersion)(f)
Expand Down

0 comments on commit 04afcfe

Please sign in to comment.