Skip to content

Commit 94f409c

Browse files
macfarlaeum602
authored andcommitted
[RPC] add SAFE and FINALIZED options for block param (hyperledger#4902)
* new test file with a block number param that is too long to be a block number * check for block hash parameter length * added unit test for safe & finalized Signed-off-by: Sally MacFarlane <[email protected]>
1 parent 59f6162 commit 94f409c

File tree

5 files changed

+103
-5
lines changed

5 files changed

+103
-5
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
- Improve SLOAD and SSTORE performance by caching empty slots [#4874](https://github.com/hyperledger/besu/pull/4874)
1111
- 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)
12+
- Added support for `safe` and `finalized` strings for the RPC methods using defaultBlock parameter [#4902](https://github.com/hyperledger/besu/pull/4902)
1213

1314
### Bug Fixes
1415

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractBlockParameterOrBlockHashMethod.java

+25
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import java.util.Optional;
2828
import java.util.OptionalLong;
29+
import java.util.function.Function;
2930
import java.util.function.Supplier;
3031

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

67+
protected Object finalizedResult(final JsonRpcRequestContext request) {
68+
return posRelatedResult(request, BlockchainQueries::finalizedBlockHeader);
69+
}
70+
71+
protected Object safeResult(final JsonRpcRequestContext request) {
72+
return posRelatedResult(request, BlockchainQueries::safeBlockHeader);
73+
}
74+
75+
private Object posRelatedResult(
76+
final JsonRpcRequestContext request,
77+
final Function<BlockchainQueries, Optional<BlockHeader>> blockHeaderSupplier) {
78+
79+
return blockHeaderSupplier
80+
.apply(blockchainQueries.get())
81+
.map(header -> resultByBlockHash(request, header.getBlockHash()))
82+
.orElseGet(
83+
() ->
84+
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.UNKNOWN_BLOCK));
85+
}
86+
6687
protected Object handleParamTypes(final JsonRpcRequestContext requestContext) {
6788
final BlockParameterOrBlockHash blockParameterOrBlockHash =
6889
blockParameterOrBlockHash(requestContext);
@@ -72,6 +93,10 @@ protected Object handleParamTypes(final JsonRpcRequestContext requestContext) {
7293
result = latestResult(requestContext);
7394
} else if (blockParameterOrBlockHash.isPending()) {
7495
result = pendingResult(requestContext);
96+
} else if (blockParameterOrBlockHash.isSafe()) {
97+
result = safeResult(requestContext);
98+
} else if (blockParameterOrBlockHash.isFinalized()) {
99+
result = finalizedResult(requestContext);
75100
} else if (blockParameterOrBlockHash.isNumeric() || blockParameterOrBlockHash.isEarliest()) {
76101
final OptionalLong blockNumber = blockParameterOrBlockHash.getNumber();
77102
if (blockNumber.isEmpty() || blockNumber.getAsLong() < 0) {

ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/BlockParameterOrBlockHash.java

+25-3
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import com.fasterxml.jackson.databind.JsonNode;
2828

2929
/**
30-
* Represents a block parameter that can be a special value ("pending", "earliest", "latest") or a
31-
* number formatted as a hex string or a block hash.
30+
* Represents a block parameter (or block hash) that can be a special value ("pending", "earliest",
31+
* "latest", "finalized", "safe") or a number formatted as a hex string, or a block hash.
3232
*
3333
* <p>When distinguishing between a hash and a number it is presumed that a hash won't have three
3434
* quarters of the leading bytes as zero. This is fine for block hashes but not for precompiled
@@ -61,11 +61,23 @@ public BlockParameterOrBlockHash(final Object value) throws JsonProcessingExcept
6161
number = OptionalLong.empty();
6262
blockHash = Optional.empty();
6363
requireCanonical = false;
64-
} else if (normalizedValue.length() > 16) {
64+
} else if (Objects.equals(normalizedValue, "safe")) {
65+
type = BlockParameterType.SAFE;
66+
number = OptionalLong.empty();
67+
blockHash = Optional.empty();
68+
requireCanonical = false;
69+
} else if (Objects.equals(normalizedValue, "finalized")) {
70+
type = BlockParameterType.FINALIZED;
71+
number = OptionalLong.empty();
72+
blockHash = Optional.empty();
73+
requireCanonical = false;
74+
} else if (normalizedValue.length() >= 65) { // with or without hex prefix
6575
type = BlockParameterType.HASH;
6676
number = OptionalLong.empty();
6777
blockHash = Optional.of(Hash.fromHexStringLenient(normalizedValue));
6878
requireCanonical = false;
79+
} else if (normalizedValue.length() > 16) {
80+
throw new IllegalArgumentException("hex number > 64 bits");
6981
} else {
7082
type = BlockParameterType.NUMERIC;
7183
number = OptionalLong.of(Long.decode(value.toString()));
@@ -112,6 +124,14 @@ public boolean isLatest() {
112124
return this.type == BlockParameterType.LATEST;
113125
}
114126

127+
public boolean isSafe() {
128+
return this.type == BlockParameterType.SAFE;
129+
}
130+
131+
public boolean isFinalized() {
132+
return this.type == BlockParameterType.FINALIZED;
133+
}
134+
115135
public boolean isEarliest() {
116136
return this.type == BlockParameterType.EARLIEST;
117137
}
@@ -128,6 +148,8 @@ private enum BlockParameterType {
128148
EARLIEST,
129149
LATEST,
130150
PENDING,
151+
SAFE,
152+
FINALIZED,
131153
NUMERIC,
132154
HASH
133155
}

ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,13 @@ public class EthCallTest {
6464
@Mock private BlockchainQueries blockchainQueries;
6565
@Mock private TransactionSimulator transactionSimulator;
6666

67+
@Mock private BlockHeader blockHeader;
68+
6769
@Before
6870
public void setUp() {
6971
method = new EthCall(blockchainQueries, transactionSimulator);
72+
blockHeader = mock(BlockHeader.class);
73+
when(blockHeader.getBlockHash()).thenReturn(Hash.ZERO);
7074
}
7175

7276
@Test
@@ -160,6 +164,32 @@ public void shouldUseCorrectBlockNumberWhenEarliest() {
160164
verify(transactionSimulator).process(any(), any(), any(), any());
161165
}
162166

167+
@Test
168+
public void shouldUseCorrectBlockNumberWhenSafe() {
169+
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "safe");
170+
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
171+
when(blockchainQueries.safeBlockHeader()).thenReturn(Optional.of(blockHeader));
172+
when(transactionSimulator.process(any(), any(), any(), any())).thenReturn(Optional.empty());
173+
method.response(request);
174+
175+
verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
176+
verify(blockchainQueries).safeBlockHeader();
177+
verify(transactionSimulator).process(any(), any(), any(), any());
178+
}
179+
180+
@Test
181+
public void shouldUseCorrectBlockNumberWhenFinalized() {
182+
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "finalized");
183+
when(blockchainQueries.getBlockHeaderByHash(Hash.ZERO)).thenReturn(Optional.of(blockHeader));
184+
when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(blockHeader));
185+
when(transactionSimulator.process(any(), any(), any(), any())).thenReturn(Optional.empty());
186+
method.response(request);
187+
188+
verify(blockchainQueries).getBlockHeaderByHash(Hash.ZERO);
189+
verify(blockchainQueries).finalizedBlockHeader();
190+
verify(transactionSimulator).process(any(), any(), any(), any());
191+
}
192+
163193
@Test
164194
public void shouldUseCorrectBlockNumberWhenSpecified() {
165195
final JsonRpcRequestContext request = ethCallRequest(callParameter(), Quantity.create(13L));
@@ -226,7 +256,7 @@ public void shouldAutoSelectIsAllowedExceedingBalanceToFalseWhenFeesAreZero() {
226256
private void internalAutoSelectIsAllowedExceedingBalance(
227257
final JsonCallParameter callParameter,
228258
final Optional<Wei> baseFee,
229-
final boolean isAllowedExeedingBalance) {
259+
final boolean isAllowedExceedingBalance) {
230260
final JsonRpcRequestContext request = ethCallRequest(callParameter, "latest");
231261

232262
final BlockHeader blockHeader = mock(BlockHeader.class);
@@ -241,7 +271,7 @@ private void internalAutoSelectIsAllowedExceedingBalance(
241271
final TransactionValidationParams transactionValidationParams =
242272
ImmutableTransactionValidationParams.builder()
243273
.from(TransactionValidationParams.transactionSimulator())
244-
.isAllowExceedingBalance(isAllowedExeedingBalance)
274+
.isAllowExceedingBalance(isAllowedExceedingBalance)
245275
.build();
246276

247277
verify(transactionSimulator).process(any(), eq(transactionValidationParams), any(), any());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"request": {
3+
"id": 487,
4+
"jsonrpc": "2.0",
5+
"method": "eth_getTransactionCount",
6+
"params": [
7+
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
8+
"0xD5C9556Ec8Fef8A240EDdFdd01433Dc6EC08CBCa"
9+
]
10+
},
11+
"response": {
12+
"jsonrpc": "2.0",
13+
"id": 487,
14+
"error" : {
15+
"code" : -32602,
16+
"message" : "Invalid params"
17+
}
18+
},
19+
"statusCode": 400
20+
}

0 commit comments

Comments
 (0)