Skip to content

Commit

Permalink
util: Add ssz roots capability for withdrawals hash tree root (#2488)
Browse files Browse the repository at this point in the history
* util: Add ssz roots capability for withdrawals hash tree root

* Fix karma setup

* Update rlp to v4.0.0

* add cl spec testcase

* better naming

* improve wording

Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>

* improve wording

Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>

* fix

Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>
  • Loading branch information
g11tech and acolytec3 authored Jan 18, 2023
1 parent 8bcdb48 commit 6508bf9
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 2 deletions.
46 changes: 46 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions packages/block/src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
bufferToHex,
intToHex,
isHexPrefixed,
ssz,
} from '@ethereumjs/util'
import { keccak256 } from 'ethereum-cryptography/keccak'
import { ethers } from 'ethers'
Expand Down Expand Up @@ -52,6 +53,14 @@ export class Block {
return trie.root()
}

/**
* Returns the ssz root for array of withdrawal transactions.
* @param wts array of Withdrawal to compute the root of
*/
public static async generateWithdrawalsSSZRoot(withdrawals: Withdrawal[]) {
ssz.Withdrawals.hashTreeRoot(withdrawals.map((wt) => wt.toValue()))
}

/**
* Returns the txs trie root for array of TypedTransaction
* @param txs array of TypedTransaction to compute the root of
Expand Down
3 changes: 3 additions & 0 deletions packages/util/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ module.exports = function (config) {
karmaTypescriptConfig: {
bundlerOptions: {
entrypoints: /\.spec\.ts$/,
acornOptions: {
ecmaVersion: 11,
},
},
tsconfig: './tsconfig.json',
},
Expand Down
3 changes: 2 additions & 1 deletion packages/util/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@
"tsc": "../../config/cli/ts-compile.sh"
},
"dependencies": {
"@ethereumjs/rlp": "^4.0.0-beta.2",
"@chainsafe/ssz": "^0.10.0",
"@ethereumjs/rlp": "^4.0.0",
"async": "^3.2.4",
"ethereum-cryptography": "^1.1.2"
},
Expand Down
2 changes: 2 additions & 0 deletions packages/util/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,5 @@ export const KECCAK256_RLP = Buffer.from(KECCAK256_RLP_S, 'hex')
* RLP encoded empty string
*/
export const RLP_EMPTY_STRING = Buffer.from([0x80])

export const MAX_WITHDRAWALS_PER_PAYLOAD = 16
5 changes: 5 additions & 0 deletions packages/util/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export * from './signature'
*/
export * from './bytes'

/**
* SSZ containers
*/
export * as ssz from './ssz'

/**
* Helpful TypeScript types
*/
Expand Down
24 changes: 24 additions & 0 deletions packages/util/src/ssz.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
ByteVectorType,
ContainerType,
ListCompositeType,
UintBigintType,
UintNumberType,
} from '@chainsafe/ssz'

import { MAX_WITHDRAWALS_PER_PAYLOAD } from './constants'

export const UintNum64 = new UintNumberType(8)
export const UintBigInt64 = new UintBigintType(8)
export const Bytes20 = new ByteVectorType(20)

export const Withdrawal = new ContainerType(
{
index: UintBigInt64,
validatorIndex: UintBigInt64,
address: Bytes20,
amount: UintBigInt64,
},
{ typeName: 'Withdrawal', jsonCase: 'eth2' }
)
export const Withdrawals = new ListCompositeType(Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD)
9 changes: 9 additions & 0 deletions packages/util/src/withdrawal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ export class Withdrawal {
return Withdrawal.toBufferArray(this)
}

toValue() {
return {
index: this.index,
validatorIndex: this.validatorIndex,
address: this.address.buf,
amount: this.amount,
}
}

toJSON() {
return {
index: bigIntToHex(this.index),
Expand Down
94 changes: 94 additions & 0 deletions packages/util/test/ssz.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as tape from 'tape'

import { Withdrawal, ssz } from '../src'
const withdrawalsData = [
{
index: BigInt(0),
validatorIndex: BigInt(65535),
address: Buffer.from('0000000000000000000000000000000000000000', 'hex'),
amount: BigInt('0'),
},
{
index: BigInt(1),
validatorIndex: BigInt(65536),
address: Buffer.from('0100000000000000000000000000000000000000', 'hex'),
amount: BigInt('04523128485832663883'),
},
{
index: BigInt(2),
validatorIndex: BigInt(65537),
address: Buffer.from('0200000000000000000000000000000000000000', 'hex'),
amount: BigInt('09046256971665327767'),
},
{
index: BigInt(4),
validatorIndex: BigInt(65538),
address: Buffer.from('0300000000000000000000000000000000000000', 'hex'),
amount: BigInt('13569385457497991651'),
},
{
index: BigInt(4),
validatorIndex: BigInt(65539),
address: Buffer.from('0400000000000000000000000000000000000000', 'hex'),
amount: BigInt('18446744073709551615'),
},
{
index: BigInt(5),
validatorIndex: BigInt(65540),
address: Buffer.from('0500000000000000000000000000000000000000', 'hex'),
amount: BigInt('02261564242916331941'),
},
{
index: BigInt(6),
validatorIndex: BigInt(65541),
address: Buffer.from('0600000000000000000000000000000000000000', 'hex'),
amount: BigInt('02713877091499598330'),
},
{
index: BigInt(7),
validatorIndex: BigInt(65542),
address: Buffer.from('0700000000000000000000000000000000000000', 'hex'),
amount: BigInt('03166189940082864718'),
},
]

tape('ssz', (t) => {
t.test('withdrawals', (st) => {
const withdrawals = withdrawalsData.map((wt) => Withdrawal.fromWithdrawalData(wt))
const withdrawalsValue = withdrawals.map((wt) => wt.toValue())
const sszValues = ssz.Withdrawals.toViewDU(withdrawalsData)
.toValue()
.map((wt) => {
wt.address = Buffer.from(wt.address)
return wt
})
st.deepEqual(sszValues, withdrawalsValue, 'sszValues should be same as withdrawalsValue')
const withdrawalsRoot = ssz.Withdrawals.hashTreeRoot(withdrawalsValue)
st.equal(
Buffer.from(withdrawalsRoot).toString('hex'),
'bd97f65e513f870484e85927510acb291fcfb3e593c05ab7f21f206921264946',
'ssz root should match'
)
st.end()
})

const specWithdrawals = [
// https://github.com/ethereum/consensus-spec-tests/tree/v1.3.0-rc.1/tests/mainnet/capella/ssz_static/Withdrawal/ssz_random/case_0
{
index: BigInt('17107150653359250726'),
validatorIndex: BigInt('1906681273455760070'),
address: Buffer.from('02ab1379b6334b58df82c85d50ff1214663cba20', 'hex'),
amount: BigInt('5055030296454530815'),
},
]

t.test('match spec v1.3.0-rc.1', (st) => {
const withdrawalsRoot = ssz.Withdrawal.hashTreeRoot(specWithdrawals[0])
st.equal(
Buffer.from(withdrawalsRoot).toString('hex'),
'ed9cec6fb8ee22b146059d02c38940cca1dd22a00d0132b000999b983fceff95',
'ssz root should match'
)
st.end()
})
})
8 changes: 7 additions & 1 deletion packages/util/test/withdrawal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,18 @@ tape('Withdrawal', (t) => {
st.end()
})

t.test('fromValuesArray and toJSON', (st) => {
t.test('fromValuesArray, toJSON and toValue', (st) => {
const withdrawals = (gethWithdrawalsBuffer as WithdrawalBuffer[]).map(
Withdrawal.fromValuesArray
)
const withdrawalsJson = withdrawals.map((wt) => wt.toJSON())
st.deepEqual(withdrawalsGethVector, withdrawalsJson, 'Withdrawals json should match')

const withdrawalsValue = withdrawals.map((wt) => wt.toValue())
st.deepEqual(
withdrawalsValue.map((wt) => `0x${wt.address.toString('hex')}`),
withdrawalsJson.map((wt) => wt.address)
)
st.end()
})
})

0 comments on commit 6508bf9

Please sign in to comment.