From 008258f58c13c0b6721d052f6bbf6b24d6d8cdff Mon Sep 17 00:00:00 2001 From: Shaun Colley <79477620+smcio@users.noreply.github.com> Date: Tue, 22 Sep 2026 16:24:25 +0100 Subject: [PATCH 1/4] rewards: preserve credits on inactive Alpenglow stakes Retain activation status from the existing points calculation and exclude inactive stakes from the Alpenglow skipped-reward credit advance. Preserve explicit forced-update cases. Add an epoch-116 fixture that reproduces the original slot 6264001 bank hash before the fix and the expected footer hash afterward. Include reward regressions in the race CI job. --- .github/workflows/go_build.yml | 3 + pkg/rewards/alpenglow_rewards_test.go | 53 + pkg/rewards/inactive_stakes_test.go | 118 ++ pkg/rewards/rewards.go | 22 +- pkg/rewards/testdata/epoch116/README.md | 49 + .../testdata/epoch116/inactive-stakes.json | 1693 +++++++++++++++++ 6 files changed, 1932 insertions(+), 6 deletions(-) create mode 100644 pkg/rewards/inactive_stakes_test.go create mode 100644 pkg/rewards/testdata/epoch116/README.md create mode 100644 pkg/rewards/testdata/epoch116/inactive-stakes.json diff --git a/.github/workflows/go_build.yml b/.github/workflows/go_build.yml index 50383467f..bb29441f7 100644 --- a/.github/workflows/go_build.yml +++ b/.github/workflows/go_build.yml @@ -17,3 +17,6 @@ jobs: - name: Build run: go build -v ./cmd/mithril + + - name: Rewards and replay regressions + run: GOMAXPROCS=2 go test -race -p 2 -count=1 ./pkg/rewards ./pkg/replay diff --git a/pkg/rewards/alpenglow_rewards_test.go b/pkg/rewards/alpenglow_rewards_test.go index 76136886d..ae9d451bb 100644 --- a/pkg/rewards/alpenglow_rewards_test.go +++ b/pkg/rewards/alpenglow_rewards_test.go @@ -116,6 +116,59 @@ func TestAlpenglowEarnedPointsAreNotCreditsOnly(t *testing.T) { )) } +func TestAlpenglowSkippedRewardCreditsRespectStakeActivation(t *testing.T) { + votePubkey := solana.PublicKey{1} + voteState := &sealevel.VoteStateVersions{ + Type: sealevel.VoteStateVersionV4, + V4: sealevel.VoteState4{EpochCredits: []sealevel.EpochCredits{{ + Epoch: 115, Credits: 2_000, PrevCredits: 1_000, + }}}, + } + mode := RewardCalculationMode{ + FullAlpenglow: true, + RewardEpochDelegatedStakes: map[solana.PublicKey]uint64{votePubkey: 1_000_000}, + } + for _, tc := range []struct { + name string + activation, deactivation uint64 + advance bool + }{ + {"fully cooled in rewarded epoch", 103, 114, false}, + {"not yet activating", 116, math.MaxUint64, false}, + {"activating in rewarded epoch", 115, math.MaxUint64, true}, + {"effective fractional reward", 103, math.MaxUint64, true}, + {"still cooling in rewarded epoch", 103, 115, true}, + } { + t.Run(tc.name, func(t *testing.T) { + delegation := &sealevel.Delegation{ + VoterPubkey: votePubkey, StakeLamports: 1, + ActivationEpoch: tc.activation, DeactivationEpoch: tc.deactivation, + CreditsObserved: 1_000, + } + pcs := calculateStakePointsAndCredits(solana.PublicKey{}, &sealevel.SysvarStakeHistory{}, + delegation, voteState, nil, 115, mode) + require.True(t, pcs.Points.Eq(wide.Uint128{})) + require.Equal(t, uint64(2_000), pcs.NewCreditsObserved) + require.Equal(t, tc.advance, shouldForceCreditsOnly(pcs, 1, tc.activation, 115, 1_000, mode)) + }) + } +} + +func TestInactiveStakePreservesExplicitCreditUpdates(t *testing.T) { + pcs := CalculatedStakePoints{NewCreditsObserved: 2_000, Inactive: true} + mode := RewardCalculationMode{FullAlpenglow: true} + require.False(t, shouldForceCreditsOnly(pcs, 1, 103, 115, 1_000, mode)) + require.True(t, shouldForceCreditsOnly(pcs, 0, 103, 115, 1_000, mode), "disabled inflation") + require.True(t, shouldForceCreditsOnly(pcs, 1, 115, 115, 1_000, mode), "activation epoch") + pcs.ForceCreditsUpdateWithSkippedReward = true + require.True(t, shouldForceCreditsOnly(pcs, 1, 103, 115, 3_000, mode), "vote credit rewind") + + // Tower does not inherit Alpenglow's automatic skipped-reward advance. + pcs.ForceCreditsUpdateWithSkippedReward = false + pcs.Inactive = false + require.False(t, shouldForceCreditsOnly(pcs, 1, 103, 115, 1_000, RewardCalculationMode{})) +} + func TestInflationRewardsUseHistoricalSlotTimeTransitions(t *testing.T) { schedule := &sealevel.SysvarEpochSchedule{ SlotsPerEpoch: 54_000, diff --git a/pkg/rewards/inactive_stakes_test.go b/pkg/rewards/inactive_stakes_test.go new file mode 100644 index 000000000..c1edc7c95 --- /dev/null +++ b/pkg/rewards/inactive_stakes_test.go @@ -0,0 +1,118 @@ +package rewards + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/accounts" + "github.com/Overclock-Validator/mithril/pkg/accountsdb" + "github.com/Overclock-Validator/mithril/pkg/bankhash" + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/Overclock-Validator/mithril/pkg/global" + "github.com/Overclock-Validator/mithril/pkg/lthash" + "github.com/Overclock-Validator/mithril/pkg/sealevel" + bin "github.com/gagliardetto/binary" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +// This reduced incident fixture holds all unrelated slot effects constant. +// The production reward calculator, spool distributor and bank hasher must +// leave the 33 fully cooled stakes unchanged. See testdata/epoch116/README.md. +func TestEpoch116InactiveStakeBankHash(t *testing.T) { + var fixture struct { + ParentBankhash string `json:"parent_bankhash"` + Blockhash string `json:"blockhash"` + ExpectedBankhash string `json:"expected_bankhash"` + OriginalBankhash string `json:"original_bankhash"` + BaseLtHash []byte `json:"base_accounts_lt_hash"` + StakeHistory []byte `json:"stake_history"` + Stakes []struct { + Account *accounts.Account `json:"account"` + VoteEpochCredits sealevel.EpochCredits `json:"vote_epoch_credits"` + OriginalUpdatedDataSHA256 string `json:"original_updated_data_sha256"` + } `json:"stakes"` + } + raw, err := os.ReadFile("testdata/epoch116/inactive-stakes.json") + require.NoError(t, err) + require.NoError(t, json.Unmarshal(raw, &fixture)) + require.Len(t, fixture.Stakes, 33) + + dir := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(dir, "accounts"), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(dir, "largest_file_id"), make([]byte, 8), 0o644)) + db, err := accountsdb.OpenDb(dir) + require.NoError(t, err) + db.InitCaches() + t.Cleanup(db.CloseDb) + global.ClearPendingStakePubkeys() + t.Cleanup(global.ClearPendingStakePubkeys) + + parents := accounts.NewMemAccounts() + var storedAccounts, erroneousUpdates []*accounts.Account + votes := make(map[solana.PublicKey]*sealevel.VoteStateVersions) + for _, row := range fixture.Stakes { + acct := row.Account + stake, err := sealevel.UnmarshalStakeState(acct.Data) + require.NoError(t, err) + require.Equal(t, uint64(114), stake.Stake.Stake.Delegation.DeactivationEpoch) + require.Equal(t, uint64(115), row.VoteEpochCredits.Epoch) + voteKey := stake.Stake.Stake.Delegation.VoterPubkey + votes[voteKey] = &sealevel.VoteStateVersions{ + Type: sealevel.VoteStateVersionV4, + V4: sealevel.VoteState4{EpochCredits: []sealevel.EpochCredits{row.VoteEpochCredits}}, + } + require.NoError(t, parents.SetAccountWithoutLock(acct.Key, acct)) + storedAccounts = append(storedAccounts, acct) + global.EnqueuePendingStakePubkey(6264000, acct.Key) + + // Independently reconstruct the logged erroneous write, and verify its + // byte hash before using it to establish the original bad bank hash. + bad := acct.Clone() + stake.Stake.Stake.CreditsObserved = row.VoteEpochCredits.Credits + require.NoError(t, sealevel.MarshalStakeStakeInto(stake, bad.Data)) + require.Equal(t, row.OriginalUpdatedDataSHA256, fmt.Sprintf("%x", sha256.Sum256(bad.Data))) + erroneousUpdates = append(erroneousUpdates, bad) + } + stored := make(chan struct{}) + require.NoError(t, db.StoreAccounts(storedAccounts, 6264000, func() { close(stored) })) + <-stored + count, err := global.FlushPendingStakePubkeysThrough(dir, 6264000) + require.NoError(t, err) + require.Equal(t, len(fixture.Stakes), count) + db.RootedDurable = true + + f := &features.Features{} + f.EnableFeature(features.AccountsLtHash, 0) + f.EnableFeature(features.RemoveAccountsDeltaHash, 0) + calculateHash := func(updates []*accounts.Account) string { + ctx := &sealevel.SlotCtx{ + Features: f, ParentAccts: parents, + AcctsLtHash: new(lthash.LtHash).InitWithHash(fixture.BaseLtHash), + } + return solana.HashFromBytes(bankhash.CalculateBankHash(ctx, nil, updates, + solana.MustHashFromBase58(fixture.ParentBankhash), 0, + solana.MustHashFromBase58(fixture.Blockhash))).String() + } + require.Equal(t, fixture.OriginalBankhash, calculateHash(erroneousUpdates)) + + var history sealevel.SysvarStakeHistory + require.NoError(t, history.UnmarshalWithDecoder(bin.NewBinDecoder(fixture.StakeHistory))) + newRateEpoch := uint64(0) + result, err := CalculateRewardsStreaming(db, 6264000, &history, &newRateEpoch, + votes, PointValue{Rewards: 12922370184029}, 115, [32]byte{}, + &sealevel.SlotCtx{Features: f}, f, RewardCalculationMode{FullAlpenglow: true}) + require.NoError(t, err) + updated, _, distributed, burned := DistributeStakingRewardsFromSpool( + db, result.SpoolDir, result.SpoolSlot, 0, 6264001, nil) + require.Zero(t, distributed) + require.Zero(t, burned) + require.Equal(t, fixture.ExpectedBankhash, calculateHash(updated)) + require.Zero(t, result.NumStakeRewards) + require.Equal(t, uint64(1), result.NumPartitions) + require.Empty(t, updated) +} diff --git a/pkg/rewards/rewards.go b/pkg/rewards/rewards.go index ce0e824a0..bfff0ebfc 100644 --- a/pkg/rewards/rewards.go +++ b/pkg/rewards/rewards.go @@ -43,6 +43,9 @@ type CalculatedStakePoints struct { Points wide.Uint128 NewCreditsObserved uint64 ForceCreditsUpdateWithSkippedReward bool + // Inactive is set by Alpenglow points calculation when the delegation + // has neither effective nor activating stake in the rewarded epoch. + Inactive bool } const legacyInflationSlotsPerYear = 78_892_314.984 @@ -697,11 +700,14 @@ func calculateStakePointsAndCredits( } newObserved = max(newObserved, latest.Credits) - effectiveStake := delegation.StakeActivatingAndDeactivating( + status := delegation.StakeActivatingAndDeactivating( rewardedEpoch, stakeHistory, newRateActivationEpoch, - ).Effective - if earnedCredits == 0 || effectiveStake == 0 { - return CalculatedStakePoints{NewCreditsObserved: newObserved} + ) + if earnedCredits == 0 || status.Effective == 0 { + return CalculatedStakePoints{ + NewCreditsObserved: newObserved, + Inactive: status.Effective == 0 && status.Activating == 0, + } } totalStake := mode.RewardEpochDelegatedStakes[delegation.VoterPubkey] if totalStake == 0 { @@ -711,7 +717,7 @@ func calculateStakePointsAndCredits( } } points := wide.Uint128FromUint64(earnedCredits). - Mul(wide.Uint128FromUint64(effectiveStake)). + Mul(wide.Uint128FromUint64(status.Effective)). Div(wide.Uint128FromUint64(totalStake)) return CalculatedStakePoints{Points: points, NewCreditsObserved: newObserved} } @@ -789,10 +795,14 @@ func shouldForceCreditsOnly( pointValueRewards, activationEpoch, rewardedEpoch, creditsObserved uint64, mode RewardCalculationMode, ) bool { + // Agave's skipped-reward credit advance applies only to effective or + // activating Alpenglow stakes. Fully cooled stakes must retain their + // account bytes, even though their vote account has earned new credits. + // The explicit forced-update cases still take precedence. return pcs.ForceCreditsUpdateWithSkippedReward || pointValueRewards == 0 || activationEpoch == rewardedEpoch || - (mode.FullAlpenglow && pcs.Points.Eq(wide.Uint128{}) && pcs.NewCreditsObserved != creditsObserved) + (mode.FullAlpenglow && !pcs.Inactive && pcs.Points.Eq(wide.Uint128{}) && pcs.NewCreditsObserved != creditsObserved) } // CalculateRewardsStreaming performs a streaming calculation of stake rewards. diff --git a/pkg/rewards/testdata/epoch116/README.md b/pkg/rewards/testdata/epoch116/README.md new file mode 100644 index 000000000..41cd2cb6a --- /dev/null +++ b/pkg/rewards/testdata/epoch116/README.md @@ -0,0 +1,49 @@ +# Epoch 115 → 116 inactive-stake regression + +`inactive-stakes.json` is a reduced fixture from the Alpenglow failure at slot +6,264,001 on 2026-09-21, running Mithril `320ce8da`. It contains the 33 fully +cooled stake accounts that Mithril incorrectly rewrote, their vote accounts' +epoch-115 credits, and the saved StakeHistory sysvar. All 33 delegated stakes +had deactivation epoch 114 and zero effective/activating stake in epoch 115. + +The source was the supplied `/mnt/mithril-accounts` checkpoint at slot 6,264,000 +and `footer-bankhash-mismatch-slot-6264001.json` in the supplied logs. AccountsDB +was opened read-only. Account bytes are public chain data; no keys or validator +identity files are included. + +To isolate the defect, `base_accounts_lt_hash` includes all correct slot effects +and the unchanged 33 accounts. The other 792 modified accounts were reconstructed +from the parent checkpoint and deterministic slot updates; all 825 reconstructed +accounts matched the diagnostic's individual SHA-256 data hashes. Combining +their deltas with the saved parent LtHash reproduced both the diagnostic LtHash +checksum and the original bad bank hash. Undoing only the 33 credit-only writes +then reproduced the exact expected footer hash: + +| State | Bank hash | +| --- | --- | +| Original 33 erroneous writes | `CCY2QcFoGGAodDaB9RCAW2RUbbZm2xMo9dJw8tKHdDcG` | +| Preserve the 33 inactive accounts | `BBXWdsTHc3EdN8zXbcZZ8vwqtYjzggD3gp8CqkZuBvBm` | + +`TestEpoch116InactiveStakeBankHash` checks each reconstructed erroneous account +against its recorded data hash, checks the bad bank hash, and then runs the +production streaming calculator, spool distributor and bank hasher. Before the +fix, that path emitted 33 zero-lamport writes and produced the bad hash. After +the fix it emits no writes for these stakes and produces the expected hash. +Other slot effects are held constant; this is not a full signed-shred replay or +a replay of later slots. + +Reference behavior: + +- [Agave ab655329, inflation_rewards/mod.rs:254–270](https://github.com/anza-xyz/agave/blob/ab6553293094e59dee7d3e7c928c7fa1023d0684/runtime/src/inflation_rewards/mod.rs#L254-L270) + restricts skipped-reward credit advancement to effective or activating stake, + preserving the explicit forced-update cases. +- [Firedancer 57d39904, fd_rewards.c:649–678](https://github.com/firedancer-io/firedancer/blob/57d39904e3886731b96b9174ff8763ee7c36e3ad/src/flamenco/rewards/fd_rewards.c#L649-L678) + rejects an inactive credit-only update. Its + [points calculation at lines 908–917](https://github.com/firedancer-io/firedancer/blob/57d39904e3886731b96b9174ff8763ee7c36e3ad/src/flamenco/rewards/fd_rewards.c#L908-L917) + retains the inactive flag from the existing activation-status calculation. + +Run with: + +```sh +go test ./pkg/rewards -run TestEpoch116InactiveStakeBankHash -count=1 -v +``` diff --git a/pkg/rewards/testdata/epoch116/inactive-stakes.json b/pkg/rewards/testdata/epoch116/inactive-stakes.json new file mode 100644 index 000000000..e6dec5b48 --- /dev/null +++ b/pkg/rewards/testdata/epoch116/inactive-stakes.json @@ -0,0 +1,1693 @@ +{ + "parent_bankhash": "B2Yi5ecGCiZRpvjdrLeQj2SdSncKgi6zEmVJxpMfzfyB", + "blockhash": "GBQcgk9JL3YFaK1GvpKLGpCbpmJ3My4oYWztnAJ3wirY", + "expected_bankhash": "BBXWdsTHc3EdN8zXbcZZ8vwqtYjzggD3gp8CqkZuBvBm", + "original_bankhash": "CCY2QcFoGGAodDaB9RCAW2RUbbZm2xMo9dJw8tKHdDcG", + "base_accounts_lt_hash": "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", + "stake_history": "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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "stakes": [ + { + "account": { + "Slot": 6210151, + "Key": "3mZwezEBuKcCs42NfAEZfdCbCMyPaV6wnus263mgrhjP", + "Lamports": 59002277492, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI9JSTu3nHzfydAzLjeA87Aht6Ost2Mol90Y42gdbSqB9HisvA0AAABnAAAAAAAAAHIAAAAAAAAAAAAAAAAAAACZzNzwgAgAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "7e91c9547ff107e827f22f226df525ee83be7e9eb22bc8afd812c92c235c69ba", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 9430889728015, + "PrevCredits": 9349889838233 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "453ETE3U7Usc4ss87sszs3EvTR5ZYQQWcZHgs9XiAZdY", + "Lamports": 3306766663971, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADSIHvAB+hqCeyUMqrtA7RF7gf0pF5+WOdx24l28NcLZoyuE6gEDAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAALJPnDAREAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "88983f1b8c3153b155fc54b8ffcdbe468b3356ce007cdab77d95cab1d3c50dc3", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 18772882129308, + "PrevCredits": 18699280524299 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "462wmoxLUzHfxcURxwkMj5f7cmeVXeSeaFU5uXd9jrQE", + "Lamports": 30092278839, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB7glTPNCA5Xp0adNqBsSaCIXSwoB3DhCXGyxm9mwIQut+aAAQcAAAA0AAAAAAAAAHIAAAAAAAAAAAAAAAAAAABJX296lgQAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "a0c0c400478bf441c374e56141b0122846da672b9b1a5d01157374dadfa83649", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 5082302209818, + "PrevCredits": 5044345724745 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "4aNZYt3giNatv4J58sYHwRNs8L6ZQpLqG9GZiyqrXUqS", + "Lamports": 2329209663118, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK8Fa5ghODdNV981iyfO/aADxuCTmU56Dtg15Wf9Xk+LDhmUTx4CAAABAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAAeHbzX0wkAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "78674eaaabfbe62cf2e558bd33e01dd53a33a7a4024e52eba673bb1acf73ff83", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 10909280173169, + "PrevCredits": 10805462179102 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "4u4XDhXRtXA9QqP8uC9vpoyw4KLaeWHm7g2gP4aLDGK1", + "Lamports": 59002277492, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABtRAg7KdM61d5wq/bIMit41UurMHM2/gwzsJrDkSJWr9HisvA0AAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAC8dSEI5AgAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "f49eb225b3f858e7609ffecf5e9d2734cb28fe754ca697800e0a44a144ab0f3d", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 9855957906862, + "PrevCredits": 9775481976252 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "4zrGtmZj2s7akr4FyiJ7E3fQY4n59cZoVEmL192GttRq", + "Lamports": 3956852941834, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApBQ/86W982eATp1soYW/TvCCjcBXX1dK637et+U7Qaio6tRpkDAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAABz+BFZ1gkAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "6a0c148dbf978bfec9e6e5e3227ad57e6089c056b2d23730b85be7bdcc2a3384", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 10900522634265, + "PrevCredits": 10816222001267 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "5ZEUi72iZM7ozwisZV2jezjgR3BLBQToz8ZxpdnqYaue", + "Lamports": 10521999279304, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEvFm+VinHP4nTd2zkbv4cVplRt3TlVeTEidtXhPv7vISK/k15EJAABiAAAAAAAAAHIAAAAAAAAAAAAAAAAAAABDEW5ATQcAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "a0c42ad17b70bd37283facbe1655a9401660d0ba96e7c57fad1ba452c46c77c8", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 8111781721836, + "PrevCredits": 8028374831427 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "6dNDcZRYeX7zoGirAbYxjJVCoHg14KLcy1ndb5a6hLtp", + "Lamports": 92402274844, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALJZyCh5aoPz7tWdy6VqoilN4lxeoCa10yfJpJBoYSa8nPx3gxUAAAAAAAAAAAAAAHIAAAAAAAAAAAAAAAAAAACEXlHvNAgAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "4914fbc352aae3350f8dd39a94733fc3bd927e1ccd2ed471ac81318d759594f9", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 9114167602897, + "PrevCredits": 9023446408836 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "7Xwjq1Pu1ibBufp4mdu51yrRzUoadak98sdj6BQhGmBH", + "Lamports": 59002277493, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMb+8swy4nz1EbP63bJ/XUX49DQYo/CiTC3dFaYhgfYs9XisvA0AAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAACE/LmcrgcAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "01df0af720551818974311706b7a5ebb9c5b333eb90e877e0f227f08fd4cf9e3", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 8523194289275, + "PrevCredits": 8446535138436 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "7kGbq8H94roSeiCvasCE8kWq2Kx1dCTZQTUnkiPqibTg", + "Lamports": 59002277493, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaFSKZ/FpeRshn8N9+v+AkQycTuIPwqrs5Yf2mgYnGv9XisvA0AAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAAF9xJssgcAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "f04a2844299ed1e7c980b160a89a508f61a135ee5d333ddbb948aabb00b80a6d", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 8544891256299, + "PrevCredits": 8462898755333 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "7uyNRdfhmk6SwJPfM172LF4ajEUKUkP9RC2okeB3RZsC", + "Lamports": 48931548636, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLFnpY8I58W61QVYzmx6wfM3seg+Q0PxwXVhMb9YvpaXFhpZAsAAAAzAAAAAAAAAHIAAAAAAAAAAAAAAAAAAABZwrzIpQYAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "d2a561a19b6f7cd09f21985876978ae5ab1e11c7cc9262c5a17caf9af6e0c92b", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 7371534095782, + "PrevCredits": 7309107184217 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "8QYDcU8pvmMKvovzUiqWq4H7ZFiWpbet4HwUnDy5XTYK", + "Lamports": 1002282880, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgYhGqxkhQFABeF03ECpK9RUUF1hdavhflLAu/MvP/rAMqaOwAAAABtAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAAs5ZJ1sQUAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "ff84dc2a8426d4deb78786afc87899cbb4c052b6c20346fac76f7fcd865026c5", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 6318982736717, + "PrevCredits": 6259739911468 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "8n3LHx61MeuZTF28hkHvsiEtKSEXLUQDYCMHS8sCQ264", + "Lamports": 3058382969924, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9GQ73y9miIEoMD1/GtvVm6Z6RC6u1sNUKM6BM6jWJ0xMaxFcgCAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAABQGTGKHhEAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "20b17fb68d98d290dfa4742237ac719219e23147a8f7cdfb27ac1be790f9e8d8", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 18900384617754, + "PrevCredits": 18822865164624 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "98joChCUmuTBYZx8xHDJzJzbF37HPuP4EHqm3BwD8KSo", + "Lamports": 132002282880, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAite2/mh1w1kW4RMXwAjDtR4kt3TcBCr/oy5ngX7hOQACjQux4AAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAACf+Oklb0QAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "7c11ae1d9490b3f0876c515a564337a0170c21fae8c3cb3f43c66e5dee2cd34f", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 76179624781196, + "PrevCredits": 75244168149151 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "9PAPBcFDq536ZzMWhNywZAYPZt5mF92VadrJc5ctoQqY", + "Lamports": 1894659468576, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALo09fj+TmHMhldcHlC9iwfK4NZlNl+rkERD8XdoYYH1oFdeIrkBAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAC9udrpXwwAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "ba19d0de0f1d7ccded2e93dd4b9d57b8077397c396005d1918b4d6dd719196b7", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 13714985011803, + "PrevCredits": 13606084852157 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "ALrrFs8DAkNHjyce6LhwkotcBQp8U9dGKb4prUHZp5GF", + "Lamports": 1002282880, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACZSY+h/WqtJAAWy9FwQ5zvCPyGbQUYwp5yepcxIn0qbAMqaOwAAAABwAAAAAAAAAHIAAAAAAAAAAAAAAAAAAACXOQj8AgEAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "a96af37ed2c6d36cfc77971b4432cb7c62f4e045b3883a5b7e2e6434fa21dec9", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 1147511243813, + "PrevCredits": 1112329959831 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "ARPzersuLPyg9ZJaxYuKc2eDKKg8t4qWXpTy9TzKjLJU", + "Lamports": 44842277493, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF6PbC1s5mob1cKneVa1heDI7d/UjwFUwoZKG6DN1okV9QSscAoAAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAAZRdBpGAcAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "7c3b359b3e2c72b291d1703977f464395b1772f9197466913c9f87661d3ebac0", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 7864995243760, + "PrevCredits": 7801435866393 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "AXC6s8QGstr1nnTJ5ranGEeSZ7rcHKVeQav4r4J8icJL", + "Lamports": 2951443346384, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2DXLDpqE+RtusMr3795aLUMJhXsVQnXUHgtyhqt1j0UJ6YL68CAAAAAAAAAAAAAHIAAAAAAAAAAAAAAAAAAACQUqzjgQ8AAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "0e44daa0385ca084667f7e44f0fb96571b6b963f6bd046bf66c2c7fb31e232ce", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 17293215270489, + "PrevCredits": 17050544919184 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "BWiQJLo1TGhbNp3ionXVFPnSgfGimGU9FVHm1hdwLKbJ", + "Lamports": 30092278839, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUkqa5BeE51KvSv12H//GyC8rI6ScrXABI9UawzUXiot+aAAQcAAAA0AAAAAAAAAHIAAAAAAAAAAAAAAAAAAADlkymHrAQAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "8cf243491cd54f9d1efc732287caf92bd392dfc1ad410c74c94e2d8f4e4deb18", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 5181125525357, + "PrevCredits": 5139048535013 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "CbFTDpV9HjcnVQReCUwuZmLMp6PjMoWZBA7VTvLzszQ", + "Lamports": 5114239502629, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANp1F9fR0omLpsfk9YL0X7w0EuA6D8E39bIn27KGqRgppfNKwKYEAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAADfLChErwgAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "6bf6057cfa3c4d2a7d786bb5f325032604cca8eb30391468f2caf1b71406ce57", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 9639820890790, + "PrevCredits": 9548855782623 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "D5Zx5bsdVDg2kBZkPvaF59ULdKd8LACtScaWE7NEAbtw", + "Lamports": 1002282880, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANbDQeBxoTnMt62Ygze0/L0wq3Tv5ynlZXdjhuu6lY/4AMqaOwAAAABuAAAAAAAAAHIAAAAAAAAAAAAAAAAAAABO6xz/EAYAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "2480dc54ffb62581eec0ca3bcb7201b2ba4260a97bdc186e2f2345241abade3c", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 6731352374437, + "PrevCredits": 6670069328718 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "DNCCPW9Bsv8SdcDMFDA7b2HT5jKxgUnBrA2gkKXwe5Bg", + "Lamports": 44252278839, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEZlguB/hNNN/i6p/xPZEHXCTr6qIxawtvTnx9A4WE77t1qBTQoAAAAWAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAAJpOYcEAUAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "a02301fdf180b2f542124caf7da178cae4397acc833030b9dca28116d6e2b747", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 5630002073869, + "PrevCredits": 5566762492937 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "E2TDLwJNmzvg6K7MhrkQ3HV4n4mp2zL49FqGwV4tFUfC", + "Lamports": 1002282880, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIZbuXtVwiaoNZBvLxeR/DEy46f1oQ/1DEqV1R+wxiWyAMqaOwAAAABvAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAA+JHwoWwgAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "2ecdc0e446f6e0c112c947c79b6976cbdeea776ecb4fe62ece89b1f725e3ae48", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 9263388828449, + "PrevCredits": 9187614270526 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "EA3MVKFbieGtDZDG4bmCiwnE2HWndMuWcjMjRNSrbMBC", + "Lamports": 3787600669028, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGc29KCGn33qCWHYLj1Li6Q1LaT9ffcV8Q3PJ6AssCM5NN03nEDAAAGAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAChfXpwQQkAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "c0bf9bd502e0b675a186b4b855812ae1915d04110a951499d911e6174ed2b2b2", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 10252916692527, + "PrevCredits": 10176664599969 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "Eijmmf2xFvjStkcu6WQRj93x5iYP1uv5zuZP8PARdqd", + "Lamports": 3449661803946, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC3bR7fFvVz26LQA9LQq1JGFKWXl7hg6BaC5sp0SpTflKvi6LyMDAAAAAAAAAAAAAHIAAAAAAAAAAAAAAAAAAABF19K7FxEAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "a4810c657cb2161f3c47f0452d78dc74d9d4597763d82c3dfd48b3d1dc43dfe9", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 18871239559140, + "PrevCredits": 18793633077061 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "FxhNhXDkoqMTcXqyHxYXW8BJAdpWH5qdyz3qgpiySrKn", + "Lamports": 71250450995, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHeMPT3PZmkZZcARjam+dHkyZP326BpHWqy/ktZchIums8S4lhAAAAAgAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAAXAHi5oAYAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "ab580bd8bb513651006208d529d58dbe8e40d0306141e4b9bbe32c0559040af8", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 7382306449058, + "PrevCredits": 7287376183319 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "GAyfc6a5E5uzXQgQ4KXHuJyunyGNtLGZgUUdWKEL4Df9", + "Lamports": 59002277493, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0QDCxOaKoUheu2WKN7BFVixKbK4ogx2BYksnFTbTzL9XisvA0AAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAACYANgjswcAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "d635c01a4fc8bc710b4ca6d9b0b0ba960b9ab3ad8ac0359ee8adbdd62ad30f3a", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 8554183692312, + "PrevCredits": 8465981898904 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "H2dM1xYSeuZaujtByMa4jvPwUVYjfxzUL3K9iVLaCW9G", + "Lamports": 64469922880, + "Data": "AgAAAIDVIgAAAAAAzi6d3+ZZh+CVMQop8DVJNLt57mKRukRhiZHz0dnIWEDOLp3f5lmH4JUxCinwNUk0u3nuYpG6RGGJkfPR2chYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiQo+w+o08xym3k1JBF+HYHIJZGTDaZaufK/simzs1bwB6SAg8AAAAgAAAAAAAAAHIAAAAAAAAAAAAAAAAAAACe3jqUQyIAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "b39033f338419cea17108c3989a6331d8272749095fcf3ad856031cbab264110", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 38085420659301, + "PrevCredits": 37673645039262 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "Hh8u21hJnAvE9PMNBmNTsDUTseY6bYfwvbZQgj8R2vcU", + "Lamports": 44842277493, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJOHGqQMr9AgJ5hIj044ilcd/38t2ijdTubnJyZ6iT+d9QSscAoAAABGAAAAAAAAAHIAAAAAAAAAAAAAAAAAAABymDuk3wUAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "0569f4f3dbc16ab5c284f6efaefd1e2b6b5b8db18ffa1b1fbd0da98468c54d78", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 6525335322632, + "PrevCredits": 6458091214962 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "HicPaN5ogXhE5jQ99WotjGUQLPJ11bdCqXjJhMKUq5N", + "Lamports": 1690908095830, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ1KF6URsOxn3mdoAgUdjof1ae904ReOis3chjrZx0h+1h/XsYkBAAAAAAAAAAAAAHIAAAAAAAAAAAAAAAAAAADt4ogGQgUAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "47ad9ddb61ff1edee564852ac0e48ff45b47a377b0ca1a5f1e0aef74a27627b4", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 5826444097536, + "PrevCredits": 5781135614701 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "c6Ev8GfADHdrGtvH1VqwH7JmN28PisZgGfUy64RE7np", + "Lamports": 3782196946839, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDb0jorArBVkC7lcchWRZHVxqD+PrkqtMdnxFvaxkbfF5JenHADAAACAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAA1TQUrswgAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "b361cd2d770b1b3432d58f8098c654f300754fab58787d6033dae1e724618467", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 9647673972566, + "PrevCredits": 9565613935925 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "eHboMWq5bmx1DxdKw79zpse75XLk2GEgeRnJG9nysJn", + "Lamports": 5125666350406, + "Data": "AgAAAIDVIgAAAAAA/LTRFge4YpO0iugL7CcyV2OPIzVpqNRFyNcRlWoBALL8tNEWB7hik7SK6AvsJzJXY48jNWmo1EXI1xGVagEAsgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPznsTwPeDl/u5+jig9MvOBeS/RKUvjM0z9ITapNTvPoxs9iaakEAAAAAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAAEpkAsaggAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "8e052b16e36ecb6e80919ada5456816e25e3b804fe9119319cff9861e7242e25", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 9334067720448, + "PrevCredits": 9252101989892 + } + }, + { + "account": { + "Slot": 6210151, + "Key": "v5pSSEZAuvG9ewGhdMNVcTVHeJPzFnGse3mX4FBEyLT", + "Lamports": 1082110856099, + "Data": "AgAAAIDVIgAAAAAAzi6d3+ZZh+CVMQop8DVJNLt57mKRukRhiZHz0dnIWEDOLp3f5lmH4JUxCinwNUk0u3nuYpG6RGGJkfPR2chYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHeMPT3PZmkZZcARjam+dHkyZP326BpHWqy/ktZchIumI3ay8vsAAAAgAAAAAAAAAHIAAAAAAAAAAAAAAAAAAAAXAHi5oAYAAAAAAAA=", + "Owner": [ + 6, + 161, + 216, + 23, + 145, + 55, + 84, + 42, + 152, + 52, + 55, + 189, + 254, + 42, + 122, + 178, + 85, + 127, + 83, + 92, + 138, + 120, + 114, + 43, + 104, + 164, + 157, + 192, + 0, + 0, + 0, + 0 + ], + "Executable": false, + "RentEpoch": 18446744073709551615, + "IsDummy": false + }, + "original_updated_data_sha256": "053f427dd888bbe45c6e0f772faf6eac1f9ede5822cc74477c757eaac75cb8b1", + "vote_epoch_credits": { + "Epoch": 115, + "Credits": 7382306449058, + "PrevCredits": 7287376183319 + } + } + ] +} \ No newline at end of file From 5bfcdd6900c93a36c67671246a5f8d1e0da6f185 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Tue, 15 Sep 2026 07:21:18 -0500 Subject: [PATCH 2/4] replay: retire completed rewards bookkeeping after durable promotion --- docs/rewards-unwind-retirement.md | 58 +++++++++++++ pkg/replay/block.go | 7 ++ pkg/replay/rewards_retirement.go | 49 +++++++++++ pkg/replay/rewards_retirement_test.go | 119 ++++++++++++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 docs/rewards-unwind-retirement.md create mode 100644 pkg/replay/rewards_retirement.go create mode 100644 pkg/replay/rewards_retirement_test.go diff --git a/docs/rewards-unwind-retirement.md b/docs/rewards-unwind-retirement.md new file mode 100644 index 000000000..edf207be8 --- /dev/null +++ b/docs/rewards-unwind-retirement.md @@ -0,0 +1,58 @@ +# Retiring durable rewards bookkeeping + +A completed partitioned-rewards distribution used to leave its in-memory +descriptor alive for the rest of the replay attempt. The fork-switch guard +rejects any such descriptor because account-overlay unwind cannot restore the +consumed spool or its distribution counters. This is necessary while completion +is speculative, but unnecessarily forces checkpoint replay after completion +has become durable. + +Replay now observes the inactive EpochRewards sysvar in a successfully executed +bank's immutable snapshot, with zero partitions remaining. It remembers that +bank's slot and the exact distribution descriptor. Only applying a successful +durable fold through that slot retires the descriptor. Later bank observations +do not move the completion slot forward. A new descriptor/epoch invalidates the +old evidence; missing sysvars or unknown completion retain the old fallback. + +## Safety and recovery contract + +- Completion in memory, certificate finality, and submitting a fold do not + authorize retirement. Failed folds leave the durable watermark unchanged. +- Active distribution and completed-but-not-durable distribution retain the + existing rewards guard. No spool reconstruction or rewards rollback is added. +- After retirement, in-memory switches still require the existing epoch, + vote/stake-cache, parent-context, sysvar and transaction-status checks. + Switches at/below the durable watermark still require durable recovery. +- Completion evidence is replay-thread-owned and process-local. It does not + change checkpoint formats, signing reservations, persisted vote history, + clean-shutdown rules or restart authorization. Restart retains the existing + persisted EpochRewards validation. No extra file or disk sync is introduced. + +## Incident motivating the change + +On Zen 5, distribution completed at slot 3,942,001. At a later parent-linked +switch, the durable checkpoint was already 3,944,067; child 3,944,076 selected +parent 3,944,073, abandoning the suffix from 3,944,074. The remaining descriptor +forced the rewards-window fallback even though completion was below the root. +Checkpoint recovery re-fetched previously received blocks, with logged waits +of 2.739 seconds and 0.967 seconds. A buffered 665-transaction block waited +3,613.510 ms for replay admission and then executed in 7.520 ms. + +These are incident observations, not a before/after benchmark or a measurement +of checkpoint encoding/fsync time. Thirteen observed FAST aggregates omitted +our vote during the recovery interval; that does not prove absence from every +FAST aggregate or a single cause for all thirteen omissions. No live latency +improvement is established until a comparable switch exercises the new path. + +## Validation + +`rewards_retirement_test.go` covers active/missing bank state, unknown completion, +the exact durable boundary, later-bank observations, generation changes, failed +and successful folds, and an exact-parent unwind after retirement (including +account values, resume state and immutable rewards sysvars). Existing unwind +tests still require fallback for zero-remaining bookkeeping without retirement, +cross-epoch switches, dirty vote/stake caches and invalid parent snapshots. + +Full replay/rewards race suites passed locally and in the combined native +build; native node recovery/checkpoint race tests, vet and validator build also +passed. These are software tests, not mainnet power-loss qualification. diff --git a/pkg/replay/block.go b/pkg/replay/block.go index 495ed53a8..c31153b1a 100644 --- a/pkg/replay/block.go +++ b/pkg/replay/block.go @@ -1747,6 +1747,7 @@ func ReplayBlocks( var unwoundParentBankSysvars *sealevel.BankSysvars var partitionedEpochRewardsEnabled bool var partitionedRewardsInfo *rewards.PartitionedRewardDistributionInfo + var rewardsCompletion partitionedRewardsCompletion var featuresActivatedInFirstSlot []*accounts.Account var parentFeaturesActivatedInFirstSlot []*accounts.Account @@ -2063,6 +2064,10 @@ func ReplayBlocks( mithrilState.LastRootedSlot = promotedThrough mithrilState.LastRootedBankhash = rootedCtx.Bankhash mithrilState.LastRootedContext = rootedCtx + if rewardsCompletion.retire(&partitionedRewardsInfo, promotedThrough) { + rewardsHoldBelowSlot = 0 + mlog.Log.Infof("epoch rewards bookkeeping retired through durable slot %d; later fork switches may unwind in memory", promotedThrough) + } if transactionStatuses.Root(promotedThrough) { mlog.Log.Infof("transaction status cache reconstructed complete %d-root coverage through durable slot %d", maxTransactionStatusRoots, promotedThrough) @@ -2914,6 +2919,7 @@ func ReplayBlocks( boundaryParentCtx = epochBoundaryParentCtx(acctsDb, block, currentEpoch, replayCtx.CurrentFeatures) } partitionedRewardsInfo = handleEpochTransition(acctsDb, partitionedEpochRewardsEnabled, boundaryParentCtx, replayCtx, epochSchedule, replayCtx.CurrentFeatures, block, currentEpoch, rpcc, dbgOpts) + rewardsCompletion = partitionedRewardsCompletion{} currentEpoch = block.Epoch justCrossedEpochBoundary = true // While partitioned rewards are distributing, hold durable promotion @@ -3026,6 +3032,7 @@ func ReplayBlocks( } // The successful child now owns its derived snapshot. Any later bank uses // lastSlotCtx; the one-shot retained unwind bridge is no longer needed. + rewardsCompletion.observeBank(partitionedRewardsInfo, lastSlotCtx.BankSysvars()) unwoundParentBankSysvars = nil postProcessBlockStart := processBlockEnd statusViewStart := time.Now() diff --git a/pkg/replay/rewards_retirement.go b/pkg/replay/rewards_retirement.go new file mode 100644 index 000000000..a03a00cbf --- /dev/null +++ b/pkg/replay/rewards_retirement.go @@ -0,0 +1,49 @@ +package replay + +import ( + "github.com/Overclock-Validator/mithril/pkg/rewards" + "github.com/Overclock-Validator/mithril/pkg/sealevel" +) + +// partitionedRewardsCompletion is replay-thread-owned, process-local evidence +// that a successfully executed bank contains all effects of this distribution. +// It is not a checkpoint or signing authority. Until that bank is durable, +// tryInLoopUnwind must still reject even a zero-remaining distribution: its +// spool has been consumed and cannot be rolled back with the account overlay. +type partitionedRewardsCompletion struct { + info *rewards.PartitionedRewardDistributionInfo + slot uint64 +} + +// observeBank must run only after successful block execution/publication, using +// that bank's immutable sysvars (never the speculative global sysvar cache). +// If the first completed bank lacks evidence, recording a later descendant is +// conservative: retirement then waits for that later bank to become durable. +func (c *partitionedRewardsCompletion) observeBank(info *rewards.PartitionedRewardDistributionInfo, bank *sealevel.BankSysvars) { + if c.info != info { + *c = partitionedRewardsCompletion{info: info} + } + if info == nil || c.slot != 0 || info.NumRewardPartitionsRemaining != 0 || bank == nil || bank.Slot() == 0 { + return + } + epochRewards, ok := bank.EpochRewards() + if ok && !epochRewards.Active { + c.slot = bank.Slot() + } +} + +// retire is called only when replay applies a successfully committed fold and +// advances LastRootedSlot. Finality, an enqueued/in-flight fold, and a failed +// commit do not acknowledge durability. At this boundary every rewards effect +// is in AccountsDB; in-memory switches above it cannot undo distribution. +// Switches at/below it still take durable recovery, whose persisted +// EpochRewards validation remains unchanged. Restart loses this optional +// evidence and reconstructs state through the existing recovery path. +func (c *partitionedRewardsCompletion) retire(info **rewards.PartitionedRewardDistributionInfo, durableSlot uint64) bool { + if *info == nil || *info != c.info || c.slot == 0 || durableSlot < c.slot || (*info).NumRewardPartitionsRemaining != 0 { + return false + } + *info = nil + *c = partitionedRewardsCompletion{} + return true +} diff --git a/pkg/replay/rewards_retirement_test.go b/pkg/replay/rewards_retirement_test.go new file mode 100644 index 000000000..460584eee --- /dev/null +++ b/pkg/replay/rewards_retirement_test.go @@ -0,0 +1,119 @@ +package replay + +import ( + "bytes" + "encoding/base64" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/accounts" + "github.com/Overclock-Validator/mithril/pkg/rewards" + "github.com/Overclock-Validator/mithril/pkg/sealevel" + "github.com/Overclock-Validator/mithril/pkg/state" + bin "github.com/gagliardetto/binary" + "github.com/mr-tron/base58" + "github.com/stretchr/testify/require" +) + +func TestRewardsRetirementRequiresCompletedBankAndDurability(t *testing.T) { + active := &sealevel.SysvarEpochRewards{Active: true} + var raw bytes.Buffer + require.NoError(t, active.MarshalWithEncoder(bin.NewBinEncoder(&raw))) + activeBank, err := sealevel.NewBankSysvars(5, &accounts.Account{Key: sealevel.SysvarEpochRewardsAddr, Data: raw.Bytes()}) + require.NoError(t, err) + missingBank, err := sealevel.NewBankSysvars(5) + require.NoError(t, err) + for _, tc := range []struct { + name string + remaining uint64 + bank *sealevel.BankSysvars + }{ + {"active distribution", 1, testUnwindBankSysvars(t, 5, 50)}, + {"active bank", 0, activeBank}, + {"missing bank", 0, nil}, + {"missing rewards", 0, missingBank}, + {"unknown slot", 0, testUnwindBankSysvars(t, 0, 50)}, + } { + t.Run(tc.name, func(t *testing.T) { + info := &rewards.PartitionedRewardDistributionInfo{NumRewardPartitionsRemaining: tc.remaining} + var completed partitionedRewardsCompletion + completed.observeBank(info, tc.bank) + require.False(t, completed.retire(&info, 100)) + require.NotNil(t, info) + }) + } + + info := &rewards.PartitionedRewardDistributionInfo{} + var completed partitionedRewardsCompletion + require.False(t, completed.retire(&info, 100), "zero remaining without observed completion is insufficient") + completed.observeBank(info, testUnwindBankSysvars(t, 5, 50)) + completed.observeBank(info, testUnwindBankSysvars(t, 7, 50)) + require.False(t, completed.retire(&info, 4), "uncommitted completion must retain the guard") + require.True(t, completed.retire(&info, 5), "later observations must not postpone recorded completion") + require.Nil(t, info) + require.False(t, completed.retire(&info, 100), "retirement is one-shot") +} + +func TestRewardsRetirementDoesNotCrossGenerations(t *testing.T) { + old := &rewards.PartitionedRewardDistributionInfo{SpoolSlot: 1} + next := &rewards.PartitionedRewardDistributionInfo{SpoolSlot: 10} + var completed partitionedRewardsCompletion + completed.observeBank(old, testUnwindBankSysvars(t, 5, 50)) + require.False(t, completed.retire(&next, 100), "old completion cannot retire new bookkeeping") + completed.observeBank(next, testUnwindBankSysvars(t, 11, 60)) + require.False(t, completed.retire(&next, 10)) + require.True(t, completed.retire(&next, 11)) +} + +func TestRewardsRetirementWaitsForSuccessfulFold(t *testing.T) { + fc := &fakeCommitter{durable: accounts.NewMemAccounts(), failOn: 5} + tail := asyncTestTail(fc, 5, 6) + info := &rewards.PartitionedRewardDistributionInfo{} + var completed partitionedRewardsCompletion + completed.observeBank(info, testUnwindBankSysvars(t, 5, 50)) + job, err := tail.buildFoldJob(6, true) + require.NoError(t, err) + require.NotNil(t, job) + root := uint64(4) + require.False(t, completed.retire(&info, root), "capturing a job does not make its bank durable") + require.Error(t, runFoldJob(fc, job)) + require.False(t, completed.retire(&info, root), "a failed fold leaves the old durable root") + fc.failOn = 0 + require.NoError(t, runFoldJob(fc, job)) + ctx := tail.applyFoldJob(job) + require.NotNil(t, ctx) + root = job.through + require.True(t, completed.retire(&info, root)) +} + +func TestRewardsRetirementAllowsExactParentUnwind(t *testing.T) { + resetVoteStakeDirty() + t.Cleanup(resetVoteStakeDirty) + info := &rewards.PartitionedRewardDistributionInfo{} + var completed partitionedRewardsCompletion + completed.observeBank(info, testUnwindBankSysvars(t, 5, 50)) + tail := newUnrootedTail(&fakeDurable{}, &fakeCommitter{durable: accounts.NewMemAccounts()}, 512, 1, "") + parent := &state.ResumeContext{Slot: 7, Bankhash: base58.Encode(make([]byte, 32)), AcctsLtHash: base64.StdEncoding.EncodeToString(make([]byte, 2048)), Capitalization: 700} + bank := testUnwindBankSysvars(t, 7, 50) + tail.Add(7, []*accounts.Account{testAccount(1, 71)}, testHashBytes(7)) + tail.SetContext(7, parent, bank) + tail.Add(8, []*accounts.Account{testAccount(1, 81)}, testHashBytes(8)) + tail.SetContext(8, &state.ResumeContext{Slot: 8}, testUnwindBankSysvars(t, 8, 999)) + sw := &CertifiedSwitch{Slot: 8} + ms := &state.MithrilState{LastRootedSlot: 4} + sched := &sealevel.SysvarEpochSchedule{SlotsPerEpoch: 432000} + rs, _, reason := tryInLoopUnwind(sw, tail, ms, sched, 0, info) + require.Nil(t, rs) + require.Equal(t, unwindFallbackRewardsWindow, reason) + ms.LastRootedSlot = 5 + markVoteStakeDirty(5) // completed reward writes are also below the durable root + require.True(t, completed.retire(&info, ms.LastRootedSlot)) + rs, restored, reason := tryInLoopUnwind(sw, tail, ms, sched, 0, info) + require.Empty(t, reason) + require.Same(t, bank, restored, "use the surviving bank, never abandoned reward sysvars") + want, err := ResumeStateFromRootedContext(parent, nil) + require.NoError(t, err) + require.Equal(t, want, rs, "resume state must match rebuilding the exact retained parent") + acct, err := tail.GetAccount(8, testAccount(1, 0).Key) + require.NoError(t, err) + require.Equal(t, uint64(71), acct.Lamports, "abandoned account writes must be removed") +} From 35d73a7e49072b363d67abf0c7e2284071f339e8 Mon Sep 17 00:00:00 2001 From: Shaun Colley <79477620+smcio@users.noreply.github.com> Date: Tue, 22 Sep 2026 16:24:36 +0100 Subject: [PATCH 3/4] replay: checkpoint reward windows only after verified completion Keep the epoch boundary replayable when distribution consumes its last spool before a failed bank verification. Apply the hold again after finality gating, and commit the full reward window atomically so ordinary fold batches cannot leave an unrecoverable active EpochRewards checkpoint. Cover failed distribution, finality lag, completion generations, small fold batches and failed-commit retry. Document the epoch-116 diagnosis, bank-hash evidence and verified recovery checkpoint. --- docs/epoch116-reward-divergence.md | 82 +++++++++++++++++++++++++++ pkg/replay/block.go | 36 ++++++++---- pkg/replay/promotion.go | 17 ++++++ pkg/replay/rewards_retirement.go | 13 +++++ pkg/replay/rewards_retirement_test.go | 63 ++++++++++++++++++++ 5 files changed, 201 insertions(+), 10 deletions(-) create mode 100644 docs/epoch116-reward-divergence.md diff --git a/docs/epoch116-reward-divergence.md b/docs/epoch116-reward-divergence.md new file mode 100644 index 000000000..9d93ab52c --- /dev/null +++ b/docs/epoch116-reward-divergence.md @@ -0,0 +1,82 @@ +# Epoch 115 → 116 reward divergence + +The supplied run (`320ce8da`, started 2026-09-18) passed the epoch-boundary bank +at slot **6,264,000**, then stopped at **6,264,001** with a footer bank-hash +mismatch. Both blocks had zero transactions. The mismatch was a deterministic +reward-account state divergence, not a transaction-execution panic. + +At the boundary, rewards for epoch 115 were calculated and vote commissions +were paid. The following bank distributed one staking-reward partition with +724 stake records. Of those records, 33 paid zero lamports but advanced +`credits_observed` on stakes that had deactivated in epoch 114 and were fully +inactive in epoch 115. Agave leaves those accounts unchanged. The missing +effective-or-activating condition in `shouldForceCreditsOnly` caused the extra +writes and changed the LtHash. + +Reconstructing all 825 modified accounts from the saved parent accounts and +the diagnostic's per-account hashes reproduced the original computed bank +hash. Removing only those 33 writes reproduced the expected footer hash +exactly. The [reduced fixture and regression](../pkg/rewards/testdata/epoch116/README.md) +preserve this evidence and link the Agave/Firedancer rules. The code retains +activation status from the existing calculation, so no extra stake scan is +needed. Credit rewinds, disabled inflation, activation-epoch updates and +fractional rewards on effective stakes retain their prior behavior. + +## Checkpoint failure and recovery + +The distribution decremented its remaining-partition counter to zero before +the failing bank's footer was verified. The promotion guard used only that +counter, so graceful shutdown released the epoch hold and persisted slot +6,264,000 with an active EpochRewards sysvar. The spool had already been +consumed. That checkpoint cannot resume distribution directly. + +Promotion now waits for the successfully verified bank's immutable inactive +EpochRewards state. It also waits for that bank to pass the normal finality +and verification gates. The boundary through completion is committed in one +fold, including when the configured batch size is smaller than the rewards +window. A failed bank, a finality cutoff inside the window, or a failed fold +keeps the prior checkpoint. Memory remains bounded by the existing tail cap; +the completion fold can be larger than the usual batch, once per epoch. + +The supplied data includes a retained fold and transaction-status checkpoint +at **6,263,999**, the last slot of epoch 115. Read-only validation confirmed its +57,677,519-byte transaction-status checkpoint, complete retained-root coverage, +selected block identity, inactive EpochRewards state, and all 122 account undo +targets for reverting the boundary bank. With the fixed binary, the existing +`run --rewind-to-slot 6263999` option can restore that boundary and recalculate +rewards, provided historical blocks/shreds remain available. Keep the normal +node configuration and signing-history recovery checks. A direct restart from +6,264,000 cannot recover the missing distribution bookkeeping. + +The supplied local shred spool does not contain slots 6,264,000–6,264,001. On +2026-09-22 the configured primary RPC also reported 6,264,001 as pruned (first +available block 6,764,691). Replaying from the retained boundary therefore needs +another historical source; otherwise use the fixed binary with a fresh +snapshot. Original accounts, ledger, logs and signing history were preserved; +the fix was not deployed to a live validator during diagnosis. + +## Validation + +The branch was fetched and fast-forwarded from `320ce8da` to `be29319f` before +implementation. The reduced production-path regression failed on `be29319f` +with the incident's exact bad hash and passed after the fix. Additional tests +cover inactive, activating, effective and cooling stakes, forced credit-update +exceptions, failed distribution, finality lag, distribution generations, +atomic completion folds and failed-commit retry. + +Passed locally with Go 1.27.1 on linux/amd64: + +```sh +go test -race ./pkg/rewards ./pkg/replay ./pkg/accountsdb ./pkg/bankhash -count=1 +GOMAXPROCS=2 go test -race -p 2 -count=1 \ + ./pkg/alpenglow ./pkg/consensus ./pkg/replay ./pkg/rewards \ + ./pkg/turbine ./pkg/sigverify ./pkg/blockprod/... \ + ./cmd/mithril/node ./cmd/mithril/configcmd +go vet ./pkg/rewards ./pkg/replay ./pkg/accountsdb ./pkg/bankhash +go build ./cmd/mithril +git diff --check +``` + +The CI regression job now includes `pkg/rewards`, so the incident fixture runs +on pull requests. These checks do not establish live post-restart catch-up or +later-slot parity; the original node was not restarted. diff --git a/pkg/replay/block.go b/pkg/replay/block.go index c31153b1a..ecb4e92df 100644 --- a/pkg/replay/block.go +++ b/pkg/replay/block.go @@ -1929,8 +1929,8 @@ func ReplayBlocks( var highestExecutedSlot uint64 // highest slot ProcessBlock has executed; bounds the promotion-gate walk // While partitioned rewards distribute, promotion holds below the boundary // block so a crash-resume always re-runs it (the distribution bookkeeping is - // RAM-only and not reconstructible mid-window). Self-clears when the window - // completes (NumRewardPartitionsRemaining reaches 0). + // RAM-only and not reconstructible mid-window). Release requires a verified + // completion bank, committed atomically with the whole rewards window. var rewardsHoldBelowSlot uint64 // Alpenglow finality identities captured at observe/ingest time for the promotion // gate (the tracker's own state may be pruned by promotion time). Pruned as slots @@ -2175,13 +2175,9 @@ func ReplayBlocks( } promoteThrough := safePromoteTarget(lastRootedWatermark, verifierRequired, verifiedWM, replayDivergenceFloor) // Partitioned-rewards window: hold promotion below the boundary block - // until every partition distributes, so a crash-resume re-runs the - // boundary and rebuilds the RAM-only distribution bookkeeping. - if rewardsHoldBelowSlot > 0 && partitionedRewardsInfo != nil && partitionedRewardsInfo.NumRewardPartitionsRemaining > 0 { - if promoteThrough >= rewardsHoldBelowSlot { - promoteThrough = rewardsHoldBelowSlot - 1 - } - } + // until the completion bank verifies and is eligible to fold, so a + // failed distribution re-runs the boundary and rebuilds its bookkeeping. + promoteThrough = rewardsCompletion.limitPromotion(partitionedRewardsInfo, rewardsHoldBelowSlot, promoteThrough) if promoteThrough <= mithrilState.LastRootedSlot { // Operator signal: promotion is fully stalled (verifier lag, // divergence floor, or rewards hold) while finality has run at @@ -2212,6 +2208,8 @@ func ReplayBlocks( mlog.Log.FileOnlyf("alpenglow gate: checked=%d matched=%d no_finality=%d no_local_id=%d", gateStats.checked, gateStats.matched, gateStats.noFinality, gateStats.noLocalID) } + // The finality gate can stop before the verified completion bank. + promoteThrough = rewardsCompletion.limitPromotion(partitionedRewardsInfo, rewardsHoldBelowSlot, promoteThrough) if promoteThrough <= mithrilState.LastRootedSlot { return false } @@ -2225,6 +2223,18 @@ func ReplayBlocks( if res := promoter.drain(); res != nil { applyFoldOutcome(res) } + if rewardsHoldBelowSlot > 0 && partitionedRewardsInfo != nil && promoteThrough >= rewardsHoldBelowSlot { + job, jerr := unrootedTailState.buildRewardsCompletionFoldJob(rewardsCompletion.slot) + if jerr != nil { + mlog.Log.Errorf("rooted-durable: rewards completion fold: %v", jerr) + return false + } + if err := runFoldJob(unrootedTailState.committer, job); err != nil { + mlog.Log.Errorf("rooted-durable: rewards completion fold: %v", err) + return false + } + applyFoldOutcome(&foldResult{job: job}) + } promotedThrough, rootedCtx, perr := unrootedTailState.flush(promoteThrough) if perr != nil { mlog.Log.Errorf("rooted-durable: forced fold stopped at slot %d: %v", promotedThrough, perr) @@ -2238,7 +2248,13 @@ func ReplayBlocks( // when idle; completions are applied at the top of this function on a // later iteration. if !promoter.inFlight { - job, jerr := unrootedTailState.buildFoldJob(promoteThrough, false) + var job *foldJob + var jerr error + if rewardsHoldBelowSlot > 0 && partitionedRewardsInfo != nil && promoteThrough >= rewardsHoldBelowSlot { + job, jerr = unrootedTailState.buildRewardsCompletionFoldJob(rewardsCompletion.slot) + } else { + job, jerr = unrootedTailState.buildFoldJob(promoteThrough, false) + } if jerr != nil { mlog.Log.Errorf("rooted-durable: %v; watermark held back", jerr) return false diff --git a/pkg/replay/promotion.go b/pkg/replay/promotion.go index c750009ea..51d966a44 100644 --- a/pkg/replay/promotion.go +++ b/pkg/replay/promotion.go @@ -388,6 +388,23 @@ type foldResult struct { err error } +// buildRewardsCompletionFoldJob puts the entire rewards window in one commit. +// A normal batch cutoff inside that window would leave an active EpochRewards +// checkpoint without the RAM-only spool bookkeeping needed to resume it. The +// retained tail already bounds the size of this once-per-epoch fold. +func (t *unrootedTail) buildRewardsCompletionFoldJob(through uint64) (*foldJob, error) { + whole := *t + whole.batchSlots = t.overlay.HeldSlots() + job, err := whole.buildFoldJob(through, true) + if err != nil { + return nil, err + } + if job == nil || job.through != through { + return nil, fmt.Errorf("rewards completion bank %d is absent from retained fold prefix", through) + } + return job, nil +} + // buildFoldJob snapshots the FIRST fold chunk of the rooted prefix <= through // (loop thread). force also takes a trailing partial chunk. Returns nil when // no chunk is ready. A missing chunk-top context is an error — a context-less diff --git a/pkg/replay/rewards_retirement.go b/pkg/replay/rewards_retirement.go index a03a00cbf..18783e382 100644 --- a/pkg/replay/rewards_retirement.go +++ b/pkg/replay/rewards_retirement.go @@ -15,6 +15,19 @@ type partitionedRewardsCompletion struct { slot uint64 } +// limitPromotion keeps the boundary replayable until a successfully verified +// completion bank is eligible for promotion. Consuming the last spool changes +// the RAM counter before footer verification and is not completion evidence. +func (c *partitionedRewardsCompletion) limitPromotion(info *rewards.PartitionedRewardDistributionInfo, boundary, through uint64) uint64 { + if boundary == 0 || info == nil { + return through + } + if info.NumRewardPartitionsRemaining != 0 || c.info != info || c.slot == 0 || through < c.slot { + return min(through, boundary-1) + } + return through +} + // observeBank must run only after successful block execution/publication, using // that bank's immutable sysvars (never the speculative global sysvar cache). // If the first completed bank lacks evidence, recording a later descendant is diff --git a/pkg/replay/rewards_retirement_test.go b/pkg/replay/rewards_retirement_test.go index 460584eee..aaa169ae1 100644 --- a/pkg/replay/rewards_retirement_test.go +++ b/pkg/replay/rewards_retirement_test.go @@ -3,6 +3,7 @@ package replay import ( "bytes" "encoding/base64" + "fmt" "testing" "github.com/Overclock-Validator/mithril/pkg/accounts" @@ -53,6 +54,68 @@ func TestRewardsRetirementRequiresCompletedBankAndDurability(t *testing.T) { require.False(t, completed.retire(&info, 100), "retirement is one-shot") } +func TestRewardsPromotionRetainsBoundaryAfterFailedDistribution(t *testing.T) { + const boundary = uint64(6264000) + info := &rewards.PartitionedRewardDistributionInfo{NumRewardPartitionsRemaining: 1} + var completed partitionedRewardsCompletion + require.Equal(t, boundary-1, completed.limitPromotion(info, boundary, boundary+1)) + + // Distribution consumes the final spool before ProcessBlock checks the + // footer. The epoch-116 failure took this path; no successful bank was + // observed, so forced shutdown must not persist the boundary bank. + info.NumRewardPartitionsRemaining = 0 + require.Equal(t, boundary-1, completed.limitPromotion(info, boundary, boundary+1)) + completed.observeBank(info, nil) + require.Equal(t, boundary-1, completed.limitPromotion(info, boundary, boundary+1)) + + completed.observeBank(info, testUnwindBankSysvars(t, boundary+1, 50)) + require.Equal(t, boundary-2, completed.limitPromotion(info, boundary, boundary-2)) + require.Equal(t, boundary-1, completed.limitPromotion(info, boundary, boundary), "finality stopped inside rewards window") + require.Equal(t, boundary+1, completed.limitPromotion(info, boundary, boundary+1)) + + next := &rewards.PartitionedRewardDistributionInfo{} + require.Equal(t, boundary-1, completed.limitPromotion(next, boundary, boundary+2), "completion belongs to another distribution") + require.Equal(t, boundary+2, completed.limitPromotion(nil, boundary, boundary+2)) + require.Equal(t, boundary+2, completed.limitPromotion(info, 0, boundary+2)) +} + +func TestRewardsCompletionFoldCannotCheckpointInsideWindow(t *testing.T) { + for _, batchSize := range []int{1, 2, 128} { + t.Run(fmt.Sprintf("batch_%d", batchSize), func(t *testing.T) { + fc := &fakeCommitter{durable: accounts.NewMemAccounts(), failOn: 7} + tail := asyncTestTail(fc, 5, 6, 7, 8) + tail.batchSlots = batchSize + info := &rewards.PartitionedRewardDistributionInfo{} + var completed partitionedRewardsCompletion + completed.observeBank(info, testUnwindBankSysvars(t, 7, 50)) + through := completed.limitPromotion(info, 5, 8) + require.Equal(t, uint64(8), through) + + job, err := tail.buildRewardsCompletionFoldJob(completed.slot) + require.NoError(t, err) + require.NotNil(t, job) + require.Equal(t, uint64(7), job.through) + require.Len(t, job.chunk, 3, "the entire distribution must share one commit") + require.Equal(t, batchSize, tail.batchSlots, "normal batching is unchanged") + require.Error(t, runFoldJob(fc, job)) + require.Empty(t, fc.throughs) + require.False(t, completed.retire(&info, 4)) + + fc.failOn = 0 + job, err = tail.buildRewardsCompletionFoldJob(completed.slot) + require.NoError(t, err) + require.NoError(t, runFoldJob(fc, job)) + require.Equal(t, []uint64{7}, fc.throughs) + tail.applyFoldJob(job) + require.True(t, completed.retire(&info, 7)) + require.Equal(t, 1, tail.overlay.HeldSlots(), "later banks remain buffered") + }) + } + tail := asyncTestTail(&fakeCommitter{durable: accounts.NewMemAccounts()}, 5, 6) + _, err := tail.buildRewardsCompletionFoldJob(7) + require.ErrorContains(t, err, "absent from retained fold prefix") +} + func TestRewardsRetirementDoesNotCrossGenerations(t *testing.T) { old := &rewards.PartitionedRewardDistributionInfo{SpoolSlot: 1} next := &rewards.PartitionedRewardDistributionInfo{SpoolSlot: 10} From 0fe2fc17345af7a8594e871fc7a3fad1091f036a Mon Sep 17 00:00:00 2001 From: Shaun Colley <79477620+smcio@users.noreply.github.com> Date: Tue, 22 Sep 2026 16:27:41 +0100 Subject: [PATCH 4/4] docs: remove epoch 116 incident report --- docs/epoch116-reward-divergence.md | 82 ------------------------------ 1 file changed, 82 deletions(-) delete mode 100644 docs/epoch116-reward-divergence.md diff --git a/docs/epoch116-reward-divergence.md b/docs/epoch116-reward-divergence.md deleted file mode 100644 index 9d93ab52c..000000000 --- a/docs/epoch116-reward-divergence.md +++ /dev/null @@ -1,82 +0,0 @@ -# Epoch 115 → 116 reward divergence - -The supplied run (`320ce8da`, started 2026-09-18) passed the epoch-boundary bank -at slot **6,264,000**, then stopped at **6,264,001** with a footer bank-hash -mismatch. Both blocks had zero transactions. The mismatch was a deterministic -reward-account state divergence, not a transaction-execution panic. - -At the boundary, rewards for epoch 115 were calculated and vote commissions -were paid. The following bank distributed one staking-reward partition with -724 stake records. Of those records, 33 paid zero lamports but advanced -`credits_observed` on stakes that had deactivated in epoch 114 and were fully -inactive in epoch 115. Agave leaves those accounts unchanged. The missing -effective-or-activating condition in `shouldForceCreditsOnly` caused the extra -writes and changed the LtHash. - -Reconstructing all 825 modified accounts from the saved parent accounts and -the diagnostic's per-account hashes reproduced the original computed bank -hash. Removing only those 33 writes reproduced the expected footer hash -exactly. The [reduced fixture and regression](../pkg/rewards/testdata/epoch116/README.md) -preserve this evidence and link the Agave/Firedancer rules. The code retains -activation status from the existing calculation, so no extra stake scan is -needed. Credit rewinds, disabled inflation, activation-epoch updates and -fractional rewards on effective stakes retain their prior behavior. - -## Checkpoint failure and recovery - -The distribution decremented its remaining-partition counter to zero before -the failing bank's footer was verified. The promotion guard used only that -counter, so graceful shutdown released the epoch hold and persisted slot -6,264,000 with an active EpochRewards sysvar. The spool had already been -consumed. That checkpoint cannot resume distribution directly. - -Promotion now waits for the successfully verified bank's immutable inactive -EpochRewards state. It also waits for that bank to pass the normal finality -and verification gates. The boundary through completion is committed in one -fold, including when the configured batch size is smaller than the rewards -window. A failed bank, a finality cutoff inside the window, or a failed fold -keeps the prior checkpoint. Memory remains bounded by the existing tail cap; -the completion fold can be larger than the usual batch, once per epoch. - -The supplied data includes a retained fold and transaction-status checkpoint -at **6,263,999**, the last slot of epoch 115. Read-only validation confirmed its -57,677,519-byte transaction-status checkpoint, complete retained-root coverage, -selected block identity, inactive EpochRewards state, and all 122 account undo -targets for reverting the boundary bank. With the fixed binary, the existing -`run --rewind-to-slot 6263999` option can restore that boundary and recalculate -rewards, provided historical blocks/shreds remain available. Keep the normal -node configuration and signing-history recovery checks. A direct restart from -6,264,000 cannot recover the missing distribution bookkeeping. - -The supplied local shred spool does not contain slots 6,264,000–6,264,001. On -2026-09-22 the configured primary RPC also reported 6,264,001 as pruned (first -available block 6,764,691). Replaying from the retained boundary therefore needs -another historical source; otherwise use the fixed binary with a fresh -snapshot. Original accounts, ledger, logs and signing history were preserved; -the fix was not deployed to a live validator during diagnosis. - -## Validation - -The branch was fetched and fast-forwarded from `320ce8da` to `be29319f` before -implementation. The reduced production-path regression failed on `be29319f` -with the incident's exact bad hash and passed after the fix. Additional tests -cover inactive, activating, effective and cooling stakes, forced credit-update -exceptions, failed distribution, finality lag, distribution generations, -atomic completion folds and failed-commit retry. - -Passed locally with Go 1.27.1 on linux/amd64: - -```sh -go test -race ./pkg/rewards ./pkg/replay ./pkg/accountsdb ./pkg/bankhash -count=1 -GOMAXPROCS=2 go test -race -p 2 -count=1 \ - ./pkg/alpenglow ./pkg/consensus ./pkg/replay ./pkg/rewards \ - ./pkg/turbine ./pkg/sigverify ./pkg/blockprod/... \ - ./cmd/mithril/node ./cmd/mithril/configcmd -go vet ./pkg/rewards ./pkg/replay ./pkg/accountsdb ./pkg/bankhash -go build ./cmd/mithril -git diff --check -``` - -The CI regression job now includes `pkg/rewards`, so the incident fixture runs -on pull requests. These checks do not establish live post-restart catch-up or -later-slot parity; the original node was not restarted.