Skip to content

Commit c8168c0

Browse files
authored
fix(xdc): persist epoch rewards on canonical block added (#12288)
* fix no persist rewards on block processing path * log on exception * use TransactionProcessor to read when doing rewards * remove pruning and make rewards lookups hash based * unnecessary using * simplify cache rewards instead * save epoch in foreground * updated tests * remove using * Set reward cache after validation * test changes * add missing fields in config * reduce logs a bit * added missing LimitPenaltyEpoch * format
1 parent 8398cfa commit c8168c0

26 files changed

Lines changed: 385 additions & 336 deletions

src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,18 @@ private void ValidateProcessedBlock(Block suggestedBlock, ProcessingOptions opti
100100
throw new InvalidBlockException(suggestedBlock, error);
101101
}
102102

103+
PostValidation(suggestedBlock, block, receipts, options);
104+
}
105+
106+
protected virtual void PostValidation(Block suggestedBlock, Block processedBlock, TxReceipt[] receipts, ProcessingOptions options)
107+
{
103108
// Block is valid, copy the execution artifacts back onto the suggested block.
104109
// Forward sync suggests blocks without BAL payloads, so the generated BAL needs to
105110
// follow the suggested block through main-chain updates and persistence.
106-
suggestedBlock.AccountChanges = block.AccountChanges;
107-
suggestedBlock.ExecutionRequests = block.ExecutionRequests;
108-
suggestedBlock.GeneratedBlockAccessList = block.GeneratedBlockAccessList;
109-
suggestedBlock.EncodedBlockAccessList = block.EncodedBlockAccessList ?? suggestedBlock.EncodedBlockAccessList;
111+
suggestedBlock.AccountChanges = processedBlock.AccountChanges;
112+
suggestedBlock.ExecutionRequests = processedBlock.ExecutionRequests;
113+
suggestedBlock.GeneratedBlockAccessList = processedBlock.GeneratedBlockAccessList;
114+
suggestedBlock.EncodedBlockAccessList = processedBlock.EncodedBlockAccessList ?? suggestedBlock.EncodedBlockAccessList;
110115
}
111116

112117
protected bool ShouldComputeStateRoot(BlockHeader header) =>

src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestDepositContract.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,15 @@ public Address[] GetCandidatesByStake(BlockHeader blockHeader) =>
2020
public Address[] GetCandidates(BlockHeader blockHeader) =>
2121
candidateContainer.MasternodeCandidates.Select(m => m.Address).ToArray();
2222

23+
public Address[] GetCandidates(ITransactionProcessor transactionProcessor, BlockHeader blockHeader) =>
24+
GetCandidates(blockHeader);
25+
2326
public UInt256 GetCandidateStake(BlockHeader blockHeader, Address candidate) =>
2427
10_000_000.Ether;
2528

29+
public UInt256 GetCandidateStake(ITransactionProcessor transactionProcessor, BlockHeader blockHeader, Address candidate) =>
30+
GetCandidateStake(blockHeader, candidate);
31+
2632
public Address GetCandidateOwner(BlockHeader blockHeader, Address candidate) =>
2733
throw new System.NotImplementedException();
2834

src/Nethermind/Nethermind.Xdc.Test/ModuleTests/PenaltyTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ private static MockedPenaltyContext CreateMockedPenaltyContext(
228228
xdcSpec.MergeSignRange.Returns((ulong)MergeSignRange);
229229
xdcSpec.IsTipUpgradePenaltyEnabled.Returns(true);
230230
xdcSpec.LimitPenaltyEpoch.Returns((ulong)limitPenaltyEpoch);
231-
xdcSpec.MinimumSigningTx.Returns(2);
232-
xdcSpec.MinimumMinerBlockPerEpoch.Returns(1);
231+
xdcSpec.MinimumSigningTx.Returns(2UL);
232+
xdcSpec.MinimumMinerBlockPerEpoch.Returns(1UL);
233233

234234
ISpecProvider specProvider = Substitute.For<ISpecProvider>();
235235
specProvider.GetSpec(Arg.Any<ForkActivation>()).Returns(xdcSpec);

src/Nethermind/Nethermind.Xdc.Test/ModuleTests/RewardTests.cs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using Nethermind.Xdc.Test.Helpers;
2222
using Nethermind.Xdc.Types;
2323
using NSubstitute;
24+
using NSubstitute.ExceptionExtensions;
2425
using NUnit.Framework;
2526
using System.Collections.Generic;
2627
using System.Linq;
@@ -91,8 +92,7 @@ public async Task TestHookRewardV2()
9192
masternodeVotingContract,
9293
Substitute.For<IMintedRecordContract>(),
9394
signingTxCache,
94-
CreateXdcTransactionProcessor(chain.SpecProvider, masternodeVotingContract, chain.MainWorldState),
95-
Substitute.For<IRewardsStore>()
95+
CreateXdcTransactionProcessor(chain.SpecProvider, masternodeVotingContract, chain.MainWorldState)
9696
);
9797

9898
XdcBlockHeader head = (XdcBlockHeader)chain.BlockTree.Head!.Header;
@@ -228,8 +228,7 @@ public async Task TestHookRewardV2SplitReward()
228228
masternodeVotingContract,
229229
Substitute.For<IMintedRecordContract>(),
230230
signingTxCache,
231-
CreateXdcTransactionProcessor(chain.SpecProvider, masternodeVotingContract, chain.MainWorldState),
232-
Substitute.For<IRewardsStore>()
231+
CreateXdcTransactionProcessor(chain.SpecProvider, masternodeVotingContract, chain.MainWorldState)
233232
);
234233

235234
XdcBlockHeader head = (XdcBlockHeader)chain.BlockTree.Head!.Header;
@@ -408,7 +407,7 @@ public void RewardCalculator_SplitReward_MatchesRounding()
408407
.Returns(ci => ci.ArgAt<Address>(1));
409408

410409
SigningTxCache signingTxCache = new(tree, specProvider);
411-
XdcRewardCalculator rewardCalculator = new(epochSwitchManager, specProvider, tree, votingContract, Substitute.For<IMintedRecordContract>(), signingTxCache, CreateXdcTransactionProcessor(specProvider, votingContract), Substitute.For<IRewardsStore>());
410+
XdcRewardCalculator rewardCalculator = new(epochSwitchManager, specProvider, tree, votingContract, Substitute.For<IMintedRecordContract>(), signingTxCache, CreateXdcTransactionProcessor(specProvider, votingContract));
412411
BlockReward[] rewards = rewardCalculator.CalculateRewards(blocks.Last());
413412

414413
Assert.That(rewards, Has.Length.EqualTo(3));
@@ -534,8 +533,12 @@ public void TestHookRewardAfterUpgrade()
534533
observer1.Address,
535534
observer2.Address,
536535
];
537-
votingContract.GetCandidates(Arg.Any<BlockHeader>()).Returns(rewardCandidates);
538-
votingContract.GetCandidateStake(Arg.Any<BlockHeader>(), Arg.Any<Address>()).Returns(UInt256.One);
536+
votingContract.GetCandidates(Arg.Any<BlockHeader>())
537+
.Throws(new InvalidOperationException("Readonly candidates lookup should not be used for block-processing rewards."));
538+
votingContract.GetCandidates(Arg.Any<ITransactionProcessor>(), Arg.Any<BlockHeader>()).Returns(rewardCandidates);
539+
votingContract.GetCandidateStake(Arg.Any<BlockHeader>(), Arg.Any<Address>())
540+
.Throws(new InvalidOperationException("Readonly stake lookup should not be used for block-processing rewards."));
541+
votingContract.GetCandidateStake(Arg.Any<ITransactionProcessor>(), Arg.Any<BlockHeader>(), Arg.Any<Address>()).Returns(UInt256.One);
539542

540543
IWorldState worldState = TestWorldStateFactory.CreateForTest(TestMemDbProvider.Init(), LimboLogs.Instance);
541544
using IDisposable _ = worldState.BeginScope(IWorldState.PreGenesis);
@@ -556,8 +559,7 @@ public void TestHookRewardAfterUpgrade()
556559
votingContract,
557560
mintedRecordContract,
558561
signingTxCache,
559-
transactionProcessor,
560-
Substitute.For<IRewardsStore>());
562+
transactionProcessor);
561563

562564
BlockReward[] rewards = rewardCalculator.CalculateRewards(blocks[(int)checkpointNumber]);
563565

@@ -608,8 +610,7 @@ public void RewardCalculator_CalculateRewardsForSignersAndHolders_MatchesExpecte
608610
masternodeVotingContract,
609611
Substitute.For<IMintedRecordContract>(),
610612
signingTxCache,
611-
CreateXdcTransactionProcessor(specProvider, masternodeVotingContract),
612-
Substitute.For<IRewardsStore>()
613+
CreateXdcTransactionProcessor(specProvider, masternodeVotingContract)
613614
);
614615

615616
UInt256 totalReward = UInt256.Parse("171000000000000000000");
@@ -647,8 +648,7 @@ public void DistributeRewards_OwnerIsFoundation_MatchesReferenceOverwriteBehavio
647648
masternodeVotingContract,
648649
Substitute.For<IMintedRecordContract>(),
649650
signingTxCache,
650-
CreateXdcTransactionProcessor(specProvider, masternodeVotingContract),
651-
Substitute.For<IRewardsStore>());
651+
CreateXdcTransactionProcessor(specProvider, masternodeVotingContract));
652652

653653
Address signer = new("0x80b329b66ddfe2180904d6ae737283a3f1860b83");
654654
Address foundationWalletAddr = new("0x5cb041be27deb4a506ad63d082c6043b4a5c6898");
@@ -687,11 +687,9 @@ private static XdcRewardCalculatorSource CreateRewardCalculatorSource(bool isSub
687687
IMasternodeVotingContract masternodeVotingContract = Substitute.For<IMasternodeVotingContract>();
688688
IMintedRecordContract mintedRecordContract = Substitute.For<IMintedRecordContract>();
689689
ISigningTxCache signingTxCache = Substitute.For<ISigningTxCache>();
690-
IRewardsStore rewardsStore = Substitute.For<IRewardsStore>();
691-
692690
return isSubnet
693-
? new XdcSubnetRewardCalculatorSource(epochSwitchManager, specProvider, blockTree, masternodeVotingContract, mintedRecordContract, signingTxCache, rewardsStore)
694-
: new XdcRewardCalculatorSource(epochSwitchManager, specProvider, blockTree, masternodeVotingContract, mintedRecordContract, signingTxCache, rewardsStore);
691+
? new XdcSubnetRewardCalculatorSource(epochSwitchManager, specProvider, blockTree, masternodeVotingContract, mintedRecordContract, signingTxCache)
692+
: new XdcRewardCalculatorSource(epochSwitchManager, specProvider, blockTree, masternodeVotingContract, mintedRecordContract, signingTxCache);
695693
}
696694

697695
private static XdcTransactionProcessor CreateXdcTransactionProcessor(

src/Nethermind/Nethermind.Xdc.Test/ModuleTests/RpcModuleTests.cs

Lines changed: 5 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -829,23 +829,15 @@ public void GetRewardByAccount_ShouldReturnSuccess_WhenRewardsExist()
829829
];
830830

831831
_epochSwitchManager.GetEpochSwitchInfoBetween(beginHeader, endHeader).Returns(epochSwitchInfos);
832-
_rewardsStore.TryGetRetainedRange(out Arg.Any<ulong>(), out Arg.Any<ulong>())
833-
.Returns(callInfo =>
834-
{
835-
callInfo[0] = epoch1;
836-
callInfo[1] = epoch2;
837-
return true;
838-
});
839-
840-
_rewardsStore.HasEpochRewards(epoch1).Returns(true);
841-
_rewardsStore.HasEpochRewards(epoch2).Returns(true);
842-
_rewardsStore.TryGetAccountReward(account, epoch1, out Arg.Any<UInt256>())
832+
_rewardsStore.HasEpochRewards(TestItem.KeccakA).Returns(true);
833+
_rewardsStore.HasEpochRewards(TestItem.KeccakB).Returns(true);
834+
_rewardsStore.TryGetAccountReward(account, TestItem.KeccakA, out Arg.Any<UInt256>())
843835
.Returns(callInfo =>
844836
{
845837
callInfo[2] = (UInt256)10;
846838
return true;
847839
});
848-
_rewardsStore.TryGetAccountReward(account, epoch2, out Arg.Any<UInt256>())
840+
_rewardsStore.TryGetAccountReward(account, TestItem.KeccakB, out Arg.Any<UInt256>())
849841
.Returns(callInfo =>
850842
{
851843
callInfo[2] = (UInt256)20;
@@ -865,45 +857,6 @@ public void GetRewardByAccount_ShouldReturnSuccess_WhenRewardsExist()
865857
Assert.That(result.Data.Total.TotalAccountReward, Is.EqualTo((UInt256)30));
866858
}
867859

868-
[Test]
869-
public void GetRewardByAccount_ShouldReturnFail_WhenRequestIsPruned()
870-
{
871-
// Arrange
872-
Address account = TestItem.AddressA;
873-
const ulong begin = 100;
874-
const ulong end = 200;
875-
const ulong requestedEpoch = 120;
876-
const ulong oldestRetained = 150;
877-
const ulong newestRetained = 300;
878-
879-
XdcBlockHeader beginHeader = Build.A.XdcBlockHeader().WithNumber(begin).TestObject;
880-
XdcBlockHeader endHeader = Build.A.XdcBlockHeader().WithNumber(end).TestObject;
881-
882-
_blockTree.FindHeader(begin).Returns(beginHeader);
883-
_blockTree.FindHeader(end).Returns(endHeader);
884-
885-
EpochSwitchInfo[] epochSwitchInfos =
886-
[
887-
new EpochSwitchInfo(Array.Empty<Address>(), Array.Empty<Address>(), Array.Empty<Address>(), new BlockRoundInfo(TestItem.KeccakA, 1, (long)requestedEpoch)),
888-
];
889-
890-
_epochSwitchManager.GetEpochSwitchInfoBetween(beginHeader, endHeader).Returns(epochSwitchInfos);
891-
_rewardsStore.TryGetRetainedRange(out Arg.Any<ulong>(), out Arg.Any<ulong>())
892-
.Returns(callInfo =>
893-
{
894-
callInfo[0] = oldestRetained;
895-
callInfo[1] = newestRetained;
896-
return true;
897-
});
898-
899-
// Act
900-
ResultWrapper<AccountRewardResponse> result = _rpcModule.GetRewardByAccount(account, begin, end);
901-
902-
// Assert
903-
Assert.That(result.Result, Is.Not.EqualTo(Result.Success));
904-
Assert.That(result.ErrorCode, Is.EqualTo(ErrorCodes.PrunedHistoryUnavailable));
905-
}
906-
907860
[Test]
908861
public void GetRewardByAccount_ShouldReturnFail_WhenRewardsMissingForEpoch()
909862
{
@@ -925,8 +878,7 @@ public void GetRewardByAccount_ShouldReturnFail_WhenRewardsMissingForEpoch()
925878
];
926879

927880
_epochSwitchManager.GetEpochSwitchInfoBetween(beginHeader, endHeader).Returns(epochSwitchInfos);
928-
_rewardsStore.TryGetRetainedRange(out Arg.Any<ulong>(), out Arg.Any<ulong>()).Returns(false);
929-
_rewardsStore.HasEpochRewards(epoch).Returns(false);
881+
_rewardsStore.HasEpochRewards(TestItem.KeccakA).Returns(false);
930882

931883
// Act
932884
ResultWrapper<AccountRewardResponse> result = _rpcModule.GetRewardByAccount(account, begin, end);

0 commit comments

Comments
 (0)