From 91f4e33b7be52dab53ad30281934db79e6739d39 Mon Sep 17 00:00:00 2001 From: Maycon Amaro <131882788+mayconamaroCW@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:48:58 -0300 Subject: [PATCH 1/3] ci: solve warnings (#997) * ci: use checkout v4 * ci: use setup just v2 * ci: use setup node v4 --- .github/workflows/_setup-e2e.yml | 6 +++--- .github/workflows/check-features.yml | 4 ++-- .github/workflows/comment-tag-report.yml | 2 +- .github/workflows/deploy.yml | 2 +- .github/workflows/doc-test.yml | 4 ++-- .github/workflows/docs-release.yml | 4 ++-- .github/workflows/e2e-contracts-postgres.yml | 6 +++--- .github/workflows/e2e-contracts-rocks.yml | 6 +++--- .github/workflows/e2e-contracts.yml | 6 +++--- .github/workflows/int-test.yml | 4 ++-- .github/workflows/lint-check.yml | 4 ++-- .github/workflows/outdated.yml | 4 ++-- .github/workflows/unit-test.yml | 4 ++-- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/_setup-e2e.yml b/.github/workflows/_setup-e2e.yml index 4be536c97..b7e8ebc4c 100644 --- a/.github/workflows/_setup-e2e.yml +++ b/.github/workflows/_setup-e2e.yml @@ -22,7 +22,7 @@ jobs: timeout-minutes: 35 steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -46,10 +46,10 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: .tool-versions diff --git a/.github/workflows/check-features.yml b/.github/workflows/check-features.yml index 3ee94c215..ffe43b23e 100644 --- a/.github/workflows/check-features.yml +++ b/.github/workflows/check-features.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -56,7 +56,7 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Set up Test Dependencies if: ${{ steps.cache-cargo.outputs.cache-hit != 'true' }} diff --git a/.github/workflows/comment-tag-report.yml b/.github/workflows/comment-tag-report.yml index 98d9c1b78..87ef6094c 100644 --- a/.github/workflows/comment-tag-report.yml +++ b/.github/workflows/comment-tag-report.yml @@ -15,7 +15,7 @@ jobs: timeout-minutes: 10 steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Rust run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index de93eb09a..c226c308a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -26,7 +26,7 @@ jobs: continue-on-error: true steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/doc-test.yml b/.github/workflows/doc-test.yml index 4c6cbb0d1..df693da4c 100644 --- a/.github/workflows/doc-test.yml +++ b/.github/workflows/doc-test.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -51,7 +51,7 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Test docs run: just test-doc diff --git a/.github/workflows/docs-release.yml b/.github/workflows/docs-release.yml index e633d1f01..c17672a06 100644 --- a/.github/workflows/docs-release.yml +++ b/.github/workflows/docs-release.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -49,7 +49,7 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Generate docs run: | diff --git a/.github/workflows/e2e-contracts-postgres.yml b/.github/workflows/e2e-contracts-postgres.yml index 942320fba..238ba741f 100644 --- a/.github/workflows/e2e-contracts-postgres.yml +++ b/.github/workflows/e2e-contracts-postgres.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -63,10 +63,10 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: .tool-versions diff --git a/.github/workflows/e2e-contracts-rocks.yml b/.github/workflows/e2e-contracts-rocks.yml index fdf7f303e..d2dab234a 100644 --- a/.github/workflows/e2e-contracts-rocks.yml +++ b/.github/workflows/e2e-contracts-rocks.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -63,10 +63,10 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: .tool-versions diff --git a/.github/workflows/e2e-contracts.yml b/.github/workflows/e2e-contracts.yml index 571556917..05f26a2f0 100644 --- a/.github/workflows/e2e-contracts.yml +++ b/.github/workflows/e2e-contracts.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -63,10 +63,10 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Install Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version-file: .tool-versions diff --git a/.github/workflows/int-test.yml b/.github/workflows/int-test.yml index 94f2cc122..5ffade2da 100644 --- a/.github/workflows/int-test.yml +++ b/.github/workflows/int-test.yml @@ -37,7 +37,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -59,7 +59,7 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Integration tests run: just test-int diff --git a/.github/workflows/lint-check.yml b/.github/workflows/lint-check.yml index 4eaf6c181..b3319d480 100644 --- a/.github/workflows/lint-check.yml +++ b/.github/workflows/lint-check.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -57,7 +57,7 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Just lint-check run: just lint-check -2024-01-01 diff --git a/.github/workflows/outdated.yml b/.github/workflows/outdated.yml index 9fc3b1cca..8675c3c1a 100644 --- a/.github/workflows/outdated.yml +++ b/.github/workflows/outdated.yml @@ -15,13 +15,13 @@ jobs: timeout-minutes: 10 steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Rust run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Check outdated run: just outdated > outdated.txt diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 058350e94..06b43769d 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Cache Cargo Registry and Target Directory uses: actions/cache@v4.0.2 @@ -58,7 +58,7 @@ jobs: run: sudo apt-get install -y protobuf-compiler - name: Set up Just - uses: extractions/setup-just@v1 + uses: extractions/setup-just@v2 - name: Unit tests run: just test-unit From bef8b354a2d9c13f2e6da9ad12bedb6f694a9443 Mon Sep 17 00:00:00 2001 From: Evgenii Zaitsev <97302011+EvgeniiZaitsevCW@users.noreply.github.com> Date: Wed, 5 Jun 2024 03:08:02 +0700 Subject: [PATCH 2/3] test: add dense storage contract tests (#995) * test: minor improvements in existing contract tests * test: add dense storage contract tests --- e2e/contracts/TestContractDenseStorage.sol | 47 ++++++++ ...arallel-contract-dense-storage.abm.test.ts | 52 +++++++++ .../automine/e2e-tx-parallel-contract.test.ts | 8 +- .../automine/e2e-tx-serial-contract.test.ts | 8 +- ...arallel-contract-dense-storage.ebm.test.ts | 56 ++++++++++ e2e/test/helpers/contract-dense-storage.ts | 103 ++++++++++++++++++ e2e/test/helpers/rpc.ts | 14 ++- 7 files changed, 276 insertions(+), 12 deletions(-) create mode 100644 e2e/contracts/TestContractDenseStorage.sol create mode 100644 e2e/test/automine/e2e-tx-parallel-contract-dense-storage.abm.test.ts create mode 100644 e2e/test/external/e2e-tx-parallel-contract-dense-storage.ebm.test.ts create mode 100644 e2e/test/helpers/contract-dense-storage.ts diff --git a/e2e/contracts/TestContractDenseStorage.sol b/e2e/contracts/TestContractDenseStorage.sol new file mode 100644 index 000000000..a4fea9b79 --- /dev/null +++ b/e2e/contracts/TestContractDenseStorage.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract TestContractDenseStorage { + + struct Record { + uint16 field16; + uint16 reserve; + uint32 field32; + uint64 field64; + uint128 field128; + } + + mapping(address => Record) public records; + + /// @dev Changes fields of the record for an account. + /// @param changeOfField16 The value to change of the appropriate fields of the record. + /// @param changeOfField32 The value to change of the appropriate fields of the record. + /// @param changeOfField64 The value to change of the appropriate fields of the record. + /// @param changeOfField128 The value to change of the appropriate fields of the record. + function change( + address account, + int256 changeOfField16, + int256 changeOfField32, + int256 changeOfField64, + int256 changeOfField128 + ) public { + Record storage record = records[account]; + + record.field16 = uint16(uint256(int256(uint256(record.field16)) + changeOfField16)); + record.field32 = uint32(uint256(int256(uint256(record.field32)) + changeOfField32)); + record.field64 = uint64(uint256(int256(uint256(record.field64)) + changeOfField64)); + record.field128 = uint128(uint256(int256(uint256(record.field128)) + changeOfField128)); + } + + /// @dev Sets the exact field values of the record for an an account. + /// @param newRecord The new content of the record for the account. + function set(address account, Record calldata newRecord) public { + records[account] = newRecord; + } + + /// @dev Gets the record of an account. + /// @return The record of the account. + function get(address account) public view returns (Record memory) { + return records[account]; + } +} diff --git a/e2e/test/automine/e2e-tx-parallel-contract-dense-storage.abm.test.ts b/e2e/test/automine/e2e-tx-parallel-contract-dense-storage.abm.test.ts new file mode 100644 index 000000000..8e92218db --- /dev/null +++ b/e2e/test/automine/e2e-tx-parallel-contract-dense-storage.abm.test.ts @@ -0,0 +1,52 @@ +import { Account, ALICE, BOB, randomAccounts } from "../helpers/account"; +import { deployTestContractDenseStorage, sendRawTransactions, } from "../helpers/rpc"; +import { + AccountRecord, + applyRecordChange, + compareRecords, + defineRecordChange, + initialRecord, + inverseRecordChange, + prepareSignedTxOfRecordChange +} from "../helpers/contract-dense-storage"; + +describe("Transaction: parallel for the 'TestContractDenseStorage' contract", async () => { + it("Parallel transactions execute properly when no reverts are expected", async () => { + const contract = await deployTestContractDenseStorage(); + + const expectedRecords: Record = {}; + expectedRecords[ALICE.address] = { ...initialRecord }; + expectedRecords[BOB.address] = { ...initialRecord }; + + // Set initial records + await contract.set(ALICE.address, expectedRecords[ALICE.address]); + await contract.set(BOB.address, expectedRecords[BOB.address]); + + + // Prepare a pair of transactions: one for Alice and another for Bob + const txCountPerUser = 32; + const senders: Account[] = randomAccounts(txCountPerUser * 2); + const signedTxs: string[] = []; + for (let txIndexPerUser = 0; txIndexPerUser < txCountPerUser; ++txIndexPerUser) { + const recordChange1 = defineRecordChange(txIndexPerUser); + const recordChange2 = inverseRecordChange(recordChange1); + applyRecordChange(expectedRecords[ALICE.address], recordChange1); + applyRecordChange(expectedRecords[BOB.address], recordChange2); + + const sender1 = senders[txIndexPerUser * 2]; + const sender2 = senders[txIndexPerUser * 2 + 1]; + const signTx1: string = await prepareSignedTxOfRecordChange(contract, sender1, ALICE, recordChange1); + const signTx2: string = await prepareSignedTxOfRecordChange(contract, sender2, BOB, recordChange2); + + signedTxs.push(signTx1); + signedTxs.push(signTx2); + } + + // Send transactions in parallel + await sendRawTransactions(signedTxs); + + // Verify + compareRecords(await contract.get(ALICE.address), expectedRecords[ALICE.address]); + compareRecords(await contract.get(BOB.address), expectedRecords[BOB.address]); + }); +}); diff --git a/e2e/test/automine/e2e-tx-parallel-contract.test.ts b/e2e/test/automine/e2e-tx-parallel-contract.test.ts index 297955098..a56e50e5d 100644 --- a/e2e/test/automine/e2e-tx-parallel-contract.test.ts +++ b/e2e/test/automine/e2e-tx-parallel-contract.test.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; -import { TestContractBalances, TestContractCounter } from "../typechain-types"; +import { TestContractBalances, TestContractCounter } from "../../typechain-types"; import { ALICE, BOB, CHARLIE, randomAccounts } from "../helpers/account"; import { TX_PARAMS, @@ -12,7 +12,7 @@ import { } from "../helpers/rpc"; describe("Transaction: parallel TestContractBalances", async () => { - var _contract: TestContractBalances; + let _contract: TestContractBalances; it("Resets blockchain", async () => { await sendReset(); @@ -94,7 +94,7 @@ describe("Transaction: parallel TestContractBalances", async () => { }); describe("Transaction: parallel TestContractCounter", async () => { - var _contract: TestContractCounter; + let _contract: TestContractCounter; it("Resets blockchain", async () => { await sendReset(); @@ -112,7 +112,7 @@ describe("Transaction: parallel TestContractCounter", async () => { const incSender = ALICE; const doubleSender = BOB; - // send pair of inc and double requests + // send a pair of inc and double requests for (let i = 0; i < 20; i++) { // calculate expected double counter const doubleCounter = Number(await _contract.getDoubleCounter()); diff --git a/e2e/test/automine/e2e-tx-serial-contract.test.ts b/e2e/test/automine/e2e-tx-serial-contract.test.ts index 38f7c295c..80889d5ea 100644 --- a/e2e/test/automine/e2e-tx-serial-contract.test.ts +++ b/e2e/test/automine/e2e-tx-serial-contract.test.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; -import { TestContractBalances } from "../typechain-types"; +import { TestContractBalances } from "../../typechain-types"; import { CHARLIE } from "../helpers/account"; import { calculateSlotPosition, @@ -23,8 +23,8 @@ const CONTRACT_TOPIC_ADD = "0x2728c9d3205d667bbc0eefdfeda366261b4d021949630c047f const CONTRACT_TOPIC_SUB = "0xf9c652bcdb0eed6299c6a878897eb3af110dbb265833e7af75ad3d2c2f4a980c"; describe("Transaction: serial TestContractBalances", () => { - var _contract: TestContractBalances; - var _block: number; + let _contract: TestContractBalances; + let _block: number; it("Resets blockchain", async () => { await sendReset(); @@ -91,7 +91,7 @@ describe("Transaction: serial TestContractBalances", () => { const transaction: Transaction = await send("eth_getTransactionByHash", [deploymentTransactionHash]); expect(transaction.from).eq(CHARLIE.address, "tx.from"); - expect(transaction.to).eq(null), "tx.to"; + expect(transaction.to).eq(null, "tx.to"); expect(transaction.value).eq('0x0', "tx.value"); expect(transaction.gas).match(HEX_PATTERN, "tx.gas format"); expect(transaction.gasPrice).match(HEX_PATTERN, "tx.gasPrice format"); diff --git a/e2e/test/external/e2e-tx-parallel-contract-dense-storage.ebm.test.ts b/e2e/test/external/e2e-tx-parallel-contract-dense-storage.ebm.test.ts new file mode 100644 index 000000000..abe238725 --- /dev/null +++ b/e2e/test/external/e2e-tx-parallel-contract-dense-storage.ebm.test.ts @@ -0,0 +1,56 @@ +import { Account, ALICE, BOB, randomAccounts } from "../helpers/account"; +import { deployTestContractDenseStorage, sendEvmMine, sendRawTransactions, } from "../helpers/rpc"; +import { + AccountRecord, + applyRecordChange, + compareRecords, + defineRecordChange, + initialRecord, + inverseRecordChange, + prepareSignedTxOfRecordChange +} from "../helpers/contract-dense-storage"; + +describe("Transaction: parallel for the 'TestContractDenseStorage' contract", async () => { + it("Parallel transactions execute properly when no reverts are expected", async () => { + const contract = await deployTestContractDenseStorage(); + await sendEvmMine(); + + const expectedRecords: Record = {}; + expectedRecords[ALICE.address] = { ...initialRecord }; + expectedRecords[BOB.address] = { ...initialRecord }; + + // Set initial records + await contract.set(ALICE.address, expectedRecords[ALICE.address]); + await sendEvmMine(); + await contract.set(BOB.address, expectedRecords[BOB.address]); + await sendEvmMine(); + + + // Prepare a pair of transactions: one for Alice and another for Bob + const txCountPerUser = 32; + const senders: Account[] = randomAccounts(txCountPerUser * 2); + const signedTxs: string[] = []; + for (let txIndexPerUser = 0; txIndexPerUser < txCountPerUser; ++txIndexPerUser) { + const recordChange1 = defineRecordChange(txIndexPerUser); + const recordChange2 = inverseRecordChange(recordChange1); + applyRecordChange(expectedRecords[ALICE.address], recordChange1); + applyRecordChange(expectedRecords[BOB.address], recordChange2); + + const sender1 = senders[txIndexPerUser * 2]; + const sender2 = senders[txIndexPerUser * 2 + 1]; + const signTx1: string = await prepareSignedTxOfRecordChange(contract, sender1, ALICE, recordChange1); + const signTx2: string = await prepareSignedTxOfRecordChange(contract, sender2, BOB, recordChange2); + + signedTxs.push(signTx1); + signedTxs.push(signTx2); + } + + // Send transactions in parallel + await sendRawTransactions(signedTxs); + await sendEvmMine(); + + // Verify + compareRecords(await contract.get(ALICE.address), expectedRecords[ALICE.address]); + compareRecords(await contract.get(BOB.address), expectedRecords[BOB.address]); + }); +}); diff --git a/e2e/test/helpers/contract-dense-storage.ts b/e2e/test/helpers/contract-dense-storage.ts new file mode 100644 index 000000000..cd597b14d --- /dev/null +++ b/e2e/test/helpers/contract-dense-storage.ts @@ -0,0 +1,103 @@ +import { expect } from "chai"; +import { prepareSignedTx } from "./rpc"; +import { Account } from "./account"; +import { TestContractDenseStorage } from "../../typechain-types"; + +export interface AccountRecord { + field16: bigint; + reserve: bigint; + field32: bigint; + field64: bigint; + field128: bigint; + + [key: string]: bigint; // Index signature +} + +export const initialRecord: AccountRecord = { + field16: BigInt(2) ** BigInt(16 - 1), // 0x8000 + reserve: BigInt("0xABCD"), + field32: BigInt(2) ** BigInt(32 - 1), // 0x8000_0000 + field64: BigInt(2) ** BigInt(64 - 1), // 0x8000_0000_0000_0000 + field128: BigInt(2) ** BigInt(128 - 1) // 0x8000_0000_0000_0000_0000_0000_0000_0000 +}; + +export interface RecordChange { + forField16: bigint; + forField32: bigint; + forField64: bigint; + forField128: bigint; +} + +export function defineRecordChange(index: number): RecordChange { + let changeOfField16: bigint = BigInt(2) ** BigInt(16 - 6) - BigInt(index); + if ((index & 1) !== 0) { + changeOfField16 *= BigInt(-1); + } + let changeOfField32: bigint = BigInt(2) ** BigInt(32 - 6) - BigInt(33 * index); + if ((index & 2) !== 0) { + changeOfField32 *= BigInt(-1); + } + let changeOfField64: bigint = BigInt(2) ** BigInt(64 - 6) - BigInt(555 * index); + if ((index & 4) !== 0) { + changeOfField64 *= BigInt(-1); + } + let changeOfField128: bigint = BigInt(2) ** BigInt(128 - 6) - BigInt(7777 * index); + if ((index & 8) !== 0) { + changeOfField64 *= BigInt(-1); + } + + return { + forField16: changeOfField16, + forField32: changeOfField32, + forField64: changeOfField64, + forField128: changeOfField128 + }; +} + +export function inverseRecordChange(change: RecordChange): RecordChange { + return { + forField16: -change.forField16, + forField32: -change.forField32, + forField64: -change.forField64, + forField128: -change.forField128, + }; +} + +export function applyRecordChange( + record: AccountRecord, + change: RecordChange, +) { + record.field16 += change.forField16; + record.field32 += change.forField32; + record.field64 += change.forField64; + record.field128 += change.forField128; +} + +export function compareRecords(actualRecord: any, expectedRecord: AccountRecord) { + Object.keys(expectedRecord).forEach(property => { + expect(actualRecord[property]).to.eq( + expectedRecord[property], + `Mismatch in the "${property}" property of the storage record` + ); + }); +} + +export async function prepareSignedTxOfRecordChange( + contract: TestContractDenseStorage, + sender: Account, + targetAccount: Account, + recordChange: RecordChange +): Promise { + return await prepareSignedTx({ + contract, + account: sender, + methodName: "change", + methodParameters: [ + targetAccount.address, + recordChange.forField16, + recordChange.forField32, + recordChange.forField64, + recordChange.forField128 + ] + }); +} \ No newline at end of file diff --git a/e2e/test/helpers/rpc.ts b/e2e/test/helpers/rpc.ts index 413550d27..dd5843742 100644 --- a/e2e/test/helpers/rpc.ts +++ b/e2e/test/helpers/rpc.ts @@ -4,8 +4,8 @@ import { BaseContract, Block, Contract, - JsonRpcProvider, JsonRpcApiProviderOptions, + JsonRpcProvider, keccak256, TransactionReceipt, TransactionResponse @@ -15,7 +15,7 @@ import { HttpNetworkConfig } from "hardhat/types"; import { Numbers } from "web3-types"; import { WebSocket } from "ws"; -import { TestContractBalances, TestContractCounter } from "../../typechain-types"; +import { TestContractBalances, TestContractCounter, TestContractDenseStorage } from "../../typechain-types"; import { Account, CHARLIE } from "./account"; import { currentMiningIntervalInMs, currentNetwork, isStratus } from "./network"; @@ -150,18 +150,24 @@ export async function sendExpect(method: string, params: any[] = []): Promise { const testContractFactory = await ethers.getContractFactory("TestContractBalances"); return await testContractFactory.connect(CHARLIE.signer()).deploy(); } -// Deploys the TestContractCounter. +// Deploys the "TestContractCounter" contract. export async function deployTestContractCounter(): Promise { const testContractFactory = await ethers.getContractFactory("TestContractCounter"); return await testContractFactory.connect(CHARLIE.signer()).deploy(); } +// Deploys the "TestContractDenseStorage" contract. +export async function deployTestContractDenseStorage(): Promise { + const testContractFactory = await ethers.getContractFactory("TestContractDenseStorage"); + return await testContractFactory.connect(CHARLIE.signer()).deploy(); +} + // Converts a number to Blockchain hex representation (prefixed with 0x). export function toHex(number: number | bigint): string { return "0x" + number.toString(16); From f3a9beb218225addbd5144e83bd9811a7222ac52 Mon Sep 17 00:00:00 2001 From: Renato Dinhani <101204870+dinhani-cw@users.noreply.github.com> Date: Tue, 4 Jun 2024 17:54:26 -0300 Subject: [PATCH 3/3] feat: track evm execution in tracing (#1001) --- src/config.rs | 19 ++++++------ src/eth/executor.rs | 22 ++++++++++++-- src/eth/storage/stratus_storage.rs | 48 +++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 26 deletions(-) diff --git a/src/config.rs b/src/config.rs index 1d5f83a83..86c427c2f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,6 +13,7 @@ use display_json::DebugAsJson; use tokio::runtime::Builder; use tokio::runtime::Handle; use tokio::runtime::Runtime; +use tracing::info_span; use crate::eth::evm::revm::Revm; use crate::eth::evm::Evm; @@ -182,6 +183,7 @@ impl ExecutorConfig { tracing::info!(config = ?self, "creating executor"); // spawn evm in background using native threads + // TODO: move EVMs initialization to Executor. let (evm_tx, evm_rx) = crossbeam_channel::unbounded::(); for _ in 1..=num_evms { // create evm resources @@ -194,31 +196,30 @@ impl ExecutorConfig { let evm_rx = evm_rx.clone(); // spawn thread that will run evm - // todo: needs a way to signal error like a cancellation token in case it fails to initialize let t = thread::Builder::new().name("evm".into()); info_task_spawn(TASK_NAME); t.spawn(move || { - // init tokio + // init services let _tokio_guard = evm_tokio.enter(); - - // init storage if let Err(e) = Handle::current().block_on(evm_storage.allocate_evm_thread_resources()) { tracing::error!(reason = ?e, "failed to allocate evm storage resources"); } - - // init evm let mut evm = Revm::new(evm_storage, evm_config); // keep executing transactions until the channel is closed - while let Ok((input, tx)) = evm_rx.recv() { + while let Ok(task) = evm_rx.recv() { if GlobalState::warn_if_shutdown(TASK_NAME) { return; } + // enter span from another thread + let span = task.span_id.map(|id| info_span!(parent: id, "executor::evm")); + let _span_enter = span.as_ref().map(|span| span.enter()); + // execute - let result = evm.execute(input); - if let Err(e) = tx.send(result) { + let result = evm.execute(task.input); + if let Err(e) = task.response_tx.send(result) { tracing::error!(reason = ?e, "failed to send evm execution result"); }; } diff --git a/src/eth/executor.rs b/src/eth/executor.rs index 0d1d6e54b..4ff2bb819 100644 --- a/src/eth/executor.rs +++ b/src/eth/executor.rs @@ -13,6 +13,7 @@ use tokio::sync::broadcast; use tokio::sync::mpsc; use tokio::sync::oneshot; use tokio::sync::Mutex; +use tracing::span::Id; use tracing::Span; use super::Consensus; @@ -44,7 +45,23 @@ use crate::ext::SpanExt; use crate::infra::metrics; use crate::infra::BlockchainClient; -pub type EvmTask = (EvmInput, oneshot::Sender>); +#[derive(Debug)] +pub struct EvmTask { + pub span_id: Option, + pub input: EvmInput, + pub response_tx: oneshot::Sender>, +} + +impl EvmTask { + pub fn new(input: EvmInput, response_tx: oneshot::Sender>) -> Self { + Self { + span_id: Span::current().id(), + input, + response_tx, + } + } +} + pub struct Executor { /// Channel to send transactions to background EVMs. evm_tx: crossbeam_channel::Sender, @@ -337,10 +354,9 @@ impl Executor { // ------------------------------------------------------------------------- /// Submits a transaction to the EVM and awaits for its execution. - #[tracing::instrument(name = "executor::evm", skip_all)] async fn execute_in_evm(&self, evm_input: EvmInput) -> anyhow::Result { let (execution_tx, execution_rx) = oneshot::channel::>(); - self.evm_tx.send((evm_input, execution_tx))?; + self.evm_tx.send(EvmTask::new(evm_input, execution_tx))?; execution_rx.await? } } diff --git a/src/eth/storage/stratus_storage.rs b/src/eth/storage/stratus_storage.rs index 805128d74..11685631f 100644 --- a/src/eth/storage/stratus_storage.rs +++ b/src/eth/storage/stratus_storage.rs @@ -158,8 +158,10 @@ impl StratusStorage { Ok(()) } - #[tracing::instrument(skip_all)] + #[tracing::instrument(name = "storage::set_mined_block_number", skip_all, fields(number))] pub async fn set_mined_block_number(&self, number: BlockNumber) -> anyhow::Result<()> { + Span::with(|s| s.rec("number", &number)); + #[cfg(feature = "metrics")] { let start = metrics::now(); @@ -228,14 +230,14 @@ impl StratusStorage { #[tracing::instrument(name = "storage::read_account", skip_all, fields(address, point_in_time))] pub async fn read_account(&self, address: &Address, point_in_time: &StoragePointInTime) -> anyhow::Result { - #[cfg(feature = "metrics")] - let start = metrics::now(); - Span::with(|s| { s.rec("address", address); s.rec("point_in_time", point_in_time); }); + #[cfg(feature = "metrics")] + let start = metrics::now(); + // read from temp only if present if point_in_time.is_present() { if let Some(account) = self.temp.read_account(address).await? { @@ -263,8 +265,14 @@ impl StratusStorage { } } - #[tracing::instrument(skip_all)] + #[tracing::instrument(name = "storage::read_slot", skip_all, fields(address, index, point_in_time))] pub async fn read_slot(&self, address: &Address, index: &SlotIndex, point_in_time: &StoragePointInTime) -> anyhow::Result { + Span::with(|s| { + s.rec("address", address); + s.rec("index", index); + s.rec("point_in_time", point_in_time); + }); + #[cfg(feature = "metrics")] let start = metrics::now(); @@ -343,36 +351,48 @@ impl StratusStorage { // Blocks // ------------------------------------------------------------------------- - #[tracing::instrument(skip_all)] - pub async fn save_execution(&self, transaction_execution: TransactionExecution) -> anyhow::Result<()> { + #[tracing::instrument(name = "storage::save_execution", skip_all, fields(hash))] + pub async fn save_execution(&self, tx: TransactionExecution) -> anyhow::Result<()> { + Span::with(|s| { + s.rec("hash", &tx.hash()); + }); + #[cfg(feature = "metrics")] { let start = metrics::now(); - let result = self.temp.save_execution(transaction_execution).await; + let result = self.temp.save_execution(tx).await; metrics::inc_storage_save_execution(start.elapsed(), result.is_ok()); result } #[cfg(not(feature = "metrics"))] - self.temp.save_execution(transaction_execution).await + self.temp.save_execution(tx).await } - #[tracing::instrument(skip_all)] + #[tracing::instrument(name = "storage::finish_block", skip_all, fields(number))] pub async fn finish_block(&self) -> anyhow::Result { #[cfg(feature = "metrics")] - { + let result = { let start = metrics::now(); let result = self.temp.finish_block().await; metrics::inc_storage_finish_block(start.elapsed(), result.is_ok()); result - } + }; #[cfg(not(feature = "metrics"))] - self.temp.finish_block().await + let result = self.temp.finish_block().await; + + if let Ok(ref block) = result { + Span::with(|s| s.rec("number", &block.number)); + } + + result } - #[tracing::instrument(skip_all)] + #[tracing::instrument(name = "storage::save_block", skip_all, fields(number))] pub async fn save_block(&self, block: Block) -> anyhow::Result<()> { + Span::with(|s| s.rec("number", block.number())); + #[cfg(feature = "metrics")] { let (start, label_size_by_tx, label_size_by_gas) = (metrics::now(), block.label_size_by_transactions(), block.label_size_by_gas());