|
14 | 14 | */ |
15 | 15 | package net.consensys.shomei.blocktracing; |
16 | 16 |
|
| 17 | +import static org.junit.jupiter.api.Assertions.assertEquals; |
| 18 | +import static org.junit.jupiter.api.Assertions.assertNotNull; |
| 19 | +import static org.junit.jupiter.api.Assertions.assertTrue; |
17 | 20 | import static org.mockito.ArgumentMatchers.any; |
18 | 21 | import static org.mockito.Mockito.doAnswer; |
19 | 22 | import static org.mockito.Mockito.mock; |
|
22 | 25 | import static org.mockito.Mockito.times; |
23 | 26 | import static org.mockito.Mockito.verify; |
24 | 27 |
|
| 28 | +import net.consensys.linea.zktracer.ZkTracer; |
25 | 29 | import net.consensys.shomei.cli.ShomeiCliOptions; |
26 | 30 | import net.consensys.shomei.context.TestShomeiContext; |
27 | 31 | import net.consensys.shomei.trielog.TrieLogValue; |
28 | 32 | import net.consensys.shomei.trielog.ZkAccountValue; |
| 33 | +import net.consensys.shomei.trielog.ZkTrieLogFactory; |
| 34 | +import net.consensys.shomei.trielog.ZkTrieLogService; |
29 | 35 |
|
30 | 36 | import java.math.BigInteger; |
31 | 37 | import java.util.Collections; |
32 | 38 | import java.util.Map; |
33 | 39 | import java.util.Optional; |
34 | 40 | import java.util.Set; |
| 41 | +import java.util.concurrent.atomic.AtomicReference; |
35 | 42 |
|
| 43 | +import org.apache.tuweni.bytes.Bytes; |
36 | 44 | import org.apache.tuweni.bytes.Bytes32; |
37 | 45 | import org.apache.tuweni.units.bigints.UInt256; |
| 46 | +import org.bouncycastle.math.ec.custom.sec.SecP256K1FieldElement; |
| 47 | +import org.hyperledger.besu.crypto.SECPSignature; |
38 | 48 | import org.hyperledger.besu.datatypes.AccountValue; |
39 | 49 | import org.hyperledger.besu.datatypes.Address; |
40 | 50 | import org.hyperledger.besu.datatypes.Hash; |
41 | 51 | import org.hyperledger.besu.datatypes.StorageSlotKey; |
| 52 | +import org.hyperledger.besu.datatypes.TransactionType; |
42 | 53 | import org.hyperledger.besu.datatypes.Wei; |
| 54 | +import org.hyperledger.besu.ethereum.BlockProcessingResult; |
| 55 | +import org.hyperledger.besu.ethereum.chain.Blockchain; |
| 56 | +import org.hyperledger.besu.ethereum.core.Block; |
| 57 | +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; |
| 58 | +import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; |
| 59 | +import org.hyperledger.besu.ethereum.core.MutableWorldState; |
| 60 | +import org.hyperledger.besu.ethereum.core.Transaction; |
| 61 | +import org.hyperledger.besu.ethereum.core.TransactionReceipt; |
| 62 | +import org.hyperledger.besu.ethereum.mainnet.BlockProcessor; |
| 63 | +import org.hyperledger.besu.plugin.ServiceManager; |
43 | 64 | import org.hyperledger.besu.plugin.data.BlockHeader; |
| 65 | +import org.hyperledger.besu.plugin.services.BlockImportTracerProvider; |
| 66 | +import org.hyperledger.besu.plugin.services.TrieLogService; |
| 67 | +import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; |
| 68 | +import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer; |
44 | 69 | import org.hyperledger.besu.plugin.services.trielogs.TrieLog; |
45 | 70 | import org.hyperledger.besu.plugin.services.trielogs.TrieLogAccumulator; |
| 71 | +import org.hyperledger.besu.testutil.BlockTestUtil; |
46 | 72 | import org.junit.jupiter.api.BeforeEach; |
47 | 73 | import org.junit.jupiter.api.Test; |
48 | 74 | import org.junit.jupiter.api.extension.ExtendWith; |
@@ -215,4 +241,132 @@ void testAccumulatorMissingSlot() { |
215 | 241 | // ✅ Verify alert() was never called |
216 | 242 | verify(comparator, times(1)).alert(any()); |
217 | 243 | } |
| 244 | + |
| 245 | + @Test |
| 246 | + public void assertTrieLogContainsHubStateOnNewContractSload() { |
| 247 | + testContext.getCliOptions().zkTraceComparisonMask = 15; |
| 248 | + var mockPluginServiceManager = mock(ServiceManager.class); |
| 249 | + |
| 250 | + // setup mock plugin service manager zktrielogfactory: |
| 251 | + var zkTrieLogService = mock(ZkTrieLogService.class); |
| 252 | + var zkTrieLogFactoryImpl = spy(new ZkTrieLogFactory(testContext)); |
| 253 | + doAnswer(__ -> Optional.of(zkTrieLogService)) |
| 254 | + .when(mockPluginServiceManager) |
| 255 | + .getService(TrieLogService.class); |
| 256 | + doAnswer(__ -> Optional.of(zkTrieLogFactoryImpl)).when(zkTrieLogService).getTrieLogFactory(); |
| 257 | + AtomicReference<TrieLog> capturedTrieLog = new AtomicReference<>(); |
| 258 | + doAnswer( |
| 259 | + invocation -> { |
| 260 | + var trielog = (TrieLog) invocation.callRealMethod(); |
| 261 | + capturedTrieLog.set(trielog); |
| 262 | + return trielog; |
| 263 | + }) |
| 264 | + .when(zkTrieLogFactoryImpl) |
| 265 | + .create(any(TrieLogAccumulator.class), any(BlockHeader.class)); |
| 266 | + |
| 267 | + var zkTracerProviderSpy = |
| 268 | + spy( |
| 269 | + new ZkBlockImportTracerProvider( |
| 270 | + testContext, () -> Optional.of(BigInteger.valueOf(59144L)))); |
| 271 | + testContext.setBlockImportTraceProvider(zkTracerProviderSpy); |
| 272 | + |
| 273 | + // Set up test world state and blockchain |
| 274 | + final BlockchainSetupUtil setupUtil = |
| 275 | + BlockchainSetupUtil.createForEthashChain( |
| 276 | + new BlockTestUtil.ChainResources( |
| 277 | + this.getClass().getClassLoader().getResource("zktestGenesis.json"), |
| 278 | + BlockTestUtil.class.getClassLoader().getResource("chain.blocks")), |
| 279 | + DataStorageFormat.BONSAI, |
| 280 | + mockPluginServiceManager); |
| 281 | + final Blockchain blockchain = setupUtil.getBlockchain(); |
| 282 | + final MutableWorldState worldState = setupUtil.getWorldArchive().getWorldState(); |
| 283 | + |
| 284 | + var spyProtocolContext = spy(setupUtil.getProtocolContext()); |
| 285 | + |
| 286 | + // setup zktracerprovider plugin service |
| 287 | + AtomicReference<ZkTracer> capturedTracer = new AtomicReference<>(); |
| 288 | + doAnswer(__ -> Optional.of(zkTracerProviderSpy)) |
| 289 | + .when(mockPluginServiceManager) |
| 290 | + .getService(BlockImportTracerProvider.class); |
| 291 | + doAnswer(__ -> mockPluginServiceManager).when(spyProtocolContext).getPluginServiceManager(); |
| 292 | + doAnswer( |
| 293 | + invocation -> { |
| 294 | + BlockAwareOperationTracer tracer = |
| 295 | + (BlockAwareOperationTracer) invocation.callRealMethod(); |
| 296 | + capturedTracer.set((ZkTracer) tracer); |
| 297 | + return tracer; |
| 298 | + }) |
| 299 | + .when(zkTracerProviderSpy) |
| 300 | + .getBlockImportTracer(any(BlockHeader.class)); |
| 301 | + |
| 302 | + var r = |
| 303 | + Bytes.fromHexString("0xde0db8ce81c8092823a2d91b3a3cc421bc6b384b562f567e67b26c5443ff28e9") |
| 304 | + .toUnsignedBigInteger(); |
| 305 | + var s = |
| 306 | + Bytes.fromHexString("0x44457e69f445284d1f1ba99107b53c1d4b3d1e8c7283ff504341e5a92699c09d") |
| 307 | + .toUnsignedBigInteger(); |
| 308 | + var sig = SECPSignature.create(r, s, (byte) 0, SecP256K1FieldElement.Q); |
| 309 | + |
| 310 | + var tx = |
| 311 | + new Transaction.Builder() |
| 312 | + .signature(sig) |
| 313 | + .type(TransactionType.EIP1559) |
| 314 | + .sender(Address.fromHexString("0x43370108f30ee5ed54a9565f37af3be8502903f5")) |
| 315 | + .to(Address.fromHexString("0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789")) |
| 316 | + .nonce(14261) |
| 317 | + .value(Wei.ZERO) |
| 318 | + .payload( |
| 319 | + Bytes.fromHexString( |
| 320 | + "0x1fad948c000000000000000000000000000000000000000000000000000000000000004000000000000000000000000043370108f30ee5ed54a9565f37af3be8502903f50000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000051ce6a8e868fa0c928346ac7489a74432c44bc933db076b8929744925f16e35a14de0c7e122e512efe282e76000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000007a120000000000000000000000000000000000000000000000000000000000006b08a000000000000000000000000000000000000000000000000000000000000b1580000000000000000000000000000000000000000000000000000000065fa53ff000000000000000000000000000000000000000000000000000000005cb5068300000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000783fbe35a874284e41c955331a363c1ea085301a8dd8fd8f440000000000000000000000001708dc05842c8fbd5e2e60a0adbd1b2efab061640000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084b61d27f60000000000000000000000000acf75e7c29bd27f1cd6ed47c8769c61bc6a68810000000000000000000000000000000000000000000000000006379da05b600000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004189f4d1fe2626f881082f93a336e51d6fad4faecfaffb843307c6c658bc4ae3ac737b6ece16b3c206b2d26efa8ca1ee6ac85e6a1e984456b7282404592e3d12e31c00000000000000000000000000000000000000000000000000000000000000")) |
| 321 | + .maxPriorityFeePerGas(Wei.wrap(Bytes.fromHexString("5cb50683"))) |
| 322 | + .maxFeePerGas(Wei.wrap(Bytes.fromHexString("0x5cb5068b"))) |
| 323 | + .gasLimit(1396683L) |
| 324 | + .chainId(BigInteger.valueOf(59144)) |
| 325 | + .build(); |
| 326 | + |
| 327 | + // Process block with this tx |
| 328 | + final BlockDataGenerator gen = new BlockDataGenerator(); |
| 329 | + final Block block = |
| 330 | + gen.block( |
| 331 | + BlockDataGenerator.BlockOptions.create() |
| 332 | + .addTransaction(tx) |
| 333 | + .hasOmmers(false) |
| 334 | + // .setStateRoot(Hash.fromHexString("0xc70a52d661bf92297e17d79b0cb04c508f3767acecf6145171884a82ea5e8a9d")) |
| 335 | + .setStateRoot( |
| 336 | + Hash.fromHexString( |
| 337 | + "0x543543c7c19059bbc9c384ab7344bc281fe08938732480a1bc48d01e3ba8090b"))); |
| 338 | + final BlockProcessor processor = |
| 339 | + setupUtil |
| 340 | + .getProtocolSchedule() |
| 341 | + .getByBlockHeader(blockchain.getChainHeadHeader()) |
| 342 | + .getBlockProcessor(); |
| 343 | + |
| 344 | + final BlockProcessingResult result = |
| 345 | + processor.processBlock(spyProtocolContext, blockchain, worldState, block); |
| 346 | + |
| 347 | + // Assert transaction is present |
| 348 | + assertEquals(1, result.getReceipts().size()); |
| 349 | + |
| 350 | + // Check that status == 0 (reverted) |
| 351 | + final TransactionReceipt receipt = result.getReceipts().get(0); |
| 352 | + assertEquals(0, receipt.getStatus()); |
| 353 | + |
| 354 | + var create2Address = Address.fromHexString("0x51ce6a8e868fa0c928346ac7489a74432c44bc93"); |
| 355 | + var create2StorageSeen = |
| 356 | + capturedTracer.get().getStoragesSeenByHubForRelativeBlock(1).get(create2Address); |
| 357 | + assertNotNull(create2StorageSeen); |
| 358 | + |
| 359 | + var trieLog = capturedTrieLog.get(); |
| 360 | + var create2TrielogSeen = trieLog.getStorageChanges().get(create2Address); |
| 361 | + assertNotNull(create2TrielogSeen); |
| 362 | + |
| 363 | + assertEquals(create2StorageSeen.size(), create2TrielogSeen.size()); |
| 364 | + // known reverted storage slot key: |
| 365 | + final Bytes32 revertedSlotKey = |
| 366 | + Bytes32.fromHexString("0x322cf19c484104d3b1a9c2982ebae869ede3fa5f6c4703ca41b9a48c76ee0300"); |
| 367 | + assertTrue(create2StorageSeen.contains(revertedSlotKey)); |
| 368 | + var trieLogSlotVal = |
| 369 | + create2TrielogSeen.get(new StorageSlotKey(UInt256.fromBytes(revertedSlotKey))); |
| 370 | + assertNotNull(trieLogSlotVal); |
| 371 | + } |
218 | 372 | } |
0 commit comments