Skip to content

[RPC] add SAFE and FINALIZED options for block param #4902

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

### Additions and Improvements
- RPC methods that lookup block by hash will now return an error response if no block found [#4582](https://github.com/hyperledger/besu/pull/4582)
- Added support for `safe` and `finalized` strings for the RPC methods using defaultBlock parameter [#4902](https://github.com/hyperledger/besu/pull/4902)

### Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import java.util.Optional;
import java.util.OptionalLong;
import java.util.function.Function;
import java.util.function.Supplier;

import com.google.common.base.Suppliers;
Expand Down Expand Up @@ -63,6 +64,26 @@ protected Object latestResult(final JsonRpcRequestContext request) {
request, getBlockchainQueries().getBlockchain().getChainHead().getHash());
}

protected Object finalizedResult(final JsonRpcRequestContext request) {
return posRelatedResult(request, BlockchainQueries::finalizedBlockHeader);
}

protected Object safeResult(final JsonRpcRequestContext request) {
return posRelatedResult(request, BlockchainQueries::safeBlockHeader);
}

private Object posRelatedResult(
final JsonRpcRequestContext request,
final Function<BlockchainQueries, Optional<BlockHeader>> blockHeaderSupplier) {

return blockHeaderSupplier
.apply(blockchainQueries.get())
.map(header -> resultByBlockHash(request, header.getBlockHash()))
.orElseGet(
() ->
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.UNKNOWN_BLOCK));
}

protected Object handleParamTypes(final JsonRpcRequestContext requestContext) {
final BlockParameterOrBlockHash blockParameterOrBlockHash =
blockParameterOrBlockHash(requestContext);
Expand All @@ -72,6 +93,10 @@ protected Object handleParamTypes(final JsonRpcRequestContext requestContext) {
result = latestResult(requestContext);
} else if (blockParameterOrBlockHash.isPending()) {
result = pendingResult(requestContext);
} else if (blockParameterOrBlockHash.isSafe()) {
result = safeResult(requestContext);
} else if (blockParameterOrBlockHash.isFinalized()) {
result = finalizedResult(requestContext);
} else if (blockParameterOrBlockHash.isNumeric() || blockParameterOrBlockHash.isEarliest()) {
final OptionalLong blockNumber = blockParameterOrBlockHash.getNumber();
if (blockNumber.isEmpty() || blockNumber.getAsLong() < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
import com.fasterxml.jackson.databind.JsonNode;

/**
* Represents a block parameter that can be a special value ("pending", "earliest", "latest") or a
* number formatted as a hex string or a block hash.
* Represents a block parameter (or block hash) that can be a special value ("pending", "earliest",
* "latest", "finalized", "safe") or a number formatted as a hex string, or a block hash.
*
* <p>When distinguishing between a hash and a number it is presumed that a hash won't have three
* quarters of the leading bytes as zero. This is fine for block hashes but not for precompiled
Expand Down Expand Up @@ -61,11 +61,23 @@ public BlockParameterOrBlockHash(final Object value) throws JsonProcessingExcept
number = OptionalLong.empty();
blockHash = Optional.empty();
requireCanonical = false;
} else if (normalizedValue.length() > 16) {
} else if (Objects.equals(normalizedValue, "safe")) {
type = BlockParameterType.SAFE;
number = OptionalLong.empty();
blockHash = Optional.empty();
requireCanonical = false;
} else if (Objects.equals(normalizedValue, "finalized")) {
type = BlockParameterType.FINALIZED;
number = OptionalLong.empty();
blockHash = Optional.empty();
requireCanonical = false;
} else if (normalizedValue.length() >= 65) { // with or without hex prefix
type = BlockParameterType.HASH;
number = OptionalLong.empty();
blockHash = Optional.of(Hash.fromHexStringLenient(normalizedValue));
requireCanonical = false;
} else if (normalizedValue.length() > 16) {
throw new IllegalArgumentException("hex number > 64 bits");
} else {
type = BlockParameterType.NUMERIC;
number = OptionalLong.of(Long.decode(value.toString()));
Expand Down Expand Up @@ -112,6 +124,14 @@ public boolean isLatest() {
return this.type == BlockParameterType.LATEST;
}

public boolean isSafe() {
return this.type == BlockParameterType.SAFE;
}

public boolean isFinalized() {
return this.type == BlockParameterType.FINALIZED;
}

public boolean isEarliest() {
return this.type == BlockParameterType.EARLIEST;
}
Expand All @@ -128,6 +148,8 @@ private enum BlockParameterType {
EARLIEST,
LATEST,
PENDING,
SAFE,
FINALIZED,
NUMERIC,
HASH
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,13 @@ public class EthCallTest {
@Mock private BlockchainQueries blockchainQueries;
@Mock private TransactionSimulator transactionSimulator;

@Mock private BlockHeader blockHeader;

@Before
public void setUp() {
method = new EthCall(blockchainQueries, transactionSimulator);
blockHeader = mock(BlockHeader.class);
when(blockHeader.getBlockHash()).thenReturn(Hash.ZERO);
}

@Test
Expand Down Expand Up @@ -160,6 +164,32 @@ public void shouldUseCorrectBlockNumberWhenEarliest() {
verify(transactionSimulator).process(any(), any(), any(), any());
}

@Test
public void shouldUseCorrectBlockNumberWhenSafe() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "safe");
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
when(blockchainQueries.safeBlockHeader()).thenReturn(Optional.of(blockHeader));
when(transactionSimulator.process(any(), any(), any(), any())).thenReturn(Optional.empty());
method.response(request);

verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
verify(blockchainQueries).safeBlockHeader();
verify(transactionSimulator).process(any(), any(), any(), any());
}

@Test
public void shouldUseCorrectBlockNumberWhenFinalized() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "finalized");
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(blockHeader));
when(transactionSimulator.process(any(), any(), any(), any())).thenReturn(Optional.empty());
method.response(request);

verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
verify(blockchainQueries).finalizedBlockHeader();
verify(transactionSimulator).process(any(), any(), any(), any());
}

@Test
public void shouldUseCorrectBlockNumberWhenSpecified() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), Quantity.create(13L));
Expand Down Expand Up @@ -226,7 +256,7 @@ public void shouldAutoSelectIsAllowedExceedingBalanceToFalseWhenFeesAreZero() {
private void internalAutoSelectIsAllowedExceedingBalance(
final JsonCallParameter callParameter,
final Optional<Wei> baseFee,
final boolean isAllowedExeedingBalance) {
final boolean isAllowedExceedingBalance) {
final JsonRpcRequestContext request = ethCallRequest(callParameter, "latest");

final BlockHeader blockHeader = mock(BlockHeader.class);
Expand All @@ -241,7 +271,7 @@ private void internalAutoSelectIsAllowedExceedingBalance(
final TransactionValidationParams transactionValidationParams =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(isAllowedExeedingBalance)
.isAllowExceedingBalance(isAllowedExceedingBalance)
.build();

verify(transactionSimulator).process(any(), eq(transactionValidationParams), any(), any());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"request": {
"id": 487,
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"params": [
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"0xD5C9556Ec8Fef8A240EDdFdd01433Dc6EC08CBCa"
]
},
"response": {
"jsonrpc": "2.0",
"id": 487,
"error" : {
"code" : -32602,
"message" : "Invalid params"
}
},
"statusCode": 400
}