Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright contributors to Besu.
*
* 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.parameters;

import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.CodeDelegation;

import java.math.BigInteger;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class SetCodeAuthorizationParameter {

private final String chainId;
private final String address;
private final String nonce;
private final String yParity;
private final String r;
private final String s;

@JsonCreator
public SetCodeAuthorizationParameter(
@JsonProperty("chainId") final String chainId,
@JsonProperty("address") final String address,
@JsonProperty("nonce") final String nonce,
@JsonProperty("yParity") final String yParity,
@JsonProperty("r") final String r,
@JsonProperty("s") final String s) {
this.chainId = chainId;
this.address = address;
this.nonce = nonce;
this.yParity = yParity;
this.r = r;
this.s = s;
}

public CodeDelegation toAuthorization() {
BigInteger rValue = new BigInteger(r.substring(2), 16);
BigInteger sValue = new BigInteger(s.substring(2), 16);
byte yParityByte = (byte) Integer.parseInt(yParity.substring(2), 16);

// Convert yParity to v (27 or 28 for legacy, or 0/1 for EIP-155)
BigInteger v = BigInteger.valueOf(yParityByte);

SECPSignature signature = SECPSignature.create(rValue, sValue, yParityByte, v);

return new org.hyperledger.besu.ethereum.core.CodeDelegation(
new BigInteger(chainId.substring(2), 16),
Address.fromHexString(address),
Long.decode(nonce),
signature);
}

// Getters...
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is empty and can be deleted

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright contributors to Besu.
*
* 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.methods;

class BigInteger {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is empty and can be deleted

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright contributors to Besu.
*
* 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.methods;

class BlockchainQueries {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright contributors to Besu.
*
* 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.methods;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.ethereum.api.ApiConfiguration;
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.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class EIP7702EstimateGasTest {

@Mock private BlockchainQueries blockchainQueries;
@Mock private TransactionSimulator transactionSimulator;
@Mock private BlockHeader blockHeader;
@Mock private ApiConfiguration apiConfiguration;

private EthEstimateGas ethEstimateGas;

@BeforeEach
public void setUp() {
when(apiConfiguration.getEstimateGasToleranceRatio()).thenReturn(0.0);
when(blockchainQueries.headBlockHeader()).thenReturn(blockHeader);

ethEstimateGas = new EthEstimateGas(blockchainQueries, transactionSimulator, apiConfiguration);
}

@Test
public void shouldEstimateGasForEIP7702Transaction() {
// Create call parameter as a Map (simulating JSON input)
final Map<String, Object> callParam = new HashMap<>();
callParam.put("from", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
callParam.put("to", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
callParam.put("value", "0x1");
callParam.put("gas", "0x5208");

// Mock the transaction simulator result
final TransactionSimulatorResult mockResult = mock(TransactionSimulatorResult.class);
when(mockResult.isSuccessful()).thenReturn(true);
when(mockResult.getGasEstimate()).thenReturn(50000L);

when(transactionSimulator.process(any(), any(), any(), any()))
.thenReturn(Optional.of(mockResult));

// Create JSON-RPC request
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_estimateGas", new Object[] {callParam}));

// Execute
final JsonRpcResponse response = ethEstimateGas.response(request);

// Verify
assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class);
final JsonRpcSuccessResponse successResponse = (JsonRpcSuccessResponse) response;
assertThat(successResponse.getResult()).isNotNull();

final String gasHex = (String) successResponse.getResult();
final long gasEstimate = Long.decode(gasHex);
assertThat(gasEstimate).isEqualTo(50000L);
}

@Test
public void shouldEstimateGasWithMultipleAuthorizations() {
final Map<String, Object> callParam = new HashMap<>();
callParam.put("from", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
callParam.put("to", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
callParam.put("value", "0x1");

final TransactionSimulatorResult mockResult = mock(TransactionSimulatorResult.class);
when(mockResult.isSuccessful()).thenReturn(true);
when(mockResult.getGasEstimate()).thenReturn(46000L);

when(transactionSimulator.process(any(), any(), any(), any()))
.thenReturn(Optional.of(mockResult));

final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_estimateGas", new Object[] {callParam}));

final JsonRpcResponse response = ethEstimateGas.response(request);

assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class);
final String gasHex = (String) ((JsonRpcSuccessResponse) response).getResult();
final long gasEstimate = Long.decode(gasHex);
assertThat(gasEstimate).isEqualTo(46000L);
}

@Test
public void shouldHandleTransactionWithoutExplicitGas() {
final Map<String, Object> callParam = new HashMap<>();
callParam.put("from", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
callParam.put("to", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
callParam.put("value", "0x1");

final TransactionSimulatorResult mockResult = mock(TransactionSimulatorResult.class);
when(mockResult.isSuccessful()).thenReturn(true);
when(mockResult.getGasEstimate()).thenReturn(33500L);

when(transactionSimulator.process(any(), any(), any(), any()))
.thenReturn(Optional.of(mockResult));

final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_estimateGas", new Object[] {callParam}));

final JsonRpcResponse response = ethEstimateGas.response(request);

assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.tracing.OperationTracer;

import java.util.List;
import java.util.Optional;
import java.util.OptionalLong;

Expand Down Expand Up @@ -515,6 +516,31 @@ public void shouldUseTxGasLimitCapWhenLessThatBlockGasLimit() {
eq(pendingBlockHeader));
}

@Test
public void shouldEstimateGasForEIP7702Transaction() {
// Setup authorization list
final List<SetCodeAuthorization> authorizationList =
List.of(
new SetCodeAuthorization(
java.math.BigInteger.valueOf(1L), // chainId
Address.fromHexString("0x..."), // contract address
0L, // nonce
(byte) 0, // yParity
Bytes.fromHexString("0x..."), // r
Bytes.fromHexString("0x...") // s
));

// Create transaction with authorization list
final JsonRpcRequestContext request = requestWithAuthorizationList(authorizationList);

// Execute estimation
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, "0x...");
final JsonRpcResponse actualResponse = method.response(request);

// Verify
assertThat(actualResponse).usingRecursiveComparison().isEqualTo(expectedResponse);
}

private void failEstimationOnTxMinGas() {
getMockTransactionSimulatorResult(
false,
Expand Down Expand Up @@ -737,4 +763,9 @@ private JsonRpcRequestContext ethEstimateGasRequestWithStateOverrides(
new JsonRpcRequest(
"2.0", "eth_estimateGas", new Object[] {callParameter, blockParam, overrides}));
}

private JsonRpcRequestContext requestWithAuthorizationList(
List<SetCodeAuthorization> authorizationList) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this class do? It sems to only throw exceptions

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright contributors to Besu.
*
* 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.methods;

import org.hyperledger.besu.datatypes.Address;

import java.math.BigInteger;

import org.apache.tuweni.bytes.Bytes;

class SetCodeAuthorization {

SetCodeAuthorization(
BigInteger valueOf,
Address fromHexString,
long l,
byte b,
Bytes fromHexString0,
Bytes fromHexString1) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"request": {
"id": 1,
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [
{
"from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"to": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"value": "0x1",
"authorizationList": [
{
"chainId": "0x1",
"address": "0x1234567890123456789012345678901234567890",
"nonce": "0x0",
"yParity": "0x0",
"r": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"s": "0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321"
}
]
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 1,
"result": "0x82f8"
},
"statusCode": 200,
"comment": "Test with yParity field (EIP-7702 spec compliant)"
}
Loading