Fix Access List Encoding in JSON-RPC (#1936)

Signed-off-by: Ratan Rai Sur <ratan.r.sur@gmail.com>
pull/1940/head
Ratan (Rai) Sur 4 years ago committed by GitHub
parent 490ca8c863
commit 0187bed075
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 8
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java
  3. 8
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java
  4. 58
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResultTest.java
  5. 35
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AccessList.java
  6. 73
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/AccessListEntry.java
  7. 18
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java
  8. 27
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java
  9. 9
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java
  10. 11
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BerlinGasCalculator.java
  11. 10
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java
  12. 3
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java

@ -19,6 +19,9 @@
## 21.1.0
* Added activation blocks for Berlin Network Upgrade [\#1929](https://github.com/hyperledger/besu/pull/1929)
### Bug Fixes
* Fixed representation of access list for access list transactions in JSON-RPC results.
## 21.1.0-RC2
### Additions and Improvements
* Support for the Berlin Network Upgrade, although the block number must be set manually with `--override-genesis-config=berlinBlock=<blocknumber>`. This is because the block numbers haven't been determined yet. The next release will include the number in the genesis file so it will support Berlin with no intervention. [\#1898](https://github.com/hyperledger/besu/pull/1898)

@ -15,11 +15,13 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.AccessList;
import org.hyperledger.besu.ethereum.core.AccessListEntry;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@ -49,7 +51,7 @@ import org.apache.tuweni.bytes.Bytes;
public class TransactionCompleteResult implements TransactionResult {
@JsonInclude(JsonInclude.Include.NON_NULL)
private final AccessList accessList;
private final List<AccessListEntry> accessList;
private final String blockHash;
private final String blockNumber;
@ -102,7 +104,7 @@ public class TransactionCompleteResult implements TransactionResult {
}
@JsonGetter(value = "accessList")
public AccessList getAccessList() {
public List<AccessListEntry> getAccessList() {
return accessList;
}

@ -14,12 +14,14 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
import org.hyperledger.besu.ethereum.core.AccessList;
import org.hyperledger.besu.ethereum.core.AccessListEntry;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@ -43,7 +45,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
public class TransactionPendingResult implements TransactionResult {
@JsonInclude(JsonInclude.Include.NON_NULL)
private final AccessList accessList;
private final List<AccessListEntry> accessList;
private final String chainId;
private final String from;
@ -91,7 +93,7 @@ public class TransactionPendingResult implements TransactionResult {
}
@JsonGetter(value = "accessList")
public AccessList getAccessList() {
public List<AccessListEntry> getAccessList() {
return accessList;
}

@ -0,0 +1,58 @@
/*
* 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.api.jsonrpc.internal.results;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.plugin.data.TransactionType;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
public class TransactionCompleteResultTest {
@Test
public void accessListTransactionFields() throws JsonProcessingException {
final BlockDataGenerator gen = new BlockDataGenerator();
final Transaction transaction = gen.transaction(TransactionType.ACCESS_LIST);
final TransactionCompleteResult transactionCompleteResult =
new TransactionCompleteResult(
new TransactionWithMetadata(
transaction,
136,
Hash.fromHexString(
"0xfc84c3946cb419cbd8c2c68d5e79a3b2a03a8faff4d9e2be493f5a07eb5da95e"),
0));
final ObjectMapper objectMapper = new ObjectMapper();
final String jsonString =
objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(transactionCompleteResult);
assertThat(jsonString)
.startsWith(
"{\n"
+ " \"accessList\" : [ {\n"
+ " \"address\" : \"0x47902028e61cfdc243d9d16008aabc9fb77cc723\",\n"
+ " \"storageKeys\" : [ ]\n"
+ " }, {\n"
+ " \"address\" : \"0xa56017e14f1ce8b1698341734a6823ce02043e01\",\n"
+ " \"storageKeys\" : [ \"0x6b544901214a2ddab82fec85c0b9fe0549c475be5b887bb4b8995b24fb5c6846\", \"0xf88b527b4f9d4c1391f1678b23ba4f9c9cd7bc93eb5776f4f036753448642946\" ]\n"
+ " } ],");
}
}

@ -1,35 +0,0 @@
/*
*
* * 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;
import static java.util.Collections.emptyList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.tuweni.bytes.Bytes32;
public class AccessList extends ArrayList<Map.Entry<Address, List<Bytes32>>> {
public AccessList(final List<Map.Entry<Address, List<Bytes32>>> accessList) {
super(accessList);
}
public static final AccessList EMPTY = new AccessList(emptyList());
}

@ -0,0 +1,73 @@
/*
* 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;
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.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.apache.tuweni.bytes.Bytes32;
@JsonSerialize(using = AccessListEntry.Serializer.class)
public class AccessListEntry {
private final Address address;
private final List<Bytes32> storageKeys;
public AccessListEntry(final Address address, final List<Bytes32> storageKeys) {
this.address = address;
this.storageKeys = storageKeys;
}
public Address getAddress() {
return address;
}
public List<Bytes32> getStorageKeys() {
return storageKeys;
}
public static class Serializer extends StdSerializer<AccessListEntry> {
Serializer() {
this(null);
}
protected Serializer(final Class<AccessListEntry> 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<Bytes32> storageKeys = accessListEntry.getStorageKeys();
gen.writeArray(
storageKeys.stream().map(Bytes32::toHexString).toArray(String[]::new),
0,
storageKeys.size());
gen.writeEndObject();
}
}
}

@ -15,6 +15,7 @@
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.SECP256K1;
@ -28,6 +29,7 @@ import org.hyperledger.besu.plugin.data.Quantity;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@ -72,7 +74,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
private final Bytes payload;
private final AccessList accessList;
private final List<AccessListEntry> accessList;
private final Optional<BigInteger> chainId;
@ -134,7 +136,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
final Wei value,
final SECP256K1.Signature signature,
final Bytes payload,
final AccessList accessList,
final List<AccessListEntry> accessList,
final Address sender,
final Optional<BigInteger> chainId,
final Optional<BigInteger> v) {
@ -182,7 +184,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
value,
signature,
payload,
AccessList.EMPTY,
emptyList(),
sender,
chainId,
v);
@ -373,7 +375,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
return getTo().isPresent() ? Optional.of(payload) : Optional.empty();
}
public AccessList getAccessList() {
public List<AccessListEntry> getAccessList() {
return accessList;
}
@ -555,7 +557,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
final Optional<Address> to,
final Wei value,
final Bytes payload,
final AccessList accessList,
final List<AccessListEntry> accessList,
final Optional<BigInteger> chainId) {
final Bytes preimage;
switch (transactionType) {
@ -641,7 +643,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
final Optional<Address> to,
final Wei value,
final Bytes payload,
final AccessList accessList,
final List<AccessListEntry> accessList,
final Optional<BigInteger> chainId) {
final Bytes encode =
RLP.encode(
@ -732,7 +734,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
protected Bytes payload;
protected AccessList accessList = AccessList.EMPTY;
protected List<AccessListEntry> accessList = emptyList();
protected Address sender;
@ -795,7 +797,7 @@ public class Transaction implements org.hyperledger.besu.plugin.data.Transaction
return this;
}
public Builder accessList(final AccessList accessList) {
public Builder accessList(final List<AccessListEntry> accessList) {
this.accessList = accessList;
return this;
}

@ -25,7 +25,7 @@ import static org.hyperledger.besu.ethereum.core.Transaction.TWO;
import org.hyperledger.besu.config.GoQuorumOptions;
import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.ethereum.core.AccessList;
import org.hyperledger.besu.ethereum.core.AccessListEntry;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei;
@ -34,14 +34,10 @@ import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.math.BigInteger;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
public class TransactionDecoder {
@ -138,17 +134,16 @@ public class TransactionDecoder {
.value(Wei.of(rlpInput.readUInt256Scalar()))
.payload(rlpInput.readBytes())
.accessList(
new AccessList(
rlpInput.readList(
accessListEntryRLPInput -> {
accessListEntryRLPInput.enterList();
final Map.Entry<Address, List<Bytes32>> accessListEntry =
new AbstractMap.SimpleEntry<>(
Address.wrap(accessListEntryRLPInput.readBytes()),
accessListEntryRLPInput.readList(RLPInput::readBytes32));
accessListEntryRLPInput.leaveList();
return accessListEntry;
})));
rlpInput.readList(
accessListEntryRLPInput -> {
accessListEntryRLPInput.enterList();
final AccessListEntry accessListEntry =
new AccessListEntry(
Address.wrap(accessListEntryRLPInput.readBytes()),
accessListEntryRLPInput.readList(RLPInput::readBytes32));
accessListEntryRLPInput.leaveList();
return accessListEntry;
}));
final byte recId = (byte) rlpInput.readIntScalar();
final Transaction transaction =
preSignatureTransactionBuilder

@ -16,7 +16,7 @@ package org.hyperledger.besu.ethereum.core.encoding;
import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.ethereum.core.AccessList;
import org.hyperledger.besu.ethereum.core.AccessListEntry;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.Wei;
@ -26,6 +26,7 @@ import org.hyperledger.besu.plugin.data.Quantity;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
@ -111,7 +112,7 @@ public class TransactionEncoder {
final Optional<Address> to,
final Wei value,
final Bytes payload,
final AccessList accessList,
final List<AccessListEntry> accessList,
final RLPOutput rlpOutput) {
rlpOutput.writeLongScalar(
chainId
@ -146,9 +147,9 @@ public class TransactionEncoder {
accessList,
(accessListEntry, accessListEntryRLPOutput) -> {
accessListEntryRLPOutput.startList();
rlpOutput.writeBytes(accessListEntry.getKey());
rlpOutput.writeBytes(accessListEntry.getAddress());
rlpOutput.writeList(
accessListEntry.getValue(),
accessListEntry.getStorageKeys(),
(storageKeyBytes, storageKeyBytesRLPOutput) ->
storageKeyBytesRLPOutput.writeBytes(storageKeyBytes));
accessListEntryRLPOutput.endList();

@ -16,7 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet;
import static org.hyperledger.besu.ethereum.core.Address.BLAKE2B_F_COMPRESSION;
import org.hyperledger.besu.ethereum.core.AccessList;
import org.hyperledger.besu.ethereum.core.AccessListEntry;
import org.hyperledger.besu.ethereum.core.Account;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Gas;
@ -29,7 +29,6 @@ import org.hyperledger.besu.ethereum.vm.MessageFrame;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.HashMultimap;
@ -76,17 +75,17 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
public GasAndAccessedState transactionIntrinsicGasCostAndAccessedState(
final Transaction transaction) {
// As per https://eips.ethereum.org/EIPS/eip-2930
final AccessList accessList = transaction.getAccessList();
final List<AccessListEntry> accessList = transaction.getAccessList();
long accessedStorageCount = 0;
final Set<Address> accessedAddresses = new HashSet<>();
final Multimap<Address, Bytes32> accessedStorage = HashMultimap.create();
for (final Map.Entry<Address, List<Bytes32>> accessListEntry : accessList) {
final Address address = accessListEntry.getKey();
for (final AccessListEntry accessListEntry : accessList) {
final Address address = accessListEntry.getAddress();
accessedAddresses.add(address);
for (final Bytes32 storageKeyBytes : accessListEntry.getValue()) {
for (final Bytes32 storageKeyBytes : accessListEntry.getStorageKeys()) {
accessedStorage.put(address, storageKeyBytes);
++accessedStorageCount;
}

@ -33,14 +33,12 @@ import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Random;
@ -377,17 +375,17 @@ public class BlockDataGenerator {
.signAndBuild(generateKeyPair());
}
private AccessList accessList() {
private List<AccessListEntry> accessList() {
final List<Address> accessedAddresses =
Stream.generate(this::address).limit(1 + random.nextInt(3)).collect(toUnmodifiableList());
final List<Map.Entry<Address, List<Bytes32>>> accessedStorage = new ArrayList<>();
final List<AccessListEntry> accessedStorage = new ArrayList<>();
for (int i = 0; i < accessedAddresses.size(); ++i) {
accessedStorage.add(
new AbstractMap.SimpleEntry<>(
new AccessListEntry(
accessedAddresses.get(i),
Stream.generate(this::bytes32).limit(2L * i).collect(toUnmodifiableList())));
}
return new AccessList(accessedStorage);
return accessedStorage;
}
private Transaction eip1559Transaction(final Bytes payload, final Address to) {

@ -21,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
@ -36,7 +35,7 @@ public class TransactionBuilderTest {
final Transaction.Builder eip1559Builder = Transaction.builder().feeCap(Wei.of(5));
final Transaction.Builder accessListBuilder =
Transaction.builder()
.accessList(new AccessList(List.of(Map.entry(gen.address(), List.of(gen.bytes32())))));
.accessList(List.of(new AccessListEntry(gen.address(), List.of(gen.bytes32()))));
final Set<TransactionType> guessedTypes =
Stream.of(frontierBuilder, eip1559Builder, accessListBuilder)

Loading…
Cancel
Save