diff --git a/.github/workflows/docker.sh b/.github/workflows/docker.sh new file mode 100755 index 0000000000..6065c6faf9 --- /dev/null +++ b/.github/workflows/docker.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -ex + +image="ghcr.io/$GITHUB_REPOSITORY" +tag=$(git tag --points-at HEAD) + +if [ -z "$tag" ]; then + tag='latest' +fi + +echo $image:$tag +docker buildx create --name mybuilder --use || echo 'skip' +docker buildx inspect --bootstrap +docker buildx build --platform linux/amd64,linux/arm64 -t $image:$tag --push . +docker buildx imagetools inspect $image:$tag diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000..87bd7d5e27 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,23 @@ +name: Docker +on: + workflow_dispatch: + push: + tags: + - v* + +jobs: + build: + timeout-minutes: 45 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Login to ghcr.io + env: + PAT: ${{ secrets.GITHUB_TOKEN }} + run: printf "$PAT" | docker login --username _ --password-stdin ghcr.io + + - name: Build and push Docker images + run: ./.github/workflows/docker.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..8dc638ba72 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,16 @@ +name: test +on: + workflow_dispatch: + push: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: eth-tests + run: docker compose run --no-TTY --rm test diff --git a/core/state_transition.go b/core/state_transition.go index df2faa19a9..8a937064f5 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -426,7 +426,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } var gasRefund uint64 - if !rules.IsLondon { + if !rules.IsLondon && !rules.IsZkEvm { // Before EIP-3529: refunds were capped to gasUsed / 2 gasRefund = st.refundGas(params.RefundQuotient) } else { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..21ed25d66f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3.9' + +networks: + default: + name: zkevm-geth + +services: + build: + build: + dockerfile: docker/retesteth/Dockerfile + command: + retesteth + -t GeneralStateTests + -- + --clients zkevm + --filltests + + test: + build: + dockerfile: docker/retesteth/Dockerfile-light + command: + retesteth + -t GeneralStateTests + -- + --clients zkevm diff --git a/docker/retesteth/Dockerfile b/docker/retesteth/Dockerfile new file mode 100644 index 0000000000..9ff5d03fcf --- /dev/null +++ b/docker/retesteth/Dockerfile @@ -0,0 +1,40 @@ +FROM alpine:3.15 AS base +RUN apk add --no-cache alpine-sdk cmake linux-headers boost-dev boost-static +ENV LDFLAGS="-static" +WORKDIR /build + +FROM base as tests +RUN git clone --depth 1 -b zkevm https://github.com/privacy-scaling-explorations/eth-tests.git /tests + +FROM base AS retesteth +RUN git clone --depth 1 -b develop https://github.com/ethereum/retesteth.git /retesteth +RUN cmake /retesteth -DCMAKE_BUILD_TYPE=Release && make && cp retesteth/retesteth /target + +FROM alpine:3.13 AS lllc +RUN apk add --no-cache alpine-sdk cmake linux-headers boost-dev boost-static +ENV LDFLAGS="-static" +WORKDIR /build +RUN git clone --depth 1 -b master https://github.com/winsvega/solidity.git /lllc +RUN cmake /lllc -DCMAKE_BUILD_TYPE=Release -DLLL=1 && make lllc && cp lllc/lllc /target + +FROM base AS solc +RUN git clone --depth 1 -b v0.8.5 https://github.com/ethereum/solidity.git /solidity +RUN touch /solidity/prerelease.txt && cmake /solidity -DCMAKE_BUILD_TYPE=Release && make solc && cp solc/solc /target + +FROM golang:1.18-alpine as geth +RUN apk add --no-cache gcc musl-dev linux-headers git +WORKDIR /go-ethereum +COPY go.mod . +COPY go.sum . +RUN go mod download +ADD . . +RUN go run build/ci.go install ./cmd/evm && cp build/bin/evm /target + +FROM alpine:3.15 +ENV ETHEREUM_TEST_PATH="/tests" +COPY --from=retesteth /target /bin/retesteth +COPY --from=lllc /target /bin/lllc +COPY --from=solc /target /bin/solc +COPY --from=tests /tests /tests +COPY --from=geth /target /bin/evm +COPY docker/retesteth/data /root/.retesteth diff --git a/docker/retesteth/Dockerfile-light b/docker/retesteth/Dockerfile-light new file mode 100644 index 0000000000..1e44a7c310 --- /dev/null +++ b/docker/retesteth/Dockerfile-light @@ -0,0 +1,27 @@ +FROM alpine:3.15 AS base +RUN apk add --no-cache alpine-sdk cmake linux-headers boost-dev boost-static +ENV LDFLAGS="-static" +WORKDIR /build + +FROM base as tests +RUN git clone --depth 1 -b zkevm https://github.com/privacy-scaling-explorations/eth-tests.git /tests + +FROM base AS retesteth +RUN git clone --depth 1 -b develop https://github.com/ethereum/retesteth.git /retesteth +RUN cmake /retesteth -DCMAKE_BUILD_TYPE=Release && make && cp retesteth/retesteth /target + +FROM golang:1.18-alpine as geth +RUN apk add --no-cache gcc musl-dev linux-headers git +WORKDIR /go-ethereum +COPY go.mod . +COPY go.sum . +RUN go mod download +ADD . . +RUN go run build/ci.go install ./cmd/evm && cp build/bin/evm /target + +FROM alpine:3.15 +ENV ETHEREUM_TEST_PATH="/tests" +COPY --from=retesteth /target /bin/retesteth +COPY --from=tests /tests /tests +COPY --from=geth /target /bin/evm +COPY docker/retesteth/data /root/.retesteth diff --git a/docker/retesteth/data/version b/docker/retesteth/data/version new file mode 100644 index 0000000000..b8559c74e9 --- /dev/null +++ b/docker/retesteth/data/version @@ -0,0 +1 @@ +0.2.2-testinfo \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/config b/docker/retesteth/data/zkevm/config new file mode 100644 index 0000000000..2154e101d8 --- /dev/null +++ b/docker/retesteth/data/zkevm/config @@ -0,0 +1,218 @@ +{ + "name" : "Ethereum GO on StateTool", + "socketType" : "tranition-tool", + "socketAddress" : "start.sh", + "checkLogsHash" : true, + "chainID" : 1, + "forks" : [ + "Frontier", + "Homestead", + "EIP150", + "EIP158", + "Byzantium", + "Constantinople", + "ConstantinopleFix", + "Istanbul", + "Berlin", + "ZkEvm", + "London", + "Merge" + ], + "additionalForks" : [ + "FrontierToHomesteadAt5", + "HomesteadToEIP150At5", + "EIP158ToByzantiumAt5", + "HomesteadToDaoAt5", + "ByzantiumToConstantinopleFixAt5", + "BerlinToLondonAt5", + "ArrowGlacier", + "ArrowGlacierToMergeAtDiffC0000", + "GrayGlacier" + ], + "exceptions" : { + "AddressTooShort" : "input string too short for common.Address", + "AddressTooLong" : "rlp: input string too long for common.Address, decoding into (types.Transaction)(types.LegacyTx).To", + "NonceMax" : "nonce exceeds 2^64-1", + "NonceTooLong" : "rlp: input string too long for uint64, decoding into (types.Transaction)(types.LegacyTx).Nonce", + "InvalidVRS" : "invalid transaction v, r, s values", + "InvalidV" : "rlp: expected input string or byte for *big.Int, decoding into (types.Transaction)(types.LegacyTx).V", + "InvalidR" : "rlp: expected input string or byte for *big.Int, decoding into (types.Transaction)(types.LegacyTx).R", + "InvalidS" : "rlp: expected input string or byte for *big.Int, decoding into (types.Transaction)(types.LegacyTx).S", + "InvalidChainID" : "invalid chain id for signer", + "ECRecoveryFail" : "recovery failed", + "InvalidStateRoot" : "", + "ExtraDataTooBig" : "Error importing raw rlp block: Header extraData > 32 bytes", + "InvalidData" : "rlp: expected input string or byte for []uint8, decoding into (types.Transaction)(types.LegacyTx).Data", + "InvalidDifficulty" : "Invalid difficulty:", + "InvalidDifficulty2" : "Error in field: difficulty", + "InvalidDifficulty_TooLarge" : "Blockheader parse error: VALUE >u256", + "InvalidGasLimit" : "Header gasLimit > 0x7fffffffffffffff", + "InvalidGasLimit2" : "Invalid gaslimit:", + "InvalidGasLimit3" : "GasLimit must be < 0x7fffffffffffffff", + "InvalidGasLimit4" : "rlp: input string too long for uint64, decoding into (types.Transaction)(types.LegacyTx).Gas", + "InvalidGasLimit5" : "rlp: expected input string or byte for uint64, decoding into (types.Transaction)(types.LegacyTx).Gas", + "InvalidValue" : "value exceeds 256 bits", + "InvalidGasPrice" : "gasPrice exceeds 256 bits", + "InvalidMaxPriorityFeePerGas" : "maxPriorityFeePerGas exceeds 256 bits", + "InvalidMaxFeePerGas" : "maxFeePerGas exceeds 256 bits", + "InvalidNonce" : "rlp: expected input string or byte for uint64, decoding into (types.Transaction)(types.LegacyTx).Nonce", + "InvalidTo" : "rlp: expected input string or byte for common.Address, decoding into (types.Transaction)(types.LegacyTx).To", + "GasLimitPriceProductOverflow" : "gas * gasPrice exceeds 256 bits", + "TooMuchGasUsed" : "Invalid gasUsed:", + "TooMuchGasUsed2" : "Error importing raw rlp block: t8ntool didn't return a transaction with hash", + "LeadingZerosGasLimit" : "rlp: non-canonical integer (leading zero bytes) for uint64, decoding into (types.Transaction)(types.LegacyTx).Gas", + "LeadingZerosGasPrice" : "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).GasPrice", + "LeadingZerosValue" : "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).Value", + "LeadingZerosNonce" : "rlp: non-canonical integer (leading zero bytes) for uint64, decoding into (types.Transaction)(types.LegacyTx).Nonce", + "LeadingZerosR" : "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).R", + "LeadingZerosS" : "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).S", + "LeadingZerosV" : "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.LegacyTx).V", + "LeadingZerosDataSize" : "rlp: non-canonical size information for []uint8, decoding into (types.Transaction)(types.LegacyTx).Data", + "LeadingZerosNonceSize" : "rlp: non-canonical size information for uint64, decoding into (types.Transaction)(types.LegacyTx).Nonce", + "InvalidNumber" : "BlockHeader number != parent.number + 1", + "InvalidTimestampEqualParent" : "timestamp equals parent's", + "InvalidTimestampOlderParent" : "BlockHeader timestamp is less or equal then it's parent block!", + "InvalidLogBloom" : "Error in field: bloom", + "InvalidStateRoot" : "Error in field: stateRoot", + "InvalidGasUsed" : "Error in field: gasUsed", + "InvalidGasUsed2" : "t8ntool didn't return a transaction with hash", + "InvalidBlockMixHash" : "invalid mix digest", + "InvalidBlockNonce" : "", + "UnknownParent" : "unknown parent hash", + "UnknownParent2" : "unknown parent hash", + "InvalidReceiptsStateRoot" : "Error in field: receiptTrie", + "InvalidTransactionsRoot" : "Error in field: transactionsTrie", + "InvalidUnclesHash" : "Error in field: uncleHash", + "InvalidUncleParentHash" : "Parent block hash not found:", + "UncleInChain" : "Block is already in chain!", + "UncleIsAncestor" : "Block is already in chain!", + "UncleParentIsNotAncestor" : "Uncle number is wrong!", + "TooManyUncles" : "Too many uncles!", + "UncleIsBrother" : "Uncle is brother!", + "OutOfGas" : "out of gas", + "SenderNotEOA" : "sender not an eoa:", + "IntrinsicGas" : "t8ntool didn't return a transaction with hash", + "ExtraDataIncorrectDAO" : "BlockHeader require Dao ExtraData!", + "InvalidTransactionVRS" : "t8ntool didn't return a transaction with hash", + "BLOCKHEADER_VALUE_TOOLARGE" : "Blockheader parse error: VALUE >u256", + "TRANSACTION_VALUE_TOOLARGE" : "TransactionLegacy convertion error: VALUE >u256", + "TRANSACTION_VALUE_TOOSHORT" : "t8ntool didn't return a transaction with hash", + "OVERSIZE_RLP" : "Error importing raw rlp block: OversizeRLP", + "RLP_TooFewElements" : "rlp: too few elements ", + "RLP_TooManyElements" : "rlp: input list has too many elements ", + "RLP_InputContainsMoreThanOneValue" : "Error importing raw rlp block: OversizeRLP", + "RLP_VALUESIZE_MORE_AVAILABLEINPUTLENGTH" : "Error importing raw rlp block: UndersizeRLP", + "RLP_ELEMENT_LARGER_CONTAININGLIST_UNDERSIZE" : "Error importing raw rlp block: UndersizeRLP", + "RLP_ELEMENT_LARGER_CONTAININGLIST_OVERSIZE" : "Error importing raw rlp block: OversizeRLP", + "RLP_ExpectedInputList_EXTBLOCK" : "Error importing raw rlp block: RLP is expected to be list", + "RLP_InvalidArg0_UNMARSHAL_BYTES" : "Error importing raw rlp block: BadCast", + "RLP_ExpectedInputList_HEADER_DECODEINTO_BLOCK_EXTBLOCK" : "Error importing raw rlp block: BlockHeader RLP is expected to be list", + "RLP_InputList_TooManyElements_HEADER_DECODEINTO_BLOCK_EXTBLOCK_HEADER" : "Error importing raw rlp block: Uncleheader RLP is expected to be list", + "RLP_InputList_TooManyElements_TXDATA_DECODEINTO_BLOCK_EXTBLOCK_TXS0" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE2" : "Blockheader parse error: Key `coinbase` is not hash20", + "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TXS0_RECIPIENT" : "TransactionLegacy convertion error: Key `to` is not hash20", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT2" : "Blockheader parse error: Key `stateRoot` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST2" : "Blockheader parse error: Key `mixHash` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH2" : "Blockheader parse error: Key `parentHash` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH2" : "Blockheader parse error: Key `receiptTrie` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH" : "Blockheader parse error: Key `transactionsTrie` is not hash32", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH2" : "Blockheader parse error: Key `uncleHash` is not hash32", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT" : "Blockheader parse error: VALUE >u256", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED" : "Blockheader parse error: VALUE >u256", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME" : "Blockheader parse error: VALUE >u256", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT" : "TransactionLegacy convertion error: VALUE >u256", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH2" : "Blockheader parse error: Key `receiptTrie` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT" : "Blockheader parse error: Key `stateRoot` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST2" : "Blockheader parse error: Key `mixHash` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH2" : "Blockheader parse error: Key `parentHash` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH2" : "Blockheader parse error: Key `uncleHash` is not hash32", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH2" : "Blockheader parse error: Key `transactionsTrie` is not hash32", + "RLP_InputString_TooShort_BLOOM_DECODEINTO_BLOCK_EXTBLOCK_HEADER_BLOOM" : "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY" : "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY2" : "Blockheader parse error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT" : "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT2" : "Blockheader parse error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED" : "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED2" : "Blockheader parse error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME" : "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME2" : "Blockheader parse error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT" : "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT2" : "TransactionLegacy convertion error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NUMBER" : "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NUMBER2" : "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_PRICE" : "Error importing raw rlp block: OversizeRLP", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_R" : "TransactionLegacy convertion error: VALUE has leading 0", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_S" : "TransactionLegacy convertion error: VALUE has leading 0", + "RLP_InputString_TooLong_BLOOM_DECODEINTO_BLOCK_EXTBLOCK_HEADER_BLOOM" : "Blockheader parse error: Key `bloom` is not hash256", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TX0_RECIPIENT" : "Error importing raw rlp block: Transaction RLP field is not data!", + "RLP_InputString_TooLong_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE" : "Blockheader parse error: Key `coinbase` is not hash20", + "RLP_InputString_TooLong_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TXS0_RECIPIENT" : "TransactionLegacy convertion error: Key `to` is not hash20", + "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXR" : "Error importing raw rlp block: Transaction RLP field is not data!", + "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXS" : "Error importing raw rlp block: Transaction RLP field is not data!", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT" : "Error importing raw rlp block: Transaction RLP field is not data!", + "RLP_ExpectedInputString_NONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE" : "Error importing raw rlp block: Blockheader RLP field is not data!", + "RLP_ExpectedInputString_UINT8_DECODEINTO_BLOCK_EXTBLOCK_TXS0_PAYLOAD" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_BLOCKNONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE" : "Error importing raw rlp block: OversizeRLP", + "RLP_InputString_TooLong_BLOCKNONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE2" : "Blockheader parse error: Key `nonce` is not hash8", + "RLP_NonCanonical_SizeInfo_EXTBLOCK" : "Error importing raw rlp block: BadRLP", + "RLP_ExpectedInputList_TRANSACTION_DECODEINTO_BLOCK_EXTBLOCK_TXS" : "Error importing raw rlp block: BadCast", + "RLP_ExpectedInputList_HEADER_DECODEINTO_BLOCK_EXTBLOCK_UNCLES" : "Error importing raw rlp block: OversizeRLP", + "RLP_ExpectedInputList_TXDATA_DECODEINTO_BLOCK_EXTBLOCK_TXS0" : "Error importing raw rlp block: Transaction RLP is expected to be list", + "RLP_Error_EOF" : "ERROR(11): unexpected EOF", + "RLP_Error_RLP_Size" : "ERROR(11): rlp: value size exceeds available input length", + "RLP_Error_Size_Information" : "ERROR(11): rlp: non-canonical size information", + "LegacyBlockImportImpossible" : "Legacy block import is impossible", + "LegacyBlockImportImpossible2" : "Legacy block can only be on top of LegacyBlock", + "LegacyBlockBaseFeeTransaction" : "BaseFee transaction in a Legacy blcok", + "1559BlockImportImpossible_HeaderIsLegacy" : "1559 block must be on top of 1559", + "1559BlockImportImpossible_BaseFeeWrong": "base fee not correct!", + "1559BlockImportImpossible_InitialBaseFeeWrong": "Initial baseFee must be 1000000000", + "1559BlockImportImpossible_TargetGasLow": "gasTarget decreased too much", + "1559BlockImportImpossible_TargetGasHigh": "gasTarget increased too much", + "1559BlockImportImpossible_InitialGasLimitInvalid": "Invalid block1559: Initial gasLimit must be", + "TR_IntrinsicGas" : "intrinsic gas too low:", + "TR_NoFunds" : "insufficient funds for gas * price + value", + "TR_NoFundsValue" : "insufficient funds for transfer", + "TR_FeeCapLessThanBlocks" : "max fee per gas less than block base fee", + "TR_GasLimitReached" : "gas limit reached", + "TR_NonceTooHigh" : "nonce too high", + "TR_NonceTooLow" : "nonce too low", + "TR_TypeNotSupported" : "transaction type not supported", + "TR_TipGtFeeCap": "max priority fee per gas higher than max fee per gas", + "TR_TooShort": "typed transaction too short", + "1559BaseFeeTooLarge": "TransactionBaseFee convertion error: VALUE >u256", + "1559PriorityFeeGreaterThanBaseFee": "maxFeePerGas \u003c maxPriorityFeePerGas", + "2930AccessListAddressTooLong": "rlp: input string too long for common.Address, decoding into (types.Transaction)(types.AccessListTx).AccessList[0].Address", + "2930AccessListAddressTooShort": "rlp: input string too short for common.Address, decoding into (types.Transaction)(types.AccessListTx).AccessList[0].Address", + "2930AccessListStorageHashTooLong": "rlp: input string too long for common.Hash, decoding into (types.Transaction)(types.AccessListTx).AccessList[0].StorageKeys[0]", + "1559LeadingZerosBaseFee": "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.DynamicFeeTx).GasFeeCap", + "1559LeadingZerosPriorityFee": "rlp: non-canonical integer (leading zero bytes) for *big.Int, decoding into (types.Transaction)(types.DynamicFeeTx).GasTipCap", + "2930AccessListStorageHashTooShort": "rlp: input string too short for common.Hash, decoding into (types.Transaction)(types.AccessListTx).AccessList[0].StorageKeys[0]", + "2930AccessListStorageHashTooLong": "rlp: input string too long for common.Hash, decoding into (types.Transaction)(types.AccessListTx).AccessList[0].StorageKeys[0]", + "3675PoWBlockRejected" : "Invalid block1559: Chain switched to PoS!", + "3675PoSBlockRejected" : "Parent (transition) block has not reached TTD", + "3675PreMerge1559BlockRejected" : "Trying to import 1559 block on top of PoS block" + } +} diff --git a/docker/retesteth/data/zkevm/genesis/ArrowGlacier.json b/docker/retesteth/data/zkevm/genesis/ArrowGlacier.json new file mode 100644 index 0000000000..93b9660b39 --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/ArrowGlacier.json @@ -0,0 +1,10 @@ +{ + "params" : { + "fork" : "ArrowGlacier", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/ArrowGlacierToMergeAtDiffC0000.json b/docker/retesteth/data/zkevm/genesis/ArrowGlacierToMergeAtDiffC0000.json new file mode 100644 index 0000000000..c48072999a --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/ArrowGlacierToMergeAtDiffC0000.json @@ -0,0 +1,11 @@ +{ + "params" : { + "fork" : "ArrowGlacierToMergeAtDiffC0000", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00", + "terminalTotalDifficulty" : "0x0C0000" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/Berlin.json b/docker/retesteth/data/zkevm/genesis/Berlin.json new file mode 100644 index 0000000000..49662a2c08 --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/Berlin.json @@ -0,0 +1,10 @@ +{ + "params" : { + "fork": "Berlin", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} diff --git a/docker/retesteth/data/zkevm/genesis/BerlinToLondonAt5.json b/docker/retesteth/data/zkevm/genesis/BerlinToLondonAt5.json new file mode 100644 index 0000000000..ea402151ba --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/BerlinToLondonAt5.json @@ -0,0 +1,11 @@ +{ + "params" : { + "fork" : "BerlinToLondonAt5", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00", + "londonForkBlock" : "0x05" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/Byzantium.json b/docker/retesteth/data/zkevm/genesis/Byzantium.json new file mode 100644 index 0000000000..56cd29fe6f --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/Byzantium.json @@ -0,0 +1,9 @@ +{ + "params" : { + "fork" : "Byzantium", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/ByzantiumToConstantinopleFixAt5.json b/docker/retesteth/data/zkevm/genesis/ByzantiumToConstantinopleFixAt5.json new file mode 100644 index 0000000000..22c21a2a4d --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/ByzantiumToConstantinopleFixAt5.json @@ -0,0 +1,10 @@ +{ + "params" : { + "fork" : "ByzantiumToConstantinopleFixAt5", + "constantinopleForkBlock" : "0x05", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/Constantinople.json b/docker/retesteth/data/zkevm/genesis/Constantinople.json new file mode 100644 index 0000000000..9f957e4804 --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/Constantinople.json @@ -0,0 +1,10 @@ +{ + "params" : { + "fork" : "Constantinople", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/ConstantinopleFix.json b/docker/retesteth/data/zkevm/genesis/ConstantinopleFix.json new file mode 100644 index 0000000000..8f6923acfd --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/ConstantinopleFix.json @@ -0,0 +1,10 @@ +{ + "params" : { + "fork" : "ConstantinopleFix", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/EIP150.json b/docker/retesteth/data/zkevm/genesis/EIP150.json new file mode 100644 index 0000000000..0624894edf --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/EIP150.json @@ -0,0 +1,8 @@ +{ + "params" : { + "fork" : "EIP150", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/EIP158.json b/docker/retesteth/data/zkevm/genesis/EIP158.json new file mode 100644 index 0000000000..7642c40196 --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/EIP158.json @@ -0,0 +1,8 @@ +{ + "params" : { + "fork" : "EIP158", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/EIP158ToByzantiumAt5.json b/docker/retesteth/data/zkevm/genesis/EIP158ToByzantiumAt5.json new file mode 100644 index 0000000000..fa8b94420b --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/EIP158ToByzantiumAt5.json @@ -0,0 +1,9 @@ +{ + "params" : { + "fork" : "EIP158ToByzantiumAt5", + "byzantiumForkBlock" : "0x05", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/Frontier.json b/docker/retesteth/data/zkevm/genesis/Frontier.json new file mode 100644 index 0000000000..d237b0e57e --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/Frontier.json @@ -0,0 +1,7 @@ +{ + "params" : { + "fork" : "Frontier" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/FrontierToHomesteadAt5.json b/docker/retesteth/data/zkevm/genesis/FrontierToHomesteadAt5.json new file mode 100644 index 0000000000..9f061ed227 --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/FrontierToHomesteadAt5.json @@ -0,0 +1,8 @@ +{ + "params" : { + "fork" : "FrontierToHomesteadAt5", + "homesteadForkBlock" : "0x05" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/GrayGlacier.json b/docker/retesteth/data/zkevm/genesis/GrayGlacier.json new file mode 100644 index 0000000000..55ccfecdb2 --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/GrayGlacier.json @@ -0,0 +1,10 @@ +{ + "params" : { + "fork" : "GrayGlacier", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/Homestead.json b/docker/retesteth/data/zkevm/genesis/Homestead.json new file mode 100644 index 0000000000..ce11bf6972 --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/Homestead.json @@ -0,0 +1,8 @@ +{ + "params" : { + "fork" : "Homestead", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/HomesteadToDaoAt5.json b/docker/retesteth/data/zkevm/genesis/HomesteadToDaoAt5.json new file mode 100644 index 0000000000..8331dd86ee --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/HomesteadToDaoAt5.json @@ -0,0 +1,8 @@ +{ + "params" : { + "fork" : "HomesteadToDaoAt5", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/HomesteadToEIP150At5.json b/docker/retesteth/data/zkevm/genesis/HomesteadToEIP150At5.json new file mode 100644 index 0000000000..29064fe53c --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/HomesteadToEIP150At5.json @@ -0,0 +1,8 @@ +{ + "params" : { + "fork" : "HomesteadToEIP150At5", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/Istanbul.json b/docker/retesteth/data/zkevm/genesis/Istanbul.json new file mode 100644 index 0000000000..f7eb2e1e0d --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/Istanbul.json @@ -0,0 +1,10 @@ +{ + "params" : { + "fork" : "Istanbul", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/London.json b/docker/retesteth/data/zkevm/genesis/London.json new file mode 100644 index 0000000000..96631b0e37 --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/London.json @@ -0,0 +1,10 @@ +{ + "params" : { + "fork" : "London", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/Merge.json b/docker/retesteth/data/zkevm/genesis/Merge.json new file mode 100644 index 0000000000..2f949fad33 --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/Merge.json @@ -0,0 +1,11 @@ +{ + "params" : { + "fork" : "Merged", + "terminalTotalDifficulty" : "0x00", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} \ No newline at end of file diff --git a/docker/retesteth/data/zkevm/genesis/ZkEvm.json b/docker/retesteth/data/zkevm/genesis/ZkEvm.json new file mode 100644 index 0000000000..807bac254d --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/ZkEvm.json @@ -0,0 +1,10 @@ +{ + "params" : { + "fork": "ZkEvm", + "constantinopleForkBlock" : "0x00", + "byzantiumForkBlock" : "0x00", + "homesteadForkBlock" : "0x00" + }, + "accounts" : { + } +} diff --git a/docker/retesteth/data/zkevm/genesis/correctMiningReward.json b/docker/retesteth/data/zkevm/genesis/correctMiningReward.json new file mode 100644 index 0000000000..9ae8fabd2a --- /dev/null +++ b/docker/retesteth/data/zkevm/genesis/correctMiningReward.json @@ -0,0 +1,25 @@ +{ + "//comment" : "State Tests does not calculate mining reward in post conditions, so when filling a blockchain test out of it, the mining reward must be set", + "Frontier": "5000000000000000000", + "Homestead": "5000000000000000000", + "EIP150": "5000000000000000000", + "EIP158": "5000000000000000000", + "Byzantium": "3000000000000000000", + "Constantinople": "2000000000000000000", + "ConstantinopleFix": "2000000000000000000", + "Istanbul": "2000000000000000000", + "Berlin" : "2000000000000000000", + "London" : "2000000000000000000", + "ArrowGlacier" : "2000000000000000000", + "GrayGlacier" : "2000000000000000000", + + "ZkEvm" : "2000000000000000000", + + "//comment" : "Retesteth calculate rewards on behalf of the tool when filling state tests", + "YOLOv1" : "2000000000000000000", + "YOLOv2" : "2000000000000000000", + "YOLOv3" : "2000000000000000000", + "Aleut" : "2000000000000000000", + "Merge" : "0", + "Merged" : "0" +} diff --git a/docker/retesteth/data/zkevm/start.sh b/docker/retesteth/data/zkevm/start.sh new file mode 100755 index 0000000000..37142f37e8 --- /dev/null +++ b/docker/retesteth/data/zkevm/start.sh @@ -0,0 +1,17 @@ +#!/bin/sh +if [ $1 = "-v" ]; then + /bin/evm -v +else + stateProvided=0 + for index in ${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} ${10} ${11} ${12} ${13} ${14} ${15} ${16} ${17} ${18} ${19} ${20} ; do + if [ $index = "--input.alloc" ]; then + stateProvided=1 + break + fi + done + if [ $stateProvided -eq 1 ]; then + /bin/evm t8n ${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} ${10} ${11} ${12} ${13} ${14} ${15} ${16} ${17} ${18} ${19} ${20} --verbosity 2 + else + /bin/evm t9n ${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} ${10} ${11} ${12} ${13} ${14} ${15} ${16} ${17} ${18} ${19} ${20} + fi +fi diff --git a/eth/api_miner.go b/eth/api_miner.go index 477531d494..eb0a6412bc 100644 --- a/eth/api_miner.go +++ b/eth/api_miner.go @@ -17,11 +17,14 @@ package eth import ( + "fmt" "math/big" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/internal/ethapi" ) // MinerAPI provides an API to control the miner. @@ -83,3 +86,68 @@ func (api *MinerAPI) SetEtherbase(etherbase common.Address) bool { func (api *MinerAPI) SetRecommitInterval(interval int) { api.e.Miner().SetRecommitInterval(time.Duration(interval) * time.Millisecond) } + +// Init initializes the miner without starting mining tasks +func (api *MinerAPI) Init() (common.Address, error) { + return api.e.InitMiner() +} + +type SealBlockRequest struct { + Parent common.Hash `json:"parent" gencodec:"required"` + Random common.Hash `json:"random" gencodec:"required"` + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"optional"` +} + +func decodeTransactions(enc []hexutil.Bytes) ([]*types.Transaction, error) { + var txs = make([]*types.Transaction, len(enc)) + for i, encTx := range enc { + var tx types.Transaction + if err := tx.UnmarshalBinary(encTx); err != nil { + return nil, fmt.Errorf("invalid transaction %d: %v", i, err) + } + txs[i] = &tx + } + return txs, nil +} + +// SealBlock mines and seals a block without changing the canonical chain +// If `args.Transactions` is not nil then produces a block with only those transactions. If nil, then it consumes from the transaction pool. +// Returns the block if successful. +func (api *MinerAPI) SealBlock(args SealBlockRequest) (map[string]interface{}, error) { + var transactions []*types.Transaction + + if args.Transactions != nil { + txs, err := decodeTransactions(args.Transactions) + if err != nil { + return nil, err + } + transactions = txs + } + + block, err := api.e.Miner().SealBlockWith(args.Parent, args.Random, uint64(args.Timestamp), transactions) + + if err != nil { + return nil, err + } + + return ethapi.RPCMarshalBlock(block, true, true, api.e.APIBackend.ChainConfig()), nil +} + +// SetHead updates the canonical chain and announces the block on the p2p layer +func (api *MinerAPI) SetHead(hash common.Hash) (bool, error) { + block := api.e.BlockChain().GetBlockByHash(hash) + + if block == nil { + return false, fmt.Errorf("block %s not found", hash.Hex()) + } + + if _, err := api.e.BlockChain().SetCanonical(block); err != nil { + return false, err + } + + // Broadcast the block and announce chain insertion event + api.e.Miner().AnnounceBlock(block) + + return true, nil +} diff --git a/eth/backend.go b/eth/backend.go index 774ffaf248..fedac394d0 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -414,45 +414,56 @@ func (s *Ethereum) SetEtherbase(etherbase common.Address) { s.miner.SetEtherbase(etherbase) } +// InitMiner initializes the miner without starting mining tasks +func (s *Ethereum) InitMiner() (eb common.Address, err error) { + // Propagate the initial price point to the transaction pool + s.lock.RLock() + price := s.gasPrice + s.lock.RUnlock() + s.txPool.SetGasTip(price) + + // Configure the local mining address + eb, err = s.Etherbase() + if err != nil { + log.Error("Cannot start mining without etherbase", "err", err) + return eb, fmt.Errorf("etherbase missing: %v", err) + } + var cli *clique.Clique + if c, ok := s.engine.(*clique.Clique); ok { + cli = c + } else if cl, ok := s.engine.(*beacon.Beacon); ok { + if c, ok := cl.InnerEngine().(*clique.Clique); ok { + cli = c + } + } + if cli != nil { + wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) + if wallet == nil || err != nil { + log.Error("Etherbase account unavailable locally", "err", err) + return eb, fmt.Errorf("signer missing: %v", err) + } + cli.Authorize(eb, wallet.SignData) + } + + s.miner.SetEtherbase(eb) + // If mining is initialized, we can disable the transaction rejection mechanism + // introduced to speed sync times. + s.handler.enableSyncedFeatures() + + return eb, nil +} + // StartMining starts the miner with the given number of CPU threads. If mining // is already running, this method adjust the number of threads allowed to use // and updates the minimum price required by the transaction pool. func (s *Ethereum) StartMining() error { // If the miner was not running, initialize it if !s.IsMining() { - // Propagate the initial price point to the transaction pool - s.lock.RLock() - price := s.gasPrice - s.lock.RUnlock() - s.txPool.SetGasTip(price) - - // Configure the local mining address - eb, err := s.Etherbase() + eb, err := s.InitMiner() if err != nil { - log.Error("Cannot start mining without etherbase", "err", err) - return fmt.Errorf("etherbase missing: %v", err) + return err } - var cli *clique.Clique - if c, ok := s.engine.(*clique.Clique); ok { - cli = c - } else if cl, ok := s.engine.(*beacon.Beacon); ok { - if c, ok := cl.InnerEngine().(*clique.Clique); ok { - cli = c - } - } - if cli != nil { - wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) - if wallet == nil || err != nil { - log.Error("Etherbase account unavailable locally", "err", err) - return fmt.Errorf("signer missing: %v", err) - } - cli.Authorize(eb, wallet.SignData) - } - // If mining is started, we can disable the transaction rejection mechanism - // introduced to speed sync times. - s.handler.enableSyncedFeatures() - - go s.miner.Start() + go s.miner.Start(eb) } return nil } diff --git a/miner/miner.go b/miner/miner.go index b7273948f5..163da1f718 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -244,3 +244,14 @@ func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscript func (miner *Miner) BuildPayload(args *BuildPayloadArgs) (*Payload, error) { return miner.worker.buildPayload(args) } + +// SealBlock mines and seals a block without changing the canonical chain. +// If `txs` is not nil then produces a block with only those transactions. If nil, then it consumes from the transaction pool. +func (miner *Miner) SealBlockWith(parent common.Hash, random common.Hash, timestamp uint64, txs []*types.Transaction) (*types.Block, error) { + return miner.worker.sealBlockWith(parent, random, timestamp, txs) +} + +// AnnounceBlock broadcasts the block and emits a chain insertion event +func (miner *Miner) AnnounceBlock(block *types.Block) error { + return miner.worker.mux.Post(core.NewMinedBlockEvent{Block: block}) +} diff --git a/miner/worker.go b/miner/worker.go index 2ed91cc187..de522d6097 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -1212,3 +1212,53 @@ func signalToErr(signal int32) error { panic(fmt.Errorf("undefined signal %d", signal)) } } + +// sealBlockWith mines and seals a block without changing the canonical chain +// If `txs` is not nil then produces a block with only those transactions. If nil, then it consumes from the transaction pool. +func (w *worker) sealBlockWith(parent common.Hash, random common.Hash, timestamp uint64, txs []*types.Transaction) (*types.Block, error) { + params := &generateParams{ + timestamp: timestamp, + forceTime: true, + parentHash: parent, + coinbase: w.coinbase, + random: random, + } + + env, err := w.prepareWork(params) + if err != nil { + return nil, err + } + defer env.discard() + + if txs == nil { + w.fillTransactions(nil, env) + } else { + gasLimit := env.header.GasLimit + env.gasPool = new(core.GasPool).AddGas(gasLimit) + for _, tx := range txs { + env.state.SetTxContext(tx.Hash(), env.tcount) + if _, err := w.commitTransaction(env, tx); err != nil { + return nil, err + } + env.tcount++ + } + } + + block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, nil, env.receipts, nil) + if err != nil { + return nil, err + } + + results := make(chan *types.Block, 1) + if err := w.engine.Seal(w.chain, block, results, nil); err != nil { + return nil, err + } + block = <-results + + // Use InsertBlock... here to verify the block again to avoid inserting a sealed but invalid block + if err := w.chain.InsertBlockWithoutSetHead(block); err != nil { + return nil, err + } + + return block, nil +} diff --git a/params/config.go b/params/config.go index 9b4c1338e4..894ebf86bd 100644 --- a/params/config.go +++ b/params/config.go @@ -155,6 +155,7 @@ var ( ArrowGlacierBlock: big.NewInt(0), GrayGlacierBlock: big.NewInt(0), MergeNetsplitBlock: nil, + ZkEvmBlock: nil, ShanghaiTime: nil, CancunTime: nil, PragueTime: nil, @@ -205,6 +206,7 @@ var ( ArrowGlacierBlock: nil, GrayGlacierBlock: nil, MergeNetsplitBlock: nil, + ZkEvmBlock: nil, ShanghaiTime: nil, CancunTime: nil, PragueTime: nil, @@ -235,6 +237,7 @@ var ( ArrowGlacierBlock: big.NewInt(0), GrayGlacierBlock: big.NewInt(0), MergeNetsplitBlock: nil, + ZkEvmBlock: nil, ShanghaiTime: nil, CancunTime: nil, PragueTime: nil, @@ -265,6 +268,7 @@ var ( ArrowGlacierBlock: big.NewInt(0), GrayGlacierBlock: big.NewInt(0), MergeNetsplitBlock: big.NewInt(0), + ZkEvmBlock: big.NewInt(0), ShanghaiTime: newUint64(0), CancunTime: newUint64(0), PragueTime: nil, @@ -295,6 +299,7 @@ var ( ArrowGlacierBlock: nil, GrayGlacierBlock: nil, MergeNetsplitBlock: nil, + ZkEvmBlock: nil, ShanghaiTime: nil, CancunTime: nil, PragueTime: nil, @@ -343,6 +348,7 @@ type ChainConfig struct { ArrowGlacierBlock *big.Int `json:"arrowGlacierBlock,omitempty"` // Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated) GrayGlacierBlock *big.Int `json:"grayGlacierBlock,omitempty"` // Eip-5133 (bomb delay) switch block (nil = no fork, 0 = already activated) MergeNetsplitBlock *big.Int `json:"mergeNetsplitBlock,omitempty"` // Virtual fork after The Merge to use as a network splitter + ZkEvmBlock *big.Int `json:"zkEvmBlock,omitempty"` // zkevm switch block (nil = no fork, 0 = already activated) // Fork scheduling was switched from blocks to timestamps here @@ -457,6 +463,11 @@ func (c *ChainConfig) Description() string { banner += fmt.Sprintf(" - Merge netsplit block: #%-8v\n", c.MergeNetsplitBlock) } } + + if c.ZkEvmBlock != nil { + banner += fmt.Sprintf("\nzkEVM enabled at block: %-8v\n", c.ZkEvmBlock) + } + banner += "\n" // Create a list of forks post-merge @@ -556,6 +567,18 @@ func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *bi return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0 } +// IsZkEvm returns whether num is either equal to or greater for zkevm specific behaviour. +// zkevm inherits the `berlin` rules but selectively disables: +// - EIP-2718: Typed Transaction Envelope +// - EIP-2930: Optional access lists +// +// In addition, zkevm enables these EIPs included in `london`: +// - EIP-3541: Reject new contracts starting with the 0xEF byte +// - EIP-3529: Reduction in refunds +func (c *ChainConfig) IsZkEvm(num *big.Int) bool { + return isBlockForked(c.ZkEvmBlock, num) +} + // IsShanghai returns whether time is either equal to the Shanghai fork time or greater. func (c *ChainConfig) IsShanghai(num *big.Int, time uint64) bool { return c.IsLondon(num) && isTimestampForked(c.ShanghaiTime, time) @@ -882,6 +905,7 @@ type Rules struct { IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool IsMerge, IsShanghai, IsCancun, IsPrague bool + IsZkEvm bool IsVerkle bool } @@ -904,6 +928,7 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsBerlin: c.IsBerlin(num), IsLondon: c.IsLondon(num), IsMerge: isMerge, + IsZkEvm: c.IsZkEvm(num), IsShanghai: c.IsShanghai(num, timestamp), IsCancun: c.IsCancun(num, timestamp), IsPrague: c.IsPrague(num, timestamp), diff --git a/tests/init.go b/tests/init.go index 99b7e4d333..02ac9fa9cf 100644 --- a/tests/init.go +++ b/tests/init.go @@ -299,6 +299,20 @@ var Forks = map[string]*params.ChainConfig{ TerminalTotalDifficulty: big.NewInt(0), ShanghaiTime: u64(15_000), }, + "ZkEvm": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + ZkEvmBlock: big.NewInt(0), + }, "Cancun": { ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0),