From 2e87576245aac1845eeddb4ca95847e0be1e7839 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 1 Mar 2021 09:08:36 -0700 Subject: [PATCH 1/3] Reduce hash calculations in Bonsai Tries (#1960) Setting contract code on a bonsai account results in a re-hashing. To reduce this only set the code if the code was changed. In some cases this was responsible for 40% of load. Signed-off-by: Danno Ferrin --- .../besu/ethereum/bonsai/BonsaiWorldStateUpdater.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java index 03d7764ab43..24d37d6346f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java @@ -217,7 +217,9 @@ public void commit() { } else { updatedAccount.setBalance(tracked.getBalance()); updatedAccount.setNonce(tracked.getNonce()); - updatedAccount.setCode(tracked.getCode()); + if (tracked.codeWasUpdated()) { + updatedAccount.setCode(tracked.getCode()); + } if (tracked.getStorageWasCleared()) { updatedAccount.clearStorage(); } From f25d458345d9bf540534578462383de6584a1f50 Mon Sep 17 00:00:00 2001 From: "Ratan (Rai) Sur" Date: Mon, 1 Mar 2021 16:55:21 -0600 Subject: [PATCH 2/3] Access List State Test Support for evmtool (#1945) Update reference test hash for access list tests. Signed-off-by: Ratan Rai Sur --- .../parameters/JsonCallParameter.java | 4 +- .../results/TransactionCompleteResult.java | 3 +- .../results/TransactionPendingResult.java | 3 +- .../besu/ethereum/core/AccessListEntry.java | 40 ++------- .../besu/ethereum/core/Transaction.java | 51 +++++++---- .../core/encoding/TransactionEncoder.java | 7 +- .../json/AccessListEntryDeserializer.java | 55 ++++++++++++ .../core/json/AccessListEntrySerializer.java | 54 +++++++++++ .../GasDeserializer.java | 2 +- .../HexStringDeserializer.java | 2 +- .../ethereum/mainnet/BerlinGasCalculator.java | 3 +- .../besu/evmtool/StateTestSubCommandTest.java | 8 ++ .../hyperledger/besu/evmtool/access-list.json | 90 +++++++++++++++++++ ethereum/referencetests/build.gradle | 2 +- .../GeneralStateTestCaseSpec.java | 3 +- .../StateTestAccessListDeserializer.java | 45 ++++++++++ .../StateTestVersionedTransaction.java | 32 ++++--- ethereum/referencetests/src/test/resources | 2 +- 18 files changed, 333 insertions(+), 73 deletions(-) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/AccessListEntryDeserializer.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/AccessListEntrySerializer.java rename ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/{deserializer => json}/GasDeserializer.java (95%) rename ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/{deserializer => json}/HexStringDeserializer.java (95%) create mode 100644 ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/access-list.json create mode 100644 ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestAccessListDeserializer.java diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/JsonCallParameter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/JsonCallParameter.java index c9ff32a3444..a273392d783 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/JsonCallParameter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/JsonCallParameter.java @@ -19,8 +19,8 @@ import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.Wei; -import org.hyperledger.besu.ethereum.core.deserializer.GasDeserializer; -import org.hyperledger.besu.ethereum.core.deserializer.HexStringDeserializer; +import org.hyperledger.besu.ethereum.core.json.GasDeserializer; +import org.hyperledger.besu.ethereum.core.json.HexStringDeserializer; import org.hyperledger.besu.ethereum.transaction.CallParameter; import java.util.Optional; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java index 0d6eb01f7f9..cf085066683 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java @@ -78,8 +78,7 @@ public class TransactionCompleteResult implements TransactionResult { public TransactionCompleteResult(final TransactionWithMetadata tx) { final Transaction transaction = tx.getTransaction(); final TransactionType transactionType = transaction.getType(); - this.accessList = - transactionType.equals(TransactionType.ACCESS_LIST) ? transaction.getAccessList() : null; + this.accessList = transaction.getAccessList().orElse(null); this.blockHash = tx.getBlockHash().get().toString(); this.blockNumber = Quantity.create(tx.getBlockNumber().get()); this.chainId = transaction.getChainId().map(Quantity::create).orElse(null); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java index 470d5a2346a..33e14b7e619 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java @@ -68,8 +68,7 @@ public class TransactionPendingResult implements TransactionResult { public TransactionPendingResult(final Transaction transaction) { final TransactionType transactionType = transaction.getType(); - this.accessList = - transactionType.equals(TransactionType.ACCESS_LIST) ? transaction.getAccessList() : null; + this.accessList = transaction.getAccessList().orElse(null); this.chainId = transaction.getChainId().map(Quantity::create).orElse(null); this.from = transaction.getSender().toString(); this.gas = Quantity.create(transaction.getGasLimit()); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AccessListEntry.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AccessListEntry.java index 94459b31387..fab088e8ff2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AccessListEntry.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AccessListEntry.java @@ -14,16 +14,17 @@ */ package org.hyperledger.besu.ethereum.core; -import java.io.IOException; +import org.hyperledger.besu.ethereum.core.json.AccessListEntryDeserializer; +import org.hyperledger.besu.ethereum.core.json.AccessListEntrySerializer; + import java.util.List; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; import org.apache.tuweni.bytes.Bytes32; -@JsonSerialize(using = AccessListEntry.Serializer.class) +@JsonSerialize(using = AccessListEntrySerializer.class) +@JsonDeserialize(using = AccessListEntryDeserializer.class) public class AccessListEntry { private final Address address; private final List storageKeys; @@ -41,33 +42,4 @@ public Address getAddress() { public List getStorageKeys() { return storageKeys; } - - public static class Serializer extends StdSerializer { - - Serializer() { - this(null); - } - - protected Serializer(final Class t) { - super(t); - } - - @Override - public void serialize( - final AccessListEntry accessListEntry, - final JsonGenerator gen, - final SerializerProvider provider) - throws IOException { - gen.writeStartObject(); - gen.writeFieldName("address"); - gen.writeString(accessListEntry.getAddress().toHexString()); - gen.writeFieldName("storageKeys"); - final List storageKeys = accessListEntry.getStorageKeys(); - gen.writeArray( - storageKeys.stream().map(Bytes32::toHexString).toArray(String[]::new), - 0, - storageKeys.size()); - gen.writeEndObject(); - } - } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index 29c4074f9b2..11085abd40f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -15,7 +15,6 @@ package org.hyperledger.besu.ethereum.core; import static com.google.common.base.Preconditions.checkState; -import static java.util.Collections.emptyList; import static org.hyperledger.besu.crypto.Hash.keccak256; import org.hyperledger.besu.crypto.KeyPair; @@ -77,7 +76,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction private final Bytes payload; - private final List accessList; + private final Optional> maybeAccessList; private final Optional chainId; @@ -119,7 +118,8 @@ public static Transaction readFrom(final RLPInput rlpInput) { * @param value the value being transferred to the recipient * @param signature the signature * @param payload the payload - * @param accessList the list of addresses/storage slots this transaction intends to preload + * @param maybeAccessList the optional list of addresses/storage slots this transaction intends to + * preload * @param sender the transaction sender * @param chainId the chain id to apply the transaction to * @param v the v value. This is only passed in directly for GoQuorum private transactions @@ -141,7 +141,7 @@ public Transaction( final Wei value, final SECPSignature signature, final Bytes payload, - final List accessList, + final Optional> maybeAccessList, final Address sender, final Optional chainId, final Optional v) { @@ -149,6 +149,14 @@ public Transaction( throw new IllegalStateException( String.format("chainId '%s' and v '%s' cannot both be provided", chainId.get(), v.get())); } + if (Objects.equals(transactionType, TransactionType.ACCESS_LIST)) { + checkState( + maybeAccessList.isPresent(), "Must specify access list for access list transaction"); + } else { + checkState( + maybeAccessList.isEmpty(), + "Must not specify access list for non-access list transaction"); + } this.transactionType = transactionType; this.nonce = nonce; this.gasPrice = gasPrice; @@ -159,7 +167,7 @@ public Transaction( this.value = value; this.signature = signature; this.payload = payload; - this.accessList = accessList; + this.maybeAccessList = maybeAccessList; this.sender = sender; this.chainId = chainId; this.v = v; @@ -189,7 +197,7 @@ public Transaction( value, signature, payload, - emptyList(), + Optional.empty(), sender, chainId, v); @@ -380,8 +388,8 @@ public Optional getData() { return getTo().isPresent() ? Optional.of(payload) : Optional.empty(); } - public List getAccessList() { - return accessList; + public Optional> getAccessList() { + return maybeAccessList; } /** @@ -441,7 +449,7 @@ private Bytes32 getOrComputeSenderRecoveryHash() { to, value, payload, - accessList, + maybeAccessList, chainId); } return hashNoSignature; @@ -564,7 +572,7 @@ private static Bytes32 computeSenderRecoveryHash( final Optional
to, final Wei value, final Bytes payload, - final List accessList, + final Optional> accessList, final Optional chainId) { final Bytes preimage; switch (transactionType) { @@ -578,7 +586,18 @@ private static Bytes32 computeSenderRecoveryHash( break; case ACCESS_LIST: preimage = - accessListPreimage(nonce, gasPrice, gasLimit, to, value, payload, accessList, chainId); + accessListPreimage( + nonce, + gasPrice, + gasLimit, + to, + value, + payload, + accessList.orElseThrow( + () -> + new IllegalStateException( + "Developer error: the transaction should be guaranteed to have an access list here")), + chainId); break; default: throw new IllegalStateException( @@ -705,9 +724,9 @@ public String toString() { sb.append("sig=").append(getSignature()).append(", "); if (chainId.isPresent()) sb.append("chainId=").append(getChainId().get()).append(", "); if (v.isPresent()) sb.append("v=").append(v.get()).append(", "); - sb.append("payload=").append(getPayload()).append(", "); + sb.append("payload=").append(getPayload()); if (transactionType.equals(TransactionType.ACCESS_LIST)) { - sb.append("accessList=").append(accessList); + sb.append(", ").append("accessList=").append(maybeAccessList); } return sb.append("}").toString(); } @@ -741,7 +760,7 @@ public static class Builder { protected Bytes payload; - protected List accessList = emptyList(); + protected Optional> accessList = Optional.empty(); protected Address sender; @@ -805,7 +824,7 @@ public Builder payload(final Bytes payload) { } public Builder accessList(final List accessList) { - this.accessList = accessList; + this.accessList = Optional.ofNullable(accessList); return this; } @@ -820,7 +839,7 @@ public Builder signature(final SECPSignature signature) { } public Builder guessType() { - if (!accessList.isEmpty()) { + if (accessList.isPresent()) { transactionType = TransactionType.ACCESS_LIST; } else if (gasPremium != null || feeCap != null) { transactionType = TransactionType.EIP1559; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java index 5ffd25cea80..4518cc85448 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java @@ -97,7 +97,12 @@ static void encodeAccessList(final Transaction transaction, final RLPOutput rlpO transaction.getTo(), transaction.getValue(), transaction.getPayload(), - transaction.getAccessList(), + transaction + .getAccessList() + .orElseThrow( + () -> + new IllegalStateException( + "Developer error: access list should be guaranteed to be present")), rlpOutput); rlpOutput.writeIntScalar(transaction.getSignature().getRecId()); writeSignature(transaction, rlpOutput); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/AccessListEntryDeserializer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/AccessListEntryDeserializer.java new file mode 100644 index 00000000000..24ce759761d --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/AccessListEntryDeserializer.java @@ -0,0 +1,55 @@ +/* + * 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.core.json; + +import static com.google.common.base.Preconditions.checkState; + +import org.hyperledger.besu.ethereum.core.AccessListEntry; +import org.hyperledger.besu.ethereum.core.Address; + +import java.io.IOException; +import java.util.ArrayList; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.apache.tuweni.bytes.Bytes32; + +public class AccessListEntryDeserializer extends StdDeserializer { + private AccessListEntryDeserializer() { + this(null); + } + + protected AccessListEntryDeserializer(final Class vc) { + super(vc); + } + + @Override + public AccessListEntry deserialize(final JsonParser p, final DeserializationContext ctxt) + throws IOException { + checkState(p.nextFieldName().equals("address")); + final Address address = Address.fromHexString(p.nextTextValue()); + checkState(p.nextFieldName().equals("storageKeys")); + checkState(p.nextToken().equals(JsonToken.START_ARRAY)); + final ArrayList storageKeys = new ArrayList<>(); + while (!p.nextToken().equals(JsonToken.END_ARRAY)) { + storageKeys.add(Bytes32.fromHexString(p.getText())); + } + p.nextToken(); // consume end of object + return new AccessListEntry(address, storageKeys); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/AccessListEntrySerializer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/AccessListEntrySerializer.java new file mode 100644 index 00000000000..7106d521a7f --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/AccessListEntrySerializer.java @@ -0,0 +1,54 @@ +/* + * 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.core.json; + +import org.hyperledger.besu.ethereum.core.AccessListEntry; + +import java.io.IOException; +import java.util.List; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.apache.tuweni.bytes.Bytes32; + +public class AccessListEntrySerializer extends StdSerializer { + + AccessListEntrySerializer() { + this(null); + } + + protected AccessListEntrySerializer(final Class t) { + super(t); + } + + @Override + public void serialize( + final AccessListEntry accessListEntry, + final JsonGenerator gen, + final SerializerProvider provider) + throws IOException { + gen.writeStartObject(); + gen.writeFieldName("address"); + gen.writeString(accessListEntry.getAddress().toHexString()); + gen.writeFieldName("storageKeys"); + final List storageKeys = accessListEntry.getStorageKeys(); + gen.writeArray( + storageKeys.stream().map(Bytes32::toHexString).toArray(String[]::new), + 0, + storageKeys.size()); + gen.writeEndObject(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/deserializer/GasDeserializer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/GasDeserializer.java similarity index 95% rename from ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/deserializer/GasDeserializer.java rename to ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/GasDeserializer.java index f2a9aa51bdc..31e97a8ffa6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/deserializer/GasDeserializer.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/GasDeserializer.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.ethereum.core.deserializer; +package org.hyperledger.besu.ethereum.core.json; import org.hyperledger.besu.ethereum.core.Gas; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/deserializer/HexStringDeserializer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/HexStringDeserializer.java similarity index 95% rename from ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/deserializer/HexStringDeserializer.java rename to ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/HexStringDeserializer.java index 8e6a79d1668..d9578259a9c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/deserializer/HexStringDeserializer.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/json/HexStringDeserializer.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.ethereum.core.deserializer; +package org.hyperledger.besu.ethereum.core.json; import java.io.IOException; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BerlinGasCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BerlinGasCalculator.java index b36ff2c2e8b..f77cb4ca585 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BerlinGasCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BerlinGasCalculator.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.ethereum.mainnet; +import static java.util.Collections.emptyList; import static org.hyperledger.besu.ethereum.core.Address.BLAKE2B_F_COMPRESSION; import org.hyperledger.besu.ethereum.core.AccessListEntry; @@ -75,7 +76,7 @@ public BerlinGasCalculator() { public GasAndAccessedState transactionIntrinsicGasCostAndAccessedState( final Transaction transaction) { // As per https://eips.ethereum.org/EIPS/eip-2930 - final List accessList = transaction.getAccessList(); + final List accessList = transaction.getAccessList().orElse(emptyList()); long accessedStorageCount = 0; final Set
accessedAddresses = new HashSet<>(); diff --git a/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/StateTestSubCommandTest.java b/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/StateTestSubCommandTest.java index a4da13bcc75..fad20759693 100644 --- a/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/StateTestSubCommandTest.java +++ b/ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/StateTestSubCommandTest.java @@ -41,4 +41,12 @@ public void shouldWorkWithValidStateTest() { cmd.parseArgs(StateTestSubCommandTest.class.getResource("valid-state-test.json").getPath()); stateTestSubCommand.run(); } + + @Test + public void shouldWorkWithValidAccessListStateTest() { + final StateTestSubCommand stateTestSubCommand = new StateTestSubCommand(new EvmToolCommand()); + final CommandLine cmd = new CommandLine(stateTestSubCommand); + cmd.parseArgs(StateTestSubCommandTest.class.getResource("access-list.json").getPath()); + stateTestSubCommand.run(); + } } diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/access-list.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/access-list.json new file mode 100644 index 00000000000..466ec10e02d --- /dev/null +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/access-list.json @@ -0,0 +1,90 @@ +{ + "accessList" : { + "_info" : { + "comment" : "Access list in tr example", + "filling-rpc-server" : "evm version 1.10.0-unstable-e09dc4eb-20210211", + "filling-tool-version" : "retesteth-0.1.0-accesslist+commit.37e87c45.Linux.g++", + "lllcversion" : "Version: 0.5.14-develop.2020.6.22+commit.9189ad7a.Linux.g++", + "source" : "src/GeneralStateTestsFiller/stExample/accessListFiller.json", + "sourceHash" : "0b91ae030423fd2d178aea8c362196b24e059c2525c64f5aca273e3e277c22ed", + "labels" : { + "0" : ":label somelabel", + "1" : ":label somelabel2" + } + }, + "env" : { + "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "0x020000", + "currentGasLimit" : "0xff112233445566", + "currentNumber" : "0x01", + "currentTimestamp" : "0x03e8", + "previousHash" : "0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "post" : { + "Berlin" : [ + { + "indexes" : { + "data" : 0, + "gas" : 0, + "value" : 0 + }, + "hash" : "0x6ef2bfc9fbc7f1ad04aefc6ca5d64ec882f81c60a14f5171f181a169a6db1a6b", + "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + { + "indexes" : { + "data" : 1, + "gas" : 0, + "value" : 0 + }, + "hash" : "0x0bdf3644f34b9678b88307ffe487c8215daa8689580eacf2e93ba809843f6898", + "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + }, + "pre" : { + "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : { + "balance" : "0x0de0b6b3a7640000", + "code" : "0x600160010160005500", + "nonce" : "0x00", + "storage" : { + } + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "0x0de0b6b3a7640000", + "code" : "0x", + "nonce" : "0x00", + "storage" : { + } + } + }, + "transaction" : { + "data" : [ + "0x1122", + "0x3344" + ], + "accessLists" : [ + [ + { + "address" : "0x0000000000000000000000000000000000001337", + "storageKeys" : [ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002" + ] + } + ], + null + ], + "gasLimit" : [ + "0x061a80" + ], + "gasPrice" : "0x01", + "nonce" : "0x00", + "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value" : [ + "0x0186a0" + ] + } + } +} \ No newline at end of file diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index ad181cfc23a..e25b44f29a8 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -41,7 +41,7 @@ task ('validateReferenceTestSubmodule') { description = "Checks that the reference tests submodule is not accidentally changed" doLast { def result = new ByteArrayOutputStream() - def expectedHash = '1508126ea04cd61495b60db2f036ac823de274b1' + def expectedHash = '31d663076b6678df18983d6da912d7cad4ad3416' def submodulePath = java.nio.file.Path.of("${rootProject.projectDir}", "ethereum/referencetests/src/test/resources").toAbsolutePath() try { exec { diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java index 5a1523ca5dd..e380f9621fb 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java @@ -128,7 +128,8 @@ public static class PostSection { public PostSection( @JsonProperty("hash") final String hash, @JsonProperty("logs") final String logs, - @JsonProperty("indexes") final Indexes indexes) { + @JsonProperty("indexes") final Indexes indexes, + @JsonProperty("txbytes") final String txbytes) { this.rootHash = Hash.fromHexString(hash); this.logsHash = Hash.fromHexString(logs); this.indexes = indexes; diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestAccessListDeserializer.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestAccessListDeserializer.java new file mode 100644 index 00000000000..e771f1a4993 --- /dev/null +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestAccessListDeserializer.java @@ -0,0 +1,45 @@ +/* + * 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.referencetests; + +import org.hyperledger.besu.ethereum.core.AccessListEntry; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class StateTestAccessListDeserializer extends JsonDeserializer>> { + @Override + public List> deserialize( + final JsonParser p, final DeserializationContext ctxt) throws IOException { + final ObjectMapper objectMapper = new ObjectMapper(); + final List> accessLists = new ArrayList<>(); + while (!p.nextToken().equals(JsonToken.END_ARRAY)) { + accessLists.add( + p.currentToken().equals(JsonToken.VALUE_NULL) + ? null + : Arrays.asList(objectMapper.readValue(p, AccessListEntry[].class))); + } + return accessLists; + } +} diff --git a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java index f4784ab9854..0dc73b808d8 100644 --- a/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java +++ b/ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java @@ -18,19 +18,23 @@ import org.hyperledger.besu.crypto.KeyPair; import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.ethereum.core.AccessListEntry; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Wei; +import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.Function; import javax.annotation.Nullable; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; @@ -64,6 +68,7 @@ public class StateTestVersionedTransaction { private final List gasLimits; private final List values; private final List payloads; + private final Optional>> maybeAccessLists; /** * Constructor for populating a mock transaction with json data. @@ -75,6 +80,7 @@ public class StateTestVersionedTransaction { * @param value Amount of ether transferred in the mock transaction. * @param secretKey Secret Key of the mock transaction. * @param data Call data of the mock transaction. + * @param maybeAccessLists List of access lists of the mock transaction. Can be null. */ @JsonCreator public StateTestVersionedTransaction( @@ -84,7 +90,9 @@ public StateTestVersionedTransaction( @JsonProperty("to") final String to, @JsonProperty("value") final String[] value, @JsonProperty("secretKey") final String secretKey, - @JsonProperty("data") final String[] data) { + @JsonProperty("data") final String[] data, + @JsonDeserialize(using = StateTestAccessListDeserializer.class) @JsonProperty("accessLists") + final List> maybeAccessLists) { this.nonce = Long.decode(nonce); this.gasPrice = Wei.fromHexString(gasPrice); @@ -98,6 +106,7 @@ public StateTestVersionedTransaction( this.gasLimits = parseArray(gasLimit, Gas::fromHexString); this.values = parseArray(value, Wei::fromHexString); this.payloads = parseArray(data, Bytes::fromHexString); + this.maybeAccessLists = Optional.ofNullable(maybeAccessLists); } private static List parseArray(final String[] array, final Function parseFct) { @@ -109,14 +118,17 @@ private static List parseArray(final String[] array, final Function + transactionBuilder.accessList(accessLists.get(indexes.data)).chainId(BigInteger.ONE)); + return transactionBuilder.guessType().signAndBuild(keys); } } diff --git a/ethereum/referencetests/src/test/resources b/ethereum/referencetests/src/test/resources index 1508126ea04..31d663076b6 160000 --- a/ethereum/referencetests/src/test/resources +++ b/ethereum/referencetests/src/test/resources @@ -1 +1 @@ -Subproject commit 1508126ea04cd61495b60db2f036ac823de274b1 +Subproject commit 31d663076b6678df18983d6da912d7cad4ad3416 From 711bbfbd5e2ac175102260a16ba254beadc47daf Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Tue, 2 Mar 2021 12:21:28 +1000 Subject: [PATCH 3/3] eth_call now supports GoQuorum private transactions (#1934) eth_call now supports GoQuorum private transactions Signed-off-by: Sally MacFarlane --- .../jsonrpc/methods/EthJsonRpcMethods.java | 12 +++- .../methods/JsonRpcMethodsFactory.java | 3 +- .../goquorum/GoQuorumBlockValidator.java | 34 +-------- .../goquorum/GoQuorumPrivateStateUtil.java | 71 +++++++++++++++++++ .../transaction/TransactionSimulator.java | 56 ++++++++++++--- ...efaultMutablePrivateWorldStateUpdater.java | 2 +- ...ablePrivateAndPublicWorldStateUpdater.java | 44 ++++++++++++ 7 files changed, 174 insertions(+), 48 deletions(-) create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumPrivateStateUtil.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/GoQuorumMutablePrivateAndPublicWorldStateUpdater.java diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java index 1a2d13be1ee..839fd37eb91 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthJsonRpcMethods.java @@ -63,6 +63,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; @@ -83,6 +84,7 @@ public class EthJsonRpcMethods extends ApiGroupJsonRpcMethods { private final TransactionPool transactionPool; private final MiningCoordinator miningCoordinator; private final Set supportedCapabilities; + private final PrivacyParameters privacyParameters; public EthJsonRpcMethods( final BlockchainQueries blockchainQueries, @@ -91,7 +93,8 @@ public EthJsonRpcMethods( final FilterManager filterManager, final TransactionPool transactionPool, final MiningCoordinator miningCoordinator, - final Set supportedCapabilities) { + final Set supportedCapabilities, + final PrivacyParameters privacyParameters) { this.blockchainQueries = blockchainQueries; this.synchronizer = synchronizer; this.protocolSchedule = protocolSchedule; @@ -99,6 +102,7 @@ public EthJsonRpcMethods( this.transactionPool = transactionPool; this.miningCoordinator = miningCoordinator; this.supportedCapabilities = supportedCapabilities; + this.privacyParameters = privacyParameters; } @Override @@ -121,7 +125,8 @@ protected Map create() { new TransactionSimulator( blockchainQueries.getBlockchain(), blockchainQueries.getWorldStateArchive(), - protocolSchedule)), + protocolSchedule, + privacyParameters)), new EthGetCode(blockchainQueries), new EthGetLogs(blockchainQueries), new EthGetProof(blockchainQueries), @@ -149,7 +154,8 @@ protected Map create() { new TransactionSimulator( blockchainQueries.getBlockchain(), blockchainQueries.getWorldStateArchive(), - protocolSchedule)), + protocolSchedule, + privacyParameters)), new EthMining(miningCoordinator), new EthCoinbase(miningCoordinator), new EthProtocolVersion(supportedCapabilities), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java index d8f42a5fef6..05694a38817 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/JsonRpcMethodsFactory.java @@ -102,7 +102,8 @@ public Map methods( filterManager, transactionPool, miningCoordinator, - supportedCapabilities), + supportedCapabilities, + privacyParameters), new NetJsonRpcMethods( p2pNetwork, networkId, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java index 5f1c363e9a7..0d39807458d 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java @@ -14,14 +14,13 @@ */ package org.hyperledger.besu.ethereum.goquorum; -import static org.apache.logging.log4j.LogManager.getLogger; +import static org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStateUtil.getPrivateWorldState; import org.hyperledger.besu.ethereum.MainnetBlockValidator; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.chain.BadBlockManager; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters; -import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator; import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator; @@ -31,12 +30,8 @@ import java.util.Optional; -import org.apache.logging.log4j.Logger; - public class GoQuorumBlockValidator extends MainnetBlockValidator { - private static final Logger LOG = getLogger(); - private final GoQuorumPrivateStorage goQuorumPrivateStorage; private final WorldStateArchive goQuorumWorldStateArchive; @@ -66,34 +61,9 @@ public GoQuorumBlockValidator( protected Result processBlock( final ProtocolContext context, final MutableWorldState worldState, final Block block) { final MutableWorldState privateWorldState = - getPrivateWorldState(worldState.rootHash(), block.getHash()); + getPrivateWorldState(goQuorumPrivateStorage, goQuorumWorldStateArchive, block.getHeader()); return ((GoQuorumBlockProcessor) blockProcessor) .processBlock(context.getBlockchain(), worldState, privateWorldState, block); } - - private MutableWorldState getPrivateWorldState( - final Hash worldStateRootHash, final Hash publicBlockHash) { - final Hash privateStateRootHash = - goQuorumPrivateStorage - .getPrivateStateRootHash(worldStateRootHash) - .orElse(Hash.EMPTY_TRIE_HASH); - - final Optional maybePrivateWorldState = - goQuorumWorldStateArchive.getMutable(privateStateRootHash, publicBlockHash); - if (maybePrivateWorldState.isEmpty()) { - LOG.debug( - "Private world state not available for public world state root hash {}, public block hash {}", - worldStateRootHash, - publicBlockHash); - - /* - This should never happen because privateStateRootResolver will either return a matching - private world state root hash, or the hash for an empty world state (first private tx ever). - */ - throw new IllegalStateException( - "Private world state not available for public world state root hash " + publicBlockHash); - } - return maybePrivateWorldState.get(); - } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumPrivateStateUtil.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumPrivateStateUtil.java new file mode 100644 index 00000000000..5d7b32a59df --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumPrivateStateUtil.java @@ -0,0 +1,71 @@ +/* + * 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.goquorum; + +import static org.apache.logging.log4j.LogManager.getLogger; + +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; + +import java.util.Optional; + +import org.apache.logging.log4j.Logger; + +public class GoQuorumPrivateStateUtil { + private static final Logger LOG = getLogger(); + + public static MutableWorldState getPrivateWorldState( + final Optional goQuorumPrivacyParameters, + final BlockHeader header) { + final GoQuorumPrivateStorage goQuorumPrivateStorage = + goQuorumPrivacyParameters.orElseThrow().privateStorage(); + final WorldStateArchive goQuorumWorldStateArchive = + goQuorumPrivacyParameters.orElseThrow().worldStateArchive(); + return getPrivateWorldState(goQuorumPrivateStorage, goQuorumWorldStateArchive, header); + } + + public static MutableWorldState getPrivateWorldState( + final GoQuorumPrivateStorage goQuorumPrivateStorage, + final WorldStateArchive goQuorumWorldStateArchive, + final BlockHeader header) { + final Hash worldStateRootHash = header.getStateRoot(); + final Hash publicBlockHash = header.getHash(); + final Hash privateStateRootHash = + goQuorumPrivateStorage + .getPrivateStateRootHash(worldStateRootHash) + .orElse(Hash.EMPTY_TRIE_HASH); + + final Optional maybePrivateWorldState = + goQuorumWorldStateArchive.getMutable(privateStateRootHash, publicBlockHash); + if (maybePrivateWorldState.isEmpty()) { + LOG.debug( + "Private world state not available for public world state root hash {}, public block hash {}", + worldStateRootHash, + publicBlockHash); + + /* + This should never happen because privateStateRootResolver will either return a matching + private world state root hash, or the hash for an empty world state (first private tx ever). + */ + throw new IllegalStateException( + "Private world state not available for public world state root hash " + publicBlockHash); + } + return maybePrivateWorldState.get(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index 04a5631a45c..e3ed75bb9fc 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.transaction; +import static org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStateUtil.getPrivateWorldState; + import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; @@ -23,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.core.WorldUpdater; @@ -33,6 +36,7 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.OperationTracer; +import org.hyperledger.besu.ethereum.worldstate.GoQuorumMutablePrivateAndPublicWorldStateUpdater; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.plugin.data.TransactionType; @@ -50,7 +54,6 @@ * blockchain or to estimate the transaction gas cost. */ public class TransactionSimulator { - private static final Supplier SIGNATURE_ALGORITHM = Suppliers.memoize(SignatureAlgorithmFactory::getInstance); @@ -71,6 +74,7 @@ public class TransactionSimulator { private final Blockchain blockchain; private final WorldStateArchive worldStateArchive; private final ProtocolSchedule protocolSchedule; + private final Optional maybePrivacyParameters; public TransactionSimulator( final Blockchain blockchain, @@ -79,6 +83,18 @@ public TransactionSimulator( this.blockchain = blockchain; this.worldStateArchive = worldStateArchive; this.protocolSchedule = protocolSchedule; + this.maybePrivacyParameters = Optional.empty(); + } + + public TransactionSimulator( + final Blockchain blockchain, + final WorldStateArchive worldStateArchive, + final ProtocolSchedule protocolSchedule, + final PrivacyParameters privacyParameters) { + this.blockchain = blockchain; + this.worldStateArchive = worldStateArchive; + this.protocolSchedule = protocolSchedule; + this.maybePrivacyParameters = Optional.of(privacyParameters); } public Optional process( @@ -125,15 +141,18 @@ public Optional process( if (header == null) { return Optional.empty(); } - final MutableWorldState worldState = + + final MutableWorldState publicWorldState = worldStateArchive.getMutable(header.getStateRoot(), header.getHash()).orElse(null); - if (worldState == null) { + + if (publicWorldState == null) { return Optional.empty(); } + final WorldUpdater updater = getEffectiveWorldStateUpdater(header, publicWorldState); final Address senderAddress = callParams.getFrom() != null ? callParams.getFrom() : DEFAULT_FROM; - final Account sender = worldState.get(senderAddress); + final Account sender = publicWorldState.get(senderAddress); final long nonce = sender != null ? sender.getNonce() : 0L; final long gasLimit = callParams.getGasLimit() >= 0 ? callParams.getGasLimit() : header.getGasLimit(); @@ -141,8 +160,6 @@ public Optional process( final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO; final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY; - final WorldUpdater updater = worldState.updater(); - if (transactionValidationParams.isAllowExceedingBalance()) { updater.getOrCreate(senderAddress).getMutable().setBalance(Wei.of(UInt256.MAX_VALUE)); } @@ -182,17 +199,34 @@ public Optional process( return Optional.of(new TransactionSimulatorResult(transaction, result)); } + // return combined private/public world state updater if GoQuorum mode, otherwise the public state + private WorldUpdater getEffectiveWorldStateUpdater( + final BlockHeader header, final MutableWorldState publicWorldState) { + + if (maybePrivacyParameters.isPresent() + && maybePrivacyParameters.get().getGoQuorumPrivacyParameters().isPresent()) { + + final MutableWorldState privateWorldState = + getPrivateWorldState(maybePrivacyParameters.get().getGoQuorumPrivacyParameters(), header); + return new GoQuorumMutablePrivateAndPublicWorldStateUpdater( + publicWorldState.updater(), privateWorldState.updater()); + } + return publicWorldState.updater(); + } + public Optional doesAddressExistAtHead(final Address address) { - return doesAddressExist(address, blockchain.getChainHeadHeader()); + final BlockHeader header = blockchain.getChainHeadHeader(); + final MutableWorldState worldState = + worldStateArchive.getMutable(header.getStateRoot(), header.getHash()).orElse(null); + + return doesAddressExist(worldState, address, header); } - public Optional doesAddressExist(final Address address, final BlockHeader header) { + public Optional doesAddressExist( + final MutableWorldState worldState, final Address address, final BlockHeader header) { if (header == null) { return Optional.empty(); } - - final MutableWorldState worldState = - worldStateArchive.getMutable(header.getStateRoot(), header.getHash()).orElse(null); if (worldState == null) { return Optional.empty(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java index f3a078576d5..bc84f0b0122 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/DefaultMutablePrivateWorldStateUpdater.java @@ -30,7 +30,7 @@ public class DefaultMutablePrivateWorldStateUpdater implements WorldUpdater { protected final WorldUpdater publicWorldUpdater; - private final WorldUpdater privateWorldUpdater; + protected final WorldUpdater privateWorldUpdater; public DefaultMutablePrivateWorldStateUpdater( final WorldUpdater publicWorldUpdater, final WorldUpdater privateWorldUpdater) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/GoQuorumMutablePrivateAndPublicWorldStateUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/GoQuorumMutablePrivateAndPublicWorldStateUpdater.java new file mode 100644 index 00000000000..022c8406f19 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/worldstate/GoQuorumMutablePrivateAndPublicWorldStateUpdater.java @@ -0,0 +1,44 @@ +/* + * 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.worldstate; + +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.EvmAccount; +import org.hyperledger.besu.ethereum.core.WorldUpdater; + +// This class uses a public WorldUpdater and a private WorldUpdater to provide a +// MutableWorldStateUpdater that can read and write from BOTH the private world state and the public +// world state. +public class GoQuorumMutablePrivateAndPublicWorldStateUpdater + extends GoQuorumMutablePrivateWorldStateUpdater { + + public GoQuorumMutablePrivateAndPublicWorldStateUpdater( + final WorldUpdater publicWorldUpdater, final WorldUpdater privateWorldUpdater) { + super(publicWorldUpdater, privateWorldUpdater); + } + + @Override + public EvmAccount getAccount(final Address address) { + final EvmAccount privateAccount = privateWorldUpdater.getAccount(address); + if (privateAccount != null && !privateAccount.isEmpty()) { + return privateAccount; + } + final EvmAccount publicAccount = publicWorldUpdater.getAccount(address); + if (publicAccount != null && !publicAccount.isEmpty()) { + return publicAccount; + } + return privateAccount; + } +}