Skip to content

Commit 8d5a92c

Browse files
authored
Feature/prove trielog prestate (#124)
Signed-off-by: garyschulte <[email protected]>
1 parent ca4cebd commit 8d5a92c

File tree

9 files changed

+672
-23
lines changed

9 files changed

+672
-23
lines changed

core/src/main/java/net/consensys/shomei/storage/ZkWorldStateArchive.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ public TraceManager getTraceManager() {
192192
return traceManager;
193193
}
194194

195+
public TrieLogLayerConverter getTrieLogLayerConverter() {
196+
return trieLogLayerConverter;
197+
}
198+
195199
@Override
196200
public void close() throws IOException {
197201
// close all storages
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright ConsenSys Software Inc., 2025
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package net.consensys.shomei.trielog;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
18+
import net.consensys.shomei.storage.worldstate.InMemoryWorldStateStorage;
19+
20+
import org.apache.tuweni.bytes.Bytes;
21+
import org.apache.tuweni.units.bigints.UInt256;
22+
import org.hyperledger.besu.datatypes.Address;
23+
import org.hyperledger.besu.datatypes.Hash;
24+
import org.hyperledger.besu.datatypes.Wei;
25+
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
26+
import org.junit.jupiter.api.Test;
27+
28+
public class TrieLogLayerConverterTest {
29+
30+
// fixture generated from besu-shomei-plugin TrieLogFactoryTests.trielogfixture
31+
private static final String TRIELOG_FIXTURE = "0xf8d6a0000000000000000000000000000000000000000000000000000000000000000001f847940000000000000000000000000000000000000000c98086feeddeadbeef8080e6e5a0290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56380018080f8699400000000000000000000000000000000deadbeef80f85080f84c80880de0b6b3a7640000a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708080";
32+
private static final AccountKey MOCK_ACCOUNT = new AccountKey(Address.fromHexString("0xdeadbeef"));
33+
34+
35+
private final TrieLogLayerConverter converter = new TrieLogLayerConverter(new InMemoryWorldStateStorage());
36+
37+
@Test
38+
public void assertTrieLogDecoding() {
39+
var trielog = converter.decodeTrieLog(new BytesValueRLPInput(
40+
Bytes.fromHexString(TRIELOG_FIXTURE), true));
41+
42+
assertThat(trielog).isNotNull();
43+
var mockAccount = trielog.getAccount(MOCK_ACCOUNT);
44+
assertThat(mockAccount).isPresent();
45+
assertThat(mockAccount.get().getNonce()).isEqualTo(UInt256.ZERO);
46+
assertThat(mockAccount.get().getBalance()).isEqualTo(Wei.fromEth(1));
47+
assertThat(mockAccount.get().getCodeHash()).isEqualTo(Hash.EMPTY);
48+
}
49+
}

services/rpc/server/src/main/java/net/consensys/shomei/rpc/server/JsonRpcService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import net.consensys.shomei.fullsync.FullSyncDownloader;
1919
import net.consensys.shomei.metrics.MetricsService;
2020
import net.consensys.shomei.rpc.server.method.LineaGetProof;
21+
import net.consensys.shomei.rpc.server.method.LineaGetTrielogProof;
2122
import net.consensys.shomei.rpc.server.method.RollupDeleteZkEVMStateMerkleProofByRange;
2223
import net.consensys.shomei.rpc.server.method.RollupForkChoiceUpdated;
2324
import net.consensys.shomei.rpc.server.method.RollupGetZkEVMBlockNumber;
@@ -106,6 +107,7 @@ public JsonRpcService(
106107
new AdminChangeLogLevel(),
107108
new SendRawTrieLog(fullSyncDownloader, worldStateArchive.getTrieLogManager()),
108109
new LineaGetProof(worldStateArchive),
110+
new LineaGetTrielogProof(worldStateArchive, worldStateArchive.getTrieLogLayerConverter()),
109111
new RollupGetZkEVMBlockNumber(worldStateArchive),
110112
new RollupDeleteZkEVMStateMerkleProofByRange(worldStateArchive.getTraceManager()),
111113
new RollupForkChoiceUpdated(worldStateArchive, fullSyncDownloader),

services/rpc/server/src/main/java/net/consensys/shomei/rpc/server/ShomeiRpcMethod.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public enum ShomeiRpcMethod {
2020

2121
LINEA_GET_PROOF("linea_getProof"),
2222

23+
LINEA_GET_TRIELOG_PROOF("linea_getTrielogProof"),
24+
2325
ROLLUP_GET_ZKEVM_STATE_MERKLE_PROOF_V0("rollup_getZkEVMStateMerkleProofV0"),
2426
ROLLUP_DELETE_ZKEVM_STATE_MERKLE_PROOF_BY_RANGE("rollup_deleteZkEVMStateMerkleProofByRange"),
2527

services/rpc/server/src/main/java/net/consensys/shomei/rpc/server/error/ShomeiJsonRpcErrorResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public RpcResponseType getType() {
5858
}
5959

6060
@SuppressWarnings("unused")
61-
private record JsonError(
61+
public record JsonError(
6262
@JsonIgnore RpcErrorType jsonRpcErrorCode,
6363
@JsonGetter("message") String message,
6464
@JsonGetter("data") @JsonInclude(JsonInclude.Include.NON_NULL) Object data) {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright ConsenSys Software Inc., 2025
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package net.consensys.shomei.rpc.server.method;
15+
16+
import net.consensys.shomei.storage.ZkWorldStateArchive;
17+
import net.consensys.shomei.worldview.ZkEvmWorldState;
18+
19+
import java.util.Optional;
20+
21+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
22+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
23+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash;
24+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
25+
26+
public abstract class BlockParameterJsonRpcMethod implements JsonRpcMethod {
27+
28+
protected BlockParameterOrBlockHash getBlockParameterOrBlockHash(
29+
final int index, final JsonRpcRequestContext request) {
30+
try {
31+
return request.getRequiredParameter(index, BlockParameterOrBlockHash.class);
32+
} catch (JsonRpcParameter.JsonRpcParameterException e) {
33+
throw new RuntimeException(e);
34+
}
35+
}
36+
37+
protected Optional<ZkEvmWorldState> resolveBlockParameterToWorldState(
38+
final BlockParameterOrBlockHash blockParameterOrBlockHash,
39+
final ZkWorldStateArchive worldStateArchive) {
40+
Optional<ZkEvmWorldState> worldState = Optional.empty();
41+
if (blockParameterOrBlockHash.isNumeric()) {
42+
worldState =
43+
worldStateArchive.getCachedWorldState(blockParameterOrBlockHash.getNumber().getAsLong());
44+
} else if (blockParameterOrBlockHash.getBlockHash()) {
45+
worldState =
46+
worldStateArchive.getCachedWorldState(blockParameterOrBlockHash.getHash().orElseThrow());
47+
} else if (blockParameterOrBlockHash.isLatest()) {
48+
worldState = worldStateArchive.getCachedWorldState(worldStateArchive.getCurrentBlockHash());
49+
}
50+
return worldState;
51+
}
52+
}

services/rpc/server/src/main/java/net/consensys/shomei/rpc/server/method/LineaGetProof.java

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,13 @@
2929
import org.apache.tuweni.units.bigints.UInt256;
3030
import org.hyperledger.besu.datatypes.Address;
3131
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
32-
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
3332
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash;
3433
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
3534
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
3635
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
3736
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
3837

39-
public class LineaGetProof implements JsonRpcMethod {
38+
public class LineaGetProof extends BlockParameterJsonRpcMethod {
4039

4140
final ZkWorldStateArchive worldStateArchive;
4241

@@ -55,18 +54,11 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
5554
final AccountKey accountAddress = getAccountAddress(requestContext);
5655
final List<StorageSlotKey> slotKeys = getSlotKeys(requestContext);
5756
final BlockParameterOrBlockHash blockParameterOrBlockHash =
58-
getBlockParameterOrBlockHash(requestContext);
57+
getBlockParameterOrBlockHash(2, requestContext);
58+
59+
Optional<ZkEvmWorldState> worldState = resolveBlockParameterToWorldState(
60+
blockParameterOrBlockHash, worldStateArchive);
5961

60-
Optional<ZkEvmWorldState> worldState = Optional.empty();
61-
if (blockParameterOrBlockHash.isNumeric()) {
62-
worldState =
63-
worldStateArchive.getCachedWorldState(blockParameterOrBlockHash.getNumber().getAsLong());
64-
} else if (blockParameterOrBlockHash.getBlockHash()) {
65-
worldState =
66-
worldStateArchive.getCachedWorldState(blockParameterOrBlockHash.getHash().orElseThrow());
67-
} else if (blockParameterOrBlockHash.isLatest()) {
68-
worldState = worldStateArchive.getCachedWorldState(worldStateArchive.getCurrentBlockHash());
69-
}
7062
if (worldState.isPresent()) {
7163
final WorldStateProofProvider worldStateProofProvider =
7264
new WorldStateProofProvider(worldState.get());
@@ -99,13 +91,4 @@ private List<StorageSlotKey> getSlotKeys(final JsonRpcRequestContext request) {
9991
throw new RuntimeException(e);
10092
}
10193
}
102-
103-
private BlockParameterOrBlockHash getBlockParameterOrBlockHash(
104-
final JsonRpcRequestContext request) {
105-
try {
106-
return request.getRequiredParameter(2, BlockParameterOrBlockHash.class);
107-
} catch (JsonRpcParameter.JsonRpcParameterException e) {
108-
throw new RuntimeException(e);
109-
}
110-
}
11194
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright ConsenSys Software Inc., 2023
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package net.consensys.shomei.rpc.server.method;
15+
16+
import net.consensys.shomei.proof.MerkleAccountProof;
17+
import net.consensys.shomei.proof.WorldStateProofProvider;
18+
import net.consensys.shomei.rpc.server.ShomeiRpcMethod;
19+
import net.consensys.shomei.rpc.server.error.ShomeiJsonRpcErrorResponse;
20+
import net.consensys.shomei.storage.ZkWorldStateArchive;
21+
import net.consensys.shomei.trielog.AccountKey;
22+
import net.consensys.shomei.trielog.StorageSlotKey;
23+
import net.consensys.shomei.trielog.TrieLogLayer;
24+
import net.consensys.shomei.trielog.TrieLogLayerConverter;
25+
import net.consensys.shomei.worldview.ZkEvmWorldState;
26+
27+
import java.util.ArrayList;
28+
import java.util.HashMap;
29+
import java.util.List;
30+
import java.util.Map;
31+
import java.util.Optional;
32+
import java.util.stream.Collectors;
33+
34+
import org.apache.tuweni.bytes.Bytes;
35+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
36+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter;
37+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
38+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
39+
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType;
40+
import org.hyperledger.besu.ethereum.rlp.RLP;
41+
42+
public class LineaGetTrielogProof extends BlockParameterJsonRpcMethod {
43+
44+
private final ZkWorldStateArchive worldStateArchive;
45+
private final TrieLogLayerConverter trieLogLayerConverter;
46+
47+
public LineaGetTrielogProof(
48+
final ZkWorldStateArchive worldStateArchive,
49+
final TrieLogLayerConverter trieLogLayerConverter) {
50+
this.worldStateArchive = worldStateArchive;
51+
this.trieLogLayerConverter = trieLogLayerConverter;
52+
}
53+
54+
@Override
55+
public String getName() {
56+
return ShomeiRpcMethod.LINEA_GET_TRIELOG_PROOF.getMethodName();
57+
}
58+
59+
@Override
60+
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
61+
62+
63+
try {
64+
final String serializedTrieLogLayer = getSerializedTrieLogLayer(requestContext);
65+
final var blockParameter = getBlockParameterOrBlockHash(1, requestContext);
66+
67+
final Bytes trieLogBytes = Bytes.fromHexString(serializedTrieLogLayer);
68+
final TrieLogLayer trieLogLayer = trieLogLayerConverter.decodeTrieLog(RLP.input(trieLogBytes));
69+
70+
Optional<ZkEvmWorldState> worldState = resolveBlockParameterToWorldState(
71+
blockParameter, worldStateArchive);
72+
73+
74+
if (worldState.isPresent()) {
75+
final WorldStateProofProvider worldStateProofProvider =
76+
new WorldStateProofProvider(worldState.get());
77+
78+
final Map<AccountKey, List<StorageSlotKey>> accountsWithStorageKeys =
79+
collectAccountsAndStorageKeys(trieLogLayer);
80+
81+
final List<MerkleAccountProof> accountProofs = new ArrayList<>();
82+
83+
for (final Map.Entry<AccountKey, List<StorageSlotKey>> entry : accountsWithStorageKeys.entrySet()) {
84+
final MerkleAccountProof accountProof =
85+
worldStateProofProvider.getAccountProof(entry.getKey(), entry.getValue());
86+
accountProofs.add(accountProof);
87+
}
88+
89+
return new JsonRpcSuccessResponse(
90+
requestContext.getRequest().getId(),
91+
accountProofs);
92+
} else {
93+
return new ShomeiJsonRpcErrorResponse(
94+
requestContext.getRequest().getId(),
95+
RpcErrorType.INVALID_REQUEST,
96+
"BLOCK_MISSING_IN_CHAIN - parent block is missing");
97+
}
98+
} catch (Exception e) {
99+
return new ShomeiJsonRpcErrorResponse(
100+
requestContext.getRequest().getId(),
101+
RpcErrorType.INVALID_REQUEST,
102+
"INVALID_TRIELOG_SERIALIZATION - " + e.getMessage());
103+
}
104+
}
105+
106+
private Map<AccountKey, List<StorageSlotKey>> collectAccountsAndStorageKeys(
107+
final TrieLogLayer trieLogLayer) {
108+
109+
final Map<AccountKey, List<StorageSlotKey>> result = new HashMap<>();
110+
111+
trieLogLayer.streamAccountChanges().forEach(accountEntry -> {
112+
final AccountKey accountKey = accountEntry.getKey();
113+
result.putIfAbsent(accountKey, new ArrayList<>());
114+
});
115+
116+
trieLogLayer.streamStorageChanges().forEach(storageEntry -> {
117+
final AccountKey accountKey = storageEntry.getKey();
118+
final List<StorageSlotKey> storageKeys = storageEntry.getValue().keySet()
119+
.stream().collect(Collectors.toList());
120+
121+
result.merge(accountKey, storageKeys, (existing, newKeys) -> {
122+
existing.addAll(newKeys);
123+
return existing;
124+
});
125+
});
126+
127+
return result;
128+
}
129+
130+
private String getSerializedTrieLogLayer(final JsonRpcRequestContext request) {
131+
try {
132+
return request.getRequiredParameter(0, String.class);
133+
} catch (JsonRpcParameter.JsonRpcParameterException e) {
134+
throw new RuntimeException(e);
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)