Skip to content

Commit

Permalink
fixed acceptance test, some bug fixes if authorized account does not …
Browse files Browse the repository at this point in the history
…yet exist

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
  • Loading branch information
daniellehrner committed Jul 16, 2024
1 parent 123ce12 commit 1dad4d1
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Optional;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
Expand Down Expand Up @@ -57,10 +58,10 @@ public void buildNewBlock() throws IOException {
createEngineCall(createForkChoiceRequest(block.getHash(), blockTimeStamp));

final String payloadId;
try (final Response newPayloadResponse = buildBlockRequest.execute()) {
try (final Response buildBlockResponse = buildBlockRequest.execute()) {
payloadId =
mapper
.readTree(newPayloadResponse.body().string())
.readTree(buildBlockResponse.body().string())
.get("result")
.get("payloadId")
.asText();
Expand All @@ -72,21 +73,32 @@ public void buildNewBlock() throws IOException {

final Call getPayloadRequest = createEngineCall(createGetPayloadRequest(payloadId));

final ObjectNode executionPayload;
final String newBlockHash;
final String parentBeaconBlockRoot;
try (final Response getPayloadResponse = getPayloadRequest.execute()) {
assertThat(getPayloadResponse.code()).isEqualTo(200);

newBlockHash =
mapper
.readTree(getPayloadResponse.body().string())
.get("result")
.get("executionPayload")
.get("blockHash")
.asText();
executionPayload =
(ObjectNode)
mapper
.readTree(getPayloadResponse.body().string())
.get("result")
.get("executionPayload");

newBlockHash = executionPayload.get("blockHash").asText();
parentBeaconBlockRoot = executionPayload.remove("parentBeaconBlockRoot").asText();

assertThat(newBlockHash).isNotEmpty();
}

final Call newPayloadRequest =
createEngineCall(
createNewPayloadRequest(executionPayload.toString(), parentBeaconBlockRoot));
try (final Response newPayloadResponse = newPayloadRequest.execute()) {
assertThat(newPayloadResponse.code()).isEqualTo(200);
}

final Call moveChainAheadRequest = createEngineCall(createForkChoiceRequest(newBlockHash));

try (final Response moveChainAheadResponse = moveChainAheadRequest.execute()) {
Expand Down Expand Up @@ -155,6 +167,22 @@ private String createGetPayloadRequest(final String payloadId) {
+ "}";
}

private String createNewPayloadRequest(
final String executionPayload, final String parentBeaconBlockRoot) {
return "{"
+ " \"jsonrpc\": \"2.0\","
+ " \"method\": \"engine_newPayloadV4\","
+ " \"params\": ["
+ executionPayload
+ ",[],"
+ "\""
+ parentBeaconBlockRoot
+ "\""
+ "],"
+ " \"id\": 67"
+ "}";
}

private static void waitFor(final long durationMilliSeconds) {
try {
Thread.sleep(durationMilliSeconds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,37 @@
*/
package org.hyperledger.besu.tests.acceptance.ethereum;

import static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.SetCodeAuthorization;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Amount;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;

import java.io.IOException;
import java.math.BigInteger;
import java.util.List;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
private static final String GENESIS_FILE = "/dev/dev_prague.json";
private static final SECP256K1 secp256k1 = new SECP256K1();

private final Account authorizer =
accounts.createAccount(
Address.fromHexStringStrict("8b45ea85863ea7b3cff15a6c08e58d0b969a5151"));
Address.fromHexStringStrict("8da48afC965480220a3dB9244771bd3afcB5d895"));
private final Account transactionSponsor =
accounts.createAccount(
Address.fromHexStringStrict("1a3f5c0744c80ba7a9df07f88e456acf9fa327b8"));
Address.fromHexStringStrict("a05b21E5186Ce93d2a226722b85D6e550Ac7D6E3"));

private BesuNode besuNode;
private PragueAcceptanceTestService testService;
Expand All @@ -50,43 +60,46 @@ void setUp() throws IOException {
@Test
public void shouldTransferAllEthOfAuthorizerToSponsor() throws IOException {

// RLP encoded 7702 transaction
//
// The authorizer account has signed the 7702 authorization to uses the contract deployed in the
// genesis block,
// the transactionSponsor account has signed the transaction
//
// 0x04 || rlp([
// "0x4EF3",
// "0x",
// "0x3B9ACA00",
// "0x02540BE400",
// "0x0F4240",
// "0x8b45ea85863ea7b3cff15a6c08e58d0b969a5151",
// "0x",
// "0x085b3b00000000000000000000000000a05b21E5186Ce93d2a226722b85D6e550Ac7D6E3",
// [],
// [[
// "0x4EF3",
// "0x0000000000000000000000000000000000009999",
// ["0x"],
// "0x01",
// "0x2b0c650c3bf6701937f420a7406a3b3d72b272f9f885be4391e6347ba0e6621a",
// "0x162bced47ddc2165f6bf0744e8b893cbff22c52c3f26088b27b4eefa3f707fb5"
// ]],
// "0x",
// "0x7e496cc1739a0f66e0ce52047010573d1f2cc083d396b76400063bcbce978bdc",
// "0x31164145c74b3e03467259cac1d88c2f77c484759c4a4d8875aa7d6c04f5f613"
// ])
final String raw7702Transaction =
"04f8f3824ef380843b9aca008502540be400830f4240948b45ea85863ea7b3cff15a6c08e58d0b969a515180a4085b3b00000000000000000000000000a05b21e5186ce93d2a226722b85d6e550ac7d6e3c0f85ff85d824ef3940000000000000000000000000000000000009999c18001a02b0c650c3bf6701937f420a7406a3b3d72b272f9f885be4391e6347ba0e6621aa0162bced47ddc2165f6bf0744e8b893cbff22c52c3f26088b27b4eefa3f707fb580a07e496cc1739a0f66e0ce52047010573d1f2cc083d396b76400063bcbce978bdca031164145c74b3e03467259cac1d88c2f77c484759c4a4d8875aa7d6c04f5f613";
// 7702 transaction
final SetCodeAuthorization authorization =
SetCodeAuthorization.builder()
.chainId(BigInteger.valueOf(20211))
.address(Address.fromHexStringStrict("0000000000000000000000000000000000009999"))
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(
Bytes.fromHexString(
"11f2e7b6a734ab03fa682450e0d4681d18a944f8b83c99bf7b9b4de6c0f35ea1")
.toUnsignedBigInteger())));

final Transaction tx =
Transaction.builder()
.type(TransactionType.SET_CODE)
.chainId(BigInteger.valueOf(20211))
.nonce(0)
.maxPriorityFeePerGas(Wei.of(1000000000))
.maxFeePerGas(Wei.fromHexString("0x02540BE400"))
.gasLimit(1000000)
.to(Address.fromHexString("8da48afC965480220a3dB9244771bd3afcB5d895"))
.value(Wei.ZERO)
.payload(
Bytes32.leftPad(Bytes.fromHexString("a05b21E5186Ce93d2a226722b85D6e550Ac7D6E3")))
.accessList(List.of())
.setCodeTransactionPayloads(List.of(authorization))
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(
Bytes.fromHexString(
"3a4ff6d22d7502ef2452368165422861c01a0f72f851793b372b87888dc3c453")
.toUnsignedBigInteger())));

besuNode.execute(ethTransactions.sendRawTransaction(raw7702Transaction));
besuNode.execute(ethTransactions.sendRawTransaction(tx.encoded().toHexString()));
testService.buildNewBlock();

cluster.verify(authorizer.balanceEquals(0));

cluster.verify(
transactionSponsor.balanceEquals(Amount.wei(new BigInteger("180000000000000000000000"))));
final BigInteger transactionSponsorBalance =
besuNode.execute(ethTransactions.getBalance(transactionSponsor));
assertThat(transactionSponsorBalance).isGreaterThan(new BigInteger("170000000000000000000000"));
}
}
11 changes: 7 additions & 4 deletions acceptance-tests/tests/src/test/resources/dev/dev_prague.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,20 @@
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase":"0x0000000000000000000000000000000000000000",
"alloc":{
"55363b4d47aeccb8debf0770f37a61e333a448c1": {
"a05b21E5186Ce93d2a226722b85D6e550Ac7D6E3": {
"privateKey": "3a4ff6d22d7502ef2452368165422861c01a0f72f851793b372b87888dc3c453",
"balance": "90000000000000000000000"
},
"8b45ea85863ea7b3cff15a6c08e58d0b969a5151": {
"8da48afC965480220a3dB9244771bd3afcB5d895": {
"comment": "This account has signed a authorization for contract 0x0000000000000000000000000000000000009999 to send a 7702 transaction",
"privateKey": "11f2e7b6a734ab03fa682450e0d4681d18a944f8b83c99bf7b9b4de6c0f35ea1",
"balance": "90000000000000000000000"
},
"0x0000000000000000000000000000000000009999": {
"comment": "Contract has one method which with the following definition: sendAllEther(address). This method sends all ether of the contract to the address provided as a parameter.",
"comment": "Contract sends all its Ether to the address provided as a call data.",
"balance": "0",
"code": "6080604052348015600f57600080fd5b5060da80601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063085b3b0014602d575b600080fd5b603c60383660046076565b603e565b005b6040516001600160a01b038216904780156108fc02916000818181858888f193505050501580156072573d6000803e3d6000fd5b5050565b600060208284031215608757600080fd5b81356001600160a01b0381168114609d57600080fd5b939250505056fea2646970667358221220063646a6bca5805949d9a39b3ee096a9d9155d003fd23217dc8c4c295b328ae864736f6c634300081a0033",
"code": "5F5F5F5F475F355AF100",
"codeDecompiled": "PUSH0 PUSH0 PUSH0 PUSH0 SELFBALANCE PUSH0 CALLDATALOAD GAS CALL STOP",
"storage": {}
},
"0xa4664C40AACeBD82A2Db79f0ea36C06Bc6A19Adb": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
*/
package org.hyperledger.besu.datatypes;

import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;

import java.math.BigInteger;
import java.util.List;
Expand All @@ -25,6 +27,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;

/**
* An access list entry as defined in EIP-7702
Expand All @@ -40,6 +43,8 @@ public record SetCodeAuthorization(
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);

public static final Bytes MAGIC = Bytes.fromHexString("05");

/**
* Create access list entry.
*
Expand All @@ -62,4 +67,64 @@ public static SetCodeAuthorization createAccessListEntry(
return new SetCodeAuthorization(
chainId, address, nonces, SIGNATURE_ALGORITHM.get().createSignature(r, s, v));
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private BigInteger chainId = BigInteger.ZERO;
private Address address;
private List<Long> nonces = List.of();
private SECPSignature signature;

public Builder chainId(final BigInteger chainId) {
this.chainId = chainId;
return this;
}

public Builder address(final Address address) {
this.address = address;
return this;
}

public Builder nonces(final List<Long> nonces) {
this.nonces = nonces;
return this;
}

public Builder signature(final SECPSignature signature) {
this.signature = signature;
return this;
}

public SetCodeAuthorization signAndBuild(final KeyPair keyPair) {
final BytesValueRLPOutput output = new BytesValueRLPOutput();
output.startList();
output.writeBigIntegerScalar(chainId);
output.writeBytes(address);
output.startList();
nonces.forEach(output::writeLongScalar);
output.endList();
output.endList();

signature(
SIGNATURE_ALGORITHM
.get()
.sign(Hash.hash(Bytes.concatenate(MAGIC, output.encoded())), keyPair));
return build();
}

public SetCodeAuthorization build() {
if (address == null) {
throw new IllegalStateException("Address must be set");
}

if (signature == null) {
throw new IllegalStateException("Signature must be set");
}

return new SetCodeAuthorization(chainId, address, nonces, signature);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,32 @@ public static void encodeSetCodeInner(
rlpOutput.endList();
}

public static void encodeSingleSetCode(
public static void encodeSingleSetCodeWithoutSignature(
final SetCodeAuthorization payload, final RLPOutput rlpOutput) {
rlpOutput.startList();
rlpOutput.writeBigIntegerScalar(payload.chainId());
rlpOutput.writeBytes(payload.address().copy());
rlpOutput.startList();
payload.nonces().forEach(rlpOutput::writeLongScalar);
encodeAuthorizationDetails(payload, rlpOutput);
rlpOutput.endList();
}

private static void encodeSingleSetCode(
final SetCodeAuthorization payload, final RLPOutput rlpOutput) {
rlpOutput.startList();
encodeAuthorizationDetails(payload, rlpOutput);
rlpOutput.writeIntScalar(payload.signature().getRecId());
rlpOutput.writeBigIntegerScalar(payload.signature().getR());
rlpOutput.writeBigIntegerScalar(payload.signature().getS());
rlpOutput.endList();
}

private static void encodeAuthorizationDetails(
final SetCodeAuthorization payload, final RLPOutput rlpOutput) {
rlpOutput.writeBigIntegerScalar(payload.chainId());
rlpOutput.writeBytes(payload.address().copy());
rlpOutput.startList();
payload.nonces().forEach(rlpOutput::writeLongScalar);
rlpOutput.endList();
}

public static void encode(final Transaction transaction, final RLPOutput out) {
out.startList();
out.writeBigIntegerScalar(transaction.getChainId().orElseThrow());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
public class SetCodeTransactionProcessor {
private static final Logger LOG = LoggerFactory.getLogger(SetCodeTransactionProcessor.class);

private static final Bytes MAGIC = Bytes.of(0x05);

private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);

Expand All @@ -63,7 +61,7 @@ public Set<Address> addContractToAuthority(
recoverAuthority(payload)
.ifPresent(
authorityAddress -> {
LOG.trace("Adding authority: {}", authorityAddress);
LOG.trace("Set code authority: {}", authorityAddress);

if (!chainId.equals(BigInteger.ZERO)
&& !payload.chainId().equals(BigInteger.ZERO)
Expand Down Expand Up @@ -92,9 +90,9 @@ public Set<Address> addContractToAuthority(

private Optional<Address> recoverAuthority(final SetCodeAuthorization authorization) {
BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
SetCodeTransactionEncoder.encodeSingleSetCode(authorization, rlpOutput);
SetCodeTransactionEncoder.encodeSingleSetCodeWithoutSignature(authorization, rlpOutput);

final Hash hash = Hash.hash(MAGIC.or(rlpOutput.encoded()));
final Hash hash = Hash.hash(Bytes.concatenate(SetCodeAuthorization.MAGIC, rlpOutput.encoded()));

return SIGNATURE_ALGORITHM
.get()
Expand Down
Loading

0 comments on commit 1dad4d1

Please sign in to comment.