Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ethereumjs/statemanager

Package Overview
Dependencies
Maintainers
4
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ethereumjs/statemanager - npm Package Compare versions

Comparing version 2.3.0 to 2.4.0

42

dist/cjs/accessWitness.d.ts

@@ -1,14 +0,3 @@

import type { Address, PrefixedHexString } from '@ethereumjs/util';
/**
* Tree key constants.
*/
export declare const VERSION_LEAF_KEY: Uint8Array;
export declare const BALANCE_LEAF_KEY: Uint8Array;
export declare const NONCE_LEAF_KEY: Uint8Array;
export declare const CODE_KECCAK_LEAF_KEY: Uint8Array;
export declare const CODE_SIZE_LEAF_KEY: Uint8Array;
export declare const HEADER_STORAGE_OFFSET = 64;
export declare const CODE_OFFSET = 128;
export declare const VERKLE_NODE_WIDTH = 256;
export declare const MAIN_STORAGE_OFFSET: bigint;
import type { AccessEventFlags, AccessWitnessInterface } from '@ethereumjs/common';
import type { Address, PrefixedHexString, VerkleCrypto } from '@ethereumjs/util';
declare type StemAccessEvent = {

@@ -20,9 +9,2 @@ write?: boolean;

};
declare type AccessEventFlags = {
stemRead: boolean;
stemWrite: boolean;
chunkRead: boolean;
chunkWrite: boolean;
chunkFill: boolean;
};
declare type StemMeta = {

@@ -60,6 +42,8 @@ address: Address;

};
export declare class AccessWitness {
export declare class AccessWitness implements AccessWitnessInterface {
stems: Map<PrefixedHexString, StemAccessEvent & StemMeta>;
chunks: Map<PrefixedHexString, ChunkAccessEvent>;
verkleCrypto: VerkleCrypto;
constructor(opts?: {
verkleCrypto?: VerkleCrypto;
stems?: Map<PrefixedHexString, StemAccessEvent & StemMeta>;

@@ -71,8 +55,6 @@ chunks?: Map<PrefixedHexString, ChunkAccessEvent>;

touchAndChargeValueTransfer(caller: Address, target: Address): bigint;
touchAndChargeContractCreateInit(address: Address, { sendsValue }?: {
sendsValue?: boolean;
}): bigint;
touchAndChargeContractCreateInit(address: Address): bigint;
touchAndChargeContractCreateCompleted(address: Address): bigint;
touchTxOriginAndComputeGas(origin: Address): bigint;
touchTxExistingAndComputeGas(target: Address, { sendsValue }?: {
touchTxTargetAndComputeGas(target: Address, { sendsValue }?: {
sendsValue?: boolean;

@@ -96,13 +78,5 @@ }): bigint;

}
export declare function getTreeIndexesForStorageSlot(storageKey: bigint): {
treeIndex: bigint;
subIndex: number;
};
export declare function getTreeIndicesForCodeChunk(chunkId: number): {
treeIndex: number;
subIndex: number;
};
export declare function decodeAccessedState(treeIndex: number | bigint, chunkIndex: number): AccessedState;
export declare function decodeValue(type: AccessedStateType, value: string | null): string;
export declare function decodeValue(type: AccessedStateType, value: PrefixedHexString | null): string;
export {};
//# sourceMappingURL=accessWitness.d.ts.map

140

dist/cjs/accessWitness.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeValue = exports.decodeAccessedState = exports.getTreeIndicesForCodeChunk = exports.getTreeIndexesForStorageSlot = exports.AccessWitness = exports.AccessedStateType = exports.MAIN_STORAGE_OFFSET = exports.VERKLE_NODE_WIDTH = exports.CODE_OFFSET = exports.HEADER_STORAGE_OFFSET = exports.CODE_SIZE_LEAF_KEY = exports.CODE_KECCAK_LEAF_KEY = exports.NONCE_LEAF_KEY = exports.BALANCE_LEAF_KEY = exports.VERSION_LEAF_KEY = void 0;
exports.decodeValue = exports.decodeAccessedState = exports.AccessWitness = exports.AccessedStateType = void 0;
const util_1 = require("@ethereumjs/util");
const verkle_1 = require("@ethereumjs/verkle");
const debug_1 = require("debug");
const { debug: createDebugLogger } = debug_1.default;
const debug = createDebugLogger('statemanager:verkle:aw');
const debug = (0, debug_1.default)('statemanager:verkle:aw');
/**
* Tree key constants.
*/
exports.VERSION_LEAF_KEY = (0, util_1.toBytes)(0);
exports.BALANCE_LEAF_KEY = (0, util_1.toBytes)(1);
exports.NONCE_LEAF_KEY = (0, util_1.toBytes)(2);
exports.CODE_KECCAK_LEAF_KEY = (0, util_1.toBytes)(3);
exports.CODE_SIZE_LEAF_KEY = (0, util_1.toBytes)(4);
exports.HEADER_STORAGE_OFFSET = 64;
exports.CODE_OFFSET = 128;
exports.VERKLE_NODE_WIDTH = 256;
// export const MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(31)
// incorrect value to match with kaustinen2 offset
exports.MAIN_STORAGE_OFFSET = BigInt(256) * BigInt(2) ** BigInt(31);
const WitnessBranchReadCost = BigInt(1900);

@@ -40,2 +27,6 @@ const WitnessChunkReadCost = BigInt(200);

constructor(opts = {}) {
if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required');
}
this.verkleCrypto = opts.verkleCrypto;
this.stems = opts.stems ?? new Map();

@@ -46,7 +37,7 @@ this.chunks = opts.chunks ?? new Map();

let gas = util_1.BIGINT_0;
gas += this.touchAddressOnReadAndComputeGas(address, 0, exports.VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, exports.BALANCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, exports.CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, exports.CODE_KECCAK_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, exports.NONCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_BALANCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_CODE_HASH_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_NONCE_LEAF_KEY);
return gas;

@@ -56,4 +47,4 @@ }

let gas = util_1.BIGINT_0;
gas += this.touchAddressOnReadAndComputeGas(address, 0, exports.VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, exports.CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, util_1.VERKLE_CODE_SIZE_LEAF_KEY);
return gas;

@@ -63,14 +54,10 @@ }

let gas = util_1.BIGINT_0;
gas += this.touchAddressOnWriteAndComputeGas(caller, 0, exports.BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(target, 0, exports.BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(caller, 0, util_1.VERKLE_BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(target, 0, util_1.VERKLE_BALANCE_LEAF_KEY);
return gas;
}
touchAndChargeContractCreateInit(address, { sendsValue } = {}) {
touchAndChargeContractCreateInit(address) {
let gas = util_1.BIGINT_0;
gas += this.touchAddressOnWriteAndComputeGas(address, 0, exports.VERSION_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, exports.NONCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, exports.CODE_KECCAK_LEAF_KEY);
if (sendsValue === true) {
gas += this.touchAddressOnWriteAndComputeGas(address, 0, exports.BALANCE_LEAF_KEY);
}
gas += this.touchAddressOnWriteAndComputeGas(address, 0, util_1.VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, util_1.VERKLE_NONCE_LEAF_KEY);
return gas;

@@ -80,7 +67,7 @@ }

let gas = util_1.BIGINT_0;
gas += this.touchAddressOnWriteAndComputeGas(address, 0, exports.VERSION_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, exports.BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, exports.CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, exports.CODE_KECCAK_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, exports.NONCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, util_1.VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, util_1.VERKLE_BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, util_1.VERKLE_CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, util_1.VERKLE_CODE_HASH_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, util_1.VERKLE_NONCE_LEAF_KEY);
return gas;

@@ -90,20 +77,20 @@ }

let gas = util_1.BIGINT_0;
gas += this.touchAddressOnReadAndComputeGas(origin, 0, exports.VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, exports.CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, exports.CODE_KECCAK_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, exports.NONCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, exports.BALANCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, util_1.VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, util_1.VERKLE_CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, util_1.VERKLE_CODE_HASH_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, util_1.VERKLE_NONCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, util_1.VERKLE_BALANCE_LEAF_KEY);
return gas;
}
touchTxExistingAndComputeGas(target, { sendsValue } = {}) {
touchTxTargetAndComputeGas(target, { sendsValue } = {}) {
let gas = util_1.BIGINT_0;
gas += this.touchAddressOnReadAndComputeGas(target, 0, exports.VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, exports.CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, exports.CODE_KECCAK_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, exports.NONCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, util_1.VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, util_1.VERKLE_CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, util_1.VERKLE_CODE_HASH_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, util_1.VERKLE_NONCE_LEAF_KEY);
if (sendsValue === true) {
gas += this.touchAddressOnWriteAndComputeGas(target, 0, exports.BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(target, 0, util_1.VERKLE_BALANCE_LEAF_KEY);
}
else {
gas += this.touchAddressOnReadAndComputeGas(target, 0, exports.BALANCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, util_1.VERKLE_BALANCE_LEAF_KEY);
}

@@ -115,3 +102,3 @@ return gas;

for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkNum);
const { treeIndex, subIndex } = (0, util_1.getVerkleTreeIndicesForCodeChunk)(chunkNum);
gas += this.touchAddressOnReadAndComputeGas(contact, treeIndex, subIndex);

@@ -124,3 +111,3 @@ }

for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkNum);
const { treeIndex, subIndex } = (0, util_1.getVerkleTreeIndicesForCodeChunk)(chunkNum);
gas += this.touchAddressOnWriteAndComputeGas(contact, treeIndex, subIndex);

@@ -139,15 +126,15 @@ }

const { stemRead, stemWrite, chunkRead, chunkWrite, chunkFill } = this.touchAddress(address, treeIndex, subIndex, { isWrite });
if (stemRead) {
if (stemRead === true) {
gas += WitnessBranchReadCost;
}
if (stemWrite) {
if (stemWrite === true) {
gas += WitnessBranchWriteCost;
}
if (chunkRead) {
if (chunkRead === true) {
gas += WitnessChunkReadCost;
}
if (chunkWrite) {
if (chunkWrite === true) {
gas += WitnessChunkWriteCost;
}
if (chunkFill) {
if (chunkFill === true) {
gas += WitnessChunkFillCost;

@@ -163,3 +150,3 @@ }

const chunkFill = false;
const accessedStemKey = (0, verkle_1.getStem)(address, treeIndex);
const accessedStemKey = (0, util_1.getVerkleStem)(this.verkleCrypto, address, treeIndex);
const accessedStemHex = (0, util_1.bytesToHex)(accessedStemKey);

@@ -172,3 +159,3 @@ let accessedStem = this.stems.get(accessedStemHex);

}
const accessedChunkKey = (0, verkle_1.getKey)(accessedStemKey, (0, util_1.toBytes)(subIndex));
const accessedChunkKey = (0, util_1.getVerkleKey)(accessedStemKey, typeof subIndex === 'number' ? (0, util_1.intToBytes)(subIndex) : subIndex);
const accessedChunkKeyHex = (0, util_1.bytesToHex)(accessedChunkKey);

@@ -198,3 +185,3 @@ let accessedChunk = this.chunks.get(accessedChunkKeyHex);

shallowCopy() {
return new AccessWitness();
return new AccessWitness({ verkleCrypto: this.verkleCrypto });
}

@@ -248,23 +235,4 @@ merge(accessWitness) {

exports.AccessWitness = AccessWitness;
function getTreeIndexesForStorageSlot(storageKey) {
let position;
if (storageKey < exports.CODE_OFFSET - exports.HEADER_STORAGE_OFFSET) {
position = BigInt(exports.HEADER_STORAGE_OFFSET) + storageKey;
}
else {
position = exports.MAIN_STORAGE_OFFSET + storageKey;
}
const treeIndex = position / BigInt(exports.VERKLE_NODE_WIDTH);
const subIndex = Number(position % BigInt(exports.VERKLE_NODE_WIDTH));
return { treeIndex, subIndex };
}
exports.getTreeIndexesForStorageSlot = getTreeIndexesForStorageSlot;
function getTreeIndicesForCodeChunk(chunkId) {
const treeIndex = Math.floor((exports.CODE_OFFSET + chunkId) / exports.VERKLE_NODE_WIDTH);
const subIndex = (exports.CODE_OFFSET + chunkId) % exports.VERKLE_NODE_WIDTH;
return { treeIndex, subIndex };
}
exports.getTreeIndicesForCodeChunk = getTreeIndicesForCodeChunk;
function decodeAccessedState(treeIndex, chunkIndex) {
const position = BigInt(treeIndex) * BigInt(exports.VERKLE_NODE_WIDTH) + BigInt(chunkIndex);
const position = BigInt(treeIndex) * BigInt(util_1.VERKLE_NODE_WIDTH) + BigInt(chunkIndex);
switch (position) {

@@ -282,15 +250,15 @@ case BigInt(0):

default:
if (position < exports.HEADER_STORAGE_OFFSET) {
throw Error(`No attribute yet stored >=5 and <${exports.HEADER_STORAGE_OFFSET}`);
if (position < util_1.VERKLE_HEADER_STORAGE_OFFSET) {
throw Error(`No attribute yet stored >=5 and <${util_1.VERKLE_HEADER_STORAGE_OFFSET}`);
}
if (position >= exports.HEADER_STORAGE_OFFSET && position < exports.CODE_OFFSET) {
const slot = position - BigInt(exports.HEADER_STORAGE_OFFSET);
if (position >= util_1.VERKLE_HEADER_STORAGE_OFFSET && position < util_1.VERKLE_CODE_OFFSET) {
const slot = position - BigInt(util_1.VERKLE_HEADER_STORAGE_OFFSET);
return { type: AccessedStateType.Storage, slot };
}
else if (position >= exports.CODE_OFFSET && position < exports.MAIN_STORAGE_OFFSET) {
const codeChunkIdx = Number(position) - exports.CODE_OFFSET;
else if (position >= util_1.VERKLE_CODE_OFFSET && position < util_1.VERKLE_MAIN_STORAGE_OFFSET) {
const codeChunkIdx = Number(position) - util_1.VERKLE_CODE_OFFSET;
return { type: AccessedStateType.Code, codeOffset: codeChunkIdx * 31 };
}
else if (position >= exports.MAIN_STORAGE_OFFSET) {
const slot = BigInt(position - exports.MAIN_STORAGE_OFFSET);
else if (position >= util_1.VERKLE_MAIN_STORAGE_OFFSET) {
const slot = BigInt(position - util_1.VERKLE_MAIN_STORAGE_OFFSET);
return { type: AccessedStateType.Storage, slot };

@@ -297,0 +265,0 @@ }

@@ -34,3 +34,3 @@ import { OrderedMap } from 'js-sdsl';

*/
put(address: Address, account: Account | undefined): void;
put(address: Address, account: Account | undefined, couldBeParitalAccount?: boolean): void;
/**

@@ -37,0 +37,0 @@ * Returns the queried account or undefined if account doesn't exist

@@ -10,3 +10,2 @@ "use strict";

const types_js_1 = require("./types.js");
const { debug: createDebugLogger } = debug_1.default;
class AccountCache extends cache_js_1.Cache {

@@ -35,3 +34,3 @@ constructor(opts) {

this._diffCache.push(new Map());
this._debug = createDebugLogger('statemanager:cache:account');
this._debug = (0, debug_1.default)('statemanager:cache:account');
}

@@ -56,7 +55,11 @@ _saveCachePreState(cacheKeyHex) {

*/
put(address, account) {
put(address, account, couldBeParitalAccount = false) {
const addressHex = (0, util_1.bytesToUnprefixedHex)(address.bytes);
this._saveCachePreState(addressHex);
const elem = {
accountRLP: account !== undefined ? account.serialize() : undefined,
accountRLP: account !== undefined
? couldBeParitalAccount
? account.serializeWithPartialInfo()
: account.serialize()
: undefined,
};

@@ -63,0 +66,0 @@ if (this.DEBUG) {

@@ -5,3 +5,2 @@ "use strict";

const debug_1 = require("debug");
const { debug: createDebugLogger } = debug_1.default;
class Cache {

@@ -30,3 +29,3 @@ constructor() {

typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false;
this._debug = createDebugLogger('statemanager:cache');
this._debug = (0, debug_1.default)('statemanager:cache');
}

@@ -33,0 +32,0 @@ }

@@ -10,3 +10,2 @@ "use strict";

const types_js_1 = require("./types.js");
const { debug: createDebugLogger } = debug_1.default;
class CodeCache extends cache_js_1.Cache {

@@ -35,3 +34,3 @@ constructor(opts) {

this._diffCache.push(new Map());
this._debug = createDebugLogger('statemanager:cache:code');
this._debug = (0, debug_1.default)('statemanager:cache:code');
}

@@ -38,0 +37,0 @@ /**

@@ -10,3 +10,2 @@ "use strict";

const types_js_1 = require("./types.js");
const { debug: createDebugLogger } = debug_1.default;
class StorageCache extends cache_js_1.Cache {

@@ -36,3 +35,3 @@ constructor(opts) {

if (this.DEBUG) {
this._debug = createDebugLogger('statemanager:cache:storage');
this._debug = (0, debug_1.default)('statemanager:cache:storage');
}

@@ -39,0 +38,0 @@ }

@@ -52,2 +52,3 @@ import { Common } from '@ethereumjs/common';

getContractCode(address: Address): Promise<Uint8Array>;
getContractCodeSize(address: Address): Promise<number>;
/**

@@ -100,6 +101,4 @@ * Adds `value` to the state trie as code, and sets `codeHash` on the account

/**
* Gets the code corresponding to the provided `address`.
* @param address - Address to get the `account` for
* @returns {Promise<Uint8Array>} - Resolves with the code corresponding to the provided address.
* Returns an empty `Uint8Array` if the account has no associated code.
* Gets the account associated with `address` or `undefined` if account does not exist
* @param address - Address of the `account` to get
*/

@@ -106,0 +105,0 @@ getAccount(address: Address): Promise<Account | undefined>;

@@ -11,3 +11,2 @@ "use strict";

const index_js_1 = require("./cache/index.js");
const { debug: createDebugLogger } = debug_1.default;
const KECCAK256_RLP_EMPTY_ACCOUNT = rlp_1.RLP.encode(new util_1.Account().serialize()).slice(2);

@@ -36,3 +35,3 @@ class RPCStateManager {

typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false;
this._debug = createDebugLogger('statemanager:rpcStateManager');
this._debug = (0, debug_1.default)('statemanager:rpcStateManager');
if (typeof opts.provider === 'string' && opts.provider.startsWith('http')) {

@@ -111,2 +110,6 @@ this._provider = opts.provider;

}
async getContractCodeSize(address) {
const contractCode = await this.getContractCode(address);
return contractCode.length;
}
/**

@@ -212,6 +215,4 @@ * Adds `value` to the state trie as code, and sets `codeHash` on the account

/**
* Gets the code corresponding to the provided `address`.
* @param address - Address to get the `account` for
* @returns {Promise<Uint8Array>} - Resolves with the code corresponding to the provided address.
* Returns an empty `Uint8Array` if the account has no associated code.
* Gets the account associated with `address` or `undefined` if account does not exist
* @param address - Address of the `account` to get
*/

@@ -225,6 +226,7 @@ async getAccount(address) {

}
const rlp = (await this.getAccountFromProvider(address)).serialize();
const account = (0, util_1.equalsBytes)(rlp, KECCAK256_RLP_EMPTY_ACCOUNT) === false
? util_1.Account.fromRlpSerializedAccount(rlp)
: undefined;
const accountFromProvider = await this.getAccountFromProvider(address);
const account = (0, util_1.equalsBytes)(accountFromProvider.codeHash, new Uint8Array(32).fill(0)) ||
(0, util_1.equalsBytes)(accountFromProvider.serialize(), KECCAK256_RLP_EMPTY_ACCOUNT)
? undefined
: util_1.Account.fromRlpSerializedAccount(accountFromProvider.serialize());
this._accountCache?.put(address, account);

@@ -231,0 +233,0 @@ return account;

@@ -7,5 +7,4 @@ import { Account } from '@ethereumjs/util';

import type { DefaultStateManager } from './stateManager.js';
import type { VerkleExecutionWitness } from '@ethereumjs/block';
import type { AccountFields, Common, EVMStateManagerInterface, Proof, StorageDump, StorageRange } from '@ethereumjs/common';
import type { Address, PrefixedHexString } from '@ethereumjs/util';
import type { Address, PrefixedHexString, VerkleCrypto, VerkleExecutionWitness } from '@ethereumjs/util';
export interface VerkleState {

@@ -68,2 +67,4 @@ [key: PrefixedHexString]: PrefixedHexString | null;

accesses?: AccessWitness;
verkleCrypto: VerkleCrypto;
initialStateRoot?: Uint8Array;
}

@@ -85,3 +86,5 @@ /**

_codeCache?: CodeCache;
_cachedStateRoot?: Uint8Array;
originalStorageCache: OriginalStorageCache;
verkleCrypto: VerkleCrypto;
protected readonly _accountCacheSettings: CacheSettings;

@@ -99,2 +102,3 @@ protected readonly _storageCacheSettings: CacheSettings;

protected readonly DEBUG: boolean;
private _blockNum;
private _executionWitness?;

@@ -104,2 +108,3 @@ private _proof;

private _postState;
private _preState;
private _checkpoints;

@@ -111,14 +116,6 @@ accessWitness?: AccessWitness;

*/
constructor(opts?: StatelessVerkleStateManagerOpts);
constructor(opts: StatelessVerkleStateManagerOpts);
getTransitionStateRoot(_: DefaultStateManager, __: Uint8Array): Promise<Uint8Array>;
initVerkleExecutionWitness(executionWitness?: VerkleExecutionWitness | null, accessWitness?: AccessWitness): void;
getTreeKeyForVersion(stem: Uint8Array): Uint8Array;
getTreeKeyForBalance(stem: Uint8Array): Uint8Array;
getTreeKeyForNonce(stem: Uint8Array): Uint8Array;
getTreeKeyForCodeHash(stem: Uint8Array): Uint8Array;
getTreeKeyForCodeSize(stem: Uint8Array): Uint8Array;
getTreeKeyForCodeChunk(address: Address, chunkId: number): Uint8Array;
chunkifyCode(code: Uint8Array): void;
getTreeKeyForStorageSlot(address: Address, storageKey: bigint): Uint8Array;
checkChunkWitnessPresent(address: Address, codeOffset: number): boolean;
initVerkleExecutionWitness(blockNum: bigint, executionWitness?: VerkleExecutionWitness | null, accessWitness?: AccessWitness): void;
checkChunkWitnessPresent(address: Address, codeOffset: number): Promise<boolean>;
/**

@@ -144,2 +141,3 @@ * Copies the current instance of the `StateManager`

getContractCode(address: Address): Promise<Uint8Array>;
getContractCodeSize(address: Address): Promise<number>;
/**

@@ -177,3 +175,8 @@ * Gets the storage value associated with the provided `address` and `key`. This method returns

getProof(_: Address, __?: Uint8Array[]): Promise<Proof>;
verifyProof(parentVerkleRoot: Uint8Array): Promise<boolean>;
/**
* Verifies whether the execution witness matches the stateRoot
* @param {Uint8Array} stateRoot - The stateRoot to verify the executionWitness against
* @returns {boolean} - Returns true if the executionWitness matches the provided stateRoot, otherwise false
*/
verifyProof(stateRoot: Uint8Array): boolean;
verifyPostState(): boolean;

@@ -203,14 +206,14 @@ getComputedValue(accessedState: AccessedStateWithAddress): PrefixedHexString | null;

/**
* Gets the verkle root.
* NOTE: this needs some examination in the code where this is needed
* and if we have the verkle root present
* @returns {Promise<Uint8Array>} - Returns the verkle root of the `StateManager`
* Gets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* This is required to verify and prove verkle execution witnesses.
* @returns {Promise<Uint8Array>} - Returns the cached state root
*/
getStateRoot(): Promise<Uint8Array>;
/**
* TODO: needed?
* Maybe in this context: reset to original pre state suffice
* @param stateRoot - The verkle root to reset the instance to
* Sets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* @param stateRoot - The stateRoot to set
*/
setStateRoot(_: Uint8Array): Promise<void>;
setStateRoot(stateRoot: Uint8Array): Promise<void>;
/**

@@ -217,0 +220,0 @@ * Dumps the RLP-encoded storage values for an `account` specified by `address`.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StatelessVerkleStateManager = void 0;
const rlp_1 = require("@ethereumjs/rlp");
const util_1 = require("@ethereumjs/util");
const verkle_1 = require("@ethereumjs/verkle");
const debug_1 = require("debug");
const keccak_js_1 = require("ethereum-cryptography/keccak.js");
const utils_1 = require("ethereum-cryptography/utils");
const accessWitness_js_1 = require("./accessWitness.js");
const index_js_1 = require("./cache/index.js");
const originalStorageCache_js_1 = require("./cache/originalStorageCache.js");
const { debug: createDebugLogger } = debug_1.default;
const debug = createDebugLogger('statemanager:verkle');
const debug = (0, debug_1.default)('statemanager:verkle');
const PUSH_OFFSET = 95;

@@ -36,3 +32,3 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars

*/
constructor(opts = {}) {
constructor(opts) {
/**

@@ -47,2 +43,3 @@ * StateManager is run in DEBUG mode (default: false)

this.DEBUG = false;
this._blockNum = BigInt(0);
// State along execution (should update)

@@ -53,2 +50,3 @@ this._state = {};

this._postState = {};
this._preState = {};
// Checkpointing

@@ -90,3 +88,8 @@ this._checkpoints = [];

}
this._cachedStateRoot = opts.initialStateRoot;
this.keccakFunction = opts.common?.customCrypto.keccak256 ?? keccak_js_1.keccak256;
if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required');
}
this.verkleCrypto = opts.verkleCrypto;
// Skip DEBUG calls unless 'ethjs' included in environmental DEBUG variables

@@ -96,14 +99,2 @@ // Additional window check is to prevent vite browser bundling (and potentially other) to break

typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false;
/*
* For a custom StateManager implementation adopt these
* callbacks passed to the `Cache` instantiated to perform
* the `get`, `put` and `delete` operations with the
* desired backend.
*/
// const getCb: get = async (address) => {
// return undefined
// }
// const putCb: put = async (keyBuf, accountRlp) => {}
// const deleteCb = async (keyBuf: Uint8Array) => {}
// this._cache = new Cache({ get, putCb, deleteCb })
}

@@ -113,8 +104,11 @@ async getTransitionStateRoot(_, __) {

}
initVerkleExecutionWitness(executionWitness, accessWitness) {
initVerkleExecutionWitness(blockNum, executionWitness, accessWitness) {
this._blockNum = blockNum;
if (executionWitness === null || executionWitness === undefined) {
throw Error(`Invalid executionWitness=${executionWitness} for initVerkleExecutionWitness`);
const errorMsg = `Invalid executionWitness=${executionWitness} for initVerkleExecutionWitness`;
debug(errorMsg);
throw Error(errorMsg);
}
this._executionWitness = executionWitness;
this.accessWitness = accessWitness ?? new accessWitness_js_1.AccessWitness();
this.accessWitness = accessWitness ?? new accessWitness_js_1.AccessWitness({ verkleCrypto: this.verkleCrypto });
this._proof = executionWitness.verkleProof;

@@ -136,2 +130,7 @@ // Populate the pre-state and post-state from the executionWitness

this._state = preState;
// also maintain a separate preState unaffected by any changes in _state
this._preState = preStateRaw.reduce((prevValue, currentValue) => {
const acc = { ...prevValue, ...currentValue };
return acc;
}, {});
const postStateRaw = executionWitness.stateDiff.flatMap(({ stem, suffixDiffs }) => {

@@ -157,36 +156,5 @@ const suffixDiffPairs = suffixDiffs.map(({ newValue, suffix }) => {

}
getTreeKeyForVersion(stem) {
return (0, verkle_1.getKey)(stem, accessWitness_js_1.VERSION_LEAF_KEY);
}
getTreeKeyForBalance(stem) {
return (0, verkle_1.getKey)(stem, accessWitness_js_1.BALANCE_LEAF_KEY);
}
getTreeKeyForNonce(stem) {
return (0, verkle_1.getKey)(stem, accessWitness_js_1.NONCE_LEAF_KEY);
}
getTreeKeyForCodeHash(stem) {
return (0, verkle_1.getKey)(stem, accessWitness_js_1.CODE_KECCAK_LEAF_KEY);
}
getTreeKeyForCodeSize(stem) {
return (0, verkle_1.getKey)(stem, accessWitness_js_1.CODE_SIZE_LEAF_KEY);
}
getTreeKeyForCodeChunk(address, chunkId) {
const { treeIndex, subIndex } = (0, accessWitness_js_1.getTreeIndicesForCodeChunk)(chunkId);
return (0, verkle_1.getKey)((0, verkle_1.getStem)(address, treeIndex), (0, util_1.toBytes)(subIndex));
}
chunkifyCode(code) {
// Pad code to multiple of 31 bytes
if (code.length % 31 !== 0) {
const paddingLength = 31 - (code.length % 31);
code = (0, util_1.setLengthRight)(code, code.length + paddingLength);
}
throw new Error('Not implemented');
}
getTreeKeyForStorageSlot(address, storageKey) {
const { treeIndex, subIndex } = (0, accessWitness_js_1.getTreeIndexesForStorageSlot)(storageKey);
return (0, verkle_1.getKey)((0, verkle_1.getStem)(address, treeIndex), (0, util_1.toBytes)(subIndex));
}
checkChunkWitnessPresent(address, codeOffset) {
async checkChunkWitnessPresent(address, codeOffset) {
const chunkId = codeOffset / 31;
const chunkKey = (0, util_1.bytesToHex)(this.getTreeKeyForCodeChunk(address, chunkId));
const chunkKey = (0, util_1.bytesToHex)(await (0, util_1.getVerkleTreeKeyForCodeChunk)(address, chunkId, this.verkleCrypto));
return this._state[chunkKey] !== undefined;

@@ -200,4 +168,3 @@ }

shallowCopy() {
const stateManager = new StatelessVerkleStateManager();
stateManager.initVerkleExecutionWitness(this._executionWitness);
const stateManager = new StatelessVerkleStateManager({ verkleCrypto: this.verkleCrypto });
return stateManager;

@@ -249,17 +216,14 @@ }

}
// Get the contract code size
const codeSizeKey = this.getTreeKeyForCodeSize((0, verkle_1.getStem)(address, 0));
const codeSizeLE = (0, util_1.hexToBytes)(this._state[(0, util_1.bytesToHex)(codeSizeKey)] ?? '0x');
const codeSize = (0, util_1.bytesToInt32)(codeSizeLE, true);
// allocate the code and copy onto it from the available witness chunks
const accessedCode = new Uint8Array(codeSize);
const chunks = Math.floor((0, util_1.bytesToInt32)(codeSizeLE, true) / 31);
for (let chunkId = 0; chunkId <= chunks; chunkId++) {
const chunkKey = (0, util_1.bytesToHex)(this.getTreeKeyForCodeChunk(address, chunkId));
const codeSize = account.codeSize;
// allocate enough to fit the last chunk
const accessedCode = new Uint8Array(codeSize + 31);
const chunks = Math.floor(codeSize / 31) + 1;
for (let chunkId = 0; chunkId < chunks; chunkId++) {
const chunkKey = (0, util_1.bytesToHex)(await (0, util_1.getVerkleTreeKeyForCodeChunk)(address, chunkId, this.verkleCrypto));
const codeChunk = this._state[chunkKey];
if (codeChunk === undefined) {
throw Error(`Invalid access to a missing code chunk with chunkKey=${chunkKey}`);
}
if (codeChunk === null) {
throw Error(`Invalid access to a non existent code chunk with chunkKey=${chunkKey}`);
const errorMsg = `Invalid access to a non existent code chunk with chunkKey=${chunkKey}`;
debug(errorMsg);
throw Error(errorMsg);
}

@@ -280,4 +244,30 @@ const codeOffset = chunkId * 31;

// Return accessedCode where only accessed code has been copied
return accessedCode;
const contactCode = accessedCode.slice(0, codeSize);
if (!this._codeCacheSettings.deactivate) {
this._codeCache?.put(address, contactCode);
}
return contactCode;
}
async getContractCodeSize(address) {
if (!this._accountCacheSettings.deactivate) {
const elem = this._accountCache.get(address);
if (elem !== undefined) {
const account = elem.accountRLP !== undefined
? util_1.Account.fromRlpSerializedPartialAccount(elem.accountRLP)
: undefined;
if (account === undefined) {
const errorMsg = `account=${account} in cache`;
debug(errorMsg);
throw Error(errorMsg);
}
return account.codeSize;
}
}
// load the account basic fields and codeSize should be in it
const account = await this.getAccount(address);
if (account === undefined) {
return 0;
}
return account.codeSize;
}
/**

@@ -299,3 +289,3 @@ * Gets the storage value associated with the provided `address` and `key`. This method returns

}
const storageKey = this.getTreeKeyForStorageSlot(address, BigInt((0, util_1.bytesToHex)(key)));
const storageKey = await (0, util_1.getVerkleTreeKeyForStorageSlot)(address, BigInt((0, util_1.bytesToHex)(key)), this.verkleCrypto);
const storageValue = (0, util_1.toBytes)(this._state[(0, util_1.bytesToHex)(storageKey)]);

@@ -316,11 +306,12 @@ if (!this._storageCacheSettings.deactivate) {

if (!this._storageCacheSettings.deactivate) {
const encodedValue = rlp_1.RLP.encode(value);
this._storageCache.put(address, key, encodedValue);
this._storageCache.put(address, key, value);
}
else {
// TODO: Consider refactoring this in a writeContractStorage function? Like in stateManager.ts
const storageKey = this.getTreeKeyForStorageSlot(address, BigInt((0, util_1.bytesToHex)(key)));
const storageKey = await (0, util_1.getVerkleTreeKeyForStorageSlot)(address, BigInt((0, util_1.bytesToHex)(key)), this.verkleCrypto);
this._state[(0, util_1.bytesToHex)(storageKey)] = (0, util_1.bytesToHex)((0, util_1.setLengthRight)(value, 32));
}
}
// Note from Gabriel: Clearing storage is not actually not possible in Verkle.
// This is because the storage keys are scattered throughout the verkle tree.
/**

@@ -331,8 +322,7 @@ * Clears all storage entries for the account corresponding to `address`.

async clearContractStorage(address) {
const stem = (0, verkle_1.getStem)(address, 0);
const codeHashKey = this.getTreeKeyForCodeHash(stem);
const stem = (0, util_1.getVerkleStem)(this.verkleCrypto, address, 0);
const codeHashKey = (0, util_1.getVerkleKey)(stem, util_1.VerkleLeafType.CodeHash);
this._storageCache?.clearContractStorage(address);
// Update codeHash to `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`
this._state[(0, util_1.bytesToHex)(codeHashKey)] = util_1.KECCAK256_NULL_S;
// TODO: Clear all storage slots (how?)
}

@@ -344,32 +334,67 @@ async getAccount(address) {

return elem.accountRLP !== undefined
? util_1.Account.fromRlpSerializedAccount(elem.accountRLP)
? util_1.Account.fromRlpSerializedPartialAccount(elem.accountRLP)
: undefined;
}
}
const stem = (0, verkle_1.getStem)(address, 0);
const versionKey = this.getTreeKeyForVersion(stem);
const versionChunk = this._state[(0, util_1.bytesToHex)(versionKey)];
if (versionChunk === undefined) {
throw Error(`Missing execution witness for address=${address} versionKey=${(0, util_1.bytesToHex)(versionKey)}`);
}
// if the versionChunk is null it means the account doesn't exist in pre state
if (versionChunk === null) {
return undefined;
}
const balanceKey = this.getTreeKeyForBalance(stem);
const nonceKey = this.getTreeKeyForNonce(stem);
const codeHashKey = this.getTreeKeyForCodeHash(stem);
const stem = (0, util_1.getVerkleStem)(this.verkleCrypto, address, 0);
const versionKey = (0, util_1.getVerkleKey)(stem, util_1.VerkleLeafType.Version);
const balanceKey = (0, util_1.getVerkleKey)(stem, util_1.VerkleLeafType.Balance);
const nonceKey = (0, util_1.getVerkleKey)(stem, util_1.VerkleLeafType.Nonce);
const codeHashKey = (0, util_1.getVerkleKey)(stem, util_1.VerkleLeafType.CodeHash);
const codeSizeKey = (0, util_1.getVerkleKey)(stem, util_1.VerkleLeafType.CodeSize);
const versionRaw = this._state[(0, util_1.bytesToHex)(versionKey)];
const balanceRaw = this._state[(0, util_1.bytesToHex)(balanceKey)];
const nonceRaw = this._state[(0, util_1.bytesToHex)(nonceKey)];
const codeHashRaw = this._state[(0, util_1.bytesToHex)(codeHashKey)];
const account = util_1.Account.fromAccountData({
balance: typeof balanceRaw === 'string' ? (0, util_1.bytesToBigInt)((0, util_1.hexToBytes)(balanceRaw), true) : undefined,
nonce: typeof nonceRaw === 'string' ? (0, util_1.bytesToBigInt)((0, util_1.hexToBytes)(nonceRaw), true) : undefined,
codeHash: codeHashRaw?.length === 32 ? codeHashRaw : util_1.KECCAK256_NULL_S,
const codeSizeRaw = this._state[(0, util_1.bytesToHex)(codeSizeKey)];
// check if the account didn't exist if any of the basic keys have null
if (versionRaw === null || balanceRaw === null || nonceRaw === null || codeHashRaw === null) {
// check any of the other key shouldn't have string input available as this account didn't exist
if (typeof versionRaw === `string` ||
typeof balanceRaw === 'string' ||
typeof nonceRaw === 'string' ||
typeof codeHashRaw === 'string') {
const errorMsg = `Invalid witness for a non existing address=${address} stem=${(0, util_1.bytesToHex)(stem)}`;
debug(errorMsg);
throw Error(errorMsg);
}
else {
return undefined;
}
}
// check if codehash is correct 32 bytes prefixed hex string
if (codeHashRaw !== undefined && codeHashRaw !== null && codeHashRaw.length !== 66) {
const errorMsg = `Invalid codeHashRaw=${codeHashRaw} for address=${address} chunkKey=${(0, util_1.bytesToHex)(codeHashKey)}`;
debug(errorMsg);
throw Error(errorMsg);
}
if (versionRaw === undefined &&
balanceRaw === undefined &&
nonceRaw === undefined &&
codeHashRaw === undefined &&
codeSizeRaw === undefined) {
const errorMsg = `No witness bundled for address=${address} stem=${(0, util_1.bytesToHex)(stem)}`;
debug(errorMsg);
throw Error(errorMsg);
}
const account = util_1.Account.fromPartialAccountData({
version: typeof versionRaw === 'string' ? (0, util_1.bytesToInt32)((0, util_1.hexToBytes)(versionRaw), true) : null,
balance: typeof balanceRaw === 'string' ? (0, util_1.bytesToBigInt)((0, util_1.hexToBytes)(balanceRaw), true) : null,
nonce: typeof nonceRaw === 'string' ? (0, util_1.bytesToBigInt)((0, util_1.hexToBytes)(nonceRaw), true) : null,
codeHash: typeof codeHashRaw === 'string' ? (0, util_1.hexToBytes)(codeHashRaw) : null,
// if codeSizeRaw is null, it means account didnt exist or it was EOA either way codeSize is 0
// if codeSizeRaw is undefined, then we pass in null which in our context of partial account means
// not specified
codeSize: typeof codeSizeRaw === 'string'
? (0, util_1.bytesToInt32)((0, util_1.hexToBytes)(codeSizeRaw), true)
: codeSizeRaw === null
? 0
: null,
storageRoot: null,
});
if (this.DEBUG) {
debug(`getAccount address=${address.toString()} stem=${(0, util_1.short)(stem)} balance=${account.balance} nonce=${account.nonce} codeHash=${(0, util_1.short)(account.codeHash)} storageHash=${(0, util_1.short)(account.storageRoot)}`);
debug(`getAccount address=${address.toString()} stem=${(0, util_1.short)(stem)}`);
}
if (!this._accountCacheSettings.deactivate) {
this._accountCache?.put(address, account);
this._accountCache?.put(address, account, true);
}

@@ -380,9 +405,9 @@ return account;

if (this.DEBUG) {
debug(`putAccount address=${address.toString()} balance=${account.balance} nonce=${account.nonce} codeHash=${(0, util_1.short)(account.codeHash)} storageHash=${(0, util_1.short)(account.storageRoot)}`);
debug(`putAccount address=${address.toString()}`);
}
if (this._accountCacheSettings.deactivate) {
const stem = (0, verkle_1.getStem)(address, 0);
const balanceKey = this.getTreeKeyForBalance(stem);
const nonceKey = this.getTreeKeyForNonce(stem);
const codeHashKey = this.getTreeKeyForCodeHash(stem);
const stem = (0, util_1.getVerkleStem)(this.verkleCrypto, address, 0);
const balanceKey = (0, util_1.getVerkleKey)(stem, util_1.VerkleLeafType.Balance);
const nonceKey = (0, util_1.getVerkleKey)(stem, util_1.VerkleLeafType.Nonce);
const codeHashKey = (0, util_1.getVerkleKey)(stem, util_1.VerkleLeafType.CodeHash);
const balanceBuf = (0, util_1.setLengthRight)((0, util_1.bigIntToBytes)(account.balance, true), 32);

@@ -396,3 +421,3 @@ const nonceBuf = (0, util_1.setLengthRight)((0, util_1.bigIntToBytes)(account.nonce, true), 32);

if (account !== undefined) {
this._accountCache.put(address, account);
this._accountCache.put(address, account, true);
}

@@ -423,6 +448,6 @@ else {

}
account.nonce = accountFields.nonce ?? account.nonce;
account.balance = accountFields.balance ?? account.balance;
account.storageRoot = accountFields.storageRoot ?? account.storageRoot;
account.codeHash = accountFields.codeHash ?? account.codeHash;
account._nonce = accountFields.nonce ?? account._nonce;
account._balance = accountFields.balance ?? account._balance;
account._storageRoot = accountFields.storageRoot ?? account._storageRoot;
account._codeHash = accountFields.codeHash ?? account._codeHash;
await this.putAccount(address, account);

@@ -433,17 +458,13 @@ }

}
async verifyProof(parentVerkleRoot) {
// Implementation: https://github.com/crate-crypto/rust-verkle-wasm/blob/master/src/lib.rs#L45
// The root is the root of the current (un-updated) trie
// The proof is proof of membership of all of the accessed values
// keys_values is a map from the key of the accessed value to a tuple
// the tuple contains the old value and the updated value
//
// This function returns the new root when all of the updated values are applied
const updatedStateRoot = (0, verkle_1.verifyUpdate)(parentVerkleRoot, this._proof, // TODO: Convert this into a Uint8Array ingestible by the method
new Map() // TODO: Generate the keys_values map from the old to the updated value
);
// TODO: Not sure if this should return the updated state Root (current block) or the un-updated one (parent block)
const verkleRoot = await this.getStateRoot();
// Verify that updatedStateRoot matches the state root of the block
return (0, utils_1.equalsBytes)(updatedStateRoot, verkleRoot);
/**
* Verifies whether the execution witness matches the stateRoot
* @param {Uint8Array} stateRoot - The stateRoot to verify the executionWitness against
* @returns {boolean} - Returns true if the executionWitness matches the provided stateRoot, otherwise false
*/
verifyProof(stateRoot) {
if (this._executionWitness === undefined) {
debug('Missing executionWitness');
return false;
}
return (0, util_1.verifyVerkleProof)(this.verkleCrypto, stateRoot, this._executionWitness);
}

@@ -456,3 +477,3 @@ // Verifies that the witness post-state matches the computed post-state

// switch to false if postVerify fails
let postVerified = true;
let postFailures = 0;
for (const accessedState of this.accessWitness.accesses()) {

@@ -469,7 +490,12 @@ const { address, type } = accessedState;

accessedChunks.set(chunkKey, true);
let computedValue = this.getComputedValue(accessedState);
const computedValue = this.getComputedValue(accessedState) ?? this._preState[chunkKey];
if (computedValue === undefined) {
debug(`Block accesses missing in canonical address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`);
postFailures++;
continue;
}
let canonicalValue = this._postState[chunkKey];
if (canonicalValue === undefined) {
debug(`Block accesses missing in canonical address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`);
postVerified = false;
postFailures++;
continue;

@@ -480,5 +506,10 @@ }

if (accessedState.type === accessWitness_js_1.AccessedStateType.Code) {
computedValue = computedValue !== null ? `0x${computedValue.slice(4)}` : null;
// computedValue = computedValue !== null ? `0x${computedValue.slice(4)}` : null
canonicalValue = canonicalValue !== null ? `0x${canonicalValue.slice(4)}` : null;
}
else if (accessedState.type === accessWitness_js_1.AccessedStateType.Storage &&
canonicalValue === null &&
computedValue === ZEROVALUE) {
canonicalValue = ZEROVALUE;
}
if (computedValue !== canonicalValue) {

@@ -496,3 +527,3 @@ const decodedComputedValue = (0, accessWitness_js_1.decodeValue)(accessedState.type, computedValue);

debug(`computed=${displayComputedValue}`);
postVerified = false;
postFailures++;
}

@@ -503,7 +534,8 @@ }

debug(`Missing chunk access for canChunkKey=${canChunkKey}`);
postVerified = false;
postFailures++;
}
}
debug(`verifyPostState=${postVerified}`);
return postVerified;
const verifyPassed = postFailures === 0;
debug(`verifyPostState verifyPassed=${verifyPassed} postFailures=${postFailures}`);
return verifyPassed;
}

@@ -527,3 +559,3 @@ getComputedValue(accessedState) {

}
const balanceBigint = util_1.Account.fromRlpSerializedAccount(encodedAccount).balance;
const balanceBigint = util_1.Account.fromRlpSerializedPartialAccount(encodedAccount).balance;
return (0, util_1.bytesToHex)((0, util_1.setLengthRight)((0, util_1.bigIntToBytes)(balanceBigint, true), 32));

@@ -536,3 +568,3 @@ }

}
const nonceBigint = util_1.Account.fromRlpSerializedAccount(encodedAccount).nonce;
const nonceBigint = util_1.Account.fromRlpSerializedPartialAccount(encodedAccount).nonce;
return (0, util_1.bytesToHex)((0, util_1.setLengthRight)((0, util_1.bigIntToBytes)(nonceBigint, true), 32));

@@ -545,3 +577,3 @@ }

}
return (0, util_1.bytesToHex)(util_1.Account.fromRlpSerializedAccount(encodedAccount).codeHash);
return (0, util_1.bytesToHex)(util_1.Account.fromRlpSerializedPartialAccount(encodedAccount).codeHash);
}

@@ -556,5 +588,7 @@ case accessWitness_js_1.AccessedStateType.CodeSize: {

}
const account = util_1.Account.fromRlpSerializedAccount(encodedAccount);
const account = util_1.Account.fromRlpSerializedPartialAccount(encodedAccount);
if (account.isContract()) {
throw Error(`Code cache not found for address=${address.toString()}`);
const errorMsg = `Code cache not found for address=${address.toString()}`;
debug(errorMsg);
throw Error(errorMsg);
}

@@ -577,11 +611,12 @@ else {

const chunkSize = 31;
return (0, util_1.bytesToHex)(code.slice(codeOffset, codeOffset + chunkSize));
return (0, util_1.bytesToHex)((0, util_1.setLengthRight)(code.slice(codeOffset, codeOffset + chunkSize), chunkSize));
}
case accessWitness_js_1.AccessedStateType.Storage: {
const { slot } = accessedState;
const storage = this._storageCache?.get(address, (0, util_1.bigIntToBytes)(slot));
const key = (0, util_1.setLengthLeft)((0, util_1.bigIntToBytes)(slot), 32);
const storage = this._storageCache?.get(address, key);
if (storage === undefined) {
return null;
}
return (0, util_1.bytesToHex)(storage);
return (0, util_1.bytesToHex)((0, util_1.setLengthLeft)(storage, 32));
}

@@ -631,16 +666,21 @@ }

/**
* Gets the verkle root.
* NOTE: this needs some examination in the code where this is needed
* and if we have the verkle root present
* @returns {Promise<Uint8Array>} - Returns the verkle root of the `StateManager`
* Gets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* This is required to verify and prove verkle execution witnesses.
* @returns {Promise<Uint8Array>} - Returns the cached state root
*/
async getStateRoot() {
return new Uint8Array(0);
if (this._cachedStateRoot === undefined) {
throw new Error('Cache state root missing');
}
return this._cachedStateRoot;
}
/**
* TODO: needed?
* Maybe in this context: reset to original pre state suffice
* @param stateRoot - The verkle root to reset the instance to
* Sets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* @param stateRoot - The stateRoot to set
*/
async setStateRoot(_) { }
async setStateRoot(stateRoot) {
this._cachedStateRoot = stateRoot;
}
/**

@@ -647,0 +687,0 @@ * Dumps the RLP-encoded storage values for an `account` specified by `address`.

@@ -6,4 +6,3 @@ import { Common } from '@ethereumjs/common';

import { OriginalStorageCache } from './cache/originalStorageCache.js';
import type { AccountFields, EVMStateManagerInterface, StorageDump } from '@ethereumjs/common';
import type { StorageRange } from '@ethereumjs/common/src';
import type { AccountFields, EVMStateManagerInterface, StorageDump, StorageRange } from '@ethereumjs/common';
import type { DB, PrefixedHexString } from '@ethereumjs/util';

@@ -186,8 +185,22 @@ import type { Debugger } from 'debug';

getContractCode(address: Address): Promise<Uint8Array>;
getContractCodeSize(address: Address): Promise<number>;
/**
* Gets the storage trie for an account from the storage
* cache or does a lookup.
* Gets the storage trie for the EVM-internal account identified by the provided address/hash.
* If the storage trie is not in the local cache ('this._storageTries'),
* generates a new storage trie object based on a lookup (shallow copy from 'this._trie'),
* applies the storage root of the provided rootAccount (or an
* empty trie root if no rootAccount is provided), and stores the new entry
* in the local cache.
* @param addressOrHash Address (or other object) with populated 'bytes', or a raw Uint8Array.
* Used to identify the requested storage trie in the local cache and define the
* prefix used when creating a new storage trie.
* @param rootAccount (Optional) Account object whose 'storageRoot' is to be used as
* the root of the new storageTrie returned when there is no pre-existing trie.
* If left undefined, the EMPTY_TRIE_ROOT will be used as the root instead.
* @returns storage Trie object
* @private
*/
protected _getStorageTrie(addressOrHash: Address | Uint8Array, account?: Account): Trie;
protected _getStorageTrie(addressOrHash: Address | {
bytes: Uint8Array;
} | Uint8Array, rootAccount?: Account): Trie;
/**

@@ -194,0 +207,0 @@ * Gets the storage trie for an account from the storage

@@ -12,3 +12,2 @@ "use strict";

const originalStorageCache_js_1 = require("./cache/originalStorageCache.js");
const { debug: createDebugLogger } = debug_1.default;
/**

@@ -51,3 +50,3 @@ * Prefix to distinguish between a contract deployed with code `0x80`

typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false;
this._debug = createDebugLogger('statemanager:statemanager');
this._debug = (0, debug_1.default)('statemanager:statemanager');
this.common = opts.common ?? new common_1.Common({ chain: common_1.Chain.Mainnet });

@@ -189,5 +188,2 @@ this._checkpointCount = 0;

const codeHash = this.keccakFunction(value);
if ((0, util_1.equalsBytes)(codeHash, util_1.KECCAK256_NULL)) {
return;
}
if (this.DEBUG) {

@@ -230,21 +226,33 @@ this._debug(`Update codeHash (-> ${(0, util_1.short)(codeHash)}) for account ${address}`);

}
async getContractCodeSize(address) {
const contractCode = await this.getContractCode(address);
return contractCode.length;
}
/**
* Gets the storage trie for an account from the storage
* cache or does a lookup.
* Gets the storage trie for the EVM-internal account identified by the provided address/hash.
* If the storage trie is not in the local cache ('this._storageTries'),
* generates a new storage trie object based on a lookup (shallow copy from 'this._trie'),
* applies the storage root of the provided rootAccount (or an
* empty trie root if no rootAccount is provided), and stores the new entry
* in the local cache.
* @param addressOrHash Address (or other object) with populated 'bytes', or a raw Uint8Array.
* Used to identify the requested storage trie in the local cache and define the
* prefix used when creating a new storage trie.
* @param rootAccount (Optional) Account object whose 'storageRoot' is to be used as
* the root of the new storageTrie returned when there is no pre-existing trie.
* If left undefined, the EMPTY_TRIE_ROOT will be used as the root instead.
* @returns storage Trie object
* @private
*/
// TODO PR: have a better interface for hashed address pull?
_getStorageTrie(addressOrHash, account) {
_getStorageTrie(addressOrHash, rootAccount) {
// use hashed key for lookup from storage cache
const addressHex = (0, util_1.bytesToUnprefixedHex)(addressOrHash instanceof util_1.Address ? this.keccakFunction(addressOrHash.bytes) : addressOrHash);
const addressBytes = addressOrHash instanceof Uint8Array ? addressOrHash : this.keccakFunction(addressOrHash.bytes);
const addressHex = (0, util_1.bytesToUnprefixedHex)(addressBytes);
let storageTrie = this._storageTries[addressHex];
if (storageTrie === undefined) {
const keyPrefix = this._prefixStorageTrieKeys
? (addressOrHash instanceof util_1.Address
? this.keccakFunction(addressOrHash.bytes)
: addressOrHash).slice(0, 7)
: undefined;
const keyPrefix = this._prefixStorageTrieKeys ? addressBytes.slice(0, 7) : undefined;
storageTrie = this._trie.shallowCopy(false, { keyPrefix });
if (account !== undefined) {
storageTrie.root(account.storageRoot);
if (rootAccount !== undefined) {
storageTrie.root(rootAccount.storageRoot);
}

@@ -251,0 +259,0 @@ else {

@@ -1,14 +0,3 @@

import type { Address, PrefixedHexString } from '@ethereumjs/util';
/**
* Tree key constants.
*/
export declare const VERSION_LEAF_KEY: Uint8Array;
export declare const BALANCE_LEAF_KEY: Uint8Array;
export declare const NONCE_LEAF_KEY: Uint8Array;
export declare const CODE_KECCAK_LEAF_KEY: Uint8Array;
export declare const CODE_SIZE_LEAF_KEY: Uint8Array;
export declare const HEADER_STORAGE_OFFSET = 64;
export declare const CODE_OFFSET = 128;
export declare const VERKLE_NODE_WIDTH = 256;
export declare const MAIN_STORAGE_OFFSET: bigint;
import type { AccessEventFlags, AccessWitnessInterface } from '@ethereumjs/common';
import type { Address, PrefixedHexString, VerkleCrypto } from '@ethereumjs/util';
declare type StemAccessEvent = {

@@ -20,9 +9,2 @@ write?: boolean;

};
declare type AccessEventFlags = {
stemRead: boolean;
stemWrite: boolean;
chunkRead: boolean;
chunkWrite: boolean;
chunkFill: boolean;
};
declare type StemMeta = {

@@ -60,6 +42,8 @@ address: Address;

};
export declare class AccessWitness {
export declare class AccessWitness implements AccessWitnessInterface {
stems: Map<PrefixedHexString, StemAccessEvent & StemMeta>;
chunks: Map<PrefixedHexString, ChunkAccessEvent>;
verkleCrypto: VerkleCrypto;
constructor(opts?: {
verkleCrypto?: VerkleCrypto;
stems?: Map<PrefixedHexString, StemAccessEvent & StemMeta>;

@@ -71,8 +55,6 @@ chunks?: Map<PrefixedHexString, ChunkAccessEvent>;

touchAndChargeValueTransfer(caller: Address, target: Address): bigint;
touchAndChargeContractCreateInit(address: Address, { sendsValue }?: {
sendsValue?: boolean;
}): bigint;
touchAndChargeContractCreateInit(address: Address): bigint;
touchAndChargeContractCreateCompleted(address: Address): bigint;
touchTxOriginAndComputeGas(origin: Address): bigint;
touchTxExistingAndComputeGas(target: Address, { sendsValue }?: {
touchTxTargetAndComputeGas(target: Address, { sendsValue }?: {
sendsValue?: boolean;

@@ -96,13 +78,5 @@ }): bigint;

}
export declare function getTreeIndexesForStorageSlot(storageKey: bigint): {
treeIndex: bigint;
subIndex: number;
};
export declare function getTreeIndicesForCodeChunk(chunkId: number): {
treeIndex: number;
subIndex: number;
};
export declare function decodeAccessedState(treeIndex: number | bigint, chunkIndex: number): AccessedState;
export declare function decodeValue(type: AccessedStateType, value: string | null): string;
export declare function decodeValue(type: AccessedStateType, value: PrefixedHexString | null): string;
export {};
//# sourceMappingURL=accessWitness.d.ts.map

@@ -1,20 +0,7 @@

import { BIGINT_0, bytesToBigInt, bytesToHex, hexToBytes, toBytes } from '@ethereumjs/util';
import { getKey, getStem } from '@ethereumjs/verkle';
import { BIGINT_0, VERKLE_BALANCE_LEAF_KEY, VERKLE_CODE_HASH_LEAF_KEY, VERKLE_CODE_OFFSET, VERKLE_CODE_SIZE_LEAF_KEY, VERKLE_HEADER_STORAGE_OFFSET, VERKLE_MAIN_STORAGE_OFFSET, VERKLE_NODE_WIDTH, VERKLE_NONCE_LEAF_KEY, VERKLE_VERSION_LEAF_KEY, bytesToBigInt, bytesToHex, getVerkleKey, getVerkleStem, getVerkleTreeIndicesForCodeChunk, hexToBytes, intToBytes, } from '@ethereumjs/util';
import debugDefault from 'debug';
const { debug: createDebugLogger } = debugDefault;
const debug = createDebugLogger('statemanager:verkle:aw');
const debug = debugDefault('statemanager:verkle:aw');
/**
* Tree key constants.
*/
export const VERSION_LEAF_KEY = toBytes(0);
export const BALANCE_LEAF_KEY = toBytes(1);
export const NONCE_LEAF_KEY = toBytes(2);
export const CODE_KECCAK_LEAF_KEY = toBytes(3);
export const CODE_SIZE_LEAF_KEY = toBytes(4);
export const HEADER_STORAGE_OFFSET = 64;
export const CODE_OFFSET = 128;
export const VERKLE_NODE_WIDTH = 256;
// export const MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(31)
// incorrect value to match with kaustinen2 offset
export const MAIN_STORAGE_OFFSET = BigInt(256) * BigInt(2) ** BigInt(31);
const WitnessBranchReadCost = BigInt(1900);

@@ -37,2 +24,6 @@ const WitnessChunkReadCost = BigInt(200);

constructor(opts = {}) {
if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required');
}
this.verkleCrypto = opts.verkleCrypto;
this.stems = opts.stems ?? new Map();

@@ -43,7 +34,7 @@ this.chunks = opts.chunks ?? new Map();

let gas = BIGINT_0;
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, BALANCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, CODE_KECCAK_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, NONCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_BALANCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_HASH_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_NONCE_LEAF_KEY);
return gas;

@@ -53,4 +44,4 @@ }

let gas = BIGINT_0;
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY);
return gas;

@@ -60,14 +51,10 @@ }

let gas = BIGINT_0;
gas += this.touchAddressOnWriteAndComputeGas(caller, 0, BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(target, 0, BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(caller, 0, VERKLE_BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(target, 0, VERKLE_BALANCE_LEAF_KEY);
return gas;
}
touchAndChargeContractCreateInit(address, { sendsValue } = {}) {
touchAndChargeContractCreateInit(address) {
let gas = BIGINT_0;
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERSION_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, NONCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, CODE_KECCAK_LEAF_KEY);
if (sendsValue === true) {
gas += this.touchAddressOnWriteAndComputeGas(address, 0, BALANCE_LEAF_KEY);
}
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_NONCE_LEAF_KEY);
return gas;

@@ -77,7 +64,7 @@ }

let gas = BIGINT_0;
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERSION_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, CODE_KECCAK_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, NONCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_CODE_HASH_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_NONCE_LEAF_KEY);
return gas;

@@ -87,20 +74,20 @@ }

let gas = BIGINT_0;
gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, CODE_KECCAK_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, NONCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, BALANCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERKLE_CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERKLE_CODE_HASH_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, VERKLE_NONCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, VERKLE_BALANCE_LEAF_KEY);
return gas;
}
touchTxExistingAndComputeGas(target, { sendsValue } = {}) {
touchTxTargetAndComputeGas(target, { sendsValue } = {}) {
let gas = BIGINT_0;
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, CODE_KECCAK_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, NONCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_VERSION_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_CODE_SIZE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_CODE_HASH_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_NONCE_LEAF_KEY);
if (sendsValue === true) {
gas += this.touchAddressOnWriteAndComputeGas(target, 0, BALANCE_LEAF_KEY);
gas += this.touchAddressOnWriteAndComputeGas(target, 0, VERKLE_BALANCE_LEAF_KEY);
}
else {
gas += this.touchAddressOnReadAndComputeGas(target, 0, BALANCE_LEAF_KEY);
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_BALANCE_LEAF_KEY);
}

@@ -112,3 +99,3 @@ return gas;

for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkNum);
const { treeIndex, subIndex } = getVerkleTreeIndicesForCodeChunk(chunkNum);
gas += this.touchAddressOnReadAndComputeGas(contact, treeIndex, subIndex);

@@ -121,3 +108,3 @@ }

for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkNum);
const { treeIndex, subIndex } = getVerkleTreeIndicesForCodeChunk(chunkNum);
gas += this.touchAddressOnWriteAndComputeGas(contact, treeIndex, subIndex);

@@ -136,15 +123,15 @@ }

const { stemRead, stemWrite, chunkRead, chunkWrite, chunkFill } = this.touchAddress(address, treeIndex, subIndex, { isWrite });
if (stemRead) {
if (stemRead === true) {
gas += WitnessBranchReadCost;
}
if (stemWrite) {
if (stemWrite === true) {
gas += WitnessBranchWriteCost;
}
if (chunkRead) {
if (chunkRead === true) {
gas += WitnessChunkReadCost;
}
if (chunkWrite) {
if (chunkWrite === true) {
gas += WitnessChunkWriteCost;
}
if (chunkFill) {
if (chunkFill === true) {
gas += WitnessChunkFillCost;

@@ -160,3 +147,3 @@ }

const chunkFill = false;
const accessedStemKey = getStem(address, treeIndex);
const accessedStemKey = getVerkleStem(this.verkleCrypto, address, treeIndex);
const accessedStemHex = bytesToHex(accessedStemKey);

@@ -169,3 +156,3 @@ let accessedStem = this.stems.get(accessedStemHex);

}
const accessedChunkKey = getKey(accessedStemKey, toBytes(subIndex));
const accessedChunkKey = getVerkleKey(accessedStemKey, typeof subIndex === 'number' ? intToBytes(subIndex) : subIndex);
const accessedChunkKeyHex = bytesToHex(accessedChunkKey);

@@ -195,3 +182,3 @@ let accessedChunk = this.chunks.get(accessedChunkKeyHex);

shallowCopy() {
return new AccessWitness();
return new AccessWitness({ verkleCrypto: this.verkleCrypto });
}

@@ -244,19 +231,2 @@ merge(accessWitness) {

}
export function getTreeIndexesForStorageSlot(storageKey) {
let position;
if (storageKey < CODE_OFFSET - HEADER_STORAGE_OFFSET) {
position = BigInt(HEADER_STORAGE_OFFSET) + storageKey;
}
else {
position = MAIN_STORAGE_OFFSET + storageKey;
}
const treeIndex = position / BigInt(VERKLE_NODE_WIDTH);
const subIndex = Number(position % BigInt(VERKLE_NODE_WIDTH));
return { treeIndex, subIndex };
}
export function getTreeIndicesForCodeChunk(chunkId) {
const treeIndex = Math.floor((CODE_OFFSET + chunkId) / VERKLE_NODE_WIDTH);
const subIndex = (CODE_OFFSET + chunkId) % VERKLE_NODE_WIDTH;
return { treeIndex, subIndex };
}
export function decodeAccessedState(treeIndex, chunkIndex) {

@@ -276,15 +246,15 @@ const position = BigInt(treeIndex) * BigInt(VERKLE_NODE_WIDTH) + BigInt(chunkIndex);

default:
if (position < HEADER_STORAGE_OFFSET) {
throw Error(`No attribute yet stored >=5 and <${HEADER_STORAGE_OFFSET}`);
if (position < VERKLE_HEADER_STORAGE_OFFSET) {
throw Error(`No attribute yet stored >=5 and <${VERKLE_HEADER_STORAGE_OFFSET}`);
}
if (position >= HEADER_STORAGE_OFFSET && position < CODE_OFFSET) {
const slot = position - BigInt(HEADER_STORAGE_OFFSET);
if (position >= VERKLE_HEADER_STORAGE_OFFSET && position < VERKLE_CODE_OFFSET) {
const slot = position - BigInt(VERKLE_HEADER_STORAGE_OFFSET);
return { type: AccessedStateType.Storage, slot };
}
else if (position >= CODE_OFFSET && position < MAIN_STORAGE_OFFSET) {
const codeChunkIdx = Number(position) - CODE_OFFSET;
else if (position >= VERKLE_CODE_OFFSET && position < VERKLE_MAIN_STORAGE_OFFSET) {
const codeChunkIdx = Number(position) - VERKLE_CODE_OFFSET;
return { type: AccessedStateType.Code, codeOffset: codeChunkIdx * 31 };
}
else if (position >= MAIN_STORAGE_OFFSET) {
const slot = BigInt(position - MAIN_STORAGE_OFFSET);
else if (position >= VERKLE_MAIN_STORAGE_OFFSET) {
const slot = BigInt(position - VERKLE_MAIN_STORAGE_OFFSET);
return { type: AccessedStateType.Storage, slot };

@@ -291,0 +261,0 @@ }

@@ -34,3 +34,3 @@ import { OrderedMap } from 'js-sdsl';

*/
put(address: Address, account: Account | undefined): void;
put(address: Address, account: Account | undefined, couldBeParitalAccount?: boolean): void;
/**

@@ -37,0 +37,0 @@ * Returns the queried account or undefined if account doesn't exist

@@ -7,3 +7,2 @@ import { bytesToUnprefixedHex } from '@ethereumjs/util';

import { CacheType } from './types.js';
const { debug: createDebugLogger } = debugDefault;
export class AccountCache extends Cache {

@@ -32,3 +31,3 @@ constructor(opts) {

this._diffCache.push(new Map());
this._debug = createDebugLogger('statemanager:cache:account');
this._debug = debugDefault('statemanager:cache:account');
}

@@ -53,7 +52,11 @@ _saveCachePreState(cacheKeyHex) {

*/
put(address, account) {
put(address, account, couldBeParitalAccount = false) {
const addressHex = bytesToUnprefixedHex(address.bytes);
this._saveCachePreState(addressHex);
const elem = {
accountRLP: account !== undefined ? account.serialize() : undefined,
accountRLP: account !== undefined
? couldBeParitalAccount
? account.serializeWithPartialInfo()
: account.serialize()
: undefined,
};

@@ -60,0 +63,0 @@ if (this.DEBUG) {

import debugDefault from 'debug';
const { debug: createDebugLogger } = debugDefault;
export class Cache {

@@ -26,5 +25,5 @@ constructor() {

typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false;
this._debug = createDebugLogger('statemanager:cache');
this._debug = debugDefault('statemanager:cache');
}
}
//# sourceMappingURL=cache.js.map

@@ -7,3 +7,2 @@ import { bytesToUnprefixedHex } from '@ethereumjs/util';

import { CacheType } from './types.js';
const { debug: createDebugLogger } = debugDefault;
export class CodeCache extends Cache {

@@ -32,3 +31,3 @@ constructor(opts) {

this._diffCache.push(new Map());
this._debug = createDebugLogger('statemanager:cache:code');
this._debug = debugDefault('statemanager:cache:code');
}

@@ -35,0 +34,0 @@ /**

@@ -7,3 +7,2 @@ import { bytesToUnprefixedHex, hexToBytes } from '@ethereumjs/util';

import { CacheType } from './types.js';
const { debug: createDebugLogger } = debugDefault;
export class StorageCache extends Cache {

@@ -33,3 +32,3 @@ constructor(opts) {

if (this.DEBUG) {
this._debug = createDebugLogger('statemanager:cache:storage');
this._debug = debugDefault('statemanager:cache:storage');
}

@@ -36,0 +35,0 @@ }

@@ -52,2 +52,3 @@ import { Common } from '@ethereumjs/common';

getContractCode(address: Address): Promise<Uint8Array>;
getContractCodeSize(address: Address): Promise<number>;
/**

@@ -100,6 +101,4 @@ * Adds `value` to the state trie as code, and sets `codeHash` on the account

/**
* Gets the code corresponding to the provided `address`.
* @param address - Address to get the `account` for
* @returns {Promise<Uint8Array>} - Resolves with the code corresponding to the provided address.
* Returns an empty `Uint8Array` if the account has no associated code.
* Gets the account associated with `address` or `undefined` if account does not exist
* @param address - Address of the `account` to get
*/

@@ -106,0 +105,0 @@ getAccount(address: Address): Promise<Account | undefined>;

@@ -8,3 +8,2 @@ import { Chain, Common } from '@ethereumjs/common';

import { AccountCache, CacheType, OriginalStorageCache, StorageCache } from './cache/index.js';
const { debug: createDebugLogger } = debugDefault;
const KECCAK256_RLP_EMPTY_ACCOUNT = RLP.encode(new Account().serialize()).slice(2);

@@ -33,3 +32,3 @@ export class RPCStateManager {

typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false;
this._debug = createDebugLogger('statemanager:rpcStateManager');
this._debug = debugDefault('statemanager:rpcStateManager');
if (typeof opts.provider === 'string' && opts.provider.startsWith('http')) {

@@ -108,2 +107,6 @@ this._provider = opts.provider;

}
async getContractCodeSize(address) {
const contractCode = await this.getContractCode(address);
return contractCode.length;
}
/**

@@ -209,6 +212,4 @@ * Adds `value` to the state trie as code, and sets `codeHash` on the account

/**
* Gets the code corresponding to the provided `address`.
* @param address - Address to get the `account` for
* @returns {Promise<Uint8Array>} - Resolves with the code corresponding to the provided address.
* Returns an empty `Uint8Array` if the account has no associated code.
* Gets the account associated with `address` or `undefined` if account does not exist
* @param address - Address of the `account` to get
*/

@@ -222,6 +223,7 @@ async getAccount(address) {

}
const rlp = (await this.getAccountFromProvider(address)).serialize();
const account = equalsBytes(rlp, KECCAK256_RLP_EMPTY_ACCOUNT) === false
? Account.fromRlpSerializedAccount(rlp)
: undefined;
const accountFromProvider = await this.getAccountFromProvider(address);
const account = equalsBytes(accountFromProvider.codeHash, new Uint8Array(32).fill(0)) ||
equalsBytes(accountFromProvider.serialize(), KECCAK256_RLP_EMPTY_ACCOUNT)
? undefined
: Account.fromRlpSerializedAccount(accountFromProvider.serialize());
this._accountCache?.put(address, account);

@@ -228,0 +230,0 @@ return account;

@@ -7,5 +7,4 @@ import { Account } from '@ethereumjs/util';

import type { DefaultStateManager } from './stateManager.js';
import type { VerkleExecutionWitness } from '@ethereumjs/block';
import type { AccountFields, Common, EVMStateManagerInterface, Proof, StorageDump, StorageRange } from '@ethereumjs/common';
import type { Address, PrefixedHexString } from '@ethereumjs/util';
import type { Address, PrefixedHexString, VerkleCrypto, VerkleExecutionWitness } from '@ethereumjs/util';
export interface VerkleState {

@@ -68,2 +67,4 @@ [key: PrefixedHexString]: PrefixedHexString | null;

accesses?: AccessWitness;
verkleCrypto: VerkleCrypto;
initialStateRoot?: Uint8Array;
}

@@ -85,3 +86,5 @@ /**

_codeCache?: CodeCache;
_cachedStateRoot?: Uint8Array;
originalStorageCache: OriginalStorageCache;
verkleCrypto: VerkleCrypto;
protected readonly _accountCacheSettings: CacheSettings;

@@ -99,2 +102,3 @@ protected readonly _storageCacheSettings: CacheSettings;

protected readonly DEBUG: boolean;
private _blockNum;
private _executionWitness?;

@@ -104,2 +108,3 @@ private _proof;

private _postState;
private _preState;
private _checkpoints;

@@ -111,14 +116,6 @@ accessWitness?: AccessWitness;

*/
constructor(opts?: StatelessVerkleStateManagerOpts);
constructor(opts: StatelessVerkleStateManagerOpts);
getTransitionStateRoot(_: DefaultStateManager, __: Uint8Array): Promise<Uint8Array>;
initVerkleExecutionWitness(executionWitness?: VerkleExecutionWitness | null, accessWitness?: AccessWitness): void;
getTreeKeyForVersion(stem: Uint8Array): Uint8Array;
getTreeKeyForBalance(stem: Uint8Array): Uint8Array;
getTreeKeyForNonce(stem: Uint8Array): Uint8Array;
getTreeKeyForCodeHash(stem: Uint8Array): Uint8Array;
getTreeKeyForCodeSize(stem: Uint8Array): Uint8Array;
getTreeKeyForCodeChunk(address: Address, chunkId: number): Uint8Array;
chunkifyCode(code: Uint8Array): void;
getTreeKeyForStorageSlot(address: Address, storageKey: bigint): Uint8Array;
checkChunkWitnessPresent(address: Address, codeOffset: number): boolean;
initVerkleExecutionWitness(blockNum: bigint, executionWitness?: VerkleExecutionWitness | null, accessWitness?: AccessWitness): void;
checkChunkWitnessPresent(address: Address, codeOffset: number): Promise<boolean>;
/**

@@ -144,2 +141,3 @@ * Copies the current instance of the `StateManager`

getContractCode(address: Address): Promise<Uint8Array>;
getContractCodeSize(address: Address): Promise<number>;
/**

@@ -177,3 +175,8 @@ * Gets the storage value associated with the provided `address` and `key`. This method returns

getProof(_: Address, __?: Uint8Array[]): Promise<Proof>;
verifyProof(parentVerkleRoot: Uint8Array): Promise<boolean>;
/**
* Verifies whether the execution witness matches the stateRoot
* @param {Uint8Array} stateRoot - The stateRoot to verify the executionWitness against
* @returns {boolean} - Returns true if the executionWitness matches the provided stateRoot, otherwise false
*/
verifyProof(stateRoot: Uint8Array): boolean;
verifyPostState(): boolean;

@@ -203,14 +206,14 @@ getComputedValue(accessedState: AccessedStateWithAddress): PrefixedHexString | null;

/**
* Gets the verkle root.
* NOTE: this needs some examination in the code where this is needed
* and if we have the verkle root present
* @returns {Promise<Uint8Array>} - Returns the verkle root of the `StateManager`
* Gets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* This is required to verify and prove verkle execution witnesses.
* @returns {Promise<Uint8Array>} - Returns the cached state root
*/
getStateRoot(): Promise<Uint8Array>;
/**
* TODO: needed?
* Maybe in this context: reset to original pre state suffice
* @param stateRoot - The verkle root to reset the instance to
* Sets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* @param stateRoot - The stateRoot to set
*/
setStateRoot(_: Uint8Array): Promise<void>;
setStateRoot(stateRoot: Uint8Array): Promise<void>;
/**

@@ -217,0 +220,0 @@ * Dumps the RLP-encoded storage values for an `account` specified by `address`.

@@ -1,12 +0,8 @@

import { RLP } from '@ethereumjs/rlp';
import { Account, KECCAK256_NULL, KECCAK256_NULL_S, bigIntToBytes, bytesToBigInt, bytesToHex, bytesToInt32, hexToBytes, padToEven, setLengthRight, short, toBytes, } from '@ethereumjs/util';
import { getKey, getStem, verifyUpdate } from '@ethereumjs/verkle';
import { Account, KECCAK256_NULL, KECCAK256_NULL_S, VerkleLeafType, bigIntToBytes, bytesToBigInt, bytesToHex, bytesToInt32, getVerkleKey, getVerkleStem, getVerkleTreeKeyForCodeChunk, getVerkleTreeKeyForStorageSlot, hexToBytes, padToEven, setLengthLeft, setLengthRight, short, toBytes, verifyVerkleProof, } from '@ethereumjs/util';
import debugDefault from 'debug';
import { keccak256 } from 'ethereum-cryptography/keccak.js';
import { equalsBytes } from 'ethereum-cryptography/utils';
import { AccessWitness, AccessedStateType, BALANCE_LEAF_KEY, CODE_KECCAK_LEAF_KEY, CODE_SIZE_LEAF_KEY, NONCE_LEAF_KEY, VERSION_LEAF_KEY, decodeValue, getTreeIndexesForStorageSlot, getTreeIndicesForCodeChunk, } from './accessWitness.js';
import { AccessWitness, AccessedStateType, decodeValue } from './accessWitness.js';
import { AccountCache, CacheType, CodeCache, StorageCache } from './cache/index.js';
import { OriginalStorageCache } from './cache/originalStorageCache.js';
const { debug: createDebugLogger } = debugDefault;
const debug = createDebugLogger('statemanager:verkle');
const debug = debugDefault('statemanager:verkle');
const PUSH_OFFSET = 95;

@@ -33,3 +29,3 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars

*/
constructor(opts = {}) {
constructor(opts) {
/**

@@ -44,2 +40,3 @@ * StateManager is run in DEBUG mode (default: false)

this.DEBUG = false;
this._blockNum = BigInt(0);
// State along execution (should update)

@@ -50,2 +47,3 @@ this._state = {};

this._postState = {};
this._preState = {};
// Checkpointing

@@ -87,3 +85,8 @@ this._checkpoints = [];

}
this._cachedStateRoot = opts.initialStateRoot;
this.keccakFunction = opts.common?.customCrypto.keccak256 ?? keccak256;
if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required');
}
this.verkleCrypto = opts.verkleCrypto;
// Skip DEBUG calls unless 'ethjs' included in environmental DEBUG variables

@@ -93,14 +96,2 @@ // Additional window check is to prevent vite browser bundling (and potentially other) to break

typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false;
/*
* For a custom StateManager implementation adopt these
* callbacks passed to the `Cache` instantiated to perform
* the `get`, `put` and `delete` operations with the
* desired backend.
*/
// const getCb: get = async (address) => {
// return undefined
// }
// const putCb: put = async (keyBuf, accountRlp) => {}
// const deleteCb = async (keyBuf: Uint8Array) => {}
// this._cache = new Cache({ get, putCb, deleteCb })
}

@@ -110,8 +101,11 @@ async getTransitionStateRoot(_, __) {

}
initVerkleExecutionWitness(executionWitness, accessWitness) {
initVerkleExecutionWitness(blockNum, executionWitness, accessWitness) {
this._blockNum = blockNum;
if (executionWitness === null || executionWitness === undefined) {
throw Error(`Invalid executionWitness=${executionWitness} for initVerkleExecutionWitness`);
const errorMsg = `Invalid executionWitness=${executionWitness} for initVerkleExecutionWitness`;
debug(errorMsg);
throw Error(errorMsg);
}
this._executionWitness = executionWitness;
this.accessWitness = accessWitness ?? new AccessWitness();
this.accessWitness = accessWitness ?? new AccessWitness({ verkleCrypto: this.verkleCrypto });
this._proof = executionWitness.verkleProof;

@@ -133,2 +127,7 @@ // Populate the pre-state and post-state from the executionWitness

this._state = preState;
// also maintain a separate preState unaffected by any changes in _state
this._preState = preStateRaw.reduce((prevValue, currentValue) => {
const acc = { ...prevValue, ...currentValue };
return acc;
}, {});
const postStateRaw = executionWitness.stateDiff.flatMap(({ stem, suffixDiffs }) => {

@@ -154,36 +153,5 @@ const suffixDiffPairs = suffixDiffs.map(({ newValue, suffix }) => {

}
getTreeKeyForVersion(stem) {
return getKey(stem, VERSION_LEAF_KEY);
}
getTreeKeyForBalance(stem) {
return getKey(stem, BALANCE_LEAF_KEY);
}
getTreeKeyForNonce(stem) {
return getKey(stem, NONCE_LEAF_KEY);
}
getTreeKeyForCodeHash(stem) {
return getKey(stem, CODE_KECCAK_LEAF_KEY);
}
getTreeKeyForCodeSize(stem) {
return getKey(stem, CODE_SIZE_LEAF_KEY);
}
getTreeKeyForCodeChunk(address, chunkId) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkId);
return getKey(getStem(address, treeIndex), toBytes(subIndex));
}
chunkifyCode(code) {
// Pad code to multiple of 31 bytes
if (code.length % 31 !== 0) {
const paddingLength = 31 - (code.length % 31);
code = setLengthRight(code, code.length + paddingLength);
}
throw new Error('Not implemented');
}
getTreeKeyForStorageSlot(address, storageKey) {
const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(storageKey);
return getKey(getStem(address, treeIndex), toBytes(subIndex));
}
checkChunkWitnessPresent(address, codeOffset) {
async checkChunkWitnessPresent(address, codeOffset) {
const chunkId = codeOffset / 31;
const chunkKey = bytesToHex(this.getTreeKeyForCodeChunk(address, chunkId));
const chunkKey = bytesToHex(await getVerkleTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto));
return this._state[chunkKey] !== undefined;

@@ -197,4 +165,3 @@ }

shallowCopy() {
const stateManager = new StatelessVerkleStateManager();
stateManager.initVerkleExecutionWitness(this._executionWitness);
const stateManager = new StatelessVerkleStateManager({ verkleCrypto: this.verkleCrypto });
return stateManager;

@@ -246,17 +213,14 @@ }

}
// Get the contract code size
const codeSizeKey = this.getTreeKeyForCodeSize(getStem(address, 0));
const codeSizeLE = hexToBytes(this._state[bytesToHex(codeSizeKey)] ?? '0x');
const codeSize = bytesToInt32(codeSizeLE, true);
// allocate the code and copy onto it from the available witness chunks
const accessedCode = new Uint8Array(codeSize);
const chunks = Math.floor(bytesToInt32(codeSizeLE, true) / 31);
for (let chunkId = 0; chunkId <= chunks; chunkId++) {
const chunkKey = bytesToHex(this.getTreeKeyForCodeChunk(address, chunkId));
const codeSize = account.codeSize;
// allocate enough to fit the last chunk
const accessedCode = new Uint8Array(codeSize + 31);
const chunks = Math.floor(codeSize / 31) + 1;
for (let chunkId = 0; chunkId < chunks; chunkId++) {
const chunkKey = bytesToHex(await getVerkleTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto));
const codeChunk = this._state[chunkKey];
if (codeChunk === undefined) {
throw Error(`Invalid access to a missing code chunk with chunkKey=${chunkKey}`);
}
if (codeChunk === null) {
throw Error(`Invalid access to a non existent code chunk with chunkKey=${chunkKey}`);
const errorMsg = `Invalid access to a non existent code chunk with chunkKey=${chunkKey}`;
debug(errorMsg);
throw Error(errorMsg);
}

@@ -277,4 +241,30 @@ const codeOffset = chunkId * 31;

// Return accessedCode where only accessed code has been copied
return accessedCode;
const contactCode = accessedCode.slice(0, codeSize);
if (!this._codeCacheSettings.deactivate) {
this._codeCache?.put(address, contactCode);
}
return contactCode;
}
async getContractCodeSize(address) {
if (!this._accountCacheSettings.deactivate) {
const elem = this._accountCache.get(address);
if (elem !== undefined) {
const account = elem.accountRLP !== undefined
? Account.fromRlpSerializedPartialAccount(elem.accountRLP)
: undefined;
if (account === undefined) {
const errorMsg = `account=${account} in cache`;
debug(errorMsg);
throw Error(errorMsg);
}
return account.codeSize;
}
}
// load the account basic fields and codeSize should be in it
const account = await this.getAccount(address);
if (account === undefined) {
return 0;
}
return account.codeSize;
}
/**

@@ -296,3 +286,3 @@ * Gets the storage value associated with the provided `address` and `key`. This method returns

}
const storageKey = this.getTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)));
const storageKey = await getVerkleTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)), this.verkleCrypto);
const storageValue = toBytes(this._state[bytesToHex(storageKey)]);

@@ -313,11 +303,12 @@ if (!this._storageCacheSettings.deactivate) {

if (!this._storageCacheSettings.deactivate) {
const encodedValue = RLP.encode(value);
this._storageCache.put(address, key, encodedValue);
this._storageCache.put(address, key, value);
}
else {
// TODO: Consider refactoring this in a writeContractStorage function? Like in stateManager.ts
const storageKey = this.getTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)));
const storageKey = await getVerkleTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)), this.verkleCrypto);
this._state[bytesToHex(storageKey)] = bytesToHex(setLengthRight(value, 32));
}
}
// Note from Gabriel: Clearing storage is not actually not possible in Verkle.
// This is because the storage keys are scattered throughout the verkle tree.
/**

@@ -328,8 +319,7 @@ * Clears all storage entries for the account corresponding to `address`.

async clearContractStorage(address) {
const stem = getStem(address, 0);
const codeHashKey = this.getTreeKeyForCodeHash(stem);
const stem = getVerkleStem(this.verkleCrypto, address, 0);
const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash);
this._storageCache?.clearContractStorage(address);
// Update codeHash to `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`
this._state[bytesToHex(codeHashKey)] = KECCAK256_NULL_S;
// TODO: Clear all storage slots (how?)
}

@@ -341,32 +331,67 @@ async getAccount(address) {

return elem.accountRLP !== undefined
? Account.fromRlpSerializedAccount(elem.accountRLP)
? Account.fromRlpSerializedPartialAccount(elem.accountRLP)
: undefined;
}
}
const stem = getStem(address, 0);
const versionKey = this.getTreeKeyForVersion(stem);
const versionChunk = this._state[bytesToHex(versionKey)];
if (versionChunk === undefined) {
throw Error(`Missing execution witness for address=${address} versionKey=${bytesToHex(versionKey)}`);
}
// if the versionChunk is null it means the account doesn't exist in pre state
if (versionChunk === null) {
return undefined;
}
const balanceKey = this.getTreeKeyForBalance(stem);
const nonceKey = this.getTreeKeyForNonce(stem);
const codeHashKey = this.getTreeKeyForCodeHash(stem);
const stem = getVerkleStem(this.verkleCrypto, address, 0);
const versionKey = getVerkleKey(stem, VerkleLeafType.Version);
const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance);
const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce);
const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash);
const codeSizeKey = getVerkleKey(stem, VerkleLeafType.CodeSize);
const versionRaw = this._state[bytesToHex(versionKey)];
const balanceRaw = this._state[bytesToHex(balanceKey)];
const nonceRaw = this._state[bytesToHex(nonceKey)];
const codeHashRaw = this._state[bytesToHex(codeHashKey)];
const account = Account.fromAccountData({
balance: typeof balanceRaw === 'string' ? bytesToBigInt(hexToBytes(balanceRaw), true) : undefined,
nonce: typeof nonceRaw === 'string' ? bytesToBigInt(hexToBytes(nonceRaw), true) : undefined,
codeHash: codeHashRaw?.length === 32 ? codeHashRaw : KECCAK256_NULL_S,
const codeSizeRaw = this._state[bytesToHex(codeSizeKey)];
// check if the account didn't exist if any of the basic keys have null
if (versionRaw === null || balanceRaw === null || nonceRaw === null || codeHashRaw === null) {
// check any of the other key shouldn't have string input available as this account didn't exist
if (typeof versionRaw === `string` ||
typeof balanceRaw === 'string' ||
typeof nonceRaw === 'string' ||
typeof codeHashRaw === 'string') {
const errorMsg = `Invalid witness for a non existing address=${address} stem=${bytesToHex(stem)}`;
debug(errorMsg);
throw Error(errorMsg);
}
else {
return undefined;
}
}
// check if codehash is correct 32 bytes prefixed hex string
if (codeHashRaw !== undefined && codeHashRaw !== null && codeHashRaw.length !== 66) {
const errorMsg = `Invalid codeHashRaw=${codeHashRaw} for address=${address} chunkKey=${bytesToHex(codeHashKey)}`;
debug(errorMsg);
throw Error(errorMsg);
}
if (versionRaw === undefined &&
balanceRaw === undefined &&
nonceRaw === undefined &&
codeHashRaw === undefined &&
codeSizeRaw === undefined) {
const errorMsg = `No witness bundled for address=${address} stem=${bytesToHex(stem)}`;
debug(errorMsg);
throw Error(errorMsg);
}
const account = Account.fromPartialAccountData({
version: typeof versionRaw === 'string' ? bytesToInt32(hexToBytes(versionRaw), true) : null,
balance: typeof balanceRaw === 'string' ? bytesToBigInt(hexToBytes(balanceRaw), true) : null,
nonce: typeof nonceRaw === 'string' ? bytesToBigInt(hexToBytes(nonceRaw), true) : null,
codeHash: typeof codeHashRaw === 'string' ? hexToBytes(codeHashRaw) : null,
// if codeSizeRaw is null, it means account didnt exist or it was EOA either way codeSize is 0
// if codeSizeRaw is undefined, then we pass in null which in our context of partial account means
// not specified
codeSize: typeof codeSizeRaw === 'string'
? bytesToInt32(hexToBytes(codeSizeRaw), true)
: codeSizeRaw === null
? 0
: null,
storageRoot: null,
});
if (this.DEBUG) {
debug(`getAccount address=${address.toString()} stem=${short(stem)} balance=${account.balance} nonce=${account.nonce} codeHash=${short(account.codeHash)} storageHash=${short(account.storageRoot)}`);
debug(`getAccount address=${address.toString()} stem=${short(stem)}`);
}
if (!this._accountCacheSettings.deactivate) {
this._accountCache?.put(address, account);
this._accountCache?.put(address, account, true);
}

@@ -377,9 +402,9 @@ return account;

if (this.DEBUG) {
debug(`putAccount address=${address.toString()} balance=${account.balance} nonce=${account.nonce} codeHash=${short(account.codeHash)} storageHash=${short(account.storageRoot)}`);
debug(`putAccount address=${address.toString()}`);
}
if (this._accountCacheSettings.deactivate) {
const stem = getStem(address, 0);
const balanceKey = this.getTreeKeyForBalance(stem);
const nonceKey = this.getTreeKeyForNonce(stem);
const codeHashKey = this.getTreeKeyForCodeHash(stem);
const stem = getVerkleStem(this.verkleCrypto, address, 0);
const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance);
const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce);
const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash);
const balanceBuf = setLengthRight(bigIntToBytes(account.balance, true), 32);

@@ -393,3 +418,3 @@ const nonceBuf = setLengthRight(bigIntToBytes(account.nonce, true), 32);

if (account !== undefined) {
this._accountCache.put(address, account);
this._accountCache.put(address, account, true);
}

@@ -420,6 +445,6 @@ else {

}
account.nonce = accountFields.nonce ?? account.nonce;
account.balance = accountFields.balance ?? account.balance;
account.storageRoot = accountFields.storageRoot ?? account.storageRoot;
account.codeHash = accountFields.codeHash ?? account.codeHash;
account._nonce = accountFields.nonce ?? account._nonce;
account._balance = accountFields.balance ?? account._balance;
account._storageRoot = accountFields.storageRoot ?? account._storageRoot;
account._codeHash = accountFields.codeHash ?? account._codeHash;
await this.putAccount(address, account);

@@ -430,17 +455,13 @@ }

}
async verifyProof(parentVerkleRoot) {
// Implementation: https://github.com/crate-crypto/rust-verkle-wasm/blob/master/src/lib.rs#L45
// The root is the root of the current (un-updated) trie
// The proof is proof of membership of all of the accessed values
// keys_values is a map from the key of the accessed value to a tuple
// the tuple contains the old value and the updated value
//
// This function returns the new root when all of the updated values are applied
const updatedStateRoot = verifyUpdate(parentVerkleRoot, this._proof, // TODO: Convert this into a Uint8Array ingestible by the method
new Map() // TODO: Generate the keys_values map from the old to the updated value
);
// TODO: Not sure if this should return the updated state Root (current block) or the un-updated one (parent block)
const verkleRoot = await this.getStateRoot();
// Verify that updatedStateRoot matches the state root of the block
return equalsBytes(updatedStateRoot, verkleRoot);
/**
* Verifies whether the execution witness matches the stateRoot
* @param {Uint8Array} stateRoot - The stateRoot to verify the executionWitness against
* @returns {boolean} - Returns true if the executionWitness matches the provided stateRoot, otherwise false
*/
verifyProof(stateRoot) {
if (this._executionWitness === undefined) {
debug('Missing executionWitness');
return false;
}
return verifyVerkleProof(this.verkleCrypto, stateRoot, this._executionWitness);
}

@@ -453,3 +474,3 @@ // Verifies that the witness post-state matches the computed post-state

// switch to false if postVerify fails
let postVerified = true;
let postFailures = 0;
for (const accessedState of this.accessWitness.accesses()) {

@@ -466,7 +487,12 @@ const { address, type } = accessedState;

accessedChunks.set(chunkKey, true);
let computedValue = this.getComputedValue(accessedState);
const computedValue = this.getComputedValue(accessedState) ?? this._preState[chunkKey];
if (computedValue === undefined) {
debug(`Block accesses missing in canonical address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`);
postFailures++;
continue;
}
let canonicalValue = this._postState[chunkKey];
if (canonicalValue === undefined) {
debug(`Block accesses missing in canonical address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`);
postVerified = false;
postFailures++;
continue;

@@ -477,5 +503,10 @@ }

if (accessedState.type === AccessedStateType.Code) {
computedValue = computedValue !== null ? `0x${computedValue.slice(4)}` : null;
// computedValue = computedValue !== null ? `0x${computedValue.slice(4)}` : null
canonicalValue = canonicalValue !== null ? `0x${canonicalValue.slice(4)}` : null;
}
else if (accessedState.type === AccessedStateType.Storage &&
canonicalValue === null &&
computedValue === ZEROVALUE) {
canonicalValue = ZEROVALUE;
}
if (computedValue !== canonicalValue) {

@@ -493,3 +524,3 @@ const decodedComputedValue = decodeValue(accessedState.type, computedValue);

debug(`computed=${displayComputedValue}`);
postVerified = false;
postFailures++;
}

@@ -500,7 +531,8 @@ }

debug(`Missing chunk access for canChunkKey=${canChunkKey}`);
postVerified = false;
postFailures++;
}
}
debug(`verifyPostState=${postVerified}`);
return postVerified;
const verifyPassed = postFailures === 0;
debug(`verifyPostState verifyPassed=${verifyPassed} postFailures=${postFailures}`);
return verifyPassed;
}

@@ -524,3 +556,3 @@ getComputedValue(accessedState) {

}
const balanceBigint = Account.fromRlpSerializedAccount(encodedAccount).balance;
const balanceBigint = Account.fromRlpSerializedPartialAccount(encodedAccount).balance;
return bytesToHex(setLengthRight(bigIntToBytes(balanceBigint, true), 32));

@@ -533,3 +565,3 @@ }

}
const nonceBigint = Account.fromRlpSerializedAccount(encodedAccount).nonce;
const nonceBigint = Account.fromRlpSerializedPartialAccount(encodedAccount).nonce;
return bytesToHex(setLengthRight(bigIntToBytes(nonceBigint, true), 32));

@@ -542,3 +574,3 @@ }

}
return bytesToHex(Account.fromRlpSerializedAccount(encodedAccount).codeHash);
return bytesToHex(Account.fromRlpSerializedPartialAccount(encodedAccount).codeHash);
}

@@ -553,5 +585,7 @@ case AccessedStateType.CodeSize: {

}
const account = Account.fromRlpSerializedAccount(encodedAccount);
const account = Account.fromRlpSerializedPartialAccount(encodedAccount);
if (account.isContract()) {
throw Error(`Code cache not found for address=${address.toString()}`);
const errorMsg = `Code cache not found for address=${address.toString()}`;
debug(errorMsg);
throw Error(errorMsg);
}

@@ -574,11 +608,12 @@ else {

const chunkSize = 31;
return bytesToHex(code.slice(codeOffset, codeOffset + chunkSize));
return bytesToHex(setLengthRight(code.slice(codeOffset, codeOffset + chunkSize), chunkSize));
}
case AccessedStateType.Storage: {
const { slot } = accessedState;
const storage = this._storageCache?.get(address, bigIntToBytes(slot));
const key = setLengthLeft(bigIntToBytes(slot), 32);
const storage = this._storageCache?.get(address, key);
if (storage === undefined) {
return null;
}
return bytesToHex(storage);
return bytesToHex(setLengthLeft(storage, 32));
}

@@ -628,16 +663,21 @@ }

/**
* Gets the verkle root.
* NOTE: this needs some examination in the code where this is needed
* and if we have the verkle root present
* @returns {Promise<Uint8Array>} - Returns the verkle root of the `StateManager`
* Gets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* This is required to verify and prove verkle execution witnesses.
* @returns {Promise<Uint8Array>} - Returns the cached state root
*/
async getStateRoot() {
return new Uint8Array(0);
if (this._cachedStateRoot === undefined) {
throw new Error('Cache state root missing');
}
return this._cachedStateRoot;
}
/**
* TODO: needed?
* Maybe in this context: reset to original pre state suffice
* @param stateRoot - The verkle root to reset the instance to
* Sets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* @param stateRoot - The stateRoot to set
*/
async setStateRoot(_) { }
async setStateRoot(stateRoot) {
this._cachedStateRoot = stateRoot;
}
/**

@@ -644,0 +684,0 @@ * Dumps the RLP-encoded storage values for an `account` specified by `address`.

@@ -6,4 +6,3 @@ import { Common } from '@ethereumjs/common';

import { OriginalStorageCache } from './cache/originalStorageCache.js';
import type { AccountFields, EVMStateManagerInterface, StorageDump } from '@ethereumjs/common';
import type { StorageRange } from '@ethereumjs/common/src';
import type { AccountFields, EVMStateManagerInterface, StorageDump, StorageRange } from '@ethereumjs/common';
import type { DB, PrefixedHexString } from '@ethereumjs/util';

@@ -186,8 +185,22 @@ import type { Debugger } from 'debug';

getContractCode(address: Address): Promise<Uint8Array>;
getContractCodeSize(address: Address): Promise<number>;
/**
* Gets the storage trie for an account from the storage
* cache or does a lookup.
* Gets the storage trie for the EVM-internal account identified by the provided address/hash.
* If the storage trie is not in the local cache ('this._storageTries'),
* generates a new storage trie object based on a lookup (shallow copy from 'this._trie'),
* applies the storage root of the provided rootAccount (or an
* empty trie root if no rootAccount is provided), and stores the new entry
* in the local cache.
* @param addressOrHash Address (or other object) with populated 'bytes', or a raw Uint8Array.
* Used to identify the requested storage trie in the local cache and define the
* prefix used when creating a new storage trie.
* @param rootAccount (Optional) Account object whose 'storageRoot' is to be used as
* the root of the new storageTrie returned when there is no pre-existing trie.
* If left undefined, the EMPTY_TRIE_ROOT will be used as the root instead.
* @returns storage Trie object
* @private
*/
protected _getStorageTrie(addressOrHash: Address | Uint8Array, account?: Account): Trie;
protected _getStorageTrie(addressOrHash: Address | {
bytes: Uint8Array;
} | Uint8Array, rootAccount?: Account): Trie;
/**

@@ -194,0 +207,0 @@ * Gets the storage trie for an account from the storage

@@ -9,3 +9,2 @@ import { Chain, Common } from '@ethereumjs/common';

import { OriginalStorageCache } from './cache/originalStorageCache.js';
const { debug: createDebugLogger } = debugDefault;
/**

@@ -48,3 +47,3 @@ * Prefix to distinguish between a contract deployed with code `0x80`

typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false;
this._debug = createDebugLogger('statemanager:statemanager');
this._debug = debugDefault('statemanager:statemanager');
this.common = opts.common ?? new Common({ chain: Chain.Mainnet });

@@ -186,5 +185,2 @@ this._checkpointCount = 0;

const codeHash = this.keccakFunction(value);
if (equalsBytes(codeHash, KECCAK256_NULL)) {
return;
}
if (this.DEBUG) {

@@ -227,21 +223,33 @@ this._debug(`Update codeHash (-> ${short(codeHash)}) for account ${address}`);

}
async getContractCodeSize(address) {
const contractCode = await this.getContractCode(address);
return contractCode.length;
}
/**
* Gets the storage trie for an account from the storage
* cache or does a lookup.
* Gets the storage trie for the EVM-internal account identified by the provided address/hash.
* If the storage trie is not in the local cache ('this._storageTries'),
* generates a new storage trie object based on a lookup (shallow copy from 'this._trie'),
* applies the storage root of the provided rootAccount (or an
* empty trie root if no rootAccount is provided), and stores the new entry
* in the local cache.
* @param addressOrHash Address (or other object) with populated 'bytes', or a raw Uint8Array.
* Used to identify the requested storage trie in the local cache and define the
* prefix used when creating a new storage trie.
* @param rootAccount (Optional) Account object whose 'storageRoot' is to be used as
* the root of the new storageTrie returned when there is no pre-existing trie.
* If left undefined, the EMPTY_TRIE_ROOT will be used as the root instead.
* @returns storage Trie object
* @private
*/
// TODO PR: have a better interface for hashed address pull?
_getStorageTrie(addressOrHash, account) {
_getStorageTrie(addressOrHash, rootAccount) {
// use hashed key for lookup from storage cache
const addressHex = bytesToUnprefixedHex(addressOrHash instanceof Address ? this.keccakFunction(addressOrHash.bytes) : addressOrHash);
const addressBytes = addressOrHash instanceof Uint8Array ? addressOrHash : this.keccakFunction(addressOrHash.bytes);
const addressHex = bytesToUnprefixedHex(addressBytes);
let storageTrie = this._storageTries[addressHex];
if (storageTrie === undefined) {
const keyPrefix = this._prefixStorageTrieKeys
? (addressOrHash instanceof Address
? this.keccakFunction(addressOrHash.bytes)
: addressOrHash).slice(0, 7)
: undefined;
const keyPrefix = this._prefixStorageTrieKeys ? addressBytes.slice(0, 7) : undefined;
storageTrie = this._trie.shallowCopy(false, { keyPrefix });
if (account !== undefined) {
storageTrie.root(account.storageRoot);
if (rootAccount !== undefined) {
storageTrie.root(rootAccount.storageRoot);
}

@@ -248,0 +256,0 @@ else {

{
"name": "@ethereumjs/statemanager",
"version": "2.3.0",
"version": "2.4.0",
"description": "An Ethereum statemanager implementation",

@@ -46,4 +46,4 @@ "keywords": [

"prepublishOnly": "../../config/cli/prepublish.sh && npm run test:node",
"test": "npm run test:node && npm run test:browser",
"test:browser": "npx vitest run --config=vitest.config.browser.ts --browser.name=webkit --browser.provider=playwright --browser.headless",
"test": "npm run test:node",
"test:browser": "npx vitest run --config=./vitest.config.browser.mts",
"test:node": "npx vitest run",

@@ -53,9 +53,8 @@ "tsc": "../../config/cli/ts-compile.sh"

"dependencies": {
"@ethereumjs/common": "^4.3.0",
"@ethereumjs/common": "^4.4.0",
"@ethereumjs/rlp": "^5.0.2",
"@ethereumjs/trie": "^6.2.0",
"@ethereumjs/util": "^9.0.3",
"@ethereumjs/verkle": "^0.0.2",
"@ethereumjs/trie": "^6.2.1",
"@ethereumjs/util": "^9.1.0",
"debug": "^4.3.3",
"ethereum-cryptography": "^2.1.3",
"ethereum-cryptography": "^2.2.1",
"js-sdsl": "^4.1.4",

@@ -65,7 +64,8 @@ "lru-cache": "10.1.0"

"devDependencies": {
"@ethereumjs/block": "^5.2.0",
"@ethereumjs/genesis": "^0.2.2",
"@ethereumjs/block": "^5.3.0",
"@ethereumjs/genesis": "^0.2.3",
"@types/debug": "^4.1.9",
"rustbn-wasm": "^0.4.0"
"rustbn-wasm": "^0.4.0",
"verkle-cryptography-wasm": "^0.4.5"
}
}

@@ -1,9 +0,26 @@

import { BIGINT_0, bytesToBigInt, bytesToHex, hexToBytes, toBytes } from '@ethereumjs/util'
import { getKey, getStem } from '@ethereumjs/verkle'
import {
BIGINT_0,
VERKLE_BALANCE_LEAF_KEY,
VERKLE_CODE_HASH_LEAF_KEY,
VERKLE_CODE_OFFSET,
VERKLE_CODE_SIZE_LEAF_KEY,
VERKLE_HEADER_STORAGE_OFFSET,
VERKLE_MAIN_STORAGE_OFFSET,
VERKLE_NODE_WIDTH,
VERKLE_NONCE_LEAF_KEY,
VERKLE_VERSION_LEAF_KEY,
bytesToBigInt,
bytesToHex,
getVerkleKey,
getVerkleStem,
getVerkleTreeIndicesForCodeChunk,
hexToBytes,
intToBytes,
} from '@ethereumjs/util'
import debugDefault from 'debug'
import type { Address, PrefixedHexString } from '@ethereumjs/util'
import type { AccessEventFlags, AccessWitnessInterface } from '@ethereumjs/common'
import type { Address, PrefixedHexString, VerkleCrypto } from '@ethereumjs/util'
const { debug: createDebugLogger } = debugDefault
const debug = createDebugLogger('statemanager:verkle:aw')
const debug = debugDefault('statemanager:verkle:aw')

@@ -13,15 +30,2 @@ /**

*/
export const VERSION_LEAF_KEY = toBytes(0)
export const BALANCE_LEAF_KEY = toBytes(1)
export const NONCE_LEAF_KEY = toBytes(2)
export const CODE_KECCAK_LEAF_KEY = toBytes(3)
export const CODE_SIZE_LEAF_KEY = toBytes(4)
export const HEADER_STORAGE_OFFSET = 64
export const CODE_OFFSET = 128
export const VERKLE_NODE_WIDTH = 256
// export const MAIN_STORAGE_OFFSET = BigInt(256) ** BigInt(31)
// incorrect value to match with kaustinen2 offset
export const MAIN_STORAGE_OFFSET = BigInt(256) * BigInt(2) ** BigInt(31)
const WitnessBranchReadCost = BigInt(1900)

@@ -35,14 +39,6 @@ const WitnessChunkReadCost = BigInt(200)

type StemAccessEvent = { write?: boolean }
// chunk fill access event is not being charged right now in kaustinen2 but will be rectified
// chunk fill access event is not being charged right now in kaustinen but will be rectified
// in upcoming iterations
type ChunkAccessEvent = StemAccessEvent & { fill?: boolean }
type AccessEventFlags = {
stemRead: boolean
stemWrite: boolean
chunkRead: boolean
chunkWrite: boolean
chunkFill: boolean
}
// Since stem is pedersen hashed, it is useful to maintain the reverse relationship

@@ -76,8 +72,9 @@ type StemMeta = { address: Address; treeIndex: number | bigint }

export class AccessWitness {
export class AccessWitness implements AccessWitnessInterface {
stems: Map<PrefixedHexString, StemAccessEvent & StemMeta>
chunks: Map<PrefixedHexString, ChunkAccessEvent>
verkleCrypto: VerkleCrypto
constructor(
opts: {
verkleCrypto?: VerkleCrypto
stems?: Map<PrefixedHexString, StemAccessEvent & StemMeta>

@@ -87,2 +84,6 @@ chunks?: Map<PrefixedHexString, ChunkAccessEvent>

) {
if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required')
}
this.verkleCrypto = opts.verkleCrypto
this.stems = opts.stems ?? new Map<PrefixedHexString, StemAccessEvent & StemMeta>()

@@ -95,7 +96,7 @@ this.chunks = opts.chunks ?? new Map<PrefixedHexString, ChunkAccessEvent>()

gas += this.touchAddressOnReadAndComputeGas(address, 0, VERSION_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, BALANCE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, CODE_KECCAK_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, NONCE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_BALANCE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_HASH_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_NONCE_LEAF_KEY)

@@ -108,4 +109,4 @@ return gas

gas += this.touchAddressOnReadAndComputeGas(address, 0, VERSION_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY)

@@ -118,4 +119,4 @@ return gas

gas += this.touchAddressOnWriteAndComputeGas(caller, 0, BALANCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(target, 0, BALANCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(caller, 0, VERKLE_BALANCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(target, 0, VERKLE_BALANCE_LEAF_KEY)

@@ -125,14 +126,7 @@ return gas

touchAndChargeContractCreateInit(
address: Address,
{ sendsValue }: { sendsValue?: boolean } = {}
): bigint {
touchAndChargeContractCreateInit(address: Address): bigint {
let gas = BIGINT_0
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERSION_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, NONCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, CODE_KECCAK_LEAF_KEY)
if (sendsValue === true) {
gas += this.touchAddressOnWriteAndComputeGas(address, 0, BALANCE_LEAF_KEY)
}
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_NONCE_LEAF_KEY)

@@ -145,7 +139,7 @@ return gas

gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERSION_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, BALANCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, CODE_SIZE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, CODE_KECCAK_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, NONCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_VERSION_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_BALANCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_CODE_SIZE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_CODE_HASH_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(address, 0, VERKLE_NONCE_LEAF_KEY)

@@ -158,8 +152,8 @@ return gas

gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERSION_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(origin, 0, CODE_SIZE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(origin, 0, CODE_KECCAK_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERKLE_VERSION_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERKLE_CODE_SIZE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(origin, 0, VERKLE_CODE_HASH_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, NONCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, BALANCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, VERKLE_NONCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(origin, 0, VERKLE_BALANCE_LEAF_KEY)

@@ -169,14 +163,14 @@ return gas

touchTxExistingAndComputeGas(target: Address, { sendsValue }: { sendsValue?: boolean } = {}) {
touchTxTargetAndComputeGas(target: Address, { sendsValue }: { sendsValue?: boolean } = {}) {
let gas = BIGINT_0
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERSION_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(target, 0, CODE_SIZE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(target, 0, CODE_KECCAK_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(target, 0, NONCE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_VERSION_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_CODE_SIZE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_CODE_HASH_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_NONCE_LEAF_KEY)
if (sendsValue === true) {
gas += this.touchAddressOnWriteAndComputeGas(target, 0, BALANCE_LEAF_KEY)
gas += this.touchAddressOnWriteAndComputeGas(target, 0, VERKLE_BALANCE_LEAF_KEY)
} else {
gas += this.touchAddressOnReadAndComputeGas(target, 0, BALANCE_LEAF_KEY)
gas += this.touchAddressOnReadAndComputeGas(target, 0, VERKLE_BALANCE_LEAF_KEY)
}

@@ -187,6 +181,6 @@

touchCodeChunksRangeOnReadAndChargeGas(contact: Address, startPc: number, endPc: number) {
touchCodeChunksRangeOnReadAndChargeGas(contact: Address, startPc: number, endPc: number): bigint {
let gas = BIGINT_0
for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkNum)
const { treeIndex, subIndex } = getVerkleTreeIndicesForCodeChunk(chunkNum)
gas += this.touchAddressOnReadAndComputeGas(contact, treeIndex, subIndex)

@@ -197,6 +191,10 @@ }

touchCodeChunksRangeOnWriteAndChargeGas(contact: Address, startPc: number, endPc: number) {
touchCodeChunksRangeOnWriteAndChargeGas(
contact: Address,
startPc: number,
endPc: number
): bigint {
let gas = BIGINT_0
for (let chunkNum = Math.floor(startPc / 31); chunkNum <= Math.floor(endPc / 31); chunkNum++) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkNum)
const { treeIndex, subIndex } = getVerkleTreeIndicesForCodeChunk(chunkNum)
gas += this.touchAddressOnWriteAndComputeGas(contact, treeIndex, subIndex)

@@ -238,16 +236,16 @@ }

if (stemRead) {
if (stemRead === true) {
gas += WitnessBranchReadCost
}
if (stemWrite) {
if (stemWrite === true) {
gas += WitnessBranchWriteCost
}
if (chunkRead) {
if (chunkRead === true) {
gas += WitnessChunkReadCost
}
if (chunkWrite) {
if (chunkWrite === true) {
gas += WitnessChunkWriteCost
}
if (chunkFill) {
if (chunkFill === true) {
gas += WitnessChunkFillCost

@@ -277,3 +275,3 @@ }

const accessedStemKey = getStem(address, treeIndex)
const accessedStemKey = getVerkleStem(this.verkleCrypto, address, treeIndex)
const accessedStemHex = bytesToHex(accessedStemKey)

@@ -287,3 +285,6 @@ let accessedStem = this.stems.get(accessedStemHex)

const accessedChunkKey = getKey(accessedStemKey, toBytes(subIndex))
const accessedChunkKey = getVerkleKey(
accessedStemKey,
typeof subIndex === 'number' ? intToBytes(subIndex) : subIndex
)
const accessedChunkKeyHex = bytesToHex(accessedChunkKey)

@@ -319,3 +320,3 @@ let accessedChunk = this.chunks.get(accessedChunkKeyHex)

shallowCopy(): AccessWitness {
return new AccessWitness()
return new AccessWitness({ verkleCrypto: this.verkleCrypto })
}

@@ -325,3 +326,3 @@

for (const [chunkKey, chunkValue] of accessWitness.chunks.entries()) {
const stemKey = chunkKey.slice(0, chunkKey.length - 2)
const stemKey = chunkKey.slice(0, chunkKey.length - 2) as PrefixedHexString
const stem = accessWitness.stems.get(stemKey)

@@ -352,3 +353,3 @@ if (stem === undefined) {

// drop the last byte
const stemKey = chunkKey.slice(0, chunkKey.length - 2)
const stemKey = chunkKey.slice(0, chunkKey.length - 2) as PrefixedHexString
const stem = this.stems.get(stemKey)

@@ -374,25 +375,2 @@ if (stem === undefined) {

export function getTreeIndexesForStorageSlot(storageKey: bigint): {
treeIndex: bigint
subIndex: number
} {
let position: bigint
if (storageKey < CODE_OFFSET - HEADER_STORAGE_OFFSET) {
position = BigInt(HEADER_STORAGE_OFFSET) + storageKey
} else {
position = MAIN_STORAGE_OFFSET + storageKey
}
const treeIndex = position / BigInt(VERKLE_NODE_WIDTH)
const subIndex = Number(position % BigInt(VERKLE_NODE_WIDTH))
return { treeIndex, subIndex }
}
export function getTreeIndicesForCodeChunk(chunkId: number) {
const treeIndex = Math.floor((CODE_OFFSET + chunkId) / VERKLE_NODE_WIDTH)
const subIndex = (CODE_OFFSET + chunkId) % VERKLE_NODE_WIDTH
return { treeIndex, subIndex }
}
export function decodeAccessedState(treeIndex: number | bigint, chunkIndex: number): AccessedState {

@@ -412,14 +390,14 @@ const position = BigInt(treeIndex) * BigInt(VERKLE_NODE_WIDTH) + BigInt(chunkIndex)

default:
if (position < HEADER_STORAGE_OFFSET) {
throw Error(`No attribute yet stored >=5 and <${HEADER_STORAGE_OFFSET}`)
if (position < VERKLE_HEADER_STORAGE_OFFSET) {
throw Error(`No attribute yet stored >=5 and <${VERKLE_HEADER_STORAGE_OFFSET}`)
}
if (position >= HEADER_STORAGE_OFFSET && position < CODE_OFFSET) {
const slot = position - BigInt(HEADER_STORAGE_OFFSET)
if (position >= VERKLE_HEADER_STORAGE_OFFSET && position < VERKLE_CODE_OFFSET) {
const slot = position - BigInt(VERKLE_HEADER_STORAGE_OFFSET)
return { type: AccessedStateType.Storage, slot }
} else if (position >= CODE_OFFSET && position < MAIN_STORAGE_OFFSET) {
const codeChunkIdx = Number(position) - CODE_OFFSET
} else if (position >= VERKLE_CODE_OFFSET && position < VERKLE_MAIN_STORAGE_OFFSET) {
const codeChunkIdx = Number(position) - VERKLE_CODE_OFFSET
return { type: AccessedStateType.Code, codeOffset: codeChunkIdx * 31 }
} else if (position >= MAIN_STORAGE_OFFSET) {
const slot = BigInt(position - MAIN_STORAGE_OFFSET)
} else if (position >= VERKLE_MAIN_STORAGE_OFFSET) {
const slot = BigInt(position - VERKLE_MAIN_STORAGE_OFFSET)
return { type: AccessedStateType.Storage, slot }

@@ -434,3 +412,3 @@ } else {

export function decodeValue(type: AccessedStateType, value: string | null): string {
export function decodeValue(type: AccessedStateType, value: PrefixedHexString | null): string {
if (value === null) {

@@ -437,0 +415,0 @@ return ''

@@ -11,3 +11,2 @@ import { bytesToUnprefixedHex } from '@ethereumjs/util'

import type { Account, Address } from '@ethereumjs/util'
const { debug: createDebugLogger } = debugDefault

@@ -49,3 +48,3 @@ /**

this._diffCache.push(new Map<string, AccountCacheElement | undefined>())
this._debug = createDebugLogger('statemanager:cache:account')
this._debug = debugDefault('statemanager:cache:account')
}

@@ -71,7 +70,16 @@

*/
put(address: Address, account: Account | undefined): void {
put(
address: Address,
account: Account | undefined,
couldBeParitalAccount: boolean = false
): void {
const addressHex = bytesToUnprefixedHex(address.bytes)
this._saveCachePreState(addressHex)
const elem = {
accountRLP: account !== undefined ? account.serialize() : undefined,
accountRLP:
account !== undefined
? couldBeParitalAccount
? account.serializeWithPartialInfo()
: account.serialize()
: undefined,
}

@@ -78,0 +86,0 @@

import debugDefault from 'debug'
import type { Debugger } from 'debug'
const { debug: createDebugLogger } = debugDefault

@@ -35,4 +34,4 @@ export class Cache {

this._debug = createDebugLogger('statemanager:cache')
this._debug = debugDefault('statemanager:cache')
}
}

@@ -12,4 +12,2 @@ import { bytesToUnprefixedHex } from '@ethereumjs/util'

const { debug: createDebugLogger } = debugDefault
/**

@@ -49,3 +47,3 @@ * Represents a cached code element.

this._diffCache.push(new Map<string, CodeCacheElement | undefined>())
this._debug = createDebugLogger('statemanager:cache:code')
this._debug = debugDefault('statemanager:cache:code')
}

@@ -52,0 +50,0 @@

@@ -11,3 +11,2 @@ import { bytesToUnprefixedHex, hexToBytes } from '@ethereumjs/util'

import type { Address } from '@ethereumjs/util'
const { debug: createDebugLogger } = debugDefault

@@ -51,3 +50,3 @@ /**

if (this.DEBUG) {
this._debug = createDebugLogger('statemanager:cache:storage')
this._debug = debugDefault('statemanager:cache:storage')
}

@@ -54,0 +53,0 @@ }

@@ -26,5 +26,4 @@ import { Chain, Common } from '@ethereumjs/common'

} from '@ethereumjs/common'
import type { Address } from '@ethereumjs/util'
import type { Address, PrefixedHexString } from '@ethereumjs/util'
import type { Debugger } from 'debug'
const { debug: createDebugLogger } = debugDefault

@@ -61,3 +60,3 @@ export interface RPCStateManagerOpts {

this._debug = createDebugLogger('statemanager:rpcStateManager')
this._debug = debugDefault('statemanager:rpcStateManager')
if (typeof opts.provider === 'string' && opts.provider.startsWith('http')) {

@@ -141,2 +140,7 @@ this._provider = opts.provider

async getContractCodeSize(address: Address): Promise<number> {
const contractCode = await this.getContractCode(address)
return contractCode.length
}
/**

@@ -243,3 +247,3 @@ * Adds `value` to the state trie as code, and sets `codeHash` on the account

const proofBuf = proof.accountProof.map((proofNode: string) => toBytes(proofNode))
const proofBuf = proof.accountProof.map((proofNode: PrefixedHexString) => toBytes(proofNode))

@@ -254,6 +258,4 @@ const verified = await Trie.verifyProof(address.bytes, proofBuf, {

/**
* Gets the code corresponding to the provided `address`.
* @param address - Address to get the `account` for
* @returns {Promise<Uint8Array>} - Resolves with the code corresponding to the provided address.
* Returns an empty `Uint8Array` if the account has no associated code.
* Gets the account associated with `address` or `undefined` if account does not exist
* @param address - Address of the `account` to get
*/

@@ -268,7 +270,9 @@ async getAccount(address: Address): Promise<Account | undefined> {

const rlp = (await this.getAccountFromProvider(address)).serialize()
const accountFromProvider = await this.getAccountFromProvider(address)
const account =
equalsBytes(rlp, KECCAK256_RLP_EMPTY_ACCOUNT) === false
? Account.fromRlpSerializedAccount(rlp)
: undefined
equalsBytes(accountFromProvider.codeHash, new Uint8Array(32).fill(0)) ||
equalsBytes(accountFromProvider.serialize(), KECCAK256_RLP_EMPTY_ACCOUNT)
? undefined
: Account.fromRlpSerializedAccount(accountFromProvider.serialize())
this._accountCache?.put(address, account)

@@ -275,0 +279,0 @@

@@ -1,2 +0,1 @@

import { RLP } from '@ethereumjs/rlp'
import {

@@ -6,2 +5,3 @@ Account,

KECCAK256_NULL_S,
VerkleLeafType,
bigIntToBytes,

@@ -11,25 +11,18 @@ bytesToBigInt,

bytesToInt32,
getVerkleKey,
getVerkleStem,
getVerkleTreeKeyForCodeChunk,
getVerkleTreeKeyForStorageSlot,
hexToBytes,
padToEven,
setLengthLeft,
setLengthRight,
short,
toBytes,
verifyVerkleProof,
} from '@ethereumjs/util'
import { getKey, getStem, verifyUpdate } from '@ethereumjs/verkle'
import debugDefault from 'debug'
import { keccak256 } from 'ethereum-cryptography/keccak.js'
import { equalsBytes } from 'ethereum-cryptography/utils'
import {
AccessWitness,
AccessedStateType,
BALANCE_LEAF_KEY,
CODE_KECCAK_LEAF_KEY,
CODE_SIZE_LEAF_KEY,
NONCE_LEAF_KEY,
VERSION_LEAF_KEY,
decodeValue,
getTreeIndexesForStorageSlot,
getTreeIndicesForCodeChunk,
} from './accessWitness.js'
import { AccessWitness, AccessedStateType, decodeValue } from './accessWitness.js'
import { AccountCache, CacheType, CodeCache, StorageCache } from './cache/index.js'

@@ -40,3 +33,2 @@ import { OriginalStorageCache } from './cache/originalStorageCache.js'

import type { DefaultStateManager } from './stateManager.js'
import type { VerkleExecutionWitness } from '@ethereumjs/block'
import type {

@@ -50,8 +42,12 @@ AccountFields,

} from '@ethereumjs/common'
import type { Address, PrefixedHexString } from '@ethereumjs/util'
import type {
Address,
PrefixedHexString,
VerkleCrypto,
VerkleExecutionWitness,
VerkleProof,
} from '@ethereumjs/util'
const { debug: createDebugLogger } = debugDefault
const debug = debugDefault('statemanager:verkle')
const debug = createDebugLogger('statemanager:verkle')
export interface VerkleState {

@@ -120,2 +116,4 @@ [key: PrefixedHexString]: PrefixedHexString | null

accesses?: AccessWitness
verkleCrypto: VerkleCrypto
initialStateRoot?: Uint8Array
}

@@ -146,5 +144,7 @@

_codeCache?: CodeCache
_cachedStateRoot?: Uint8Array
originalStorageCache: OriginalStorageCache
verkleCrypto: VerkleCrypto
protected readonly _accountCacheSettings: CacheSettings

@@ -164,5 +164,6 @@ protected readonly _storageCacheSettings: CacheSettings

private _blockNum = BigInt(0)
private _executionWitness?: VerkleExecutionWitness
private _proof: Uint8Array | undefined
private _proof: VerkleProof | undefined

@@ -175,2 +176,3 @@ // State along execution (should update)

private _postState: VerkleState = {}
private _preState: VerkleState = {}

@@ -186,3 +188,3 @@ // Checkpointing

*/
constructor(opts: StatelessVerkleStateManagerOpts = {}) {
constructor(opts: StatelessVerkleStateManagerOpts) {
this.originalStorageCache = new OriginalStorageCache(this.getContractStorage.bind(this))

@@ -230,4 +232,11 @@

this._cachedStateRoot = opts.initialStateRoot
this.keccakFunction = opts.common?.customCrypto.keccak256 ?? keccak256
if (opts.verkleCrypto === undefined) {
throw new Error('verkle crypto required')
}
this.verkleCrypto = opts.verkleCrypto
// Skip DEBUG calls unless 'ethjs' included in environmental DEBUG variables

@@ -237,15 +246,2 @@ // Additional window check is to prevent vite browser bundling (and potentially other) to break

typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false
/*
* For a custom StateManager implementation adopt these
* callbacks passed to the `Cache` instantiated to perform
* the `get`, `put` and `delete` operations with the
* desired backend.
*/
// const getCb: get = async (address) => {
// return undefined
// }
// const putCb: put = async (keyBuf, accountRlp) => {}
// const deleteCb = async (keyBuf: Uint8Array) => {}
// this._cache = new Cache({ get, putCb, deleteCb })
}

@@ -258,13 +254,17 @@

public initVerkleExecutionWitness(
blockNum: bigint,
executionWitness?: VerkleExecutionWitness | null,
accessWitness?: AccessWitness
) {
this._blockNum = blockNum
if (executionWitness === null || executionWitness === undefined) {
throw Error(`Invalid executionWitness=${executionWitness} for initVerkleExecutionWitness`)
const errorMsg = `Invalid executionWitness=${executionWitness} for initVerkleExecutionWitness`
debug(errorMsg)
throw Error(errorMsg)
}
this._executionWitness = executionWitness
this.accessWitness = accessWitness ?? new AccessWitness()
this.accessWitness = accessWitness ?? new AccessWitness({ verkleCrypto: this.verkleCrypto })
this._proof = executionWitness.verkleProof as unknown as Uint8Array
this._proof = executionWitness.verkleProof

@@ -290,5 +290,11 @@ // Populate the pre-state and post-state from the executionWitness

// also maintain a separate preState unaffected by any changes in _state
this._preState = preStateRaw.reduce((prevValue, currentValue) => {
const acc = { ...prevValue, ...currentValue }
return acc
}, {})
const postStateRaw = executionWitness.stateDiff.flatMap(({ stem, suffixDiffs }) => {
const suffixDiffPairs = suffixDiffs.map(({ newValue, suffix }) => {
const key = `${stem}${padToEven(Number(suffix).toString(16))}`
const key = `${stem}${padToEven(Number(suffix).toString(16))}` as PrefixedHexString
// A postState value of null means there was no change from the preState.

@@ -316,46 +322,7 @@ // In this implementation, we therefore replace null with the preState.

getTreeKeyForVersion(stem: Uint8Array) {
return getKey(stem, VERSION_LEAF_KEY)
}
getTreeKeyForBalance(stem: Uint8Array) {
return getKey(stem, BALANCE_LEAF_KEY)
}
getTreeKeyForNonce(stem: Uint8Array) {
return getKey(stem, NONCE_LEAF_KEY)
}
getTreeKeyForCodeHash(stem: Uint8Array) {
return getKey(stem, CODE_KECCAK_LEAF_KEY)
}
getTreeKeyForCodeSize(stem: Uint8Array) {
return getKey(stem, CODE_SIZE_LEAF_KEY)
}
getTreeKeyForCodeChunk(address: Address, chunkId: number) {
const { treeIndex, subIndex } = getTreeIndicesForCodeChunk(chunkId)
return getKey(getStem(address, treeIndex), toBytes(subIndex))
}
chunkifyCode(code: Uint8Array) {
// Pad code to multiple of 31 bytes
if (code.length % 31 !== 0) {
const paddingLength = 31 - (code.length % 31)
code = setLengthRight(code, code.length + paddingLength)
}
throw new Error('Not implemented')
}
getTreeKeyForStorageSlot(address: Address, storageKey: bigint) {
const { treeIndex, subIndex } = getTreeIndexesForStorageSlot(storageKey)
return getKey(getStem(address, treeIndex), toBytes(subIndex))
}
checkChunkWitnessPresent(address: Address, codeOffset: number) {
async checkChunkWitnessPresent(address: Address, codeOffset: number) {
const chunkId = codeOffset / 31
const chunkKey = bytesToHex(this.getTreeKeyForCodeChunk(address, chunkId))
const chunkKey = bytesToHex(
await getVerkleTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto)
)
return this._state[chunkKey] !== undefined

@@ -370,4 +337,3 @@ }

shallowCopy(): EVMStateManagerInterface {
const stateManager = new StatelessVerkleStateManager()
stateManager.initVerkleExecutionWitness(this._executionWitness!)
const stateManager = new StatelessVerkleStateManager({ verkleCrypto: this.verkleCrypto })
return stateManager

@@ -426,18 +392,17 @@ }

// Get the contract code size
const codeSizeKey = this.getTreeKeyForCodeSize(getStem(address, 0))
const codeSizeLE = hexToBytes(this._state[bytesToHex(codeSizeKey)] ?? '0x')
const codeSize = bytesToInt32(codeSizeLE, true)
// allocate the code and copy onto it from the available witness chunks
const accessedCode = new Uint8Array(codeSize)
const codeSize = account.codeSize
// allocate enough to fit the last chunk
const accessedCode = new Uint8Array(codeSize + 31)
const chunks = Math.floor(bytesToInt32(codeSizeLE, true) / 31)
for (let chunkId = 0; chunkId <= chunks; chunkId++) {
const chunkKey = bytesToHex(this.getTreeKeyForCodeChunk(address, chunkId))
const chunks = Math.floor(codeSize / 31) + 1
for (let chunkId = 0; chunkId < chunks; chunkId++) {
const chunkKey = bytesToHex(
await getVerkleTreeKeyForCodeChunk(address, chunkId, this.verkleCrypto)
)
const codeChunk = this._state[chunkKey]
if (codeChunk === undefined) {
throw Error(`Invalid access to a missing code chunk with chunkKey=${chunkKey}`)
}
if (codeChunk === null) {
throw Error(`Invalid access to a non existent code chunk with chunkKey=${chunkKey}`)
const errorMsg = `Invalid access to a non existent code chunk with chunkKey=${chunkKey}`
debug(errorMsg)
throw Error(errorMsg)
}

@@ -459,5 +424,35 @@

// Return accessedCode where only accessed code has been copied
return accessedCode
const contactCode = accessedCode.slice(0, codeSize)
if (!this._codeCacheSettings.deactivate) {
this._codeCache?.put(address, contactCode)
}
return contactCode
}
async getContractCodeSize(address: Address): Promise<number> {
if (!this._accountCacheSettings.deactivate) {
const elem = this._accountCache!.get(address)
if (elem !== undefined) {
const account =
elem.accountRLP !== undefined
? Account.fromRlpSerializedPartialAccount(elem.accountRLP)
: undefined
if (account === undefined) {
const errorMsg = `account=${account} in cache`
debug(errorMsg)
throw Error(errorMsg)
}
return account.codeSize
}
}
// load the account basic fields and codeSize should be in it
const account = await this.getAccount(address)
if (account === undefined) {
return 0
}
return account.codeSize
}
/**

@@ -480,3 +475,7 @@ * Gets the storage value associated with the provided `address` and `key`. This method returns

const storageKey = this.getTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)))
const storageKey = await getVerkleTreeKeyForStorageSlot(
address,
BigInt(bytesToHex(key)),
this.verkleCrypto
)
const storageValue = toBytes(this._state[bytesToHex(storageKey)])

@@ -500,7 +499,10 @@

if (!this._storageCacheSettings.deactivate) {
const encodedValue = RLP.encode(value)
this._storageCache!.put(address, key, encodedValue)
this._storageCache!.put(address, key, value)
} else {
// TODO: Consider refactoring this in a writeContractStorage function? Like in stateManager.ts
const storageKey = this.getTreeKeyForStorageSlot(address, BigInt(bytesToHex(key)))
const storageKey = await getVerkleTreeKeyForStorageSlot(
address,
BigInt(bytesToHex(key)),
this.verkleCrypto
)
this._state[bytesToHex(storageKey)] = bytesToHex(setLengthRight(value, 32))

@@ -510,2 +512,4 @@ }

// Note from Gabriel: Clearing storage is not actually not possible in Verkle.
// This is because the storage keys are scattered throughout the verkle tree.
/**

@@ -516,9 +520,7 @@ * Clears all storage entries for the account corresponding to `address`.

async clearContractStorage(address: Address): Promise<void> {
const stem = getStem(address, 0)
const codeHashKey = this.getTreeKeyForCodeHash(stem)
const stem = getVerkleStem(this.verkleCrypto, address, 0)
const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash)
this._storageCache?.clearContractStorage(address)
// Update codeHash to `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`
this._state[bytesToHex(codeHashKey)] = KECCAK256_NULL_S
// TODO: Clear all storage slots (how?)
}

@@ -531,3 +533,3 @@

return elem.accountRLP !== undefined
? Account.fromRlpSerializedAccount(elem.accountRLP)
? Account.fromRlpSerializedPartialAccount(elem.accountRLP)
: undefined

@@ -537,43 +539,78 @@ }

const stem = getStem(address, 0)
const versionKey = this.getTreeKeyForVersion(stem)
const versionChunk = this._state[bytesToHex(versionKey)]
if (versionChunk === undefined) {
throw Error(
`Missing execution witness for address=${address} versionKey=${bytesToHex(versionKey)}`
)
const stem = getVerkleStem(this.verkleCrypto, address, 0)
const versionKey = getVerkleKey(stem, VerkleLeafType.Version)
const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance)
const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce)
const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash)
const codeSizeKey = getVerkleKey(stem, VerkleLeafType.CodeSize)
const versionRaw = this._state[bytesToHex(versionKey)]
const balanceRaw = this._state[bytesToHex(balanceKey)]
const nonceRaw = this._state[bytesToHex(nonceKey)]
const codeHashRaw = this._state[bytesToHex(codeHashKey)]
const codeSizeRaw = this._state[bytesToHex(codeSizeKey)]
// check if the account didn't exist if any of the basic keys have null
if (versionRaw === null || balanceRaw === null || nonceRaw === null || codeHashRaw === null) {
// check any of the other key shouldn't have string input available as this account didn't exist
if (
typeof versionRaw === `string` ||
typeof balanceRaw === 'string' ||
typeof nonceRaw === 'string' ||
typeof codeHashRaw === 'string'
) {
const errorMsg = `Invalid witness for a non existing address=${address} stem=${bytesToHex(
stem
)}`
debug(errorMsg)
throw Error(errorMsg)
} else {
return undefined
}
}
// if the versionChunk is null it means the account doesn't exist in pre state
if (versionChunk === null) {
return undefined
// check if codehash is correct 32 bytes prefixed hex string
if (codeHashRaw !== undefined && codeHashRaw !== null && codeHashRaw.length !== 66) {
const errorMsg = `Invalid codeHashRaw=${codeHashRaw} for address=${address} chunkKey=${bytesToHex(
codeHashKey
)}`
debug(errorMsg)
throw Error(errorMsg)
}
const balanceKey = this.getTreeKeyForBalance(stem)
const nonceKey = this.getTreeKeyForNonce(stem)
const codeHashKey = this.getTreeKeyForCodeHash(stem)
if (
versionRaw === undefined &&
balanceRaw === undefined &&
nonceRaw === undefined &&
codeHashRaw === undefined &&
codeSizeRaw === undefined
) {
const errorMsg = `No witness bundled for address=${address} stem=${bytesToHex(stem)}`
debug(errorMsg)
throw Error(errorMsg)
}
const balanceRaw = this._state[bytesToHex(balanceKey)]
const nonceRaw = this._state[bytesToHex(nonceKey)]
const codeHashRaw = this._state[bytesToHex(codeHashKey)]
const account = Account.fromAccountData({
balance:
typeof balanceRaw === 'string' ? bytesToBigInt(hexToBytes(balanceRaw), true) : undefined,
nonce: typeof nonceRaw === 'string' ? bytesToBigInt(hexToBytes(nonceRaw), true) : undefined,
codeHash: codeHashRaw?.length === 32 ? codeHashRaw : KECCAK256_NULL_S,
const account = Account.fromPartialAccountData({
version: typeof versionRaw === 'string' ? bytesToInt32(hexToBytes(versionRaw), true) : null,
balance: typeof balanceRaw === 'string' ? bytesToBigInt(hexToBytes(balanceRaw), true) : null,
nonce: typeof nonceRaw === 'string' ? bytesToBigInt(hexToBytes(nonceRaw), true) : null,
codeHash: typeof codeHashRaw === 'string' ? hexToBytes(codeHashRaw) : null,
// if codeSizeRaw is null, it means account didnt exist or it was EOA either way codeSize is 0
// if codeSizeRaw is undefined, then we pass in null which in our context of partial account means
// not specified
codeSize:
typeof codeSizeRaw === 'string'
? bytesToInt32(hexToBytes(codeSizeRaw), true)
: codeSizeRaw === null
? 0
: null,
storageRoot: null,
})
if (this.DEBUG) {
debug(
`getAccount address=${address.toString()} stem=${short(stem)} balance=${
account.balance
} nonce=${account.nonce} codeHash=${short(account.codeHash)} storageHash=${short(
account.storageRoot
)}`
)
debug(`getAccount address=${address.toString()} stem=${short(stem)}`)
}
if (!this._accountCacheSettings.deactivate) {
this._accountCache?.put(address, account)
this._accountCache?.put(address, account, true)
}

@@ -586,14 +623,10 @@

if (this.DEBUG) {
debug(
`putAccount address=${address.toString()} balance=${account.balance} nonce=${
account.nonce
} codeHash=${short(account.codeHash)} storageHash=${short(account.storageRoot)}`
)
debug(`putAccount address=${address.toString()}`)
}
if (this._accountCacheSettings.deactivate) {
const stem = getStem(address, 0)
const balanceKey = this.getTreeKeyForBalance(stem)
const nonceKey = this.getTreeKeyForNonce(stem)
const codeHashKey = this.getTreeKeyForCodeHash(stem)
const stem = getVerkleStem(this.verkleCrypto, address, 0)
const balanceKey = getVerkleKey(stem, VerkleLeafType.Balance)
const nonceKey = getVerkleKey(stem, VerkleLeafType.Nonce)
const codeHashKey = getVerkleKey(stem, VerkleLeafType.CodeHash)

@@ -608,3 +641,3 @@ const balanceBuf = setLengthRight(bigIntToBytes(account.balance, true), 32)

if (account !== undefined) {
this._accountCache!.put(address, account)
this._accountCache!.put(address, account, true)
} else {

@@ -639,6 +672,6 @@ this._accountCache!.del(address)

account.nonce = accountFields.nonce ?? account.nonce
account.balance = accountFields.balance ?? account.balance
account.storageRoot = accountFields.storageRoot ?? account.storageRoot
account.codeHash = accountFields.codeHash ?? account.codeHash
account._nonce = accountFields.nonce ?? account._nonce
account._balance = accountFields.balance ?? account._balance
account._storageRoot = accountFields.storageRoot ?? account._storageRoot
account._codeHash = accountFields.codeHash ?? account._codeHash
await this.putAccount(address, account)

@@ -650,23 +683,14 @@ }

}
/**
* Verifies whether the execution witness matches the stateRoot
* @param {Uint8Array} stateRoot - The stateRoot to verify the executionWitness against
* @returns {boolean} - Returns true if the executionWitness matches the provided stateRoot, otherwise false
*/
verifyProof(stateRoot: Uint8Array): boolean {
if (this._executionWitness === undefined) {
debug('Missing executionWitness')
return false
}
async verifyProof(parentVerkleRoot: Uint8Array): Promise<boolean> {
// Implementation: https://github.com/crate-crypto/rust-verkle-wasm/blob/master/src/lib.rs#L45
// The root is the root of the current (un-updated) trie
// The proof is proof of membership of all of the accessed values
// keys_values is a map from the key of the accessed value to a tuple
// the tuple contains the old value and the updated value
//
// This function returns the new root when all of the updated values are applied
const updatedStateRoot: Uint8Array = verifyUpdate(
parentVerkleRoot,
this._proof!, // TODO: Convert this into a Uint8Array ingestible by the method
new Map() // TODO: Generate the keys_values map from the old to the updated value
)
// TODO: Not sure if this should return the updated state Root (current block) or the un-updated one (parent block)
const verkleRoot = await this.getStateRoot()
// Verify that updatedStateRoot matches the state root of the block
return equalsBytes(updatedStateRoot, verkleRoot)
return verifyVerkleProof(this.verkleCrypto, stateRoot, this._executionWitness)
}

@@ -680,3 +704,3 @@

// switch to false if postVerify fails
let postVerified = true
let postFailures = 0

@@ -694,5 +718,13 @@ for (const accessedState of this.accessWitness!.accesses()) {

accessedChunks.set(chunkKey, true)
let computedValue = this.getComputedValue(accessedState)
let canonicalValue: string | null | undefined = this._postState[chunkKey]
const computedValue = this.getComputedValue(accessedState) ?? this._preState[chunkKey]
if (computedValue === undefined) {
debug(
`Block accesses missing in canonical address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`
)
postFailures++
continue
}
let canonicalValue: PrefixedHexString | null | undefined = this._postState[chunkKey]
if (canonicalValue === undefined) {

@@ -702,3 +734,3 @@ debug(

)
postVerified = false
postFailures++
continue

@@ -710,4 +742,10 @@ }

if (accessedState.type === AccessedStateType.Code) {
computedValue = computedValue !== null ? `0x${computedValue.slice(4)}` : null
// computedValue = computedValue !== null ? `0x${computedValue.slice(4)}` : null
canonicalValue = canonicalValue !== null ? `0x${canonicalValue.slice(4)}` : null
} else if (
accessedState.type === AccessedStateType.Storage &&
canonicalValue === null &&
computedValue === ZEROVALUE
) {
canonicalValue = ZEROVALUE
}

@@ -733,3 +771,3 @@

debug(`computed=${displayComputedValue}`)
postVerified = false
postFailures++
}

@@ -741,8 +779,10 @@ }

debug(`Missing chunk access for canChunkKey=${canChunkKey}`)
postVerified = false
postFailures++
}
}
debug(`verifyPostState=${postVerified}`)
return postVerified
const verifyPassed = postFailures === 0
debug(`verifyPostState verifyPassed=${verifyPassed} postFailures=${postFailures}`)
return verifyPassed
}

@@ -768,3 +808,3 @@

const balanceBigint = Account.fromRlpSerializedAccount(encodedAccount).balance
const balanceBigint = Account.fromRlpSerializedPartialAccount(encodedAccount).balance
return bytesToHex(setLengthRight(bigIntToBytes(balanceBigint, true), 32))

@@ -778,3 +818,3 @@ }

}
const nonceBigint = Account.fromRlpSerializedAccount(encodedAccount).nonce
const nonceBigint = Account.fromRlpSerializedPartialAccount(encodedAccount).nonce
return bytesToHex(setLengthRight(bigIntToBytes(nonceBigint, true), 32))

@@ -788,3 +828,3 @@ }

}
return bytesToHex(Account.fromRlpSerializedAccount(encodedAccount).codeHash)
return bytesToHex(Account.fromRlpSerializedPartialAccount(encodedAccount).codeHash)
}

@@ -801,5 +841,7 @@

const account = Account.fromRlpSerializedAccount(encodedAccount)
const account = Account.fromRlpSerializedPartialAccount(encodedAccount)
if (account.isContract()) {
throw Error(`Code cache not found for address=${address.toString()}`)
const errorMsg = `Code cache not found for address=${address.toString()}`
debug(errorMsg)
throw Error(errorMsg)
} else {

@@ -824,3 +866,3 @@ return null

const chunkSize = 31
return bytesToHex(code.slice(codeOffset, codeOffset + chunkSize))
return bytesToHex(setLengthRight(code.slice(codeOffset, codeOffset + chunkSize), chunkSize))
}

@@ -830,7 +872,9 @@

const { slot } = accessedState
const storage = this._storageCache?.get(address, bigIntToBytes(slot))
const key = setLengthLeft(bigIntToBytes(slot), 32)
const storage = this._storageCache?.get(address, key)
if (storage === undefined) {
return null
}
return bytesToHex(storage)
return bytesToHex(setLengthLeft(storage, 32))
}

@@ -886,17 +930,22 @@ }

/**
* Gets the verkle root.
* NOTE: this needs some examination in the code where this is needed
* and if we have the verkle root present
* @returns {Promise<Uint8Array>} - Returns the verkle root of the `StateManager`
* Gets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* This is required to verify and prove verkle execution witnesses.
* @returns {Promise<Uint8Array>} - Returns the cached state root
*/
async getStateRoot(): Promise<Uint8Array> {
return new Uint8Array(0)
if (this._cachedStateRoot === undefined) {
throw new Error('Cache state root missing')
}
return this._cachedStateRoot
}
/**
* TODO: needed?
* Maybe in this context: reset to original pre state suffice
* @param stateRoot - The verkle root to reset the instance to
* Sets the cache state root.
* This is used to persist the stateRoot between blocks, so that blocks can retrieve the stateRoot of the parent block.
* @param stateRoot - The stateRoot to set
*/
async setStateRoot(_: Uint8Array): Promise<void> {}
async setStateRoot(stateRoot: Uint8Array): Promise<void> {
this._cachedStateRoot = stateRoot
}

@@ -903,0 +952,0 @@ /**

@@ -31,7 +31,10 @@ import { Chain, Common } from '@ethereumjs/common'

import type { AccountFields, EVMStateManagerInterface, StorageDump } from '@ethereumjs/common'
import type { StorageRange } from '@ethereumjs/common/src'
import type {
AccountFields,
EVMStateManagerInterface,
StorageDump,
StorageRange,
} from '@ethereumjs/common'
import type { DB, PrefixedHexString } from '@ethereumjs/util'
import type { Debugger } from 'debug'
const { debug: createDebugLogger } = debugDefault

@@ -199,3 +202,3 @@ export type StorageProof = {

this._debug = createDebugLogger('statemanager:statemanager')
this._debug = debugDefault('statemanager:statemanager')

@@ -361,5 +364,2 @@ this.common = opts.common ?? new Common({ chain: Chain.Mainnet })

const codeHash = this.keccakFunction(value)
if (equalsBytes(codeHash, KECCAK256_NULL)) {
return
}

@@ -407,24 +407,38 @@ if (this.DEBUG) {

async getContractCodeSize(address: Address): Promise<number> {
const contractCode = await this.getContractCode(address)
return contractCode.length
}
/**
* Gets the storage trie for an account from the storage
* cache or does a lookup.
* Gets the storage trie for the EVM-internal account identified by the provided address/hash.
* If the storage trie is not in the local cache ('this._storageTries'),
* generates a new storage trie object based on a lookup (shallow copy from 'this._trie'),
* applies the storage root of the provided rootAccount (or an
* empty trie root if no rootAccount is provided), and stores the new entry
* in the local cache.
* @param addressOrHash Address (or other object) with populated 'bytes', or a raw Uint8Array.
* Used to identify the requested storage trie in the local cache and define the
* prefix used when creating a new storage trie.
* @param rootAccount (Optional) Account object whose 'storageRoot' is to be used as
* the root of the new storageTrie returned when there is no pre-existing trie.
* If left undefined, the EMPTY_TRIE_ROOT will be used as the root instead.
* @returns storage Trie object
* @private
*/
// TODO PR: have a better interface for hashed address pull?
protected _getStorageTrie(addressOrHash: Address | Uint8Array, account?: Account): Trie {
protected _getStorageTrie(
addressOrHash: Address | { bytes: Uint8Array } | Uint8Array,
rootAccount?: Account
): Trie {
// use hashed key for lookup from storage cache
const addressHex = bytesToUnprefixedHex(
addressOrHash instanceof Address ? this.keccakFunction(addressOrHash.bytes) : addressOrHash
)
const addressBytes: Uint8Array =
addressOrHash instanceof Uint8Array ? addressOrHash : this.keccakFunction(addressOrHash.bytes)
const addressHex: string = bytesToUnprefixedHex(addressBytes)
let storageTrie = this._storageTries[addressHex]
if (storageTrie === undefined) {
const keyPrefix = this._prefixStorageTrieKeys
? (addressOrHash instanceof Address
? this.keccakFunction(addressOrHash.bytes)
: addressOrHash
).slice(0, 7)
: undefined
const keyPrefix = this._prefixStorageTrieKeys ? addressBytes.slice(0, 7) : undefined
storageTrie = this._trie.shallowCopy(false, { keyPrefix })
if (account !== undefined) {
storageTrie.root(account.storageRoot)
if (rootAccount !== undefined) {
storageTrie.root(rootAccount.storageRoot)
} else {

@@ -803,3 +817,3 @@ storageTrie.root(storageTrie.EMPTY_TRIE_ROOT)

storageProof: StorageProof[],
storageHash: string,
storageHash: PrefixedHexString,
address: Address,

@@ -806,0 +820,0 @@ safe: boolean = false

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc