public async Task newPayloadV1_can_insert_blocks_from_cache_when_syncing() { using MergeTestBlockchain chain = await CreateBlockChain(); IEngineRpcModule rpc = CreateEngineModule(chain); Keccak startingHead = chain.BlockTree.HeadHash; ExecutionPayloadV1 parentBlockRequest = new(Build.A.Block.WithNumber(2).TestObject); ExecutionPayloadV1[] requests = CreateBlockRequestBranch(parentBlockRequest, Address.Zero, 7); ResultWrapper <PayloadStatusV1> payloadStatus; foreach (ExecutionPayloadV1 r in requests) { payloadStatus = await rpc.engine_newPayloadV1(r); payloadStatus.Data.Status.Should().Be(nameof(PayloadStatusV1.Syncing).ToUpper()); chain.BeaconSync.IsBeaconSyncHeadersFinished().Should().BeTrue(); chain.BeaconSync.ShouldBeInBeaconHeaders().Should().BeFalse(); chain.BeaconPivot.BeaconPivotExists().Should().BeFalse(); } int pivotNum = 3; requests[pivotNum].TryGetBlock(out Block? pivotBlock); // initiate sync ForkchoiceStateV1 forkchoiceStateV1 = new(pivotBlock !.Hash, startingHead, startingHead); ResultWrapper <ForkchoiceUpdatedV1Result> forkchoiceUpdatedResult = await rpc.engine_forkchoiceUpdatedV1(forkchoiceStateV1); forkchoiceUpdatedResult.Data.PayloadStatus.Status.Should() .Be(nameof(PayloadStatusV1.Syncing).ToUpper()); // trigger insertion of blocks in cache into block tree payloadStatus = await rpc.engine_newPayloadV1(requests[^ 1]);
public virtual async Task forkchoiceUpdatedV1_should_communicate_with_boost_relay_through_http() { MergeConfig mergeConfig = new() { Enabled = true, SecondsPerSlot = 1, TerminalTotalDifficulty = "0" }; using MergeTestBlockchain chain = await CreateBlockChain(mergeConfig); IJsonSerializer serializer = chain.JsonSerializer; UInt256 timestamp = Timestamper.UnixTime.Seconds; PayloadAttributes payloadAttributes = new() { Timestamp = timestamp, SuggestedFeeRecipient = Address.Zero, PrevRandao = Keccak.Zero }; string relayUrl = "http://localhost"; MockHttpMessageHandler mockHttp = new(); mockHttp.Expect(HttpMethod.Post, relayUrl + BoostRelay.GetPayloadAttributesPath) .WithContent("{\"timestamp\":\"0x3e8\",\"prevRandao\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"suggestedFeeRecipient\":\"0x0000000000000000000000000000000000000000\"}") .Respond("application/json", "{\"timestamp\":\"0x3e9\",\"prevRandao\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"suggestedFeeRecipient\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\"}"); mockHttp.Expect(HttpMethod.Post, relayUrl + BoostRelay.SendPayloadPath) .WithContent("{\"block\":{\"parentHash\":\"0x1c53bdbf457025f80c6971a9cf50986974eed02f0a9acaeeb49cafef10efd133\",\"feeRecipient\":\"0xb7705ae4c6f81b66cdb323c65f4e8133690fc099\",\"stateRoot\":\"0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"prevRandao\":\"0x03783fac2efed8fbc9ad443e592ee30e61d65f471140c10ca155e937b435b760\",\"blockNumber\":\"0x1\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"timestamp\":\"0x3e9\",\"extraData\":\"0x\",\"baseFeePerGas\":\"0x0\",\"blockHash\":\"0xb519d89363b891216dbf9bd046f226cae9348a90ba0db00e7ade437fff7a1510\",\"transactions\":[]},\"profit\":\"0x0\"}"); DefaultHttpClient defaultHttpClient = new(mockHttp.ToHttpClient(), serializer, chain.LogManager, 1, 100); BoostRelay boostRelay = new(defaultHttpClient, relayUrl); BoostBlockImprovementContextFactory improvementContextFactory = new(chain.BlockProductionTrigger, TimeSpan.FromSeconds(5000), boostRelay, chain.StateReader); TimeSpan timePerSlot = TimeSpan.FromSeconds(1000); chain.PayloadPreparationService = new PayloadPreparationService( chain.PostMergeBlockProducer !, improvementContextFactory, TimerFactory.Default, chain.LogManager, timePerSlot); IEngineRpcModule rpc = CreateEngineModule(chain); Keccak startingHead = chain.BlockTree.HeadHash; ManualResetEvent wait = new(false); chain.PayloadPreparationService.BlockImproved += (_, _) => wait.Set(); string payloadId = rpc.engine_forkchoiceUpdatedV1(new ForkchoiceStateV1(startingHead, Keccak.Zero, startingHead), payloadAttributes).Result.Data .PayloadId !; await wait.WaitOneAsync(100, CancellationToken.None); ResultWrapper <ExecutionPayloadV1?> response = await rpc.engine_getPayloadV1(Bytes.FromHexString(payloadId)); ExecutionPayloadV1 executionPayloadV1 = response.Data !; executionPayloadV1.FeeRecipient.Should().Be(TestItem.AddressA); executionPayloadV1.PrevRandao.Should().Be(TestItem.KeccakA); mockHttp.VerifyNoOutstandingExpectation(); } [Test]
public async Task forkChoiceUpdatedV1_unknown_block_initiates_syncing() { using MergeTestBlockchain chain = await CreateBlockChain(); IEngineRpcModule rpc = CreateEngineModule(chain); Keccak? startingHead = chain.BlockTree.HeadHash; BlockHeader parent = Build.A.BlockHeader .WithNumber(1) .WithHash(TestItem.KeccakA) .WithNonce(0) .WithDifficulty(0) .TestObject; Block block = Build.A.Block .WithNumber(2) .WithParent(parent) .WithNonce(0) .WithDifficulty(0) .WithAuthor(Address.Zero) .WithPostMergeFlag(true) .TestObject; await rpc.engine_newPayloadV1(new ExecutionPayloadV1(block)); // sync has not started yet chain.BeaconSync.IsBeaconSyncHeadersFinished().Should().BeTrue(); chain.BeaconSync.IsBeaconSyncFinished(block.Header).Should().BeTrue(); chain.BeaconSync.ShouldBeInBeaconHeaders().Should().BeFalse(); chain.BeaconPivot.BeaconPivotExists().Should().BeFalse(); BlockTreePointers pointers = new() { BestKnownNumber = 0, BestSuggestedHeader = chain.BlockTree.Genesis !, BestSuggestedBody = chain.BlockTree.FindBlock(0) !, BestKnownBeaconBlock = 0, LowestInsertedHeader = null, LowestInsertedBeaconHeader = null }; AssertBlockTreePointers(chain.BlockTree, pointers); ForkchoiceStateV1 forkchoiceStateV1 = new(block.Hash !, startingHead, startingHead); ResultWrapper <ForkchoiceUpdatedV1Result> forkchoiceUpdatedResult = await rpc.engine_forkchoiceUpdatedV1(forkchoiceStateV1); forkchoiceUpdatedResult.Data.PayloadStatus.Status.Should() .Be(nameof(PayloadStatusV1.Syncing).ToUpper()); chain.BeaconSync.ShouldBeInBeaconHeaders().Should().BeTrue(); chain.BeaconSync.IsBeaconSyncHeadersFinished().Should().BeFalse(); chain.BeaconSync.IsBeaconSyncFinished(chain.BlockTree.FindBlock(block.Hash)?.Header).Should().BeFalse(); AssertBeaconPivotValues(chain.BeaconPivot, block.Header); pointers.LowestInsertedBeaconHeader = block.Header; pointers.BestKnownBeaconBlock = block.Number; pointers.LowestInsertedHeader = block.Header; AssertBlockTreePointers(chain.BlockTree, pointers); AssertExecutionStatusNotChangedV1(rpc, block.Hash !, startingHead, startingHead); }
public async Task should_return_invalid_lvh_null_on_invalid_blocks_during_the_sync() { using MergeTestBlockchain chain = await CreateBlockChain(); IEngineRpcModule rpc = CreateEngineModule(chain); Keccak? startingHead = chain.BlockTree.HeadHash; BlockHeader parent = Build.A.BlockHeader .WithNumber(1) .WithHash(TestItem.KeccakA) .WithNonce(0) .WithDifficulty(0) .TestObject; Block block = Build.A.Block .WithNumber(2) .WithParent(parent) .WithNonce(0) .WithDifficulty(0) .WithAuthor(Address.Zero) .WithPostMergeFlag(true) .TestObject; ExecutionPayloadV1 startingNewPayload = new(block); await rpc.engine_newPayloadV1(startingNewPayload); ForkchoiceStateV1 forkchoiceStateV1 = new(block.Hash !, startingHead, startingHead); ResultWrapper <ForkchoiceUpdatedV1Result> forkchoiceUpdatedResult = await rpc.engine_forkchoiceUpdatedV1(forkchoiceStateV1); forkchoiceUpdatedResult.Data.PayloadStatus.Status.Should() .Be(nameof(PayloadStatusV1.Syncing).ToUpper()); ExecutionPayloadV1[] requests = CreateBlockRequestBranch(startingNewPayload, TestItem.AddressD, 1); foreach (ExecutionPayloadV1 r in requests) { ResultWrapper <PayloadStatusV1> payloadStatus = await rpc.engine_newPayloadV1(r); payloadStatus.Data.Status.Should().Be(nameof(PayloadStatusV1.Syncing).ToUpper()); } ExecutionPayloadV1[] invalidRequests = CreateBlockRequestBranch(requests[0], TestItem.AddressD, 1); foreach (ExecutionPayloadV1 r in invalidRequests) { r.TryGetBlock(out Block? newBlock); newBlock !.Header.GasLimit = long.MaxValue; // incorrect gas limit newBlock.Header.Hash = newBlock.CalculateHash(); ResultWrapper <PayloadStatusV1> payloadStatus = await rpc.engine_newPayloadV1(new ExecutionPayloadV1(newBlock)); payloadStatus.Data.Status.Should().Be(nameof(PayloadStatusV1.Invalid).ToUpper()); payloadStatus.Data.LatestValidHash.Should().BeNull(); } }
public async Task forkchoiceUpdatedV1_should_ignore_gas_limit([Values(false, true)] bool relay) { MergeConfig mergeConfig = new() { Enabled = true, SecondsPerSlot = 1, TerminalTotalDifficulty = "0" }; using MergeTestBlockchain chain = await CreateBlockChain(mergeConfig); IBlockImprovementContextFactory improvementContextFactory; if (relay) { IBoostRelay boostRelay = Substitute.For <IBoostRelay>(); boostRelay.GetPayloadAttributes(Arg.Any <PayloadAttributes>(), Arg.Any <CancellationToken>()) .Returns(c => c.Arg <PayloadAttributes>()); improvementContextFactory = new BoostBlockImprovementContextFactory(chain.BlockProductionTrigger, TimeSpan.FromSeconds(5), boostRelay, chain.StateReader); } else { improvementContextFactory = new BlockImprovementContextFactory(chain.BlockProductionTrigger, TimeSpan.FromSeconds(5)); } TimeSpan timePerSlot = TimeSpan.FromSeconds(10); chain.PayloadPreparationService = new PayloadPreparationService( chain.PostMergeBlockProducer !, improvementContextFactory, TimerFactory.Default, chain.LogManager, timePerSlot); IEngineRpcModule rpc = CreateEngineModule(chain); Keccak startingHead = chain.BlockTree.HeadHash; UInt256 timestamp = Timestamper.UnixTime.Seconds; Keccak random = Keccak.Zero; Address feeRecipient = Address.Zero; string payloadId = rpc.engine_forkchoiceUpdatedV1(new ForkchoiceStateV1(startingHead, Keccak.Zero, startingHead), new PayloadAttributes { Timestamp = timestamp, SuggestedFeeRecipient = feeRecipient, PrevRandao = random, GasLimit = 10_000_000L }).Result.Data .PayloadId !; ResultWrapper <ExecutionPayloadV1?> response = await rpc.engine_getPayloadV1(Bytes.FromHexString(payloadId)); ExecutionPayloadV1 executionPayloadV1 = response.Data !; executionPayloadV1.GasLimit.Should().Be(4_000_000L); }
public async Task forkchoiceUpdatedV1_should_communicate_with_boost_relay() { MergeConfig mergeConfig = new() { Enabled = true, SecondsPerSlot = 1, TerminalTotalDifficulty = "0" }; using MergeTestBlockchain chain = await CreateBlockChain(mergeConfig); IBoostRelay boostRelay = Substitute.For <IBoostRelay>(); boostRelay.GetPayloadAttributes(Arg.Any <PayloadAttributes>(), Arg.Any <CancellationToken>()) .Returns(c => { PayloadAttributes payloadAttributes = c.Arg <PayloadAttributes>(); payloadAttributes.SuggestedFeeRecipient = TestItem.AddressA; payloadAttributes.PrevRandao = TestItem.KeccakA; payloadAttributes.Timestamp += 1; payloadAttributes.GasLimit = 10_000_000L; return(payloadAttributes); }); BoostBlockImprovementContextFactory improvementContextFactory = new(chain.BlockProductionTrigger, TimeSpan.FromSeconds(5), boostRelay, chain.StateReader); TimeSpan timePerSlot = TimeSpan.FromSeconds(10); chain.PayloadPreparationService = new PayloadPreparationService( chain.PostMergeBlockProducer !, improvementContextFactory, TimerFactory.Default, chain.LogManager, timePerSlot); IEngineRpcModule rpc = CreateEngineModule(chain); Keccak startingHead = chain.BlockTree.HeadHash; UInt256 timestamp = Timestamper.UnixTime.Seconds; Keccak random = Keccak.Zero; Address feeRecipient = Address.Zero; BoostExecutionPayloadV1?sentItem = null; boostRelay.When(b => b.SendPayload(Arg.Any <BoostExecutionPayloadV1>(), Arg.Any <CancellationToken>())) .Do(c => sentItem = c.Arg <BoostExecutionPayloadV1>()); ManualResetEvent wait = new(false); chain.PayloadPreparationService.BlockImproved += (_, _) => wait.Set(); string payloadId = rpc.engine_forkchoiceUpdatedV1(new ForkchoiceStateV1(startingHead, Keccak.Zero, startingHead), new PayloadAttributes { Timestamp = timestamp, SuggestedFeeRecipient = feeRecipient, PrevRandao = random }).Result.Data .PayloadId !; await wait.WaitOneAsync(100, CancellationToken.None); ResultWrapper <ExecutionPayloadV1?> response = await rpc.engine_getPayloadV1(Bytes.FromHexString(payloadId)); ExecutionPayloadV1 executionPayloadV1 = response.Data !; executionPayloadV1.FeeRecipient.Should().Be(TestItem.AddressA); executionPayloadV1.PrevRandao.Should().Be(TestItem.KeccakA); executionPayloadV1.GasLimit.Should().Be(10_000_000L); executionPayloadV1.Should().BeEquivalentTo(sentItem !.Block); sentItem.Profit.Should().Be(0); }