From 87f6c1a6def6aef95b2ea7cfc70708b88f2c7cb2 Mon Sep 17 00:00:00 2001 From: Antoine Toulme Date: Tue, 2 Mar 2021 07:35:52 -0800 Subject: [PATCH 1/5] Fix createdContract in graphql (#1965) Signed-off-by: Antoine Toulme --- .../api/graphql/internal/pojoadapter/TransactionAdapter.java | 2 +- .../besu/ethereum/api/graphql/eth_getTransactionReceipt.json | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java index a64b888e735..c53559cdcb1 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java @@ -144,7 +144,7 @@ public Optional getCumulativeGasUsed(final DataFetchingEnvironment environ public Optional getCreatedContract(final DataFetchingEnvironment environment) { final boolean contractCreated = transactionWithMetadata.getTransaction().isContractCreation(); if (contractCreated) { - final Optional
addr = transactionWithMetadata.getTransaction().getTo(); + final Optional
addr = transactionWithMetadata.getTransaction().contractAddress(); if (addr.isPresent()) { final BlockchainQueries query = getBlockchainQueries(environment); diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getTransactionReceipt.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getTransactionReceipt.json index 9a3454d437e..4d58d0a8a4a 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getTransactionReceipt.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getTransactionReceipt.json @@ -9,7 +9,9 @@ "logsBloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" }, "hash" : "0x812742182a79a8e67733edc58cfa3767aa2d7ad06439d156ddbbb33e3403b4ed", - "createdContract" : null, + "createdContract" : { + "address":"0x6295ee1b4f6dd65047762f924ecd367c17eabf8f" + }, "cumulativeGasUsed" : 493172, "gas" : 3141592, "gasUsed" : 493172, From f50eee6aec1765daba3f62baa050bdf24fc5eec7 Mon Sep 17 00:00:00 2001 From: Antoine Toulme Date: Tue, 2 Mar 2021 09:23:58 -0800 Subject: [PATCH 2/5] Fix how ethproxy handles the initial message (#1959) Signed-off-by: Antoine Toulme --- .../stratum/Stratum1EthProxyProtocol.java | 15 ++-- .../stratum/Stratum1EthProxyProtocolTest.java | 70 +++++++++++++++++++ 2 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 ethereum/stratum/src/test/java/org/hyperledger/besu/ethereum/stratum/Stratum1EthProxyProtocolTest.java diff --git a/ethereum/stratum/src/main/java/org/hyperledger/besu/ethereum/stratum/Stratum1EthProxyProtocol.java b/ethereum/stratum/src/main/java/org/hyperledger/besu/ethereum/stratum/Stratum1EthProxyProtocol.java index 6c93572e23f..13aa6c3c39f 100644 --- a/ethereum/stratum/src/main/java/org/hyperledger/besu/ethereum/stratum/Stratum1EthProxyProtocol.java +++ b/ethereum/stratum/src/main/java/org/hyperledger/besu/ethereum/stratum/Stratum1EthProxyProtocol.java @@ -18,7 +18,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.blockcreation.EthHashMiningCoordinator; import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; @@ -35,6 +34,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.json.JsonMapper; import com.google.common.io.BaseEncoding; +import io.vertx.core.json.DecodeException; import io.vertx.core.json.JsonObject; import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; @@ -65,21 +65,20 @@ public Stratum1EthProxyProtocol(final MiningCoordinator miningCoordinator) { @Override public boolean canHandle(final String initialMessage, final StratumConnection conn) { - JsonRpcRequestContext req; + JsonRpcRequest req; try { - req = new JsonObject(initialMessage).mapTo(JsonRpcRequestContext.class); - } catch (IllegalArgumentException e) { + req = new JsonObject(initialMessage).mapTo(JsonRpcRequest.class); + } catch (DecodeException | IllegalArgumentException e) { LOG.debug(e.getMessage(), e); return false; } - if (!"eth_submitLogin".equals(req.getRequest().getMethod())) { - LOG.debug("Invalid first message method: {}", req.getRequest().getMethod()); + if (!"eth_submitLogin".equals(req.getMethod())) { + LOG.debug("Invalid first message method: {}", req.getMethod()); return false; } try { - String response = - mapper.writeValueAsString(new JsonRpcSuccessResponse(req.getRequest().getId(), true)); + String response = mapper.writeValueAsString(new JsonRpcSuccessResponse(req.getId(), true)); conn.send(response + "\n"); } catch (JsonProcessingException e) { LOG.debug(e.getMessage(), e); diff --git a/ethereum/stratum/src/test/java/org/hyperledger/besu/ethereum/stratum/Stratum1EthProxyProtocolTest.java b/ethereum/stratum/src/test/java/org/hyperledger/besu/ethereum/stratum/Stratum1EthProxyProtocolTest.java new file mode 100644 index 00000000000..44c094dfef4 --- /dev/null +++ b/ethereum/stratum/src/test/java/org/hyperledger/besu/ethereum/stratum/Stratum1EthProxyProtocolTest.java @@ -0,0 +1,70 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.stratum; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import org.hyperledger.besu.ethereum.blockcreation.EthHashMiningCoordinator; +import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +public class Stratum1EthProxyProtocolTest { + + private Stratum1EthProxyProtocol protocol; + private StratumConnection conn; + private List receivedMessages; + + @Before + public void setUp() { + MiningCoordinator coordinator = mock(EthHashMiningCoordinator.class); + protocol = new Stratum1EthProxyProtocol(coordinator); + receivedMessages = new ArrayList<>(); + conn = new StratumConnection(new StratumProtocol[0], null, receivedMessages::add); + } + + @Test + public void testCanHandleEmptyString() { + assertThat(protocol.canHandle("", conn)).isFalse(); + } + + @Test + public void testCanHandleMalformedJSON() { + assertThat(protocol.canHandle("{[\"foo\",", conn)).isFalse(); + } + + @Test + public void testCanHandleWrongMethod() { + assertThat( + protocol.canHandle( + "{\"id\":0,\"method\":\"eth_byebye\",\"params\":[\"0xdeadbeefdeadbeef.worker\"]}", + conn)) + .isFalse(); + } + + @Test + public void testCanHandleWellFormedRequest() { + assertThat( + protocol.canHandle( + "{\"id\":0,\"method\":\"eth_submitLogin\",\"params\":[\"0xdeadbeefdeadbeef.worker\"]}", + conn)) + .isTrue(); + } +} From 994769232245ea205f6b18390b6862d4287d5880 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 2 Mar 2021 17:34:51 -0700 Subject: [PATCH 3/5] Add classic fork blocks to admin_nodeInfo (#1669) The classic fork blocks are not enumerated in the `admin_nodeInfo` RPC. Enumerate them. Signed-off-by: Danno Ferrin --- .../besu/config/JsonGenesisConfigOptions.java | 15 +++- .../besu/config/StubGenesisConfigOptions.java | 77 ++++++++++++++++--- .../internal/methods/AdminNodeInfoTest.java | 54 +++++++++++++ 3 files changed, 136 insertions(+), 10 deletions(-) diff --git a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java index 3982622f360..05d5ffa9b73 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java @@ -323,6 +323,8 @@ public OptionalLong getQip714BlockNumber() { public Map asMap() { final ImmutableMap.Builder builder = ImmutableMap.builder(); getChainId().ifPresent(chainId -> builder.put("chainId", chainId)); + + // mainnet fork blocks getHomesteadBlockNumber().ifPresent(l -> builder.put("homesteadBlock", l)); getDaoForkBlock() .ifPresent( @@ -345,11 +347,22 @@ public Map asMap() { getIstanbulBlockNumber().ifPresent(l -> builder.put("istanbulBlock", l)); getMuirGlacierBlockNumber().ifPresent(l -> builder.put("muirGlacierBlock", l)); getBerlinBlockNumber().ifPresent(l -> builder.put("berlinBlock", l)); + + // classic fork blocks + getClassicForkBlock().ifPresent(l -> builder.put("classicForkBlock", l)); + getEcip1015BlockNumber().ifPresent(l -> builder.put("ecip1015Block", l)); + getDieHardBlockNumber().ifPresent(l -> builder.put("dieHardBlock", l)); + getGothamBlockNumber().ifPresent(l -> builder.put("gothamBlock", l)); + getDefuseDifficultyBombBlockNumber().ifPresent(l -> builder.put("ecip1041Block", l)); + getAtlantisBlockNumber().ifPresent(l -> builder.put("atlantisBlock", l)); + getAghartaBlockNumber().ifPresent(l -> builder.put("aghartaBlock", l)); + getPhoenixBlockNumber().ifPresent(l -> builder.put("phoenixBlock", l)); + getThanosBlockNumber().ifPresent(l -> builder.put("thanosBlock", l)); + getEIP1559BlockNumber().ifPresent(l -> builder.put("eip1559Block", l)); getContractSizeLimit().ifPresent(l -> builder.put("contractSizeLimit", l)); getEvmStackSize().ifPresent(l -> builder.put("evmstacksize", l)); getEcip1017EraRounds().ifPresent(l -> builder.put("ecip1017EraRounds", l)); - getThanosBlockNumber().ifPresent(l -> builder.put("thanosBlock", l)); if (isClique()) { builder.put("clique", getCliqueConfigOptions().asMap()); diff --git a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java index 7c961791af1..ade12cf1c80 100644 --- a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java @@ -40,15 +40,15 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions { private OptionalLong berlinBlockNumber = OptionalLong.empty(); // TODO EIP-1559 change for the actual fork name when known private final OptionalLong eip1559BlockNumber = OptionalLong.empty(); - private final OptionalLong classicForkBlock = OptionalLong.empty(); - private final OptionalLong ecip1015BlockNumber = OptionalLong.empty(); - private final OptionalLong diehardBlockNumber = OptionalLong.empty(); - private final OptionalLong gothamBlockNumber = OptionalLong.empty(); - private final OptionalLong defuseDifficultyBombBlockNumber = OptionalLong.empty(); - private final OptionalLong atlantisBlockNumber = OptionalLong.empty(); - private final OptionalLong aghartaBlockNumber = OptionalLong.empty(); - private final OptionalLong phoenixBlockNumber = OptionalLong.empty(); - private final OptionalLong thanosBlockNumber = OptionalLong.empty(); + private OptionalLong classicForkBlock = OptionalLong.empty(); + private OptionalLong ecip1015BlockNumber = OptionalLong.empty(); + private OptionalLong diehardBlockNumber = OptionalLong.empty(); + private OptionalLong gothamBlockNumber = OptionalLong.empty(); + private OptionalLong defuseDifficultyBombBlockNumber = OptionalLong.empty(); + private OptionalLong atlantisBlockNumber = OptionalLong.empty(); + private OptionalLong aghartaBlockNumber = OptionalLong.empty(); + private OptionalLong phoenixBlockNumber = OptionalLong.empty(); + private OptionalLong thanosBlockNumber = OptionalLong.empty(); private Optional chainId = Optional.empty(); private OptionalInt contractSizeLimit = OptionalInt.empty(); private OptionalInt stackSizeLimit = OptionalInt.empty(); @@ -229,6 +229,8 @@ public Optional getChainId() { public Map asMap() { final ImmutableMap.Builder builder = ImmutableMap.builder(); getChainId().ifPresent(chainId -> builder.put("chainId", chainId)); + + // mainnet fork blocks getHomesteadBlockNumber().ifPresent(l -> builder.put("homesteadBlock", l)); getDaoForkBlock() .ifPresent( @@ -249,6 +251,18 @@ public Map asMap() { getBerlinBlockNumber().ifPresent(l -> builder.put("berlinBlock", l)); // TODO EIP-1559 change for the actual fork name when known getEIP1559BlockNumber().ifPresent(l -> builder.put("eip1559Block", l)); + + // classic fork blocks + getClassicForkBlock().ifPresent(l -> builder.put("classicForkBlock", l)); + getEcip1015BlockNumber().ifPresent(l -> builder.put("ecip1015Block", l)); + getDieHardBlockNumber().ifPresent(l -> builder.put("dieHardBlock", l)); + getGothamBlockNumber().ifPresent(l -> builder.put("gothamBlock", l)); + getDefuseDifficultyBombBlockNumber().ifPresent(l -> builder.put("ecip1041Block", l)); + getAtlantisBlockNumber().ifPresent(l -> builder.put("atlantisBlock", l)); + getAghartaBlockNumber().ifPresent(l -> builder.put("aghartaBlock", l)); + getPhoenixBlockNumber().ifPresent(l -> builder.put("phoenixBlock", l)); + getThanosBlockNumber().ifPresent(l -> builder.put("thanosBlock", l)); + getContractSizeLimit().ifPresent(l -> builder.put("contractSizeLimit", l)); getEvmStackSize().ifPresent(l -> builder.put("evmStackSize", l)); if (isClique()) { @@ -336,6 +350,51 @@ public StubGenesisConfigOptions berlinBlock(final long blockNumber) { return this; } + public StubGenesisConfigOptions classicForkBlock(final long blockNumber) { + classicForkBlock = OptionalLong.of(blockNumber); + return this; + } + + public StubGenesisConfigOptions ecip1015(final long blockNumber) { + ecip1015BlockNumber = OptionalLong.of(blockNumber); + return this; + } + + public StubGenesisConfigOptions dieHard(final long blockNumber) { + diehardBlockNumber = OptionalLong.of(blockNumber); + return this; + } + + public StubGenesisConfigOptions gotham(final long blockNumber) { + gothamBlockNumber = OptionalLong.of(blockNumber); + return this; + } + + public StubGenesisConfigOptions defuseDifficultyBomb(final long blockNumber) { + defuseDifficultyBombBlockNumber = OptionalLong.of(blockNumber); + return this; + } + + public StubGenesisConfigOptions atlantis(final long blockNumber) { + atlantisBlockNumber = OptionalLong.of(blockNumber); + return this; + } + + public StubGenesisConfigOptions agharta(final long blockNumber) { + aghartaBlockNumber = OptionalLong.of(blockNumber); + return this; + } + + public StubGenesisConfigOptions phoenix(final long blockNumber) { + phoenixBlockNumber = OptionalLong.of(blockNumber); + return this; + } + + public StubGenesisConfigOptions thanos(final long blockNumber) { + thanosBlockNumber = OptionalLong.of(blockNumber); + return this; + } + public StubGenesisConfigOptions chainId(final BigInteger chainId) { this.chainId = Optional.ofNullable(chainId); return this; diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminNodeInfoTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminNodeInfoTest.java index 699a0b49960..af99f899c70 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminNodeInfoTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminNodeInfoTest.java @@ -351,6 +351,60 @@ public void returnsErrorWhenP2PNotReady() { assertThat(response).isEqualToComparingFieldByField(expectedResponse); } + @SuppressWarnings("unchecked") + @Test + public void returnsClassicForkBlocks() { + when(p2pNetwork.isP2pEnabled()).thenReturn(true); + when(p2pNetwork.getLocalEnode()).thenReturn(Optional.of(defaultPeer.getEnodeURL())); + final GenesisConfigOptions genesisClassicConfigOptions = + new StubGenesisConfigOptions() + .chainId(BigInteger.valueOf(2019)) + .classicForkBlock(1) + .ecip1015(2) + .dieHard(3) + .gotham(4) + .defuseDifficultyBomb(5) + .atlantis(6) + .agharta(7) + .phoenix(8) + .thanos(9); + + final AdminNodeInfo methodClassic = + new AdminNodeInfo( + "testnetClassic/1.0/this/that", + BigInteger.valueOf(2018), + genesisClassicConfigOptions, + p2pNetwork, + blockchainQueries, + natService); + + final JsonRpcRequestContext request = adminNodeInfo(); + + final Map expectedConfig = + Map.of( + "classicForkBlock", 1L, + "ecip1015Block", 2L, + "dieHardBlock", 3L, + "gothamBlock", 4L, + "ecip1041Block", 5L, + "atlantisBlock", 6L, + "aghartaBlock", 7L, + "phoenixBlock", 8L, + "thanosBlock", 9L); + + final JsonRpcResponse response = methodClassic.response(request); + assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class); + final Object result = ((JsonRpcSuccessResponse) response).getResult(); + assertThat(result).isInstanceOf(Map.class); + final Object protocolsMap = ((Map) result).get("protocols"); + assertThat(protocolsMap).isInstanceOf(Map.class); + final Object ethMap = ((Map) protocolsMap).get("eth"); + assertThat(ethMap).isInstanceOf(Map.class); + final Object configMap = ((Map) ethMap).get("config"); + assertThat(configMap).isInstanceOf(Map.class); + assertThat(((Map) configMap)).containsAllEntriesOf(expectedConfig); + } + private JsonRpcRequestContext adminNodeInfo() { return new JsonRpcRequestContext(new JsonRpcRequest("2.0", "admin_nodeInfo", new Object[] {})); } From eddd35f3a8488141dbcfeb333441feb81fe2e01b Mon Sep 17 00:00:00 2001 From: Antoine Toulme Date: Wed, 3 Mar 2021 18:29:50 -0800 Subject: [PATCH 4/5] Do not return errors when no account is present, return a zero balance instead (#1951) Signed-off-by: Antoine Toulme --- CHANGELOG.md | 1 + .../api/graphql/GraphQLDataFetchers.java | 20 ++++++++-------- .../pojoadapter/EmptyAccountAdapter.java | 9 ++++++-- ..._getBalance_invalidAccountBlockNumber.json | 23 ++++--------------- .../eth_getBalance_invalidAccountLatest.json | 23 ++++--------------- 5 files changed, 29 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a509774f08e..c3121ca080c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bug Fixes * Fixed incorrect `groupId` in published maven pom files. +* Fixed GraphQL response for missing account, return empty account instead [\#1946](https://github.com/hyperledger/besu/issues/1946) ### Early Access Features diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java index aca65745c25..b5b5fa756a5 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkArgument; import org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.AccountAdapter; +import org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.EmptyAccountAdapter; import org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.LogAdapter; import org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.NormalBlockAdapter; import org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.PendingStateAdapter; @@ -186,8 +187,9 @@ DataFetcher> getAccountDataFetcher() { final Optional ws = blockchainQuery.getWorldState(bn); if (ws.isPresent()) { final Account account = ws.get().get(addr); - Preconditions.checkArgument( - account != null, "Account with address %s does not exist", addr); + if (account == null) { + return Optional.of(new EmptyAccountAdapter(addr)); + } return Optional.of(new AccountAdapter(account)); } else if (bn > blockchainQuery.getBlockchain().getChainHeadBlockNumber()) { // block is past chainhead @@ -201,13 +203,13 @@ DataFetcher> getAccountDataFetcher() { final long latestBn = blockchainQuery.latestBlock().get().getHeader().getNumber(); final Optional ows = blockchainQuery.getWorldState(latestBn); return ows.flatMap( - ws -> { - Account account = ws.get(addr); - Preconditions.checkArgument( - account != null, "Account with address %s does not exist", addr); - return Optional.ofNullable(account); - }) - .map(AccountAdapter::new); + ws -> { + Account account = ws.get(addr); + if (account == null) { + return Optional.of(new EmptyAccountAdapter(addr)); + } + return Optional.of(new AccountAdapter(account)); + }); } }; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/EmptyAccountAdapter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/EmptyAccountAdapter.java index 92e31efae34..15d5d5467d2 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/EmptyAccountAdapter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/EmptyAccountAdapter.java @@ -23,30 +23,35 @@ import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; -@SuppressWarnings("unused") // reflected by GraphQL -public class EmptyAccountAdapter extends AdapterBase { +public class EmptyAccountAdapter extends AccountAdapter { private final Address address; public EmptyAccountAdapter(final Address address) { + super(null); this.address = address; } + @Override public Optional
getAddress() { return Optional.of(address); } + @Override public Optional getBalance() { return Optional.of(Wei.ZERO); } + @Override public Optional getTransactionCount() { return Optional.of(0L); } + @Override public Optional getCode() { return Optional.of(Bytes.EMPTY); } + @Override public Optional getStorage(final DataFetchingEnvironment environment) { return Optional.of(Bytes32.ZERO); } diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getBalance_invalidAccountBlockNumber.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getBalance_invalidAccountBlockNumber.json index f335954c09a..540eac83ae5 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getBalance_invalidAccountBlockNumber.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getBalance_invalidAccountBlockNumber.json @@ -1,25 +1,12 @@ { "request": "{account(blockNumber:\"0x19\", address: \"0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\") { balance } }", "response": { - "errors": [ - { - "message": "Exception while fetching data (/account) : Account with address 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef does not exist", - "locations": [ - { - "line": 1, - "column": 2 - } - ], - "path": [ - "account" - ], - "extensions": { - "classification": "DataFetchingException" - } + "data": { + "account": { + "balance": "0x" } - ], - "data": null + } }, - "statusCode": 400 + "statusCode": 200 } diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getBalance_invalidAccountLatest.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getBalance_invalidAccountLatest.json index e3cae9f064f..6917f75e3a2 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getBalance_invalidAccountLatest.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/graphql/eth_getBalance_invalidAccountLatest.json @@ -1,25 +1,12 @@ { "request": "{account(address: \"0xdeaff00ddeaff00ddeaff00ddeaff00ddeaff00d\") { balance } }", "response": { - "errors": [ - { - "message": "Exception while fetching data (/account) : Account with address 0xdeaff00ddeaff00ddeaff00ddeaff00ddeaff00d does not exist", - "locations": [ - { - "line": 1, - "column": 2 - } - ], - "path": [ - "account" - ], - "extensions": { - "classification": "DataFetchingException" - } + "data" : { + "account" : { + "balance" : "0x" } - ], - "data": null + } }, - "statusCode": 400 + "statusCode": 200 } From a729dbd7745d7875c5921b1278802348fddf9cd2 Mon Sep 17 00:00:00 2001 From: Trent Mohay Date: Thu, 4 Mar 2021 14:39:57 +1100 Subject: [PATCH 5/5] Allow for switching ProtocolSchedule rules outside of Milestones (#1962) This is the first step in supporting switchable consensus mechanisms. Specifically this allows additional protocol specs to be inserted to the protocol schedule at milestones other than that explicitly specified in the genesis config. Signed-off-by: Trent Mohay --- .../clique/CliqueProtocolSchedule.java | 19 +- .../common/bft/BftProtocolSchedule.java | 12 +- .../ibftlegacy/IbftProtocolSchedule.java | 9 +- .../EthHashBlockCreatorTest.java | 9 +- .../FixedDifficultyProtocolSchedule.java | 6 +- .../mainnet/MainnetProtocolSchedule.java | 2 +- .../mainnet/MainnetProtocolSpecFactory.java | 163 ++++++++ .../mainnet/ProtocolScheduleBuilder.java | 357 ++++++++---------- .../mainnet/ProtocolSpecAdapters.java | 55 +++ .../core/ExecutionContextTestFixture.java | 3 +- .../mainnet/ProtocolScheduleBuilderTest.java | 128 +++++++ .../mainnet/ProtocolSpecAdaptersTest.java | 46 +++ .../ReferenceTestProtocolSchedules.java | 3 +- 13 files changed, 592 insertions(+), 220 deletions(-) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecAdapters.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecAdaptersTest.java diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java index edc506b86ae..191bf6770be 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java @@ -30,6 +30,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder; import java.math.BigInteger; @@ -64,14 +65,16 @@ public static ProtocolSchedule create( return new ProtocolScheduleBuilder( config, DEFAULT_CHAIN_ID, - builder -> - applyCliqueSpecificModifications( - epochManager, - cliqueConfig.getBlockPeriodSeconds(), - localNodeAddress, - builder, - eip1559, - privacyParameters.getGoQuorumPrivacyParameters().isPresent()), + ProtocolSpecAdapters.create( + 0, + builder -> + applyCliqueSpecificModifications( + epochManager, + cliqueConfig.getBlockPeriodSeconds(), + localNodeAddress, + builder, + eip1559, + privacyParameters.getGoQuorumPrivacyParameters().isPresent())), privacyParameters, isRevertReasonEnabled, config.isQuorum()) diff --git a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java index 94516a389db..a5563c9ecdc 100644 --- a/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java +++ b/consensus/common/src/main/java/org/hyperledger/besu/consensus/common/bft/BftProtocolSchedule.java @@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder; import java.math.BigInteger; @@ -44,9 +45,14 @@ public static ProtocolSchedule create( return new ProtocolScheduleBuilder( config, DEFAULT_CHAIN_ID, - builder -> - applyBftChanges( - config.getBftConfigOptions(), builder, config.isQuorum(), blockHeaderRuleset), + ProtocolSpecAdapters.create( + 0, + builder -> + applyBftChanges( + config.getBftConfigOptions(), + builder, + config.isQuorum(), + blockHeaderRuleset)), privacyParameters, isRevertReasonEnabled, config.isQuorum()) diff --git a/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java b/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java index 5bcf98a5188..694682473c3 100644 --- a/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java +++ b/consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java @@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder; import java.math.BigInteger; @@ -44,9 +45,11 @@ public static ProtocolSchedule create( return new ProtocolScheduleBuilder( config, DEFAULT_CHAIN_ID, - builder -> - applyIbftChanges( - blockPeriod, builder, config.isQuorum(), ibftConfig.getCeil2Nby3Block()), + ProtocolSpecAdapters.create( + 0, + builder -> + applyIbftChanges( + blockPeriod, builder, config.isQuorum(), ibftConfig.getCeil2Nby3Block())), privacyParameters, isRevertReasonEnabled, config.isQuorum()) diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashBlockCreatorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashBlockCreatorTest.java index 7ea143588c6..5a3dc51ad4e 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashBlockCreatorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashBlockCreatorTest.java @@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.mainnet.EthHashSolver; import org.hyperledger.besu.ethereum.mainnet.EthHasher; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.ethereum.mainnet.ValidationTestUtils; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem; @@ -71,7 +72,7 @@ public void createMainnetBlock1() throws IOException { new ProtocolScheduleBuilder( genesisConfigOptions, BigInteger.valueOf(42), - Function.identity(), + ProtocolSpecAdapters.create(0, Function.identity()), PrivacyParameters.DEFAULT, false, genesisConfigOptions.isQuorum()) @@ -130,7 +131,7 @@ public void createMainnetBlock1_fixedDifficulty1() { new ProtocolScheduleBuilder( genesisConfigOptions, BigInteger.valueOf(42), - Function.identity(), + ProtocolSpecAdapters.create(0, Function.identity()), PrivacyParameters.DEFAULT, false, genesisConfigOptions.isQuorum()) @@ -184,7 +185,7 @@ public void rewardBeneficiary_zeroReward_skipZeroRewardsFalse() { new ProtocolScheduleBuilder( genesisConfigOptions, BigInteger.valueOf(42), - Function.identity(), + ProtocolSpecAdapters.create(0, Function.identity()), PrivacyParameters.DEFAULT, false, genesisConfigOptions.isQuorum()) @@ -254,7 +255,7 @@ public void rewardBeneficiary_zeroReward_skipZeroRewardsTrue() { new ProtocolScheduleBuilder( genesisConfigOptions, BigInteger.valueOf(42), - Function.identity(), + ProtocolSpecAdapters.create(0, Function.identity()), PrivacyParameters.DEFAULT, false, genesisConfigOptions.isQuorum()) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java index 014ea997fa4..2b4ee876c4e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/difficulty/fixed/FixedDifficultyProtocolSchedule.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; /** A ProtocolSchedule which behaves similarly to MainNet, but with a much reduced difficulty. */ public class FixedDifficultyProtocolSchedule { @@ -28,7 +29,10 @@ public static ProtocolSchedule create( final boolean isRevertReasonEnabled) { return new ProtocolScheduleBuilder( config, - builder -> builder.difficultyCalculator(FixedDifficultyCalculators.calculator(config)), + ProtocolSpecAdapters.create( + 0, + builder -> + builder.difficultyCalculator(FixedDifficultyCalculators.calculator(config))), privacyParameters, isRevertReasonEnabled, config.isQuorum()) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java index 3fcc0eaf4a4..c0806ff23c4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSchedule.java @@ -47,7 +47,7 @@ public static ProtocolSchedule fromConfig( return new ProtocolScheduleBuilder( config, DEFAULT_CHAIN_ID, - Function.identity(), + ProtocolSpecAdapters.create(0, Function.identity()), privacyParameters, isRevertReasonEnabled, config.isQuorum()) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java new file mode 100644 index 00000000000..9b86ae41bdb --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java @@ -0,0 +1,163 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import java.math.BigInteger; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; + +public class MainnetProtocolSpecFactory { + + private final Optional chainId; + private final OptionalInt contractSizeLimit; + private final OptionalInt evmStackSize; + private final boolean isRevertReasonEnabled; + private final boolean quorumCompatibilityMode; + private final OptionalLong ecip1017EraRounds; + + public MainnetProtocolSpecFactory( + final Optional chainId, + final OptionalInt contractSizeLimit, + final OptionalInt evmStackSize, + final boolean isRevertReasonEnabled, + final boolean quorumCompatibilityMode, + final OptionalLong ecip1017EraRounds) { + this.chainId = chainId; + this.contractSizeLimit = contractSizeLimit; + this.evmStackSize = evmStackSize; + this.isRevertReasonEnabled = isRevertReasonEnabled; + this.quorumCompatibilityMode = quorumCompatibilityMode; + this.ecip1017EraRounds = ecip1017EraRounds; + } + + public ProtocolSpecBuilder frontierDefinition() { + return MainnetProtocolSpecs.frontierDefinition( + contractSizeLimit, evmStackSize, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder homesteadDefinition() { + return MainnetProtocolSpecs.homesteadDefinition( + contractSizeLimit, evmStackSize, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder daoRecoveryInitDefinition() { + return MainnetProtocolSpecs.daoRecoveryInitDefinition( + contractSizeLimit, evmStackSize, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder daoRecoveryTransitionDefinition() { + return MainnetProtocolSpecs.daoRecoveryTransitionDefinition( + contractSizeLimit, evmStackSize, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder tangerineWhistleDefinition() { + return MainnetProtocolSpecs.tangerineWhistleDefinition( + contractSizeLimit, evmStackSize, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder spuriousDragonDefinition() { + return MainnetProtocolSpecs.spuriousDragonDefinition( + chainId, contractSizeLimit, evmStackSize, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder byzantiumDefinition() { + return MainnetProtocolSpecs.byzantiumDefinition( + chainId, contractSizeLimit, evmStackSize, isRevertReasonEnabled, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder constantinopleDefinition() { + return MainnetProtocolSpecs.constantinopleDefinition( + chainId, contractSizeLimit, evmStackSize, isRevertReasonEnabled, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder petersburgDefinition() { + return MainnetProtocolSpecs.petersburgDefinition( + chainId, contractSizeLimit, evmStackSize, isRevertReasonEnabled, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder istanbulDefinition() { + return MainnetProtocolSpecs.istanbulDefinition( + chainId, contractSizeLimit, evmStackSize, isRevertReasonEnabled, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder muirGlacierDefinition() { + return MainnetProtocolSpecs.muirGlacierDefinition( + chainId, contractSizeLimit, evmStackSize, isRevertReasonEnabled, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder berlinDefinition() { + return MainnetProtocolSpecs.berlinDefinition( + chainId, contractSizeLimit, evmStackSize, isRevertReasonEnabled, quorumCompatibilityMode); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////// + // Classic Protocol Specs + public ProtocolSpecBuilder dieHardDefinition() { + return ClassicProtocolSpecs.dieHardDefinition( + chainId, contractSizeLimit, evmStackSize, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder gothamDefinition() { + return ClassicProtocolSpecs.gothamDefinition( + chainId, contractSizeLimit, evmStackSize, ecip1017EraRounds, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder defuseDifficultyBombDefinition() { + return ClassicProtocolSpecs.defuseDifficultyBombDefinition( + chainId, contractSizeLimit, evmStackSize, ecip1017EraRounds, quorumCompatibilityMode); + } + + public ProtocolSpecBuilder atlantisDefinition() { + return ClassicProtocolSpecs.atlantisDefinition( + chainId, + contractSizeLimit, + evmStackSize, + isRevertReasonEnabled, + ecip1017EraRounds, + quorumCompatibilityMode); + } + + public ProtocolSpecBuilder aghartaDefinition() { + return ClassicProtocolSpecs.aghartaDefinition( + chainId, + contractSizeLimit, + evmStackSize, + isRevertReasonEnabled, + ecip1017EraRounds, + quorumCompatibilityMode); + } + + public ProtocolSpecBuilder phoenixDefinition() { + return ClassicProtocolSpecs.phoenixDefinition( + chainId, + contractSizeLimit, + evmStackSize, + isRevertReasonEnabled, + ecip1017EraRounds, + quorumCompatibilityMode); + } + + public ProtocolSpecBuilder thanosDefinition() { + return ClassicProtocolSpecs.thanosDefinition( + chainId, + contractSizeLimit, + evmStackSize, + isRevertReasonEnabled, + ecip1017EraRounds, + quorumCompatibilityMode); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java index fb3398cfebb..ebfe0bbcf0e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java @@ -24,16 +24,47 @@ import java.math.BigInteger; import java.util.Optional; import java.util.OptionalLong; +import java.util.TreeMap; import java.util.function.Function; +import java.util.stream.Collectors; +import com.google.common.collect.Lists; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class ProtocolScheduleBuilder { + private static class BuilderMapEntry { + + private final long block; + private final ProtocolSpecBuilder builder; + private final Function modifier; + + public BuilderMapEntry( + final long block, + final ProtocolSpecBuilder builder, + final Function modifier) { + this.block = block; + this.builder = builder; + this.modifier = modifier; + } + + public long getBlock() { + return block; + } + + public ProtocolSpecBuilder getBuilder() { + return builder; + } + + public Function getModifier() { + return modifier; + } + } + private static final Logger LOG = LogManager.getLogger(); private final GenesisConfigOptions config; - private final Function protocolSpecAdapter; + private final ProtocolSpecAdapters protocolSpecAdapters; private final Optional defaultChainId; private final PrivacyParameters privacyParameters; private final boolean isRevertReasonEnabled; @@ -43,29 +74,39 @@ public class ProtocolScheduleBuilder { public ProtocolScheduleBuilder( final GenesisConfigOptions config, final BigInteger defaultChainId, - final Function protocolSpecAdapter, + final ProtocolSpecAdapters protocolSpecAdapters, final PrivacyParameters privacyParameters, final boolean isRevertReasonEnabled, final boolean quorumCompatibilityMode) { this( config, Optional.of(defaultChainId), - protocolSpecAdapter, + protocolSpecAdapters, privacyParameters, isRevertReasonEnabled, quorumCompatibilityMode); } + private Optional create( + final OptionalLong block, final ProtocolSpecBuilder builder) { + if (block.isEmpty()) { + return Optional.empty(); + } + final long blockVal = block.getAsLong(); + return Optional.of( + new BuilderMapEntry(blockVal, builder, protocolSpecAdapters.getModifierForBlock(blockVal))); + } + public ProtocolScheduleBuilder( final GenesisConfigOptions config, - final Function protocolSpecAdapter, + final ProtocolSpecAdapters protocolSpecAdapters, final PrivacyParameters privacyParameters, final boolean isRevertReasonEnabled, final boolean quorumCompatibilityMode) { this( config, Optional.empty(), - protocolSpecAdapter, + protocolSpecAdapters, privacyParameters, isRevertReasonEnabled, quorumCompatibilityMode); @@ -74,13 +115,13 @@ public ProtocolScheduleBuilder( private ProtocolScheduleBuilder( final GenesisConfigOptions config, final Optional defaultChainId, - final Function protocolSpecAdapter, + final ProtocolSpecAdapters protocolSpecAdapters, final PrivacyParameters privacyParameters, final boolean isRevertReasonEnabled, final boolean quorumCompatibilityMode) { this.config = config; this.defaultChainId = defaultChainId; - this.protocolSpecAdapter = protocolSpecAdapter; + this.protocolSpecAdapters = protocolSpecAdapters; this.privacyParameters = privacyParameters; this.isRevertReasonEnabled = isRevertReasonEnabled; this.quorumCompatibilityMode = quorumCompatibilityMode; @@ -91,19 +132,38 @@ public ProtocolSchedule createProtocolSchedule() { config.getChainId().map(Optional::of).orElse(defaultChainId); final MutableProtocolSchedule protocolSchedule = new MutableProtocolSchedule(chainId); + final MainnetProtocolSpecFactory specFactory = + new MainnetProtocolSpecFactory( + chainId, + config.getContractSizeLimit(), + config.getEvmStackSize(), + isRevertReasonEnabled, + quorumCompatibilityMode, + config.getEcip1017EraRounds()); + validateForkOrdering(); - addProtocolSpec( - protocolSchedule, - OptionalLong.of(0), - MainnetProtocolSpecs.frontierDefinition( - config.getContractSizeLimit(), config.getEvmStackSize(), quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getHomesteadBlockNumber(), - MainnetProtocolSpecs.homesteadDefinition( - config.getContractSizeLimit(), config.getEvmStackSize(), quorumCompatibilityMode)); + final TreeMap builders = buildMilestoneMap(specFactory, chainId); + + // At this stage, all milestones are flagged with correct modifier, but ProtocolSpecs must be + // inserted _AT_ the modifier block entry. + protocolSpecAdapters.stream() + .forEach( + entry -> { + final long modifierBlock = entry.getKey(); + final BuilderMapEntry parent = builders.floorEntry(modifierBlock).getValue(); + builders.put( + modifierBlock, + new BuilderMapEntry(modifierBlock, parent.getBuilder(), entry.getValue())); + }); + // Create the ProtocolSchedule, such that the Dao/fork milestones can be inserted + builders + .values() + .forEach(e -> addProtocolSpec(protocolSchedule, e.getBlock(), e.getBuilder(), e.modifier)); + + // NOTE: It is assumed that Daofork blocks will not be used for private networks + // as too many risks exist around inserting a protocol-spec between daoBlock and daoBlock+10. config .getDaoForkBlock() .ifPresent( @@ -112,108 +172,18 @@ public ProtocolSchedule createProtocolSchedule() { protocolSchedule.getByBlockNumber(daoBlockNumber); addProtocolSpec( protocolSchedule, - OptionalLong.of(daoBlockNumber), - MainnetProtocolSpecs.daoRecoveryInitDefinition( - config.getContractSizeLimit(), - config.getEvmStackSize(), - quorumCompatibilityMode)); + daoBlockNumber, + specFactory.daoRecoveryInitDefinition(), + protocolSpecAdapters.getModifierForBlock(daoBlockNumber)); addProtocolSpec( protocolSchedule, - OptionalLong.of(daoBlockNumber + 1), - MainnetProtocolSpecs.daoRecoveryTransitionDefinition( - config.getContractSizeLimit(), - config.getEvmStackSize(), - quorumCompatibilityMode)); - + daoBlockNumber + 1L, + specFactory.daoRecoveryTransitionDefinition(), + protocolSpecAdapters.getModifierForBlock(daoBlockNumber + 1L)); // Return to the previous protocol spec after the dao fork has completed. protocolSchedule.putMilestone(daoBlockNumber + 10, originalProtocolSpec); }); - addProtocolSpec( - protocolSchedule, - config.getTangerineWhistleBlockNumber(), - MainnetProtocolSpecs.tangerineWhistleDefinition( - config.getContractSizeLimit(), config.getEvmStackSize(), quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getSpuriousDragonBlockNumber(), - MainnetProtocolSpecs.spuriousDragonDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getByzantiumBlockNumber(), - MainnetProtocolSpecs.byzantiumDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getConstantinopleBlockNumber(), - MainnetProtocolSpecs.constantinopleDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getPetersburgBlockNumber(), - MainnetProtocolSpecs.petersburgDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getIstanbulBlockNumber(), - MainnetProtocolSpecs.istanbulDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getMuirGlacierBlockNumber(), - MainnetProtocolSpecs.muirGlacierDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - quorumCompatibilityMode)); - - addProtocolSpec( - protocolSchedule, - config.getBerlinBlockNumber(), - MainnetProtocolSpecs.berlinDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - quorumCompatibilityMode)); - - if (ExperimentalEIPs.eip1559Enabled) { - final Optional transactionPriceCalculator = - Optional.of(TransactionPriceCalculator.eip1559()); - addProtocolSpec( - protocolSchedule, - config.getEIP1559BlockNumber(), - MainnetProtocolSpecs.eip1559Definition( - chainId, - transactionPriceCalculator, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - config, - quorumCompatibilityMode)); - } - // specs for classic network config .getClassicForkBlock() @@ -231,100 +201,91 @@ public ProtocolSchedule createProtocolSchedule() { protocolSchedule.putMilestone(classicBlockNumber + 1, originalProtocolSpec); }); - addProtocolSpec( - protocolSchedule, - config.getEcip1015BlockNumber(), - ClassicProtocolSpecs.tangerineWhistleDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getDieHardBlockNumber(), - ClassicProtocolSpecs.dieHardDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getGothamBlockNumber(), - ClassicProtocolSpecs.gothamDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - config.getEcip1017EraRounds(), - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getDefuseDifficultyBombBlockNumber(), - ClassicProtocolSpecs.defuseDifficultyBombDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - config.getEcip1017EraRounds(), - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getAtlantisBlockNumber(), - ClassicProtocolSpecs.atlantisDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - config.getEcip1017EraRounds(), - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getAghartaBlockNumber(), - ClassicProtocolSpecs.aghartaDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - config.getEcip1017EraRounds(), - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getPhoenixBlockNumber(), - ClassicProtocolSpecs.phoenixDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - config.getEcip1017EraRounds(), - quorumCompatibilityMode)); - addProtocolSpec( - protocolSchedule, - config.getThanosBlockNumber(), - ClassicProtocolSpecs.thanosDefinition( - chainId, - config.getContractSizeLimit(), - config.getEvmStackSize(), - isRevertReasonEnabled, - config.getEcip1017EraRounds(), - quorumCompatibilityMode)); - LOG.info("Protocol schedule created with milestones: {}", protocolSchedule.listMilestones()); return protocolSchedule; } + private TreeMap buildMilestoneMap( + final MainnetProtocolSpecFactory specFactory, final Optional chainId) { + final TreeMap builders = + Lists.newArrayList( + create(OptionalLong.of(0), specFactory.frontierDefinition()), + create(config.getHomesteadBlockNumber(), specFactory.homesteadDefinition()), + create( + config.getTangerineWhistleBlockNumber(), + specFactory.tangerineWhistleDefinition()), + create( + config.getSpuriousDragonBlockNumber(), specFactory.spuriousDragonDefinition()), + create(config.getByzantiumBlockNumber(), specFactory.byzantiumDefinition()), + create( + config.getConstantinopleBlockNumber(), specFactory.constantinopleDefinition()), + create(config.getPetersburgBlockNumber(), specFactory.petersburgDefinition()), + create(config.getIstanbulBlockNumber(), specFactory.istanbulDefinition()), + create(config.getMuirGlacierBlockNumber(), specFactory.muirGlacierDefinition()), + create(config.getBerlinBlockNumber(), specFactory.berlinDefinition()), + // Classic Milestones + create(config.getEcip1015BlockNumber(), specFactory.tangerineWhistleDefinition()), + create(config.getDieHardBlockNumber(), specFactory.dieHardDefinition()), + create(config.getGothamBlockNumber(), specFactory.gothamDefinition()), + create( + config.getDefuseDifficultyBombBlockNumber(), + specFactory.defuseDifficultyBombDefinition()), + create(config.getAtlantisBlockNumber(), specFactory.atlantisDefinition()), + create(config.getAghartaBlockNumber(), specFactory.aghartaDefinition()), + create(config.getPhoenixBlockNumber(), specFactory.phoenixDefinition()), + create(config.getThanosBlockNumber(), specFactory.thanosDefinition())) + .stream() + .filter(Optional::isPresent) + .map(Optional::get) + .collect( + Collectors.toMap( + BuilderMapEntry::getBlock, + b -> b, + (existing, replacement) -> replacement, + TreeMap::new)); + + if (ExperimentalEIPs.eip1559Enabled) { + final Optional transactionPriceCalculator = + Optional.of(TransactionPriceCalculator.eip1559()); + final long eip1559Block = config.getEIP1559BlockNumber().getAsLong(); + builders.put( + eip1559Block, + new BuilderMapEntry( + eip1559Block, + MainnetProtocolSpecs.eip1559Definition( + chainId, + transactionPriceCalculator, + config.getContractSizeLimit(), + config.getEvmStackSize(), + isRevertReasonEnabled, + config, + quorumCompatibilityMode), + protocolSpecAdapters.getModifierForBlock(eip1559Block))); + } + + return builders; + } + private void addProtocolSpec( final MutableProtocolSchedule protocolSchedule, final OptionalLong blockNumber, final ProtocolSpecBuilder definition) { blockNumber.ifPresent( - number -> - protocolSchedule.putMilestone( - number, - protocolSpecAdapter - .apply(definition) - .badBlocksManager(badBlockManager) - .privacyParameters(privacyParameters) - .privateTransactionValidatorBuilder( - () -> new PrivateTransactionValidator(protocolSchedule.getChainId())) - .build(protocolSchedule))); + bn -> addProtocolSpec(protocolSchedule, bn, definition, Function.identity())); + } + + private void addProtocolSpec( + final MutableProtocolSchedule protocolSchedule, + final long blockNumber, + final ProtocolSpecBuilder definition, + final Function modifier) { + definition + .badBlocksManager(badBlockManager) + .privacyParameters(privacyParameters) + .privateTransactionValidatorBuilder( + () -> new PrivateTransactionValidator(protocolSchedule.getChainId())); + + protocolSchedule.putMilestone(blockNumber, modifier.apply(definition).build(protocolSchedule)); } private long validateForkOrder( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecAdapters.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecAdapters.java new file mode 100644 index 00000000000..03bb2a46356 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecAdapters.java @@ -0,0 +1,55 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import java.util.HashMap; +import java.util.Map; +import java.util.NavigableSet; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.stream.Stream; + +public class ProtocolSpecAdapters { + + final Map> modifiers; + + public ProtocolSpecAdapters( + final Map> modifiers) { + this.modifiers = modifiers; + } + + public static ProtocolSpecAdapters create( + final long block, final Function modifier) { + final Map> entries = new HashMap<>(); + entries.put(block, modifier); + return new ProtocolSpecAdapters(entries); + } + + public Function getModifierForBlock( + final long blockNumber) { + final NavigableSet epochs = new TreeSet<>(modifiers.keySet()); + final Long modifier = epochs.floor(blockNumber); + + if (modifier == null) { + return Function.identity(); + } + + return modifiers.get(modifier); + } + + public Stream>> stream() { + return modifiers.entrySet().stream(); + } +} diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java index cea7d932d55..802dfce3706 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java @@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; @@ -116,7 +117,7 @@ public ExecutionContextTestFixture build() { new ProtocolScheduleBuilder( new StubGenesisConfigOptions().petersburgBlock(0), BigInteger.valueOf(42), - Function.identity(), + ProtocolSpecAdapters.create(0, Function.identity()), new PrivacyParameters(), false, genesisConfigFile.getConfigOptions().isQuorum()) diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java new file mode 100644 index 00000000000..35db641f1ff --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java @@ -0,0 +1,128 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.config.GenesisConfigOptions; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; + +import java.math.BigInteger; +import java.util.OptionalLong; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +@RunWith(MockitoJUnitRunner.class) +public class ProtocolScheduleBuilderTest { + + @Mock GenesisConfigOptions configOptions; + + @Mock private Function modifier; + + @Test + public void modifierInsertedBetweenBlocksIsAppliedToLaterAndCreatesInterimMilestone() { + when(configOptions.getHomesteadBlockNumber()).thenReturn(OptionalLong.of(5L)); + + when(modifier.apply(any())) + .thenAnswer((Answer) invocation -> invocation.getArgument(0)); + + final ProtocolScheduleBuilder builder = + new ProtocolScheduleBuilder( + configOptions, + BigInteger.ONE, + ProtocolSpecAdapters.create(2, modifier), + new PrivacyParameters(), + false, + false); + + final MutableProtocolSchedule schedule = + (MutableProtocolSchedule) builder.createProtocolSchedule(); + + // A default spec exists at 0 (frontier), then the spec as requested in config, then another + // added at the point at which the modifier is applied. + assertThat(schedule.streamMilestoneBlocks().collect(Collectors.toList())) + .containsExactly(0L, 2L, 5L); + assertThat(schedule.getByBlockNumber(0).getName()).isEqualTo("Frontier"); + assertThat(schedule.getByBlockNumber(2).getName()).isEqualTo("Frontier"); + assertThat(schedule.getByBlockNumber(5).getName()).isEqualTo("Homestead"); + + verify(modifier, times(2)).apply(any()); + } + + @Test + public void modifierPastEndOfDefinedMilestonesGetsItsOwnMilestoneCreated() { + when(modifier.apply(any())) + .thenAnswer((Answer) invocation -> invocation.getArgument(0)); + + final ProtocolScheduleBuilder builder = + new ProtocolScheduleBuilder( + configOptions, + BigInteger.ONE, + ProtocolSpecAdapters.create(2, modifier), + new PrivacyParameters(), + false, + false); + + final MutableProtocolSchedule schedule = + (MutableProtocolSchedule) builder.createProtocolSchedule(); + + // A default spec exists at 0 (frontier), then the spec as requested in config, then another + // added at the point at which the modifier is applied. + assertThat(schedule.streamMilestoneBlocks().collect(Collectors.toList())) + .containsExactly(0L, 2L); + assertThat(schedule.getByBlockNumber(0).getName()).isEqualTo("Frontier"); + assertThat(schedule.getByBlockNumber(2).getName()).isEqualTo("Frontier"); + + verify(modifier, times(1)).apply(any()); + } + + @Test + public void modifierOnDefinedMilestoneIsAppliedButDoesNotGetAnExtraMilestoneCreated() { + when(configOptions.getHomesteadBlockNumber()).thenReturn(OptionalLong.of(5L)); + when(modifier.apply(any())) + .thenAnswer((Answer) invocation -> invocation.getArgument(0)); + + final ProtocolScheduleBuilder builder = + new ProtocolScheduleBuilder( + configOptions, + BigInteger.ONE, + ProtocolSpecAdapters.create(5, modifier), + new PrivacyParameters(), + false, + false); + + final MutableProtocolSchedule schedule = + (MutableProtocolSchedule) builder.createProtocolSchedule(); + + // A default spec exists at 0 (frontier), then the spec as requested in config, then another + // added at the point at which the modifier is applied. + assertThat(schedule.streamMilestoneBlocks().collect(Collectors.toList())) + .containsExactly(0L, 5L); + assertThat(schedule.getByBlockNumber(0).getName()).isEqualTo("Frontier"); + assertThat(schedule.getByBlockNumber(5).getName()).isEqualTo("Homestead"); + + verify(modifier, times(1)).apply(any()); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecAdaptersTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecAdaptersTest.java new file mode 100644 index 00000000000..7b01135148f --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecAdaptersTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2020 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.mainnet; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; +import java.util.function.Function; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ProtocolSpecAdaptersTest { + + @Mock private Function firstModifier; + + @Mock private Function secondModifier; + + @Test + public void specAdapterFindsTheModifierBelowRequestedBlock() { + + final ProtocolSpecAdapters adapters = + new ProtocolSpecAdapters(Map.of(3L, firstModifier, 5L, secondModifier)); + + assertThat(adapters.getModifierForBlock(3)).isEqualTo(firstModifier); + assertThat(adapters.getModifierForBlock(4)).isEqualTo(firstModifier); + assertThat(adapters.getModifierForBlock(5)).isEqualTo(secondModifier); + assertThat(adapters.getModifierForBlock(6)).isEqualTo(secondModifier); + assertThat(adapters.getModifierForBlock(0)).isEqualTo(Function.identity()); + } +} diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java index ac5775f6450..71fac1e7210 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters; import java.math.BigInteger; import java.util.Arrays; @@ -80,7 +81,7 @@ private static ProtocolSchedule createSchedule(final GenesisConfigOptions option return new ProtocolScheduleBuilder( options, CHAIN_ID, - Function.identity(), + ProtocolSpecAdapters.create(0, Function.identity()), PrivacyParameters.DEFAULT, false, options.isQuorum())