diff --git a/CHANGELOG.md b/CHANGELOG.md index 367eb8137f2..1ee66a3d62a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * add support for keccak mining and a ecip1049_dev network [\#1882](https://github.com/hyperledger/besu/pull/1882) ### 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/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java index b0dab80bf26..d3d46f67ed3 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java @@ -350,6 +350,8 @@ public PowAlgorithm getPowAlgorithm() { 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( @@ -372,11 +374,23 @@ 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)); + getEcip1049BlockNumber().ifPresent(l -> builder.put("ecip1049Block", 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()); @@ -468,7 +482,8 @@ public List getForks() { getAtlantisBlockNumber(), getAghartaBlockNumber(), getPhoenixBlockNumber(), - getThanosBlockNumber()); + getThanosBlockNumber(), + getEcip1049BlockNumber()); return forkBlockNumbers .filter(OptionalLong::isPresent) 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 270f731a65d..d8979d802b8 100644 --- a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java @@ -40,16 +40,16 @@ 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 final OptionalLong ecip1049BlockNumber = 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 OptionalLong ecip1049BlockNumber = OptionalLong.empty(); private Optional chainId = Optional.empty(); private OptionalInt contractSizeLimit = OptionalInt.empty(); private OptionalInt stackSizeLimit = OptionalInt.empty(); @@ -245,6 +245,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( @@ -265,6 +267,19 @@ 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)); + getEcip1049BlockNumber().ifPresent(l -> builder.put("ecip1049Block", l)); + getContractSizeLimit().ifPresent(l -> builder.put("contractSizeLimit", l)); getEvmStackSize().ifPresent(l -> builder.put("evmStackSize", l)); if (isClique()) { @@ -362,6 +377,56 @@ 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 ecip1049(final long blockNumber) { + ecip1049BlockNumber = OptionalLong.of(blockNumber); + return this; + } + public StubGenesisConfigOptions chainId(final BigInteger chainId) { this.chainId = Optional.ofNullable(chainId); return this; 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/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/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/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..64a1cdec34e 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,62 @@ 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) + .ecip1049(10); + + 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, + "ecip1049Block", 10L); + + 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[] {})); } 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 } 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, diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java index b659999137a..eeacfd251dc 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/PoWBlockCreatorTest.java @@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.mainnet.PoWHasher; import org.hyperledger.besu.ethereum.mainnet.PoWSolver; 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..eb899713537 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java @@ -0,0 +1,173 @@ +/* + * 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); + } + + public ProtocolSpecBuilder ecip1049Definition() { + return ClassicProtocolSpecs.ecip1049Definition( + 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 2ca940f6afd..cd9e3946f67 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,110 +201,92 @@ 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)); - addProtocolSpec( - protocolSchedule, - config.getEcip1049BlockNumber(), - ClassicProtocolSpecs.ecip1049Definition( - 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()), + create(config.getEcip1049BlockNumber(), specFactory.ecip1049Definition())) + .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()) 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 dc757f2378b..2a5784f4a55 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.MiningCoordinator; import org.hyperledger.besu.ethereum.blockcreation.PoWMiningCoordinator; @@ -34,6 +33,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; @@ -55,7 +55,7 @@ public class Stratum1EthProxyProtocol implements StratumProtocol { public Stratum1EthProxyProtocol(final MiningCoordinator miningCoordinator) { if (!(miningCoordinator instanceof PoWMiningCoordinator)) { throw new IllegalArgumentException( - "Stratum1 Proxies require an EthHashMiningCoordinator not " + "Stratum1 Proxies require an PoWMiningCoordinator not " + ((miningCoordinator == null) ? "null" : miningCoordinator.getClass().getName())); } this.miningCoordinator = miningCoordinator; @@ -64,21 +64,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/main/java/org/hyperledger/besu/ethereum/stratum/Stratum1Protocol.java b/ethereum/stratum/src/main/java/org/hyperledger/besu/ethereum/stratum/Stratum1Protocol.java index 2f7aa5c9699..e16c173533f 100644 --- a/ethereum/stratum/src/main/java/org/hyperledger/besu/ethereum/stratum/Stratum1Protocol.java +++ b/ethereum/stratum/src/main/java/org/hyperledger/besu/ethereum/stratum/Stratum1Protocol.java @@ -84,7 +84,7 @@ public Stratum1Protocol(final String extranonce, final MiningCoordinator miningC final Supplier subscriptionIdCreator) { if (!(miningCoordinator instanceof PoWMiningCoordinator)) { throw new IllegalArgumentException( - "Stratum1 requires an EthHashMiningCoordinator not " + "Stratum1 requires an PoWMiningCoordinator not " + ((miningCoordinator == null) ? "null" : miningCoordinator.getClass().getName())); } this.extranonce = extranonce; 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..7feb8c82169 --- /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.MiningCoordinator; +import org.hyperledger.besu.ethereum.blockcreation.PoWMiningCoordinator; + +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(PoWMiningCoordinator.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(); + } +}