Skip to content

Commit

Permalink
vm: update eip7002 to use system call [no ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
jochem-brouwer committed May 1, 2024
1 parent 8e67d22 commit 1235c60
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 129 deletions.
158 changes: 39 additions & 119 deletions packages/vm/src/runBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,19 @@ import {
Address,
BIGINT_0,
BIGINT_1,
BIGINT_2,
BIGINT_3,
BIGINT_8,
CLRequest,
GWEI_TO_WEI,
KECCAK256_RLP,
bigIntMin,
bigIntToBytes,
bigIntToHex,
bytesToBigInt,
bytesToHex,
concatBytes,
equalsBytes,
hexToBytes,
intToBytes,
setLengthLeft,
short,
unpadBytes,
unprefixedHexToBytes,
} from '@ethereumjs/util'
import debugDefault from 'debug'
Expand Down Expand Up @@ -979,120 +974,7 @@ export const accumulateRequests = async (vm: VM): Promise<CLRequest[]> => {
// TODO: Add in code to accumulate deposits (EIP-6110)

if (common.isActivatedEIP(7002)) {
// Partial withdrawals logic
const addressBytes = setLengthLeft(
bigIntToBytes(common.param('vm', 'withdrawalRequestPredeployAddress')),
20
)
const withdrawalsAddress = Address.fromString(bytesToHex(addressBytes))

const withdrawalsType = Number(common.param('vm', 'withdrawalRequestType'))

const excessWithdrawalsSlot = setLengthLeft(
bigIntToBytes(common.param('vm', 'excessWithdrawalsRequestStorageSlot')),
32
)
const withdrawalsCountSlot = setLengthLeft(
bigIntToBytes(common.param('vm', 'withdrawalsRequestCountStorage')),
32
)
const queueHeadSlot = setLengthLeft(
bigIntToBytes(common.param('vm', 'withdrawalsRequestQueueHeadStorageSlot')),
32
)
const queueTailSlot = setLengthLeft(
bigIntToBytes(common.param('vm', 'withdrawalsRequestTailHeadStorageSlot')),
32
)

const maxWithdrawalsPerBlock = common.param('vm', 'maxWithdrawalRequestsPerBlock')
const targetWithdrawalRequestsPerBlock = common.param('vm', 'targetWithdrawalRequestsPerBlock')
const offset = common.param('vm', 'withdrawalsRequestQueueStorageOffset')

// Dequeue withdrawal requests

const queueHeadIndex = bytesToBigInt(
await vm.stateManager.getContractStorage(withdrawalsAddress, queueHeadSlot)
)
const queueTailIndex = bytesToBigInt(
await vm.stateManager.getContractStorage(withdrawalsAddress, queueTailSlot)
)
const numInQueue = queueTailIndex - queueHeadIndex
const numDequeued = Number(bigIntMin(numInQueue, maxWithdrawalsPerBlock))

for (let i = 0; i < numDequeued; i++) {
const queueStorageSlot = offset + (queueHeadIndex + BigInt(i)) * BIGINT_3
const sourceAddress = setLengthLeft(
await vm.stateManager.getContractStorage(
withdrawalsAddress,
setLengthLeft(bigIntToBytes(queueStorageSlot), 32)
),
32
).slice(12, 32)

const slot1Data = setLengthLeft(
await vm.stateManager.getContractStorage(
withdrawalsAddress,
setLengthLeft(bigIntToBytes(queueStorageSlot + BIGINT_1), 32)
),
32
)
const slot2Data = setLengthLeft(
await vm.stateManager.getContractStorage(
withdrawalsAddress,
setLengthLeft(bigIntToBytes(queueStorageSlot + BIGINT_2), 32)
),
32
)

const concatenatedData = concatBytes(slot1Data, slot2Data)
const validatorPubkey = concatenatedData.slice(0, 48)

const amount = unpadBytes(concatenatedData.slice(48, 56))

const RLPdBytes = RLP.encode([sourceAddress, validatorPubkey, amount])
const request = new ValidatorWithdrawalRequest(withdrawalsType, RLPdBytes)
requests.push(request)
}

const newQueueHeadIndex = queueHeadIndex + BigInt(numDequeued)

if (newQueueHeadIndex === queueTailIndex) {
await vm.stateManager.putContractStorage(withdrawalsAddress, queueHeadSlot, new Uint8Array())
await vm.stateManager.putContractStorage(withdrawalsAddress, queueTailSlot, new Uint8Array())
} else {
await vm.stateManager.putContractStorage(
withdrawalsAddress,
queueHeadSlot,
bigIntToBytes(newQueueHeadIndex)
)
}

// Update the excess withdrawal requests
const previousExcess = bytesToBigInt(
await vm.stateManager.getContractStorage(withdrawalsAddress, excessWithdrawalsSlot)
)
const count = bytesToBigInt(
await vm.stateManager.getContractStorage(withdrawalsAddress, withdrawalsCountSlot)
)

let newExcess = BIGINT_0
if (previousExcess + count > targetWithdrawalRequestsPerBlock) {
newExcess = previousExcess + count - targetWithdrawalRequestsPerBlock
}

await vm.stateManager.putContractStorage(
withdrawalsAddress,
withdrawalsCountSlot,
bigIntToBytes(newExcess)
)

// Reset the withdrawals count
await vm.stateManager.putContractStorage(
withdrawalsAddress,
withdrawalsCountSlot,
new Uint8Array()
)
await _accumulateEIP7002Requests(vm, requests)
}

if (requests.length > 1) {
Expand All @@ -1103,3 +985,41 @@ export const accumulateRequests = async (vm: VM): Promise<CLRequest[]> => {
}
return requests
}

const _accumulateEIP7002Requests = async (vm: VM, requests: CLRequest[]): Promise<void> => {
console.log('Perform system call')
// TODO PERFORM LOGIC TO CHECK IF CONTRACT EXISTS
// Partial withdrawals logic
const addressBytes = setLengthLeft(
bigIntToBytes(vm.common.param('vm', 'withdrawalRequestPredeployAddress')),
20
)
const withdrawalsAddress = Address.fromString(bytesToHex(addressBytes))

const systemAddressBytes = setLengthLeft(
bigIntToBytes(vm.common.param('vm', 'systemAddress')),
20
)
const systemAddress = Address.fromString(bytesToHex(systemAddressBytes))

const results = await vm.evm.runCall({
caller: systemAddress,
gasLimit: BigInt(1_000_000),
to: withdrawalsAddress,
})

const resultsBytes = results.execResult.returnValue
if (resultsBytes.length > 0) {
const withdrawalRequestType = Number(vm.common.param('vm', 'withdrawalRequestType'))
// Each request is 76 bytes
for (let startByte = 0; startByte < resultsBytes.length; startByte += 76) {
const slicedBytes = resultsBytes.slice(startByte, startByte + 76)
const sourceAddress = slicedBytes.slice(0, 20)
const validatorPubkey = slicedBytes.slice(20, 20 + 48)
const amount = slicedBytes.slice(20 + 48, 20 + 48 + 8)
const rlpData = RLP.encode([sourceAddress, validatorPubkey, amount])
const request = new ValidatorWithdrawalRequest(withdrawalRequestType, rlpData)
requests.push(request)
}
}
}
27 changes: 17 additions & 10 deletions packages/vm/test/api/EIPs/eip-7002.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import {
setLengthLeft,
zeros,
} from '@ethereumjs/util'
import { assert, describe, expect, it } from 'vitest'
import { describe, it } from 'vitest'

import { VM } from '../../../src/vm.js'
import { setupVM } from '../utils.js'

const pkey = hexToBytes(`0x${'20'.repeat(32)}`)
Expand Down Expand Up @@ -56,15 +55,18 @@ describe('EIP-7002 tests', () => {
await vm.stateManager.putAccount(addr, acc)

// Deploy withdrawals contract
await vm.runBlock({
const results = await vm.runBlock({
block,
skipHeaderValidation: true,
skipBlockValidation: true,
generate: true,
})

const root = results.stateRoot

const validatorPubkey = hexToBytes(`0x${'20'.repeat(48)}`)
const amount = setLengthLeft(new Uint8Array(100), 8)
const amount = BigInt(12345678)
const amountBytes = setLengthLeft(bigIntToBytes(amount), 8)

const addressBytes = setLengthLeft(
bigIntToBytes(common.param('vm', 'withdrawalRequestPredeployAddress')),
Expand All @@ -74,10 +76,10 @@ describe('EIP-7002 tests', () => {

const tx = LegacyTransaction.fromTxData({
gasPrice: BigInt(100),
data: concatBytes(validatorPubkey, amount),
data: concatBytes(validatorPubkey, amountBytes),
value: BigInt(1),
to: withdrawalsAddress,
gasLimit: 100_000,
gasLimit: 200_000,
}).sign(pkey)

// Call withdrawals contract with a withdrawals request
Expand All @@ -92,16 +94,21 @@ describe('EIP-7002 tests', () => {
{ common }
)

vm.evm.events?.on('step', (e) => {
console.log(e.opcode.name, e.stack)
let generatedBlock: Block
vm.events.on('afterBlock', (e) => {
generatedBlock = e.block
})

const res = await vm.runBlock({
await vm.runBlock({
block: block2,
skipHeaderValidation: true,
skipBlockValidation: true,
generate: true,
})
console.log(res.requests)

console.log(bytesToHex(generatedBlock!.header.requestsRoot!))
console.log(generatedBlock!.requests)

//await vm.runBlock({ block: generatedBlock!, skipHeaderValidation: true, root })
})
})

0 comments on commit 1235c60

Please sign in to comment.