From a6a51db46aba428362f5b364536688bfcf2fa5d2 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 15 Sep 2023 11:15:27 -0400 Subject: [PATCH 001/117] Implements "Mining" - diverting a portion of fees to proposers --- agreement/abstractions.go | 2 +- agreement/agreementtest/simulate_test.go | 3 +- agreement/common_test.go | 3 +- agreement/fuzzer/ledger_test.go | 3 +- agreement/msgp_gen.go | 502 +++++++++++------- agreement/proposal.go | 8 +- config/consensus.go | 10 + daemon/algod/api/server/v2/handlers.go | 5 +- data/basics/fraction.go | 69 +++ data/basics/fraction_test.go | 76 +++ data/bookkeeping/block.go | 16 +- data/bookkeeping/msgp_gen.go | 304 +++++++---- data/datatest/impls.go | 4 +- data/transactions/logic/eval.go | 7 +- data/transactions/logic/fields.go | 6 + data/transactions/logic/fields_string.go | 8 +- data/transactions/logic/opcodes.go | 6 +- data/transactions/payment.go | 5 +- data/transactions/transaction_test.go | 26 +- ledger/apply/asset_test.go | 2 +- ledger/apply/keyreg_test.go | 32 +- ledger/apply/payment_test.go | 2 +- ledger/apptxn_test.go | 41 +- ledger/double_test.go | 15 +- ledger/eval/applications.go | 3 +- ledger/eval/cow.go | 5 + ledger/eval/cow_test.go | 3 +- ledger/eval/eval.go | 38 +- .../prefetcher/prefetcher_alignment_test.go | 54 +- ledger/eval_simple_test.go | 61 +++ ledger/ledger_test.go | 3 + ledger/ledgercore/validatedBlock.go | 9 +- ledger/simple_test.go | 20 +- node/node.go | 4 +- protocol/tags.go | 2 +- test/scripts/e2e_subs/goal/goal.py | 18 +- test/scripts/e2e_subs/mining.py | 98 ++++ 37 files changed, 1085 insertions(+), 388 deletions(-) create mode 100644 data/basics/fraction.go create mode 100644 data/basics/fraction_test.go create mode 100755 test/scripts/e2e_subs/mining.py diff --git a/agreement/abstractions.go b/agreement/abstractions.go index a22a3a0526..ca640260c7 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -59,7 +59,7 @@ type ValidatedBlock interface { // // Calls to Seed() or to Digest() on the copy's Block must // reflect the value of the new seed. - WithSeed(committee.Seed) ValidatedBlock + WithSeed(committee.Seed, basics.Address) ValidatedBlock // Block returns the underlying block that has been validated. Block() bookkeeping.Block diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index a0afca4d46..fa1e4c1c5c 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -79,8 +79,9 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithSeed(s committee.Seed) agreement.ValidatedBlock { +func (b testValidatedBlock) WithSeed(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { b.Inside.BlockHeader.Seed = s + b.Inside.BlockHeader.Proposer = proposer return b } diff --git a/agreement/common_test.go b/agreement/common_test.go index 9ec85618e8..11d6f5f90e 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -165,8 +165,9 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithSeed(s committee.Seed) ValidatedBlock { +func (b testValidatedBlock) WithSeed(s committee.Seed, proposer basics.Address) ValidatedBlock { b.Inside.BlockHeader.Seed = s + b.Inside.BlockHeader.Proposer = proposer return b } diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go index 00ba132294..91d261c6e7 100644 --- a/agreement/fuzzer/ledger_test.go +++ b/agreement/fuzzer/ledger_test.go @@ -93,8 +93,9 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithSeed(s committee.Seed) agreement.ValidatedBlock { +func (b testValidatedBlock) WithSeed(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { b.Inside.BlockHeader.Seed = s + b.Inside.BlockHeader.Proposer = proposer return b } diff --git a/agreement/msgp_gen.go b/agreement/msgp_gen.go index 16679ce797..91f877042f 100644 --- a/agreement/msgp_gen.go +++ b/agreement/msgp_gen.go @@ -4493,123 +4493,131 @@ func PlayerMaxSize() (s int) { func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(29) - var zb0004Mask uint64 /* 38 bits */ + zb0004Len := uint32(31) + var zb0004Mask uint64 /* 40 bits */ if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { zb0004Len-- zb0004Mask |= 0x40 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x80 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x100 } - if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { zb0004Len-- zb0004Mask |= 0x200 } - if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { zb0004Len-- zb0004Mask |= 0x400 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x800 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x1000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x2000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x4000 } - if (*z).unauthenticatedProposal.OriginalPeriod == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { zb0004Len-- zb0004Mask |= 0x8000 } - if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { + if (*z).unauthenticatedProposal.OriginalPeriod == 0 { zb0004Len-- zb0004Mask |= 0x10000 } - if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x20000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { + if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0004Len-- zb0004Mask |= 0x40000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x80000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x100000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { + zb0004Len-- + zb0004Mask |= 0x200000 + } + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { zb0004Len-- zb0004Mask |= 0x400000 } + if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { + zb0004Len-- + zb0004Mask |= 0x1000000 + } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x800000 + zb0004Mask |= 0x2000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x1000000 + zb0004Mask |= 0x4000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x2000000 + zb0004Mask |= 0x8000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x4000000 + zb0004Mask |= 0x10000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { zb0004Len-- - zb0004Mask |= 0x8000000 + zb0004Mask |= 0x20000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { zb0004Len-- - zb0004Mask |= 0x10000000 + zb0004Mask |= 0x40000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { zb0004Len-- - zb0004Mask |= 0x20000000 + zb0004Mask |= 0x80000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x40000000 + zb0004Mask |= 0x100000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x80000000 + zb0004Mask |= 0x200000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x100000000 + zb0004Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x200000000 + zb0004Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x400000000 + zb0004Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { zb0004Len-- - zb0004Mask |= 0x800000000 + zb0004Mask |= 0x2000000000 } // variable map header, size zb0004Len o = msgp.AppendMapHeader(o, zb0004Len) @@ -4620,56 +4628,61 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel) } if (zb0004Mask & 0x80) == 0 { // if not empty + // string "fc" + o = append(o, 0xa2, 0x66, 0x63) + o = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MarshalMsg(o) + } + if (zb0004Mask & 0x100) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x100) == 0 { // if not empty + if (zb0004Mask & 0x200) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0004Mask & 0x400) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0004Mask & 0x800) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0004Mask & 0x1000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0004Mask & 0x2000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0004Mask & 0x4000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0004Mask & 0x8000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0004Mask & 0x10000) == 0 { // if not empty // string "oper" o = append(o, 0xa4, 0x6f, 0x70, 0x65, 0x72) o = msgp.AppendUint64(o, uint64((*z).unauthenticatedProposal.OriginalPeriod)) } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0004Mask & 0x20000) == 0 { // if not empty // string "oprop" o = append(o, 0xa5, 0x6f, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.OriginalProposer.MarshalMsg(o) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0004Mask & 0x40000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -4681,47 +4694,52 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0004Mask & 0x80000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0004Mask & 0x100000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0004Mask & 0x200000) == 0 { // if not empty + // string "prp" + o = append(o, 0xa3, 0x70, 0x72, 0x70) + o = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MarshalMsg(o) + } + if (zb0004Mask & 0x400000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0004Mask & 0x1000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0004Mask & 0x800000) == 0 { // if not empty + if (zb0004Mask & 0x2000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0004Mask & 0x4000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x2000000) == 0 { // if not empty + if (zb0004Mask & 0x8000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0004Mask & 0x10000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0004Mask & 0x20000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { @@ -4741,42 +4759,42 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0004Mask & 0x40000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0004Mask & 0x80000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp) } - if (zb0004Mask & 0x40000000) == 0 { // if not empty + if (zb0004Mask & 0x100000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x80000000) == 0 { // if not empty + if (zb0004Mask & 0x200000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x100000000) == 0 { // if not empty + if (zb0004Mask & 0x400000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o) } - if (zb0004Mask & 0x200000000) == 0 { // if not empty + if (zb0004Mask & 0x800000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x400000000) == 0 { // if not empty + if (zb0004Mask & 0x1000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x800000000) == 0 { // if not empty + if (zb0004Mask & 0x2000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -4882,6 +4900,22 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o return } } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Proposer") + return + } + } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "FeesCollected") + return + } + } if zb0004 > 0 { zb0004-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -5184,6 +5218,18 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o err = msgp.WrapError(err, "GenesisHash") return } + case "prp": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Proposer") + return + } + case "fc": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "FeesCollected") + return + } case "fees": bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -5386,7 +5432,7 @@ func (_ *proposal) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *proposal) Msgsize() (s int) { - s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking { _ = zb0001 @@ -5404,12 +5450,12 @@ func (z *proposal) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *proposal) MsgIsZero() bool { - return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) + return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func ProposalMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.unauthenticatedProposal.Block.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) @@ -8856,127 +8902,135 @@ func ThresholdEventMaxSize() (s int) { func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(30) - var zb0004Mask uint64 /* 38 bits */ + zb0004Len := uint32(32) + var zb0004Mask uint64 /* 40 bits */ if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { zb0004Len-- zb0004Mask |= 0x80 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x100 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x200 } - if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { zb0004Len-- zb0004Mask |= 0x400 } - if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { zb0004Len-- zb0004Mask |= 0x800 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x1000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x2000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x4000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x8000 } - if (*z).unauthenticatedProposal.OriginalPeriod == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { zb0004Len-- zb0004Mask |= 0x10000 } - if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { + if (*z).unauthenticatedProposal.OriginalPeriod == 0 { zb0004Len-- zb0004Mask |= 0x20000 } - if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x40000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { + if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0004Len-- zb0004Mask |= 0x80000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x100000 } - if (*z).PriorVote.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x200000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x400000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { + if (*z).PriorVote.MsgIsZero() { + zb0004Len-- + zb0004Mask |= 0x800000 + } + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { zb0004Len-- zb0004Mask |= 0x1000000 } + if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { + zb0004Len-- + zb0004Mask |= 0x4000000 + } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x2000000 + zb0004Mask |= 0x8000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x4000000 + zb0004Mask |= 0x10000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x8000000 + zb0004Mask |= 0x20000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x10000000 + zb0004Mask |= 0x40000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { zb0004Len-- - zb0004Mask |= 0x20000000 + zb0004Mask |= 0x80000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { zb0004Len-- - zb0004Mask |= 0x40000000 + zb0004Mask |= 0x100000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { zb0004Len-- - zb0004Mask |= 0x80000000 + zb0004Mask |= 0x200000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x100000000 + zb0004Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x200000000 + zb0004Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x400000000 + zb0004Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x800000000 + zb0004Mask |= 0x2000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x1000000000 + zb0004Mask |= 0x4000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { zb0004Len-- - zb0004Mask |= 0x2000000000 + zb0004Mask |= 0x8000000000 } // variable map header, size zb0004Len o = msgp.AppendMapHeader(o, zb0004Len) @@ -8987,56 +9041,61 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel) } if (zb0004Mask & 0x100) == 0 { // if not empty + // string "fc" + o = append(o, 0xa2, 0x66, 0x63) + o = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MarshalMsg(o) + } + if (zb0004Mask & 0x200) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0004Mask & 0x400) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0004Mask & 0x800) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0004Mask & 0x1000) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0004Mask & 0x2000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0004Mask & 0x4000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0004Mask & 0x8000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0004Mask & 0x10000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0004Mask & 0x20000) == 0 { // if not empty // string "oper" o = append(o, 0xa4, 0x6f, 0x70, 0x65, 0x72) o = msgp.AppendUint64(o, uint64((*z).unauthenticatedProposal.OriginalPeriod)) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0004Mask & 0x40000) == 0 { // if not empty // string "oprop" o = append(o, 0xa5, 0x6f, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.OriginalProposer.MarshalMsg(o) } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0004Mask & 0x80000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -9048,52 +9107,57 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0004Mask & 0x100000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0004Mask & 0x200000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x200000) == 0 { // if not empty + if (zb0004Mask & 0x400000) == 0 { // if not empty + // string "prp" + o = append(o, 0xa3, 0x70, 0x72, 0x70) + o = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MarshalMsg(o) + } + if (zb0004Mask & 0x800000) == 0 { // if not empty // string "pv" o = append(o, 0xa2, 0x70, 0x76) o = (*z).PriorVote.MarshalMsg(o) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0004Mask & 0x1000000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0004Mask & 0x4000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0004Mask & 0x2000000) == 0 { // if not empty + if (zb0004Mask & 0x8000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0004Mask & 0x10000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0004Mask & 0x20000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o) } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0004Mask & 0x40000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0004Mask & 0x80000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { @@ -9113,42 +9177,42 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x40000000) == 0 { // if not empty + if (zb0004Mask & 0x100000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter) } - if (zb0004Mask & 0x80000000) == 0 { // if not empty + if (zb0004Mask & 0x200000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp) } - if (zb0004Mask & 0x100000000) == 0 { // if not empty + if (zb0004Mask & 0x400000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x200000000) == 0 { // if not empty + if (zb0004Mask & 0x800000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x400000000) == 0 { // if not empty + if (zb0004Mask & 0x1000000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o) } - if (zb0004Mask & 0x800000000) == 0 { // if not empty + if (zb0004Mask & 0x2000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x1000000000) == 0 { // if not empty + if (zb0004Mask & 0x4000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x2000000000) == 0 { // if not empty + if (zb0004Mask & 0x8000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -9254,6 +9318,22 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal return } } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Proposer") + return + } + } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "FeesCollected") + return + } + } if zb0004 > 0 { zb0004-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -9564,6 +9644,18 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal err = msgp.WrapError(err, "GenesisHash") return } + case "prp": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Proposer") + return + } + case "fc": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "FeesCollected") + return + } case "fees": bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -9772,7 +9864,7 @@ func (_ *transmittedPayload) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *transmittedPayload) Msgsize() (s int) { - s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking { _ = zb0001 @@ -9790,12 +9882,12 @@ func (z *transmittedPayload) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *transmittedPayload) MsgIsZero() bool { - return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) && ((*z).PriorVote.MsgIsZero()) + return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) && ((*z).PriorVote.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func TransmittedPayloadMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.unauthenticatedProposal.Block.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) @@ -10516,123 +10608,131 @@ func UnauthenticatedEquivocationVoteMaxSize() (s int) { func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(29) - var zb0004Mask uint64 /* 36 bits */ + zb0004Len := uint32(31) + var zb0004Mask uint64 /* 38 bits */ if (*z).Block.BlockHeader.RewardsState.RewardsLevel == 0 { zb0004Len-- zb0004Mask |= 0x40 } - if (*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { + if (*z).Block.BlockHeader.FeesCollected.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x80 } - if (*z).Block.BlockHeader.RewardsState.RewardsResidue == 0 { + if (*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x100 } - if (*z).Block.BlockHeader.GenesisID == "" { + if (*z).Block.BlockHeader.RewardsState.RewardsResidue == 0 { zb0004Len-- zb0004Mask |= 0x200 } - if (*z).Block.BlockHeader.GenesisHash.MsgIsZero() { + if (*z).Block.BlockHeader.GenesisID == "" { zb0004Len-- zb0004Mask |= 0x400 } - if (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).Block.BlockHeader.GenesisHash.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x800 } - if (*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { + if (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x1000 } - if (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x2000 } - if (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { + if (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x4000 } - if (*z).OriginalPeriod == 0 { + if (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { zb0004Len-- zb0004Mask |= 0x8000 } - if (*z).OriginalProposer.MsgIsZero() { + if (*z).OriginalPeriod == 0 { zb0004Len-- zb0004Mask |= 0x10000 } - if len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if (*z).OriginalProposer.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x20000 } - if (*z).Block.BlockHeader.Branch.MsgIsZero() { + if len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0004Len-- zb0004Mask |= 0x40000 } - if (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).Block.BlockHeader.Branch.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x80000 } - if (*z).Block.BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x100000 } - if (*z).Block.BlockHeader.Round.MsgIsZero() { + if (*z).Block.BlockHeader.Proposer.MsgIsZero() { + zb0004Len-- + zb0004Mask |= 0x200000 + } + if (*z).Block.BlockHeader.RewardsState.RewardsRate == 0 { zb0004Len-- zb0004Mask |= 0x400000 } + if (*z).Block.BlockHeader.Round.MsgIsZero() { + zb0004Len-- + zb0004Mask |= 0x1000000 + } if (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x800000 + zb0004Mask |= 0x2000000 } if (*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x1000000 + zb0004Mask |= 0x4000000 } if (*z).SeedProof.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x2000000 + zb0004Mask |= 0x8000000 } if (*z).Block.BlockHeader.Seed.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x4000000 + zb0004Mask |= 0x10000000 } if len((*z).Block.BlockHeader.StateProofTracking) == 0 { zb0004Len-- - zb0004Mask |= 0x8000000 + zb0004Mask |= 0x20000000 } if (*z).Block.BlockHeader.TxnCounter == 0 { zb0004Len-- - zb0004Mask |= 0x10000000 + zb0004Mask |= 0x40000000 } if (*z).Block.BlockHeader.TimeStamp == 0 { zb0004Len-- - zb0004Mask |= 0x20000000 + zb0004Mask |= 0x80000000 } if (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x40000000 + zb0004Mask |= 0x100000000 } if (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x80000000 + zb0004Mask |= 0x200000000 } if (*z).Block.Payset.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x100000000 + zb0004Mask |= 0x400000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x200000000 + zb0004Mask |= 0x800000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0004Len-- - zb0004Mask |= 0x400000000 + zb0004Mask |= 0x1000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false { zb0004Len-- - zb0004Mask |= 0x800000000 + zb0004Mask |= 0x2000000000 } // variable map header, size zb0004Len o = msgp.AppendMapHeader(o, zb0004Len) @@ -10643,56 +10743,61 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsLevel) } if (zb0004Mask & 0x80) == 0 { // if not empty + // string "fc" + o = append(o, 0xa2, 0x66, 0x63) + o = (*z).Block.BlockHeader.FeesCollected.MarshalMsg(o) + } + if (zb0004Mask & 0x100) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).Block.BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x100) == 0 { // if not empty + if (zb0004Mask & 0x200) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsResidue) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0004Mask & 0x400) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).Block.BlockHeader.GenesisID) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0004Mask & 0x800) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).Block.BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0004Mask & 0x1000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0004Mask & 0x2000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).Block.BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0004Mask & 0x4000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0004Mask & 0x8000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0004Mask & 0x10000) == 0 { // if not empty // string "oper" o = append(o, 0xa4, 0x6f, 0x70, 0x65, 0x72) o = msgp.AppendUint64(o, uint64((*z).OriginalPeriod)) } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0004Mask & 0x20000) == 0 { // if not empty // string "oprop" o = append(o, 0xa5, 0x6f, 0x70, 0x72, 0x6f, 0x70) o = (*z).OriginalProposer.MarshalMsg(o) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0004Mask & 0x40000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -10704,47 +10809,52 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0004Mask & 0x80000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0004Mask & 0x100000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0004Mask & 0x200000) == 0 { // if not empty + // string "prp" + o = append(o, 0xa3, 0x70, 0x72, 0x70) + o = (*z).Block.BlockHeader.Proposer.MarshalMsg(o) + } + if (zb0004Mask & 0x400000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0004Mask & 0x1000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0004Mask & 0x800000) == 0 { // if not empty + if (zb0004Mask & 0x2000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0004Mask & 0x4000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x2000000) == 0 { // if not empty + if (zb0004Mask & 0x8000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).SeedProof.MarshalMsg(o) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0004Mask & 0x10000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0004Mask & 0x20000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).Block.BlockHeader.StateProofTracking == nil { @@ -10764,42 +10874,42 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0004Mask & 0x40000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.TxnCounter) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0004Mask & 0x80000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).Block.BlockHeader.TimeStamp) } - if (zb0004Mask & 0x40000000) == 0 { // if not empty + if (zb0004Mask & 0x100000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x80000000) == 0 { // if not empty + if (zb0004Mask & 0x200000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x100000000) == 0 { // if not empty + if (zb0004Mask & 0x400000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).Block.Payset.MarshalMsg(o) } - if (zb0004Mask & 0x200000000) == 0 { // if not empty + if (zb0004Mask & 0x800000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x400000000) == 0 { // if not empty + if (zb0004Mask & 0x1000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x800000000) == 0 { // if not empty + if (zb0004Mask & 0x2000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -10905,6 +11015,22 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma return } } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).Block.BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Proposer") + return + } + } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).Block.BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "FeesCollected") + return + } + } if zb0004 > 0 { zb0004-- bts, err = (*z).Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -11207,6 +11333,18 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma err = msgp.WrapError(err, "GenesisHash") return } + case "prp": + bts, err = (*z).Block.BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Proposer") + return + } + case "fc": + bts, err = (*z).Block.BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "FeesCollected") + return + } case "fees": bts, err = (*z).Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -11409,7 +11547,7 @@ func (_ *unauthenticatedProposal) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *unauthenticatedProposal) Msgsize() (s int) { - s = 3 + 4 + (*z).Block.BlockHeader.Round.Msgsize() + 5 + (*z).Block.BlockHeader.Branch.Msgsize() + 5 + (*z).Block.BlockHeader.Seed.Msgsize() + 4 + (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).Block.BlockHeader.GenesisID) + 3 + (*z).Block.BlockHeader.GenesisHash.Msgsize() + 5 + (*z).Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).Block.BlockHeader.Round.Msgsize() + 5 + (*z).Block.BlockHeader.Branch.Msgsize() + 5 + (*z).Block.BlockHeader.Seed.Msgsize() + 4 + (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).Block.BlockHeader.GenesisID) + 3 + (*z).Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).Block.BlockHeader.FeesCollected.Msgsize() + 5 + (*z).Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).Block.BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).Block.BlockHeader.StateProofTracking { _ = zb0001 @@ -11427,12 +11565,12 @@ func (z *unauthenticatedProposal) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *unauthenticatedProposal) MsgIsZero() bool { - return ((*z).Block.BlockHeader.Round.MsgIsZero()) && ((*z).Block.BlockHeader.Branch.MsgIsZero()) && ((*z).Block.BlockHeader.Seed.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TimeStamp == 0) && ((*z).Block.BlockHeader.GenesisID == "") && ((*z).Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).Block.BlockHeader.TxnCounter == 0) && (len((*z).Block.BlockHeader.StateProofTracking) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).Block.Payset.MsgIsZero()) && ((*z).SeedProof.MsgIsZero()) && ((*z).OriginalPeriod == 0) && ((*z).OriginalProposer.MsgIsZero()) + return ((*z).Block.BlockHeader.Round.MsgIsZero()) && ((*z).Block.BlockHeader.Branch.MsgIsZero()) && ((*z).Block.BlockHeader.Seed.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TimeStamp == 0) && ((*z).Block.BlockHeader.GenesisID == "") && ((*z).Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).Block.BlockHeader.TxnCounter == 0) && (len((*z).Block.BlockHeader.StateProofTracking) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).Block.Payset.MsgIsZero()) && ((*z).SeedProof.MsgIsZero()) && ((*z).OriginalPeriod == 0) && ((*z).OriginalProposer.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func UnauthenticatedProposalMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.Block.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) diff --git a/agreement/proposal.go b/agreement/proposal.go index ac29970b16..852006952c 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -197,6 +197,12 @@ func verifyNewSeed(p unauthenticatedProposal, ledger LedgerReader) error { return fmt.Errorf("failed to obtain consensus parameters in round %d: %v", ParamsRound(rnd), err) } + if cparams.EnableMining { + if p.BlockHeader.Proposer != value.OriginalProposer { + return fmt.Errorf("payload has wrong proposer (%v != %v)", p.Proposer, value.OriginalProposer) + } + } + balanceRound := balanceRound(rnd, cparams) proposerRecord, err := ledger.LookupAgreement(balanceRound, value.OriginalProposer) if err != nil { @@ -251,7 +257,7 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve Validat return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could not derive new seed: %v", err) } - ve = ve.WithSeed(newSeed) + ve = ve.WithSeed(newSeed, address) proposal := makeProposal(ve, seedProof, period, address) value := proposalValue{ OriginalPeriod: period, diff --git a/config/consensus.go b/config/consensus.go index fc08e2990e..81c6afc84a 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -529,6 +529,13 @@ type ConsensusParams struct { // arrival times or is set to a static value. Even if this flag disables the // dynamic filter, it will be calculated and logged (but not used). DynamicFilterTimeout bool + + // EnableMining means that the proposer should be included in the BlockHeader. + EnableMining bool + + // MiningPercent specifies the percent of fees paid in a block that go to + // the proposer instead of the FeeSink. + MiningPercent uint64 } // PaysetCommitType enumerates possible ways for the block header to commit to @@ -1412,6 +1419,9 @@ func initConsensusProtocols() { vFuture.LogicSigVersion = 11 // When moving this to a release, put a new higher LogicSigVersion here + vFuture.EnableMining = true + vFuture.MiningPercent = 75 + Consensus[protocol.ConsensusFuture] = vFuture // vAlphaX versions are an separate series of consensus parameters and versions for alphanet diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 69201ca848..3eb65154b7 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -1401,10 +1401,7 @@ func (v2 *Handlers) getPendingTransactions(ctx echo.Context, max *uint64, format } // MatchAddress uses this to check FeeSink, we don't care about that here. - spec := transactions.SpecialAddresses{ - FeeSink: basics.Address{}, - RewardsPool: basics.Address{}, - } + spec := transactions.SpecialAddresses{} txnLimit := uint64(math.MaxUint64) if max != nil && *max != 0 { diff --git a/data/basics/fraction.go b/data/basics/fraction.go new file mode 100644 index 0000000000..271da85265 --- /dev/null +++ b/data/basics/fraction.go @@ -0,0 +1,69 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package basics + +import ( + "fmt" +) + +// Fraction represents the mathematical notion of rational number, but is much +// simpler than `big.Rat`. It only supports numerators and denominators of +// uint64. +type Fraction struct { + Numerator uint64 + Denominator uint64 +} + +// NewFraction creates the obvious Fraction, and checks that is not improper, +// nor dives by zero. +func NewFraction(numerator uint64, denominator uint64) Fraction { + if denominator == 0 { + panic("/0") + } + if numerator > denominator { + panic("improper fraction") + } + return Fraction{numerator, denominator} +} + +// NewPercent creates a fraction reflecting the given percentage. +func NewPercent(pct uint64) Fraction { + return NewFraction(pct, 100) +} + +// String returns a string representation of Fraction +func (frac Fraction) String() string { + return fmt.Sprintf("%d/%d", frac.Numerator, frac.Denominator) +} + +// Divvy separates a quantity into two parts according to the fraction. The first +// value is floor(q * frac), the second is q - first. +func (frac Fraction) Divvy(q uint64) (uint64, uint64) { + // can't overflow on proper fractions + first, o := Muldiv(q, frac.Numerator, frac.Denominator) + if o { + panic("overflow") + } + second := q - first + return first, second +} + +// DivvyAlgos is Divvy, but operates on MicroAlgos +func (frac Fraction) DivvyAlgos(q MicroAlgos) (MicroAlgos, MicroAlgos) { + first, second := frac.Divvy(q.Raw) + return MicroAlgos{first}, MicroAlgos{second} +} diff --git a/data/basics/fraction_test.go b/data/basics/fraction_test.go new file mode 100644 index 0000000000..9fad8dfa19 --- /dev/null +++ b/data/basics/fraction_test.go @@ -0,0 +1,76 @@ +// Copyright (C) 2019-2023 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package basics + +import ( + "math" + "testing" + + "github.com/algorand/go-algorand/test/partitiontest" + "github.com/stretchr/testify/require" +) + +func TestFraction(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + third := Fraction{1, 3} + a, b := third.Divvy(6) + require.EqualValues(t, a, 2) + require.EqualValues(t, b, 4) + + a, b = third.Divvy(10) + require.EqualValues(t, a, 3) + require.EqualValues(t, b, 7) +} + +func TestFractionAvoidsOverflow(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + biggestEven := math.MaxUint64 - uint64(1) + + half := Fraction{biggestEven / 2, biggestEven} // should operate as 1/2 even on large numbers + a, b := half.Divvy(6) + require.EqualValues(t, a, 3) + require.EqualValues(t, b, 3) + + a, b = half.Divvy(biggestEven) + require.EqualValues(t, a, biggestEven/2) + require.EqualValues(t, b, biggestEven/2) + + // ensure that overflow is avoided even if reduction isn't possible + uhalf := Fraction{biggestEven / 2, math.MaxUint64} // should be just under half + a, b = uhalf.Divvy(6) + require.EqualValues(t, a, 2) + require.EqualValues(t, b, 4) + + a, b = uhalf.Divvy(biggestEven) + require.EqualValues(t, a, biggestEven/2-1) + require.EqualValues(t, b, biggestEven/2+1) + + // and just to be super careful, ensure that there's also no reduction + // between q and the denominator by using a q that is relatively prime to + // math.MaxUint64 + + // prove 23 is relatively prime to math.MaxUint64 + require.Positive(t, math.MaxUint64%23) + + a, b = uhalf.Divvy(23) + require.EqualValues(t, a, 11) + require.EqualValues(t, b, 12) +} diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index d67621b343..987b44e867 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -58,6 +58,16 @@ type ( // Genesis hash to which this block belongs. GenesisHash crypto.Digest `codec:"gh"` + // Proposer is the proposer of this block. Like the Seed, algod adds + // this after the block is built, so that the same block can be prepared + // for multiple Players in the same node. Therefore, it can not be used + // to influence block evaluation. Populated if proto.EnableMining + Proposer basics.Address `codec:"prp"` + + // FeesCollected is the sum of all fees paid by transactions in this + // block. Populated if proto.EnableMining. + FeesCollected basics.MicroAlgos `codec:"fc"` + // Rewards. // // When a block is applied, some amount of rewards are accrued to @@ -275,9 +285,13 @@ func (block Block) GenesisHash() crypto.Digest { } // WithSeed returns a copy of the Block with the seed set to s. -func (block Block) WithSeed(s committee.Seed) Block { +func (block Block) WithSeed(s committee.Seed, proposer basics.Address) Block { c := block c.BlockHeader.Seed = s + if !c.BlockHeader.Proposer.IsZero() { + panic("Attempt to re-set the proposer.") + } + c.BlockHeader.Proposer = proposer return c } diff --git a/data/bookkeeping/msgp_gen.go b/data/bookkeeping/msgp_gen.go index cb3a63ad2e..e020a7c7bf 100644 --- a/data/bookkeeping/msgp_gen.go +++ b/data/bookkeeping/msgp_gen.go @@ -143,112 +143,120 @@ import ( func (z *Block) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(26) - var zb0004Mask uint32 /* 31 bits */ + zb0004Len := uint32(28) + var zb0004Mask uint64 /* 33 bits */ if (*z).BlockHeader.RewardsState.RewardsLevel == 0 { zb0004Len-- zb0004Mask |= 0x20 } - if (*z).BlockHeader.RewardsState.FeeSink.MsgIsZero() { + if (*z).BlockHeader.FeesCollected.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x40 } - if (*z).BlockHeader.RewardsState.RewardsResidue == 0 { + if (*z).BlockHeader.RewardsState.FeeSink.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x80 } - if (*z).BlockHeader.GenesisID == "" { + if (*z).BlockHeader.RewardsState.RewardsResidue == 0 { zb0004Len-- zb0004Mask |= 0x100 } - if (*z).BlockHeader.GenesisHash.MsgIsZero() { + if (*z).BlockHeader.GenesisID == "" { zb0004Len-- zb0004Mask |= 0x200 } - if (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).BlockHeader.GenesisHash.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x400 } - if (*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { + if (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x800 } - if (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x1000 } - if (*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0 { + if (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x2000 } - if len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if (*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0 { zb0004Len-- zb0004Mask |= 0x4000 } - if (*z).BlockHeader.Branch.MsgIsZero() { + if len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0004Len-- zb0004Mask |= 0x8000 } - if (*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).BlockHeader.Branch.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x10000 } - if (*z).BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x20000 } - if (*z).BlockHeader.Round.MsgIsZero() { + if (*z).BlockHeader.Proposer.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x40000 } - if (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { + if (*z).BlockHeader.RewardsState.RewardsRate == 0 { zb0004Len-- zb0004Mask |= 0x80000 } - if (*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero() { + if (*z).BlockHeader.Round.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x100000 } - if (*z).BlockHeader.Seed.MsgIsZero() { + if (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x200000 } - if len((*z).BlockHeader.StateProofTracking) == 0 { + if (*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x400000 } - if (*z).BlockHeader.TxnCounter == 0 { + if (*z).BlockHeader.Seed.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x800000 } - if (*z).BlockHeader.TimeStamp == 0 { + if len((*z).BlockHeader.StateProofTracking) == 0 { zb0004Len-- zb0004Mask |= 0x1000000 } - if (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { + if (*z).BlockHeader.TxnCounter == 0 { zb0004Len-- zb0004Mask |= 0x2000000 } - if (*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { + if (*z).BlockHeader.TimeStamp == 0 { zb0004Len-- zb0004Mask |= 0x4000000 } - if (*z).Payset.MsgIsZero() { + if (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x8000000 } - if (*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { + if (*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x10000000 } - if (*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { + if (*z).Payset.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x20000000 } - if (*z).BlockHeader.UpgradeVote.UpgradeApprove == false { + if (*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x40000000 } + if (*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { + zb0004Len-- + zb0004Mask |= 0x80000000 + } + if (*z).BlockHeader.UpgradeVote.UpgradeApprove == false { + zb0004Len-- + zb0004Mask |= 0x100000000 + } // variable map header, size zb0004Len o = msgp.AppendMapHeader(o, zb0004Len) if zb0004Len != 0 { @@ -258,46 +266,51 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsLevel) } if (zb0004Mask & 0x40) == 0 { // if not empty + // string "fc" + o = append(o, 0xa2, 0x66, 0x63) + o = (*z).BlockHeader.FeesCollected.MarshalMsg(o) + } + if (zb0004Mask & 0x80) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x80) == 0 { // if not empty + if (zb0004Mask & 0x100) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsResidue) } - if (zb0004Mask & 0x100) == 0 { // if not empty + if (zb0004Mask & 0x200) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).BlockHeader.GenesisID) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0004Mask & 0x400) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0004Mask & 0x800) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0004Mask & 0x1000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0004Mask & 0x2000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0004Mask & 0x4000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0004Mask & 0x8000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -309,42 +322,47 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0004Mask & 0x10000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).BlockHeader.Branch.MarshalMsg(o) } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0004Mask & 0x20000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0004Mask & 0x40000) == 0 { // if not empty + // string "prp" + o = append(o, 0xa3, 0x70, 0x72, 0x70) + o = (*z).BlockHeader.Proposer.MarshalMsg(o) + } + if (zb0004Mask & 0x80000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsRate) } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0004Mask & 0x100000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).BlockHeader.Round.MarshalMsg(o) } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0004Mask & 0x200000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0004Mask & 0x400000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x200000) == 0 { // if not empty + if (zb0004Mask & 0x800000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).BlockHeader.Seed.MarshalMsg(o) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0004Mask & 0x1000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).BlockHeader.StateProofTracking == nil { @@ -364,42 +382,42 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x800000) == 0 { // if not empty + if (zb0004Mask & 0x2000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).BlockHeader.TxnCounter) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0004Mask & 0x4000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).BlockHeader.TimeStamp) } - if (zb0004Mask & 0x2000000) == 0 { // if not empty + if (zb0004Mask & 0x8000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0004Mask & 0x10000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0004Mask & 0x20000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).Payset.MarshalMsg(o) } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0004Mask & 0x40000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0004Mask & 0x80000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x40000000) == 0 { // if not empty + if (zb0004Mask & 0x100000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).BlockHeader.UpgradeVote.UpgradeApprove) @@ -505,6 +523,22 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b return } } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Proposer") + return + } + } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "FeesCollected") + return + } + } if zb0004 > 0 { zb0004-- bts, err = (*z).BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -779,6 +813,18 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b err = msgp.WrapError(err, "GenesisHash") return } + case "prp": + bts, err = (*z).BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Proposer") + return + } + case "fc": + bts, err = (*z).BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "FeesCollected") + return + } case "fees": bts, err = (*z).BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -959,7 +1005,7 @@ func (_ *Block) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *Block) Msgsize() (s int) { - s = 3 + 4 + (*z).BlockHeader.Round.Msgsize() + 5 + (*z).BlockHeader.Branch.Msgsize() + 5 + (*z).BlockHeader.Seed.Msgsize() + 4 + (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).BlockHeader.GenesisID) + 3 + (*z).BlockHeader.GenesisHash.Msgsize() + 5 + (*z).BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).BlockHeader.Round.Msgsize() + 5 + (*z).BlockHeader.Branch.Msgsize() + 5 + (*z).BlockHeader.Seed.Msgsize() + 4 + (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).BlockHeader.GenesisID) + 3 + (*z).BlockHeader.GenesisHash.Msgsize() + 4 + (*z).BlockHeader.Proposer.Msgsize() + 3 + (*z).BlockHeader.FeesCollected.Msgsize() + 5 + (*z).BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).BlockHeader.StateProofTracking { _ = zb0001 @@ -977,12 +1023,12 @@ func (z *Block) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *Block) MsgIsZero() bool { - return ((*z).BlockHeader.Round.MsgIsZero()) && ((*z).BlockHeader.Branch.MsgIsZero()) && ((*z).BlockHeader.Seed.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).BlockHeader.TimeStamp == 0) && ((*z).BlockHeader.GenesisID == "") && ((*z).BlockHeader.GenesisHash.MsgIsZero()) && ((*z).BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).BlockHeader.RewardsState.RewardsRate == 0) && ((*z).BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).BlockHeader.TxnCounter == 0) && (len((*z).BlockHeader.StateProofTracking) == 0) && (len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).Payset.MsgIsZero()) + return ((*z).BlockHeader.Round.MsgIsZero()) && ((*z).BlockHeader.Branch.MsgIsZero()) && ((*z).BlockHeader.Seed.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).BlockHeader.TimeStamp == 0) && ((*z).BlockHeader.GenesisID == "") && ((*z).BlockHeader.GenesisHash.MsgIsZero()) && ((*z).BlockHeader.Proposer.MsgIsZero()) && ((*z).BlockHeader.FeesCollected.MsgIsZero()) && ((*z).BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).BlockHeader.RewardsState.RewardsRate == 0) && ((*z).BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).BlockHeader.TxnCounter == 0) && (len((*z).BlockHeader.StateProofTracking) == 0) && (len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).Payset.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func BlockMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) @@ -1037,108 +1083,116 @@ func BlockHashMaxSize() int { func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(25) - var zb0004Mask uint32 /* 30 bits */ + zb0004Len := uint32(27) + var zb0004Mask uint32 /* 32 bits */ if (*z).RewardsState.RewardsLevel == 0 { zb0004Len-- zb0004Mask |= 0x20 } - if (*z).RewardsState.FeeSink.MsgIsZero() { + if (*z).FeesCollected.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x40 } - if (*z).RewardsState.RewardsResidue == 0 { + if (*z).RewardsState.FeeSink.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x80 } - if (*z).GenesisID == "" { + if (*z).RewardsState.RewardsResidue == 0 { zb0004Len-- zb0004Mask |= 0x100 } - if (*z).GenesisHash.MsgIsZero() { + if (*z).GenesisID == "" { zb0004Len-- zb0004Mask |= 0x200 } - if (*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).GenesisHash.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x400 } - if (*z).UpgradeState.NextProtocol.MsgIsZero() { + if (*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x800 } - if (*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).UpgradeState.NextProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x1000 } - if (*z).UpgradeState.NextProtocolApprovals == 0 { + if (*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x2000 } - if len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if (*z).UpgradeState.NextProtocolApprovals == 0 { zb0004Len-- zb0004Mask |= 0x4000 } - if (*z).Branch.MsgIsZero() { + if len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0004Len-- zb0004Mask |= 0x8000 } - if (*z).UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).Branch.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x10000 } - if (*z).RewardsState.RewardsRate == 0 { + if (*z).UpgradeState.CurrentProtocol.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x20000 } - if (*z).Round.MsgIsZero() { + if (*z).Proposer.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x40000 } - if (*z).RewardsState.RewardsRecalculationRound.MsgIsZero() { + if (*z).RewardsState.RewardsRate == 0 { zb0004Len-- zb0004Mask |= 0x80000 } - if (*z).RewardsState.RewardsPool.MsgIsZero() { + if (*z).Round.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x100000 } - if (*z).Seed.MsgIsZero() { + if (*z).RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x200000 } - if len((*z).StateProofTracking) == 0 { + if (*z).RewardsState.RewardsPool.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x400000 } - if (*z).TxnCounter == 0 { + if (*z).Seed.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x800000 } - if (*z).TimeStamp == 0 { + if len((*z).StateProofTracking) == 0 { zb0004Len-- zb0004Mask |= 0x1000000 } - if (*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { + if (*z).TxnCounter == 0 { zb0004Len-- zb0004Mask |= 0x2000000 } - if (*z).TxnCommitments.Sha256Commitment.MsgIsZero() { + if (*z).TimeStamp == 0 { zb0004Len-- zb0004Mask |= 0x4000000 } - if (*z).UpgradeVote.UpgradeDelay.MsgIsZero() { + if (*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x8000000 } - if (*z).UpgradeVote.UpgradePropose.MsgIsZero() { + if (*z).TxnCommitments.Sha256Commitment.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x10000000 } - if (*z).UpgradeVote.UpgradeApprove == false { + if (*z).UpgradeVote.UpgradeDelay.MsgIsZero() { zb0004Len-- zb0004Mask |= 0x20000000 } + if (*z).UpgradeVote.UpgradePropose.MsgIsZero() { + zb0004Len-- + zb0004Mask |= 0x40000000 + } + if (*z).UpgradeVote.UpgradeApprove == false { + zb0004Len-- + zb0004Mask |= 0x80000000 + } // variable map header, size zb0004Len o = msgp.AppendMapHeader(o, zb0004Len) if zb0004Len != 0 { @@ -1148,46 +1202,51 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).RewardsState.RewardsLevel) } if (zb0004Mask & 0x40) == 0 { // if not empty + // string "fc" + o = append(o, 0xa2, 0x66, 0x63) + o = (*z).FeesCollected.MarshalMsg(o) + } + if (zb0004Mask & 0x80) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x80) == 0 { // if not empty + if (zb0004Mask & 0x100) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).RewardsState.RewardsResidue) } - if (zb0004Mask & 0x100) == 0 { // if not empty + if (zb0004Mask & 0x200) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).GenesisID) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0004Mask & 0x400) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0004Mask & 0x800) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0004Mask & 0x1000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0004Mask & 0x2000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0004Mask & 0x4000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0004Mask & 0x8000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -1199,42 +1258,47 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = (*z).ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0004Mask & 0x10000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).Branch.MarshalMsg(o) } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0004Mask & 0x20000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0004Mask & 0x40000) == 0 { // if not empty + // string "prp" + o = append(o, 0xa3, 0x70, 0x72, 0x70) + o = (*z).Proposer.MarshalMsg(o) + } + if (zb0004Mask & 0x80000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).RewardsState.RewardsRate) } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0004Mask & 0x100000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).Round.MarshalMsg(o) } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0004Mask & 0x200000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0004Mask & 0x400000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x200000) == 0 { // if not empty + if (zb0004Mask & 0x800000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).Seed.MarshalMsg(o) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0004Mask & 0x1000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).StateProofTracking == nil { @@ -1254,37 +1318,37 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x800000) == 0 { // if not empty + if (zb0004Mask & 0x2000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).TxnCounter) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0004Mask & 0x4000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).TimeStamp) } - if (zb0004Mask & 0x2000000) == 0 { // if not empty + if (zb0004Mask & 0x8000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0004Mask & 0x10000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0004Mask & 0x20000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0004Mask & 0x40000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0004Mask & 0x80000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).UpgradeVote.UpgradeApprove) @@ -1390,6 +1454,22 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Proposer") + return + } + } + if zb0004 > 0 { + zb0004-- + bts, err = (*z).FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "FeesCollected") + return + } + } if zb0004 > 0 { zb0004-- bts, err = (*z).RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -1656,6 +1736,18 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "GenesisHash") return } + case "prp": + bts, err = (*z).Proposer.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Proposer") + return + } + case "fc": + bts, err = (*z).FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "FeesCollected") + return + } case "fees": bts, err = (*z).RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -1830,7 +1922,7 @@ func (_ *BlockHeader) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *BlockHeader) Msgsize() (s int) { - s = 3 + 4 + (*z).Round.Msgsize() + 5 + (*z).Branch.Msgsize() + 5 + (*z).Seed.Msgsize() + 4 + (*z).TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).GenesisID) + 3 + (*z).GenesisHash.Msgsize() + 5 + (*z).RewardsState.FeeSink.Msgsize() + 4 + (*z).RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).Round.Msgsize() + 5 + (*z).Branch.Msgsize() + 5 + (*z).Seed.Msgsize() + 4 + (*z).TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).GenesisID) + 3 + (*z).GenesisHash.Msgsize() + 4 + (*z).Proposer.Msgsize() + 3 + (*z).FeesCollected.Msgsize() + 5 + (*z).RewardsState.FeeSink.Msgsize() + 4 + (*z).RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).StateProofTracking != nil { for zb0001, zb0002 := range (*z).StateProofTracking { _ = zb0001 @@ -1847,12 +1939,12 @@ func (z *BlockHeader) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *BlockHeader) MsgIsZero() bool { - return ((*z).Round.MsgIsZero()) && ((*z).Branch.MsgIsZero()) && ((*z).Seed.MsgIsZero()) && ((*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).TimeStamp == 0) && ((*z).GenesisID == "") && ((*z).GenesisHash.MsgIsZero()) && ((*z).RewardsState.FeeSink.MsgIsZero()) && ((*z).RewardsState.RewardsPool.MsgIsZero()) && ((*z).RewardsState.RewardsLevel == 0) && ((*z).RewardsState.RewardsRate == 0) && ((*z).RewardsState.RewardsResidue == 0) && ((*z).RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocolApprovals == 0) && ((*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).UpgradeVote.UpgradeApprove == false) && ((*z).TxnCounter == 0) && (len((*z).StateProofTracking) == 0) && (len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0) + return ((*z).Round.MsgIsZero()) && ((*z).Branch.MsgIsZero()) && ((*z).Seed.MsgIsZero()) && ((*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).TimeStamp == 0) && ((*z).GenesisID == "") && ((*z).GenesisHash.MsgIsZero()) && ((*z).Proposer.MsgIsZero()) && ((*z).FeesCollected.MsgIsZero()) && ((*z).RewardsState.FeeSink.MsgIsZero()) && ((*z).RewardsState.RewardsPool.MsgIsZero()) && ((*z).RewardsState.RewardsLevel == 0) && ((*z).RewardsState.RewardsRate == 0) && ((*z).RewardsState.RewardsResidue == 0) && ((*z).RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocolApprovals == 0) && ((*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).UpgradeVote.UpgradeApprove == false) && ((*z).TxnCounter == 0) && (len((*z).StateProofTracking) == 0) && (len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0) } // MaxSize returns a maximum valid message size for this message type func BlockHeaderMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) diff --git a/data/datatest/impls.go b/data/datatest/impls.go index 7c9462d40d..67bdefbb8c 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -65,8 +65,8 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated } // WithSeed implements the agreement.ValidatedBlock interface. -func (ve validatedBlock) WithSeed(s committee.Seed) agreement.ValidatedBlock { - newblock := ve.blk.WithSeed(s) +func (ve validatedBlock) WithSeed(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { + newblock := ve.blk.WithSeed(s, proposer) return validatedBlock{blk: &newblock} } diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index fe102086aa..a93407c40a 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -5593,17 +5593,20 @@ func opBlock(cx *EvalContext) error { switch fs.field { case BlkSeed: cx.Stack[last].Bytes = hdr.Seed[:] - return nil case BlkTimestamp: cx.Stack[last].Bytes = nil if hdr.TimeStamp < 0 { return fmt.Errorf("block(%d) timestamp %d < 0", round, hdr.TimeStamp) } cx.Stack[last].Uint = uint64(hdr.TimeStamp) - return nil + case BlkProposer: + cx.Stack[last].Bytes = hdr.Proposer[:] + case BlkFeesCollected: + cx.Stack[last].Uint = hdr.FeesCollected.Raw default: return fmt.Errorf("invalid block field %s", fs.field) } + return nil } // pcDetails return PC and disassembled instructions at PC up to 2 opcodes back diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index a2f971f7b7..7f962125b5 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -965,6 +965,10 @@ const ( BlkSeed BlockField = iota // BlkTimestamp is the Block's timestamp, seconds from epoch BlkTimestamp + // BlkProposer is the Block's proposer, or ZeroAddress, pre EnableMining + BlkProposer + // BlkFeesCollected is the sum of fees for the block, or 0, pre EnableMining + BlkFeesCollected invalidBlockField // compile-time constant for number of fields ) @@ -980,6 +984,8 @@ type blockFieldSpec struct { var blockFieldSpecs = [...]blockFieldSpec{ {BlkSeed, StackBytes, randomnessVersion}, {BlkTimestamp, StackUint64, randomnessVersion}, + {BlkProposer, StackAddress, incentiveVersion}, + {BlkFeesCollected, StackUint64, incentiveVersion}, } func blockFieldSpecByField(r BlockField) (blockFieldSpec, bool) { diff --git a/data/transactions/logic/fields_string.go b/data/transactions/logic/fields_string.go index 37bfeb9bcc..70f74fe11e 100644 --- a/data/transactions/logic/fields_string.go +++ b/data/transactions/logic/fields_string.go @@ -352,12 +352,14 @@ func _() { var x [1]struct{} _ = x[BlkSeed-0] _ = x[BlkTimestamp-1] - _ = x[invalidBlockField-2] + _ = x[BlkProposer-2] + _ = x[BlkFeesCollected-3] + _ = x[invalidBlockField-4] } -const _BlockField_name = "BlkSeedBlkTimestampinvalidBlockField" +const _BlockField_name = "BlkSeedBlkTimestampBlkProposerBlkFeesCollectedinvalidBlockField" -var _BlockField_index = [...]uint8{0, 7, 19, 36} +var _BlockField_index = [...]uint8{0, 7, 19, 30, 46, 63} func (i BlockField) String() string { if i < 0 || i >= BlockField(len(_BlockField_index)-1) { diff --git a/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index 18af2af1b2..134d146aa4 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -71,11 +71,13 @@ const fpVersion = 8 // changes for frame pointers and simpler function d const sharedResourcesVersion = 9 // apps can access resources from other transactions. +const pairingVersion = 10 // bn256 opcodes. will add bls12-381, and unify the available opcodes. +const spliceVersion = 10 // box splicing/resizing + // EXPERIMENTAL. These should be revisited whenever a new LogicSigVersion is // moved from vFuture to a new consensus version. If they remain unready, bump // their version, and fixup TestAssemble() in assembler_test.go. -const pairingVersion = 10 // bn256 opcodes. will add bls12-381, and unify the available opcodes. -const spliceVersion = 10 // box splicing/resizing +const incentiveVersion = 11 // block fields, heartbeat const spOpcodesVersion = 11 // falcon_verify, sumhash512 diff --git a/data/transactions/payment.go b/data/transactions/payment.go index 62eafdb1e9..d3c1432e7c 100644 --- a/data/transactions/payment.go +++ b/data/transactions/payment.go @@ -42,8 +42,11 @@ func (payment PaymentTxnFields) checkSpender(header Header, spec SpecialAddresse return fmt.Errorf("transaction cannot close account to its sender %v", header.Sender) } - // the FeeSink account may only spend to the IncentivePool + // the FeeSink account may only spend to the IncentivePool (not at all, if EnableMining) if header.Sender == spec.FeeSink { + if proto.EnableMining { + return fmt.Errorf("cannot spend from fee sink address %v", header.Sender) + } if payment.Receiver != spec.RewardsPool { return fmt.Errorf("cannot spend from fee sink's address %v to non incentive pool address %v", header.Sender, payment.Receiver) } diff --git a/data/transactions/transaction_test.go b/data/transactions/transaction_test.go index e3a25619bb..dfec67c558 100644 --- a/data/transactions/transaction_test.go +++ b/data/transactions/transaction_test.go @@ -101,13 +101,13 @@ func TestGoOnlineGoNonparticipatingContradiction(t *testing.T) { tx.KeyregTxnFields = KeyregTxnFields{ VotePK: v.OneTimeSignatureVerifier, SelectionPK: vrf.PK, + VoteKeyDilution: 1, Nonparticipation: true, } // this tx tries to both register keys to go online, and mark an account as non-participating. // it is not well-formed. - feeSink := basics.Address{0x7, 0xda, 0xcb, 0x4b, 0x6d, 0x9e, 0xd1, 0x41, 0xb1, 0x75, 0x76, 0xbd, 0x45, 0x9a, 0xe6, 0x42, 0x1d, 0x48, 0x6d, 0xa3, 0xd4, 0xef, 0x22, 0x47, 0xc4, 0x9, 0xa3, 0x96, 0xb8, 0x2e, 0xa2, 0x21} - err = tx.WellFormed(SpecialAddresses{FeeSink: feeSink}, config.Consensus[protocol.ConsensusCurrentVersion]) - require.Error(t, err) + err = tx.WellFormed(SpecialAddresses{}, config.Consensus[protocol.ConsensusCurrentVersion]) + require.ErrorContains(t, err, "go online, but vote last is set to zero") } func TestGoNonparticipatingWellFormed(t *testing.T) { @@ -125,19 +125,17 @@ func TestGoNonparticipatingWellFormed(t *testing.T) { } // this tx is well-formed - feeSink := basics.Address{0x7, 0xda, 0xcb, 0x4b, 0x6d, 0x9e, 0xd1, 0x41, 0xb1, 0x75, 0x76, 0xbd, 0x45, 0x9a, 0xe6, 0x42, 0x1d, 0x48, 0x6d, 0xa3, 0xd4, 0xef, 0x22, 0x47, 0xc4, 0x9, 0xa3, 0x96, 0xb8, 0x2e, 0xa2, 0x21} - err = tx.WellFormed(SpecialAddresses{FeeSink: feeSink}, curProto) + err = tx.WellFormed(SpecialAddresses{}, curProto) require.NoError(t, err) // but it should stop being well-formed if the protocol does not support it curProto.SupportBecomeNonParticipatingTransactions = false - err = tx.WellFormed(SpecialAddresses{FeeSink: feeSink}, curProto) - require.Error(t, err) + err = tx.WellFormed(SpecialAddresses{}, curProto) + require.ErrorContains(t, err, "mark an account as nonparticipating, but") } func TestAppCallCreateWellFormed(t *testing.T) { partitiontest.PartitionTest(t) - feeSink := basics.Address{0x7, 0xda, 0xcb, 0x4b, 0x6d, 0x9e, 0xd1, 0x41, 0xb1, 0x75, 0x76, 0xbd, 0x45, 0x9a, 0xe6, 0x42, 0x1d, 0x48, 0x6d, 0xa3, 0xd4, 0xef, 0x22, 0x47, 0xc4, 0x9, 0xa3, 0x96, 0xb8, 0x2e, 0xa2, 0x21} curProto := config.Consensus[protocol.ConsensusCurrentVersion] futureProto := config.Consensus[protocol.ConsensusFuture] addr1, err := basics.UnmarshalChecksumAddress("NDQCJNNY5WWWFLP4GFZ7MEF2QJSMZYK6OWIV2AQ7OMAVLEFCGGRHFPKJJA") @@ -253,7 +251,7 @@ func TestAppCallCreateWellFormed(t *testing.T) { } for i, usecase := range usecases { t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) { - err := usecase.tx.WellFormed(SpecialAddresses{FeeSink: feeSink}, usecase.proto) + err := usecase.tx.WellFormed(SpecialAddresses{}, usecase.proto) if usecase.expectedError != "" { require.Error(t, err) require.Contains(t, err.Error(), usecase.expectedError) @@ -267,8 +265,6 @@ func TestAppCallCreateWellFormed(t *testing.T) { func TestWellFormedErrors(t *testing.T) { partitiontest.PartitionTest(t) - feeSink := basics.Address{0x7, 0xda, 0xcb, 0x4b, 0x6d, 0x9e, 0xd1, 0x41, 0xb1, 0x75, 0x76, 0xbd, 0x45, 0x9a, 0xe6, 0x42, 0x1d, 0x48, 0x6d, 0xa3, 0xd4, 0xef, 0x22, 0x47, 0xc4, 0x9, 0xa3, 0x96, 0xb8, 0x2e, 0xa2, 0x21} - specialAddr := SpecialAddresses{FeeSink: feeSink} curProto := config.Consensus[protocol.ConsensusCurrentVersion] futureProto := config.Consensus[protocol.ConsensusFuture] protoV27 := config.Consensus[protocol.ConsensusV27] @@ -595,7 +591,7 @@ func TestWellFormedErrors(t *testing.T) { }, } for _, usecase := range usecases { - err := usecase.tx.WellFormed(specialAddr, usecase.proto) + err := usecase.tx.WellFormed(SpecialAddresses{}, usecase.proto) require.Equal(t, usecase.expectedError, err) } } @@ -632,14 +628,12 @@ func TestWellFormedKeyRegistrationTx(t *testing.T) { tx := generateDummyGoNonparticpatingTransaction(addr) curProto := config.Consensus[protocol.ConsensusCurrentVersion] - feeSink := basics.Address{0x7, 0xda, 0xcb, 0x4b, 0x6d, 0x9e, 0xd1, 0x41, 0xb1, 0x75, 0x76, 0xbd, 0x45, 0x9a, 0xe6, 0x42, 0x1d, 0x48, 0x6d, 0xa3, 0xd4, 0xef, 0x22, 0x47, 0xc4, 0x9, 0xa3, 0x96, 0xb8, 0x2e, 0xa2, 0x21} - spec := SpecialAddresses{FeeSink: feeSink} if !curProto.SupportBecomeNonParticipatingTransactions { t.Skipf("Skipping rest of test because current protocol version %v does not support become-nonparticipating transactions", protocol.ConsensusCurrentVersion) } // this tx is well-formed - err = tx.WellFormed(spec, curProto) + err = tx.WellFormed(SpecialAddresses{}, curProto) require.NoError(t, err) type keyRegTestCase struct { @@ -677,7 +671,7 @@ func TestWellFormedKeyRegistrationTx(t *testing.T) { curProto.EnableKeyregCoherencyCheck = testCase.enableKeyregCoherencyCheck curProto.EnableStateProofKeyregCheck = testCase.enableStateProofKeyregCheck curProto.MaxKeyregValidPeriod = maxValidPeriod // TODO: remove this when MaxKeyregValidPeriod is in CurrentVersion - return tx.WellFormed(spec, curProto) + return tx.WellFormed(SpecialAddresses{}, curProto) } if *generateFlag == true { diff --git a/ledger/apply/asset_test.go b/ledger/apply/asset_test.go index eddc15757e..0280ecdb60 100644 --- a/ledger/apply/asset_test.go +++ b/ledger/apply/asset_test.go @@ -90,7 +90,7 @@ func TestAssetTransfer(t *testing.T) { } var ad transactions.ApplyData - err := AssetTransfer(tx.AssetTransferTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, &ad) + err := AssetTransfer(tx.AssetTransferTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, &ad) require.NoError(t, err) if config.Consensus[protocol.ConsensusCurrentVersion].EnableAssetCloseAmount { diff --git a/ledger/apply/keyreg_test.go b/ledger/apply/keyreg_test.go index e64beea4b4..41976257ea 100644 --- a/ledger/apply/keyreg_test.go +++ b/ledger/apply/keyreg_test.go @@ -136,20 +136,20 @@ func TestKeyregApply(t *testing.T) { // Going from offline to online should be okay mockBal.addrs[src] = basics.AccountData{Status: basics.Offline} - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(0)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(0)) require.NoError(t, err) // Going from online to nonparticipatory should be okay, if the protocol supports that if mockBal.ConsensusParams().SupportBecomeNonParticipatingTransactions { tx.KeyregTxnFields = transactions.KeyregTxnFields{} tx.KeyregTxnFields.Nonparticipation = true - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(0)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(0)) require.NoError(t, err) // Nonparticipatory accounts should not be able to change status mockBal.addrs[src] = basics.AccountData{Status: basics.NotParticipating} - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(0)) - require.Error(t, err) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(0)) + require.ErrorContains(t, err, "cannot change online/offline status of non-participating account") } mockBal.version = "future" @@ -171,25 +171,25 @@ func TestKeyregApply(t *testing.T) { }, } mockBal.addrs[src] = basics.AccountData{Status: basics.Offline} - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(999)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(999)) require.NoError(t, err) - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(1000)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(1000)) require.Equal(t, errKeyregGoingOnlineExpiredParticipationKey, err) - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(1001)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(1001)) require.Equal(t, errKeyregGoingOnlineExpiredParticipationKey, err) tx.KeyregTxnFields.VoteFirst = basics.Round(1100) tx.KeyregTxnFields.VoteLast = basics.Round(1200) - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(1098)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(1098)) require.Equal(t, errKeyregGoingOnlineFirstVotingInFuture, err) - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(1099)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(1099)) require.NoError(t, err) - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(1100)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(1100)) require.NoError(t, err) testStateProofPKBeingStored(t, tx, mockBal) @@ -197,7 +197,7 @@ func TestKeyregApply(t *testing.T) { } func testStateProofPKBeingStored(t *testing.T, tx transactions.Transaction, mockBal *keyregTestBalances) { - err := Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(1100)) + err := Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(1100)) require.NoError(t, err) // expects no error with empty keyRegistration attempt rec, err := mockBal.Get(tx.Header.Sender, false) @@ -215,7 +215,7 @@ func TestStateProofPKKeyReg(t *testing.T) { tx := createTestTxn(t, src, secretParticipation, vrfSecrets) mockBal := makeMockBalances(protocol.ConsensusV30) - err := Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(0)) + err := Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(0)) require.NoError(t, err) acct, err := mockBal.Get(tx.Src(), false) @@ -223,7 +223,7 @@ func TestStateProofPKKeyReg(t *testing.T) { require.True(t, acct.StateProofID.IsEmpty()) mockBal = makeMockBalances(protocol.ConsensusCurrentVersion) - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(0)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(0)) require.NoError(t, err) acct, err = mockBal.Get(tx.Src(), false) @@ -232,7 +232,7 @@ func TestStateProofPKKeyReg(t *testing.T) { // go offline in current consensus version: StateProofID should be empty emptyKeyreg := transactions.KeyregTxnFields{} - err = Keyreg(emptyKeyreg, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(0)) + err = Keyreg(emptyKeyreg, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(0)) require.NoError(t, err) acct, err = mockBal.Get(tx.Src(), false) @@ -241,7 +241,7 @@ func TestStateProofPKKeyReg(t *testing.T) { // run same test using vFuture mockBal = makeMockBalances(protocol.ConsensusFuture) - err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(0)) + err = Keyreg(tx.KeyregTxnFields, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(0)) require.NoError(t, err) acct, err = mockBal.Get(tx.Src(), false) @@ -249,7 +249,7 @@ func TestStateProofPKKeyReg(t *testing.T) { require.False(t, acct.StateProofID.IsEmpty()) // go offline in vFuture: StateProofID should be empty - err = Keyreg(emptyKeyreg, tx.Header, mockBal, transactions.SpecialAddresses{FeeSink: feeSink}, nil, basics.Round(0)) + err = Keyreg(emptyKeyreg, tx.Header, mockBal, transactions.SpecialAddresses{}, nil, basics.Round(0)) require.NoError(t, err) acct, err = mockBal.Get(tx.Src(), false) diff --git a/ledger/apply/payment_test.go b/ledger/apply/payment_test.go index d377211a17..a0b5976d70 100644 --- a/ledger/apply/payment_test.go +++ b/ledger/apply/payment_test.go @@ -104,7 +104,7 @@ func TestPaymentApply(t *testing.T) { }, } var ad transactions.ApplyData - err := Payment(tx.PaymentTxnFields, tx.Header, mockBalV0, transactions.SpecialAddresses{FeeSink: feeSink}, &ad) + err := Payment(tx.PaymentTxnFields, tx.Header, mockBalV0, transactions.SpecialAddresses{}, &ad) require.NoError(t, err) } diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index 167d22f2dc..d94d78d63d 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -58,6 +58,10 @@ func TestPayAction(t *testing.T) { itxn_submit `)) + // We're going to test some mining effects here too, so that we have an inner transaction example. + // Do a block with no txns, so the fee sink balance as we start the next block is stable + dl.fullBlock() + payout1 := txntest.Txn{ Type: "appl", Sender: addrs[1], @@ -65,7 +69,40 @@ func TestPayAction(t *testing.T) { Accounts: []basics.Address{addrs[1]}, // pay self } - dl.fullBlock(&payout1) + proposer := addrs[7] + presink := micros(dl.t, dl.generator, genBalances.FeeSink) + preprop := micros(dl.t, dl.generator, proposer) + dl.beginBlock() + dl.txns(&payout1) + vb := dl.endBlock(proposer) + // First MiningPct > 0 + if ver >= 39 { + require.True(t, dl.generator.GenesisProto().EnableMining) + require.EqualValues(t, proposer, vb.Block().BlockHeader.Proposer) + require.EqualValues(t, 2000, vb.Block().BlockHeader.FeesCollected.Raw) + } else { + require.False(t, dl.generator.GenesisProto().EnableMining) + require.Zero(t, vb.Block().BlockHeader.Proposer) + require.Zero(t, vb.Block().BlockHeader.FeesCollected) + } + + postsink := micros(dl.t, dl.generator, genBalances.FeeSink) + postprop := micros(dl.t, dl.generator, proposer) + + // Mining checks + require.EqualValues(t, 0, postprop-preprop) // mining not moved yet + require.EqualValues(t, 2000, postsink-presink) + + dl.fullBlock() + postsink = micros(dl.t, dl.generator, genBalances.FeeSink) + postprop = micros(dl.t, dl.generator, proposer) + // First MiningPct > 0 + if ver >= 39 { + require.EqualValues(t, 500, postsink-presink) // based on 75% in config/consensus.go + require.EqualValues(t, 1500, postprop-preprop) + } else { + require.EqualValues(t, 2000, postsink-presink) // no mining yet + } ad0 := micros(dl.t, dl.generator, addrs[0]) ad1 := micros(dl.t, dl.generator, addrs[1]) @@ -90,7 +127,7 @@ func TestPayAction(t *testing.T) { ApplicationID: ai, Accounts: []basics.Address{addrs[2]}, // pay other } - vb := dl.fullBlock(&payout2) + vb = dl.fullBlock(&payout2) // confirm that modifiedAccounts can see account in inner txn deltas := vb.Delta() diff --git a/ledger/double_test.go b/ledger/double_test.go index c08212e373..00ae863399 100644 --- a/ledger/double_test.go +++ b/ledger/double_test.go @@ -48,6 +48,9 @@ type DoubleLedger struct { validator *Ledger eval *eval.BlockEvaluator + + // proposer is the default proposer unless one is supplied to endBlock. + proposer basics.Address } func (dl DoubleLedger) Close() { @@ -59,7 +62,7 @@ func (dl DoubleLedger) Close() { func NewDoubleLedger(t testing.TB, balances bookkeeping.GenesisBalances, cv protocol.ConsensusVersion, cfg config.Local, opts ...simpleLedgerOption) DoubleLedger { g := newSimpleLedgerWithConsensusVersion(t, balances, cv, cfg, opts...) v := newSimpleLedgerFull(t, balances, cv, g.GenesisHash(), cfg, opts...) - return DoubleLedger{t, g, v, nil} + return DoubleLedger{t, g, v, nil, balances.FeeSink} // FeeSink as proposer will make old code work as expected } func (dl *DoubleLedger) beginBlock() *eval.BlockEvaluator { @@ -134,8 +137,12 @@ func (dl *DoubleLedger) fullBlock(txs ...*txntest.Txn) *ledgercore.ValidatedBloc return dl.endBlock() } -func (dl *DoubleLedger) endBlock() *ledgercore.ValidatedBlock { - vb := endBlock(dl.t, dl.generator, dl.eval) +func (dl *DoubleLedger) endBlock(proposer ...basics.Address) *ledgercore.ValidatedBlock { + prp := dl.proposer + if len(proposer) > 0 { + prp = proposer[0] + } + vb := endBlock(dl.t, dl.generator, dl.eval, prp) if dl.validator != nil { // Allows setting to nil while debugging, to simplify checkBlock(dl.t, dl.validator, vb) } @@ -195,7 +202,7 @@ func checkBlock(t testing.TB, checkLedger *Ledger, vb *ledgercore.ValidatedBlock require.NoError(t, err, "%+v", reconstituted.Payset) } check.SetGenerateForTesting(true) - cb := endBlock(t, checkLedger, check) + cb := endBlock(t, checkLedger, check, vb.Block().Proposer) check.SetGenerateForTesting(false) require.Equal(t, vb.Block(), cb.Block()) diff --git a/ledger/eval/applications.go b/ledger/eval/applications.go index 8be898e05c..3aa7bd3905 100644 --- a/ledger/eval/applications.go +++ b/ledger/eval/applications.go @@ -290,8 +290,7 @@ func (cs *roundCowState) DelBox(appIdx basics.AppIndex, key string, appAddr basi func (cs *roundCowState) Perform(gi int, ep *logic.EvalParams) error { txn := &ep.TxnGroup[gi] - // move fee to pool - err := cs.Move(txn.Txn.Sender, ep.Specials.FeeSink, txn.Txn.Fee, &txn.ApplyData.SenderRewards, nil) + err := cs.takeFee(&txn.Txn, &txn.ApplyData.SenderRewards, ep) if err != nil { return err } diff --git a/ledger/eval/cow.go b/ledger/eval/cow.go index 269ce570ff..1e8f3c1a50 100644 --- a/ledger/eval/cow.go +++ b/ledger/eval/cow.go @@ -95,6 +95,8 @@ type roundCowState struct { // prevTotals contains the accounts totals for the previous round. It's being used to calculate the totals for the new round // so that we could perform the validation test on these to ensure the block evaluator generate a valid changeset. prevTotals ledgercore.AccountTotals + + feesCollected basics.MicroAlgos } var childPool = sync.Pool{ @@ -299,6 +301,8 @@ func (cb *roundCowState) commitToParent() { cb.commitParent.mods.Txids[txid] = ledgercore.IncludedTransactions{LastValid: incTxn.LastValid, Intra: commitParentBaseIdx + incTxn.Intra} } cb.commitParent.txnCount += cb.txnCount + // no overflow because of 10B algo cap + cb.commitParent.feesCollected, _ = basics.OAddA(cb.commitParent.feesCollected, cb.feesCollected) for txl, expires := range cb.mods.Txleases { cb.commitParent.mods.AddTxLease(txl, expires) @@ -342,6 +346,7 @@ func (cb *roundCowState) reset() { cb.compatibilityMode = false maps.Clear(cb.compatibilityGetKeyCache) cb.prevTotals = ledgercore.AccountTotals{} + cb.feesCollected = basics.MicroAlgos{} } // recycle resets the roundcowstate and returns it to the sync.Pool diff --git a/ledger/eval/cow_test.go b/ledger/eval/cow_test.go index 06d600d58a..81224a8d0b 100644 --- a/ledger/eval/cow_test.go +++ b/ledger/eval/cow_test.go @@ -282,6 +282,7 @@ func TestCowChildReflect(t *testing.T) { "compatibilityMode": {}, "compatibilityGetKeyCache": {}, "prevTotals": {}, + "feesCollected": {}, } cow := roundCowState{} @@ -289,7 +290,7 @@ func TestCowChildReflect(t *testing.T) { st := v.Type() for i := 0; i < v.NumField(); i++ { reflectedCowName := st.Field(i).Name - require.Containsf(t, cowFieldNames, reflectedCowName, "new field:\"%v\" added to roundCowState, please update roundCowState.reset() to handle it before fixing the test", reflectedCowName) + require.Containsf(t, cowFieldNames, reflectedCowName, "new field:\"%v\" added to roundCowState, please update roundCowState.reset() to handle it before fixing this test", reflectedCowName) } } diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index de1304988a..90baff553c 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -788,6 +788,13 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return nil, fmt.Errorf("overflowed subtracting rewards for block %v", hdr.Round) } + miningIncentive, _ := basics.NewPercent(proto.MiningPercent).DivvyAlgos(prevHeader.FeesCollected) + err = eval.state.Move(prevHeader.FeeSink, prevHeader.Proposer, miningIncentive, nil, nil) + if err != nil { + // This should be impossible. The fees were just collected, and the FeeSink cannot be emptied. + return nil, fmt.Errorf("unable to Move mining incentive") + } + if eval.Tracer != nil { eval.Tracer.BeforeBlock(&eval.block.BlockHeader) } @@ -1165,12 +1172,27 @@ func (eval *BlockEvaluator) transaction(txn transactions.SignedTxn, evalParams * return nil } +func (cs *roundCowState) takeFee(tx *transactions.Transaction, senderRewards *basics.MicroAlgos, ep *logic.EvalParams) error { + err := cs.Move(tx.Sender, ep.Specials.FeeSink, tx.Fee, senderRewards, nil) + if err != nil { + return err + } + // transactions from FeeSink should be exceedingly rare. But we can't count + // them in feesCollected because there are no net algos added to the Sink + if tx.Sender == ep.Specials.FeeSink { + return nil + } + // overflow impossible, since these sum the fees actually paid. 10B algo limit + cs.feesCollected, _ = basics.OAddA(cs.feesCollected, tx.Fee) + return nil + +} + // applyTransaction changes the balances according to this transaction. func (eval *BlockEvaluator) applyTransaction(tx transactions.Transaction, cow *roundCowState, evalParams *logic.EvalParams, gi int, ctr uint64) (ad transactions.ApplyData, err error) { params := cow.ConsensusParams() - // move fee to pool - err = cow.Move(tx.Sender, eval.specials.FeeSink, tx.Fee, &ad.SenderRewards, nil) + err = cow.takeFee(&tx, &ad.SenderRewards, evalParams) if err != nil { return } @@ -1272,6 +1294,10 @@ func (eval *BlockEvaluator) endOfBlock() error { eval.block.TxnCounter = 0 } + if eval.proto.EnableMining { + eval.block.FeesCollected = eval.state.feesCollected + } + eval.generateExpiredOnlineAccountsList() if eval.proto.StateProofInterval > 0 { @@ -1316,6 +1342,14 @@ func (eval *BlockEvaluator) endOfBlock() error { return fmt.Errorf("txn count wrong: %d != %d", eval.block.TxnCounter, expectedTxnCount) } + var expectedFeesCollected basics.MicroAlgos + if eval.proto.EnableMining { + expectedFeesCollected = eval.state.feesCollected + } + if eval.block.FeesCollected != expectedFeesCollected { + return fmt.Errorf("fees collected wrong: %v != %v", eval.block.FeesCollected, expectedFeesCollected) + } + expectedVoters, expectedVotersWeight, err2 := eval.stateProofVotersAndTotal() if err2 != nil { return err2 diff --git a/ledger/eval/prefetcher/prefetcher_alignment_test.go b/ledger/eval/prefetcher/prefetcher_alignment_test.go index 57f4c5d92b..2f79cd45d4 100644 --- a/ledger/eval/prefetcher/prefetcher_alignment_test.go +++ b/ledger/eval/prefetcher/prefetcher_alignment_test.go @@ -58,6 +58,10 @@ func rewardsPool() basics.Address { return makeAddress(101) } +func proposer() basics.Address { + return basics.Address{} +} + func genesisBlock() (bookkeeping.Block, error) { block, err := bookkeeping.MakeGenesisBlock( proto, @@ -261,6 +265,18 @@ type ledgerData struct { Creators map[creatable]struct{} } +// pretend adds the `before` addresses to the Accounts. It "pretends" that the +// addresses were prefetched, so we can get agreement with what was actually +// requested. We do this to include two addresses that are going to end up +// requested *before* prefetch is even attempted. So there's no point in +// PrefetchAccounts being modified to return them, they have been "prefetched" +// simply by accessing them. +func (ld *ledgerData) pretend(before ...basics.Address) { + for _, a := range before { + ld.Accounts[a] = struct{}{} + } +} + func prefetch(t *testing.T, l prefetcher.Ledger, txn transactions.Transaction) ledgerData { group := makeGroupFromTxn(txn) @@ -361,7 +377,7 @@ func TestEvaluatorPrefetcherAlignmentPayment(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -393,7 +409,7 @@ func TestEvaluatorPrefetcherAlignmentCreateAsset(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) // Only one (non-existing) asset is requested. Ignore it. require.Len(t, requested.Assets, 1) require.Len(t, requested.Assets[makeAddress(1)], 1) @@ -449,7 +465,7 @@ func TestEvaluatorPrefetcherAlignmentReconfigAsset(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -504,7 +520,7 @@ func TestEvaluatorPrefetcherAlignmentAssetOptIn(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -570,7 +586,7 @@ func TestEvaluatorPrefetcherAlignmentAssetOptInCloseTo(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -641,7 +657,7 @@ func TestEvaluatorPrefetcherAlignmentAssetTransfer(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) // zero transfer of any asset @@ -660,7 +676,7 @@ func TestEvaluatorPrefetcherAlignmentAssetTransfer(t *testing.T) { requested, prefetched = run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -741,7 +757,7 @@ func TestEvaluatorPrefetcherAlignmentAssetClawback(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -811,7 +827,7 @@ func TestEvaluatorPrefetcherAlignmentAssetFreeze(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -858,7 +874,7 @@ func TestEvaluatorPrefetcherAlignmentKeyreg(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -895,7 +911,7 @@ func TestEvaluatorPrefetcherAlignmentCreateApplication(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) // Only one (non-existing) asset is requested. Ignore it. require.Len(t, requested.Apps, 1) require.Len(t, requested.Apps[makeAddress(1)], 1) @@ -953,7 +969,7 @@ func TestEvaluatorPrefetcherAlignmentDeleteApplication(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -1011,7 +1027,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationOptIn(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -1075,7 +1091,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCloseOut(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -1139,7 +1155,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationClearState(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } @@ -1203,7 +1219,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCallAccountsDeclaration(t *testi requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) // Foreign accounts are not loaded, ensure they are not prefetched require.NotContains(t, prefetched.Accounts, makeAddress(5)) require.NotContains(t, prefetched.Accounts, makeAddress(3)) @@ -1271,7 +1287,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCallForeignAppsDeclaration(t *te requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) // Foreign apps are not loaded, ensure they are not prefetched require.NotContains(t, prefetched.Creators, creatable{cindex: 6, ctype: basics.AppCreatable}) require.NotContains(t, prefetched.Creators, creatable{cindex: 8, ctype: basics.AppCreatable}) @@ -1338,7 +1354,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCallForeignAssetsDeclaration(t * requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) // Foreign apps are not loaded, ensure they are not prefetched require.NotContains(t, prefetched.Creators, creatable{cindex: 6, ctype: basics.AssetCreatable}) require.NotContains(t, prefetched.Creators, creatable{cindex: 8, ctype: basics.AssetCreatable}) @@ -1385,6 +1401,6 @@ func TestEvaluatorPrefetcherAlignmentStateProof(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool(), proposer()) require.Equal(t, requested, prefetched) } diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 7354b4a567..1b10cf5352 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -211,6 +211,67 @@ func TestBlockEvaluator(t *testing.T) { require.Equal(t, bal2new.MicroAlgos.Raw, bal2.MicroAlgos.Raw-minFee.Raw) } +// TestMiningFees ensures that the proper portion of tx fees go to the proposer, +// starting in v39. +func TestMiningFees(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + genBalances, addrs, _ := ledgertesting.NewTestGenesis() + // Mining begins in v39. Start checking in v38 to test that is unchanged. + ledgertesting.TestConsensusRange(t, 38, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { + dl := NewDoubleLedger(t, genBalances, cv, cfg) + defer dl.Close() + + dl.fullBlock() + + pay := txntest.Txn{ + Type: "pay", + Sender: addrs[1], + Receiver: addrs[2], + Amount: 100000, + } + + proposer := addrs[7] + presink := micros(dl.t, dl.generator, genBalances.FeeSink) + preprop := micros(dl.t, dl.generator, proposer) + dl.beginBlock() + dl.txns(&pay, pay.Args("again")) + vb := dl.endBlock(proposer) + + if ver >= 39 { + require.True(t, dl.generator.GenesisProto().EnableMining) // version sanity check + // new fields are in the header + require.EqualValues(t, proposer, vb.Block().BlockHeader.Proposer) + require.EqualValues(t, 2000, vb.Block().BlockHeader.FeesCollected.Raw) + } else { + require.False(t, dl.generator.GenesisProto().EnableMining) + // new fields are not in the header + require.Zero(t, vb.Block().BlockHeader.Proposer) + require.Zero(t, vb.Block().BlockHeader.FeesCollected) + } + + postsink := micros(dl.t, dl.generator, genBalances.FeeSink) + postprop := micros(dl.t, dl.generator, proposer) + + // At the end of the block, all fees are still in the sink. + require.EqualValues(t, 2000, postsink-presink) + require.EqualValues(t, 0, postprop-preprop) + + // Do the next block, which moves part of the fees to proposer + dl.fullBlock() + postsink = micros(dl.t, dl.generator, genBalances.FeeSink) + postprop = micros(dl.t, dl.generator, proposer) + + if ver >= 39 { + require.EqualValues(t, 500, postsink-presink) // based on 75% in config/consensus.go + require.EqualValues(t, 1500, postprop-preprop) + } else { + require.EqualValues(t, 2000, postsink-presink) // no mining yet + } + }) +} + // TestHoldingGet tests some of the corner cases for the asset_holding_get // opcode: the asset doesn't exist, the account doesn't exist, account not opted // in, vs it has none of the asset. This is tested here, even though it should diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go index 79ae16284d..9edbdfa42d 100644 --- a/ledger/ledger_test.go +++ b/ledger/ledger_test.go @@ -173,6 +173,9 @@ func (l *Ledger) appendUnvalidatedSignedTx(t *testing.T, initAccounts map[basics if proto.TxnCounter { blk.TxnCounter = blk.TxnCounter + 1 } + if proto.EnableMining { + blk.FeesCollected = stx.Txn.Fee + } blk.Payset = append(blk.Payset, txib) blk.TxnCommitments, err = blk.PaysetCommit() require.NoError(t, err) diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 541a3a54f8..3bfef32649 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -17,6 +17,7 @@ package ledgercore import ( + "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/committee" ) @@ -39,11 +40,15 @@ func (vb ValidatedBlock) Delta() StateDelta { return vb.delta } -// WithSeed returns a copy of the ValidatedBlock with a modified seed. -func (vb ValidatedBlock) WithSeed(s committee.Seed) ValidatedBlock { +// WithSeed returns a copy of the ValidatedBlock with a modified seed and associated proposer +func (vb ValidatedBlock) WithSeed(s committee.Seed, proposer basics.Address) ValidatedBlock { newblock := vb.blk newblock.BlockHeader.Seed = s + if vb.blk.ConsensusProtocol().EnableMining { + newblock.BlockHeader.Proposer = proposer + } + return ValidatedBlock{ blk: newblock, delta: vb.delta, diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 10b87f9378..b42d3a53f2 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" + "github.com/algorand/go-algorand/data/committee" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/txntest" "github.com/algorand/go-algorand/ledger/eval" @@ -136,11 +137,20 @@ func txgroup(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, txns ...*t return eval.TransactionGroup(transactions.WrapSignedTxnsWithAD(txgroup)) } -// endBlock completes the block being created, returns the ValidatedBlock for inspection -func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator) *ledgercore.ValidatedBlock { - validatedBlock, err := eval.GenerateBlock() +// endBlock completes the block being created, returns the ValidatedBlock for +// inspection. Proposer is optional - if unset, blocks will be finished with +// ZeroAddress proposer. +func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer ...basics.Address) *ledgercore.ValidatedBlock { + vb, err := eval.GenerateBlock() require.NoError(t, err) - err = ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) + + var prp basics.Address + if len(proposer) > 0 { + prp = proposer[0] + } + *vb = vb.WithSeed(committee.Seed{}, prp) + + err = ledger.AddValidatedBlock(*vb, agreement.Certificate{}) require.NoError(t, err) // `rndBQ` gives the latest known block round added to the ledger // we should wait until `rndBQ` block to be committed to blockQueue, @@ -152,7 +162,7 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator) *ledgerco // then we return the result and continue the execution. rndBQ := ledger.Latest() ledger.WaitForCommit(rndBQ) - return validatedBlock + return vb } // main wraps up some TEAL source in a header and footer so that it is diff --git a/node/node.go b/node/node.go index 75d9bcfa27..abcb195951 100644 --- a/node/node.go +++ b/node/node.go @@ -1271,8 +1271,8 @@ type validatedBlock struct { } // WithSeed satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) WithSeed(s committee.Seed) agreement.ValidatedBlock { - lvb := vb.vb.WithSeed(s) +func (vb validatedBlock) WithSeed(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { + lvb := vb.vb.WithSeed(s, proposer) return validatedBlock{vb: &lvb} } diff --git a/protocol/tags.go b/protocol/tags.go index e980454674..1e2ef3b148 100644 --- a/protocol/tags.go +++ b/protocol/tags.go @@ -73,7 +73,7 @@ const PingReplyTagMaxSize = 8 // ProposalPayloadTagMaxSize is the maximum size of a ProposalPayloadTag message // This value is dominated by the MaxTxnBytesPerBlock -const ProposalPayloadTagMaxSize = 5247980 +const ProposalPayloadTagMaxSize = 5248065 // StateProofSigTagMaxSize is the maximum size of a StateProofSigTag message const StateProofSigTagMaxSize = 6378 diff --git a/test/scripts/e2e_subs/goal/goal.py b/test/scripts/e2e_subs/goal/goal.py index 812afe8976..a366f1b69e 100755 --- a/test/scripts/e2e_subs/goal/goal.py +++ b/test/scripts/e2e_subs/goal/goal.py @@ -229,19 +229,19 @@ def finish(self, tx, send): def keyreg(self, sender, votekey=None, selkey=None, votefst=None, votelst=None, votekd=None, send=None, **kwargs): - params = self.algod.suggested_params() + params = self.params(kwargs.pop("lifetime", 1000)) tx = txn.KeyregTxn(sender, params, votekey, selkey, votefst, votelst, votekd, **kwargs) return self.finish(tx, send) def pay(self, sender, receiver, amt: int, send=None, **kwargs): - params = self.algod.suggested_params() + params = self.params(kwargs.pop("lifetime", 1000)) tx = txn.PaymentTxn(sender, params, receiver, amt, **kwargs) return self.finish(tx, send) def acfg(self, sender, send=None, **kwargs): - params = self.algod.suggested_params() + params = self.params(kwargs.pop("lifetime", 1000)) tx = txn.AssetConfigTxn( sender, params, **kwargs, strict_empty_address_check=False ) @@ -252,7 +252,7 @@ def asset_create(self, sender, **kwargs): return self.acfg(sender, **kwargs) def axfer(self, sender, receiver, amt: int, index: int, send=None, **kwargs): - params = self.algod.suggested_params() + params = self.params(kwargs.pop("lifetime", 1000)) tx = txn.AssetTransferTxn( sender, params, receiver, amt, index, **kwargs ) @@ -263,7 +263,7 @@ def asset_optin(self, sender, index: int, **kwargs): return self.axfer(sender, sender, 0, index, **kwargs) def afrz(self, sender, index: int, target, frozen, send=None, **kwargs): - params = self.algod.suggested_params() + params = self.params(kwargs.pop("lifetime", 1000)) tx = txn.AssetFreezeTxn(sender, params, index, target, frozen, **kwargs) return self.finish(tx, send) @@ -274,9 +274,15 @@ def coerce_schema(self, values): return values return txn.StateSchema(num_uints=values[0], num_byte_slices=values[1]) + + def params(self, lifetime): + params = self.algod.suggested_params() + params.last = params.first + lifetime + return params + def appl(self, sender, index: int, on_complete=txn.OnComplete.NoOpOC, send=None, **kwargs): - params = self.algod.suggested_params() + params = self.params(kwargs.pop("lifetime", 1000)) local_schema = self.coerce_schema(kwargs.pop("local_schema", None)) global_schema = self.coerce_schema(kwargs.pop("global_schema", None)) tx = txn.ApplicationCallTxn( diff --git a/test/scripts/e2e_subs/mining.py b/test/scripts/e2e_subs/mining.py new file mode 100755 index 0000000000..5a52b4482c --- /dev/null +++ b/test/scripts/e2e_subs/mining.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python + +import base64 +import os +import sys +from goal import Goal +import algosdk.encoding as enc + +from datetime import datetime + +stamp = datetime.now().strftime("%Y%m%d_%H%M%S") +print(f"{os.path.basename(sys.argv[0])} start {stamp}") + +goal = Goal(sys.argv[1], autosend=True) + +joe = goal.new_account() + +_, err = goal.pay(goal.account, joe, amt=500_000) +assert not err, err + +# Turn off rewards for precise balance checking +_, err = goal.keyreg(joe, nonpart=True) +assert not err, err + +get_proposer = """ +#pragma version 10 + txn ApplicationArgs 0; btoi + block BlkProposer; global ZeroAddress; !=; assert + + txn ApplicationArgs 0; btoi + block BlkProposer; log + + txn ApplicationArgs 0; btoi + block BlkFeesCollected; itob; log + + int 1 +""" + + + +# During construction, the app examines an arbitrary round, a little before the latest. +examined = max(goal.params(1).first-5, 1) +txinfo, err = goal.app_create(joe, goal.assemble(get_proposer), app_args=[examined], lifetime=50) +assert not err, err +getter = txinfo['application-index'] +assert getter + +# There should be two logs, the proposer of the examined round, and the fees from that round +rnd = txinfo['confirmed-round'] +# Look at the block of the creation. We know fees collected is non-zero +block = goal.algod.block_info(rnd)['block'] +assert "fc" in block +assert block["fc"] > 0 # We don't test exact, because other tests are running +assert "prp" in block +assert block["prp"] != "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ" + +create_proposer = block["prp"] +immediately_after = goal.balance(create_proposer) +assert immediately_after > 10000000 # Our proposers in e2e tests have pretty much all the money + +# Compare the examined block's header to what the AVM saw (and logged) +block = goal.algod.block_info(examined)['block'] +print("creation", txinfo['logs'], block) +assert base64.b64decode(txinfo['logs'][0]) == enc.decode_address(block['prp']) +assert base64.b64decode(txinfo['logs'][1]) == block.get('fc',0).to_bytes(8, "big") + +# Now have the app examine the round the app was constructed, so we +# can check the log and know there should be a fee. +goal.wait_for_block(rnd+1) # because fv is set to current latest (rnd), so it `block rnd` wouldn't work +txinfo, err = goal.app_call(joe, getter, app_args=[rnd], lifetime=10) +assert not err, err + +block = goal.algod.block_info(rnd)['block'] +# note we use block['fc'], not block.get('fc', 0) +print("call", txinfo['logs'], block) +assert base64.b64decode(txinfo['logs'][0]) == enc.decode_address(block['prp']) +assert base64.b64decode(txinfo['logs'][1]) == block['fc'].to_bytes(8, "big") + +# Now let's confirm the proposer got paid. It's somewhat tricky, +# because in our e2e tests, we have only two proposers, and their +# balances may be changing all the time, because e2e_client_runner is +# taking out 1M algos at a time to run parallel subtests. We'll work +# with the balance mod 1M, so we can see the small fees being added to +# it, even if the balance has dropped by 1M. But then we _also_ need +# to worry about wraparound. +after_mining_credit = goal.balance(create_proposer) +assertion = str(after_mining_credit)+" > "+str(immediately_after) +print("credit", assertion) +M = 1_000_000 +before = immediately_after % M +after = after_mining_credit % M + +# Detect that the mining credit wrapped around with respect to 1M. +if after < M/4 and before > 3*M/4: + after += M +assert after > before, assertion +stamp = datetime.now().strftime("%Y%m%d_%H%M%S") +print(f"{os.path.basename(sys.argv[0])} OK {stamp}") From 84ee996aab346aa226a99b4fa865ec7d8e1964fb Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 28 Nov 2023 16:05:29 -0500 Subject: [PATCH 002/117] Add min and max to receive block incentives --- ledger/acctonline_expired_test.go | 5 +- ledger/apptxn_test.go | 12 ++-- ledger/eval/eval.go | 52 ++++++++++++-- ledger/eval_simple_test.go | 111 ++++++++++++++++++------------ ledger/testing/testGenesis.go | 7 +- 5 files changed, 126 insertions(+), 61 deletions(-) diff --git a/ledger/acctonline_expired_test.go b/ledger/acctonline_expired_test.go index ac7986a78b..e51494d184 100644 --- a/ledger/acctonline_expired_test.go +++ b/ledger/acctonline_expired_test.go @@ -244,9 +244,8 @@ type doubleLedgerAcctModel struct { } func newDoubleLedgerAcctModel(t testing.TB, proto protocol.ConsensusVersion, inMem bool) *doubleLedgerAcctModel { - // set 1 Algo for rewards pool size -- rewards math not supported by newMapOnlineAcctModel - genesisOpt := ledgertesting.TestGenesisRewardsPoolSize(basics.MicroAlgos{Raw: 1_000_000}) - genBalances, genAddrs, genSecrets := ledgertesting.NewTestGenesis(genesisOpt) + // rewards math not supported by newMapOnlineAcctModel + genBalances, genAddrs, genSecrets := ledgertesting.NewTestGenesis(ledgertesting.TurnOffRewards) cfg := config.GetDefaultLocal() opts := []simpleLedgerOption{simpleLedgerNotArchival()} if !inMem { diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index d94d78d63d..8126322875 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -35,7 +35,7 @@ import ( "github.com/algorand/go-algorand/test/partitiontest" ) -// TestPayAction ensures a pay in teal affects balances +// TestPayAction ensures a inner pay transaction affects balances func TestPayAction(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() @@ -59,8 +59,13 @@ func TestPayAction(t *testing.T) { `)) // We're going to test some mining effects here too, so that we have an inner transaction example. - // Do a block with no txns, so the fee sink balance as we start the next block is stable - dl.fullBlock() + proposer := basics.Address{0x01, 0x02, 0x03} + dl.txn(&txntest.Txn{ + Type: "pay", + Sender: addrs[7], + Receiver: proposer, + Amount: 1_000_000 * 1_000_000, // 1 million algos is surely an eligible amount + }) payout1 := txntest.Txn{ Type: "appl", @@ -69,7 +74,6 @@ func TestPayAction(t *testing.T) { Accounts: []basics.Address{addrs[1]}, // pay self } - proposer := addrs[7] presink := micros(dl.t, dl.generator, genBalances.FeeSink) preprop := micros(dl.t, dl.generator, proposer) dl.beginBlock() diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 90baff553c..bdf351c953 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -788,11 +788,13 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return nil, fmt.Errorf("overflowed subtracting rewards for block %v", hdr.Round) } - miningIncentive, _ := basics.NewPercent(proto.MiningPercent).DivvyAlgos(prevHeader.FeesCollected) - err = eval.state.Move(prevHeader.FeeSink, prevHeader.Proposer, miningIncentive, nil, nil) - if err != nil { - // This should be impossible. The fees were just collected, and the FeeSink cannot be emptied. - return nil, fmt.Errorf("unable to Move mining incentive") + if eval.eligibleForIncentives(prevHeader.Proposer) { + miningIncentive, _ := basics.NewPercent(proto.MiningPercent).DivvyAlgos(prevHeader.FeesCollected) + err = eval.state.Move(prevHeader.FeeSink, prevHeader.Proposer, miningIncentive, nil, nil) + if err != nil { + // This should be impossible. The fees were just collected, and the FeeSink cannot be emptied. + return nil, fmt.Errorf("unable to Move mining incentive") + } } if eval.Tracer != nil { @@ -802,6 +804,46 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return eval, nil } +const ( + // these would become ConsensusParameters if we ever wanted to change them + + // incentiveMinBalance is the minimum balance an account must have to be + // eligible for incentives. It serves a couple purposes. First, it sets a + // manageable upper bound on the total number of incentivized participating + // accounts. This means it is possible to track these accounts in memory, + // which is required to ensure absenteeism checking time is bounded. Second, + // it ensures that smaller accounts continue to operate for the same + // motivations they had before block incentives were introduced. Without + // that assurance, it is difficult to model their behaviour - might many + // participants join for the hope of easy financial rewards, but without + // caring enough to run a high-quality node? + incentiveMinBalance = 100_000 * 1_000_000 // 100K algos + + // incentiveMaxBalance is the maximum balance an account might have to be + // eligible for incentives. It encourages large accounts to split their + // stake to add resilience to consensus in the case of outages. Of course, + // nothing in protocol can prevent such accounts from running nodes that + // share fate (same machine, same data center, etc), but this serves as a + // gentle reminder. + incentiveMaxBalance = 100_000_000 * 1_000_000 // 100M algos +) + +func (eval *BlockEvaluator) eligibleForIncentives(proposer basics.Address) bool { + proposerState, err := eval.state.Get(proposer, true) + if err != nil { + return false + } + if proposerState.MicroAlgos.Raw < incentiveMinBalance { + return false + } + if proposerState.MicroAlgos.Raw > incentiveMaxBalance { + return false + } + // We'll also need a flag on the account, set to true if the account + // properly key-regged for incentives by including the "entry fee". + return true +} + // hotfix for testnet stall 08/26/2019; move some algos from testnet bank to rewards pool to give it enough time until protocol upgrade occur. // hotfix for testnet stall 11/07/2019; do the same thing func (eval *BlockEvaluator) workaroundOverspentRewards(rewardPoolBalance ledgercore.AccountData, headerRound basics.Round) (poolOld ledgercore.AccountData, err error) { diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 1b10cf5352..9f9e15912f 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -217,57 +217,78 @@ func TestMiningFees(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - genBalances, addrs, _ := ledgertesting.NewTestGenesis() + // Lots of balance checks that would be messed up by rewards + genBalances, addrs, _ := ledgertesting.NewTestGenesis(ledgertesting.TurnOffRewards) // Mining begins in v39. Start checking in v38 to test that is unchanged. ledgertesting.TestConsensusRange(t, 38, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { dl := NewDoubleLedger(t, genBalances, cv, cfg) defer dl.Close() - dl.fullBlock() - - pay := txntest.Txn{ - Type: "pay", - Sender: addrs[1], - Receiver: addrs[2], - Amount: 100000, - } - - proposer := addrs[7] - presink := micros(dl.t, dl.generator, genBalances.FeeSink) - preprop := micros(dl.t, dl.generator, proposer) - dl.beginBlock() - dl.txns(&pay, pay.Args("again")) - vb := dl.endBlock(proposer) - - if ver >= 39 { - require.True(t, dl.generator.GenesisProto().EnableMining) // version sanity check - // new fields are in the header - require.EqualValues(t, proposer, vb.Block().BlockHeader.Proposer) - require.EqualValues(t, 2000, vb.Block().BlockHeader.FeesCollected.Raw) - } else { - require.False(t, dl.generator.GenesisProto().EnableMining) - // new fields are not in the header - require.Zero(t, vb.Block().BlockHeader.Proposer) - require.Zero(t, vb.Block().BlockHeader.FeesCollected) - } - - postsink := micros(dl.t, dl.generator, genBalances.FeeSink) - postprop := micros(dl.t, dl.generator, proposer) - - // At the end of the block, all fees are still in the sink. - require.EqualValues(t, 2000, postsink-presink) - require.EqualValues(t, 0, postprop-preprop) - - // Do the next block, which moves part of the fees to proposer - dl.fullBlock() - postsink = micros(dl.t, dl.generator, genBalances.FeeSink) - postprop = micros(dl.t, dl.generator, proposer) + tooBig := basics.Address{0x01, 0x011} + tooSmall := basics.Address{0x01, 0x022} + smallest := basics.Address{0x01, 0x033} + biggest := basics.Address{0x01, 0x044} + + dl.txns(&txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: tooBig, Amount: 100_000_000*1_000_000 + 1}, + &txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: tooSmall, Amount: 100_000*1_000_000 - 1}, + &txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: smallest, Amount: 100_000 * 1_000_000}, + &txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: biggest, Amount: 100_000_000 * 1_000_000}, + ) + + for _, proposer := range []basics.Address{tooBig, tooSmall, smallest, biggest} { + t.Log(proposer) + dl.fullBlock() // start with an empty block, so no mining fees are paid at start of next one + + presink := micros(dl.t, dl.generator, genBalances.FeeSink) + preprop := micros(dl.t, dl.generator, proposer) + t.Log(" presink", presink) + t.Log(" preprop", preprop) + dl.beginBlock() + pay := txntest.Txn{ + Type: "pay", + Sender: addrs[1], + Receiver: addrs[2], + Amount: 100000, + } + dl.txns(&pay, pay.Args("again")) + vb := dl.endBlock(proposer) + + if ver >= 39 { + require.True(t, dl.generator.GenesisProto().EnableMining) // version sanity check + require.NotZero(t, dl.generator.GenesisProto().MiningPercent) // version sanity check + // new fields are in the header + require.EqualValues(t, proposer, vb.Block().BlockHeader.Proposer) + require.EqualValues(t, 2000, vb.Block().BlockHeader.FeesCollected.Raw) + } else { + require.False(t, dl.generator.GenesisProto().EnableMining) + require.Zero(t, dl.generator.GenesisProto().MiningPercent) // version sanity check + // new fields are not in the header + require.Zero(t, vb.Block().BlockHeader.Proposer) + require.Zero(t, vb.Block().BlockHeader.FeesCollected) + } - if ver >= 39 { - require.EqualValues(t, 500, postsink-presink) // based on 75% in config/consensus.go - require.EqualValues(t, 1500, postprop-preprop) - } else { - require.EqualValues(t, 2000, postsink-presink) // no mining yet + postsink := micros(dl.t, dl.generator, genBalances.FeeSink) + postprop := micros(dl.t, dl.generator, proposer) + t.Log(" postsink", postsink) + t.Log(" postprop", postprop) + + // At the end of the block, all fees are still in the sink. + require.EqualValues(t, 2000, postsink-presink) + //require.EqualValues(t, 0, postprop-preprop) + + // Do the next block, which moves part of the fees to proposer + dl.fullBlock() + postsink = micros(dl.t, dl.generator, genBalances.FeeSink) + postprop = micros(dl.t, dl.generator, proposer) + + if ver >= 39 && (proposer == smallest || proposer == biggest) { + require.EqualValues(t, 500, postsink-presink) // based on 75% in config/consensus.go + require.EqualValues(t, 1500, postprop-preprop) + } else { + // stayed in the feesink + require.EqualValues(t, 0, postprop-preprop, "%v", proposer) + require.EqualValues(t, 2000, postsink-presink) + } } }) } diff --git a/ledger/testing/testGenesis.go b/ledger/testing/testGenesis.go index a2d469dcf0..06b50be977 100644 --- a/ledger/testing/testGenesis.go +++ b/ledger/testing/testGenesis.go @@ -33,10 +33,9 @@ type testGenesisCfg struct { // TestGenesisOption provides functional options for testGenesisCfg. type TestGenesisOption func(*testGenesisCfg) -// TestGenesisRewardsPoolSize configures the rewards pool size in the genesis block. -func TestGenesisRewardsPoolSize(amount basics.MicroAlgos) TestGenesisOption { - return func(cfg *testGenesisCfg) { cfg.rewardsPoolAmount = amount } -} +// TurnOffRewards turns off the rewards pool for tests that are sensistive to +// "surprise" balance changes. +var TurnOffRewards = func(cfg *testGenesisCfg) { cfg.rewardsPoolAmount = basics.MicroAlgos{Raw: 100_000} } // NewTestGenesis creates a bunch of accounts, splits up 10B algos // between them and the rewardspool and feesink, and gives out the From c3bc1c827482edeb006c6ef8bd7befdc5e9b123f Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 29 Nov 2023 15:27:52 -0500 Subject: [PATCH 003/117] Stop checking for incentive payouts in e2e subs. --- test/scripts/e2e_subs/mining.py | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/test/scripts/e2e_subs/mining.py b/test/scripts/e2e_subs/mining.py index 5a52b4482c..04f10e4b7f 100755 --- a/test/scripts/e2e_subs/mining.py +++ b/test/scripts/e2e_subs/mining.py @@ -76,23 +76,10 @@ assert base64.b64decode(txinfo['logs'][0]) == enc.decode_address(block['prp']) assert base64.b64decode(txinfo['logs'][1]) == block['fc'].to_bytes(8, "big") -# Now let's confirm the proposer got paid. It's somewhat tricky, -# because in our e2e tests, we have only two proposers, and their -# balances may be changing all the time, because e2e_client_runner is -# taking out 1M algos at a time to run parallel subtests. We'll work -# with the balance mod 1M, so we can see the small fees being added to -# it, even if the balance has dropped by 1M. But then we _also_ need -# to worry about wraparound. -after_mining_credit = goal.balance(create_proposer) -assertion = str(after_mining_credit)+" > "+str(immediately_after) -print("credit", assertion) -M = 1_000_000 -before = immediately_after % M -after = after_mining_credit % M - -# Detect that the mining credit wrapped around with respect to 1M. -if after < M/4 and before > 3*M/4: - after += M -assert after > before, assertion +# We can not do checks on whether the proposer actually gets paid here +# because in our e2e tests, the proposers _won't_ get paid. Their +# accounts have too many algos. + stamp = datetime.now().strftime("%Y%m%d_%H%M%S") print(f"{os.path.basename(sys.argv[0])} OK {stamp}") + From 1d9b337c087ba4a8119bce2bc22a073f99433600 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 30 Nov 2023 12:13:00 -0500 Subject: [PATCH 004/117] Add IncentiveEligible field to accounts. --- daemon/algod/api/algod.oas2.json | 4 + daemon/algod/api/algod.oas3.yml | 4 + daemon/algod/api/server/v2/account.go | 1 + .../api/server/v2/generated/data/routes.go | 257 +++++------ .../v2/generated/experimental/routes.go | 257 +++++------ .../api/server/v2/generated/model/types.go | 3 + .../nonparticipating/private/routes.go | 419 +++++++++--------- .../nonparticipating/public/routes.go | 411 ++++++++--------- .../generated/participating/private/routes.go | 269 +++++------ .../generated/participating/public/routes.go | 280 ++++++------ daemon/algod/api/server/v2/handlers.go | 1 + data/basics/msgp_gen.go | 158 ++++--- data/basics/userBalance.go | 2 + ledger/ledgercore/accountdata.go | 8 +- ledger/store/trackerdb/data.go | 6 + ledger/store/trackerdb/data_test.go | 2 +- ledger/store/trackerdb/msgp_gen.go | 35 +- ledger/testing/randomAccounts.go | 5 + ledger/testing/randomAccounts_test.go | 1 - 19 files changed, 1112 insertions(+), 1011 deletions(-) diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index e327e0e3be..23f835e908 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -2983,6 +2983,10 @@ "participation": { "$ref": "#/definitions/AccountParticipation" }, + "incentive-eligible": { + "description": "Whether or not the account can receive block incentives if its balance is in range at proposal time.", + "type": "boolean" + }, "pending-rewards": { "description": "amount of MicroAlgos of pending rewards in this account.", "type": "integer" diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index be9237b90e..96e4a670d9 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -1067,6 +1067,10 @@ }, "type": "array" }, + "incentive-eligible": { + "description": "Whether or not the account can receive block incentives if its balance is in range at proposal time.", + "type": "boolean" + }, "min-balance": { "description": "MicroAlgo balance required by the account.\n\nThe requirement grows based on asset and application usage.", "type": "integer" diff --git a/daemon/algod/api/server/v2/account.go b/daemon/algod/api/server/v2/account.go index addb81b432..0d659320bf 100644 --- a/daemon/algod/api/server/v2/account.go +++ b/daemon/algod/api/server/v2/account.go @@ -123,6 +123,7 @@ func AccountDataToAccount( Status: record.Status.String(), RewardBase: &record.RewardsBase, Participation: apiParticipation, + IncentiveEligible: omitEmpty(record.IncentiveEligible), CreatedAssets: &createdAssets, TotalCreatedAssets: uint64(len(createdAssets)), CreatedApps: &createdApps, diff --git a/daemon/algod/api/server/v2/generated/data/routes.go b/daemon/algod/api/server/v2/generated/data/routes.go index 4a3438b8ff..0507c856e2 100644 --- a/daemon/algod/api/server/v2/generated/data/routes.go +++ b/daemon/algod/api/server/v2/generated/data/routes.go @@ -192,134 +192,135 @@ var swaggerSpec = []string{ "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", - "npw8mnz8/wEAAP//6aRdnSH5AAA=", + "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", + "r0Zkc775AAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/experimental/routes.go b/daemon/algod/api/server/v2/generated/experimental/routes.go index e7e56520a4..760f86cc7a 100644 --- a/daemon/algod/api/server/v2/generated/experimental/routes.go +++ b/daemon/algod/api/server/v2/generated/experimental/routes.go @@ -168,134 +168,135 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/model/types.go b/daemon/algod/api/server/v2/generated/model/types.go index b9fc3b1baa..2565f762a9 100644 --- a/daemon/algod/api/server/v2/generated/model/types.go +++ b/daemon/algod/api/server/v2/generated/model/types.go @@ -185,6 +185,9 @@ type Account struct { // Note: the raw account uses `map[int] -> Asset` for this type. CreatedAssets *[]Asset `json:"created-assets,omitempty"` + // IncentiveEligible Whether or not the account can receive block incentives if its balance is in range at proposal time. + IncentiveEligible *bool `json:"incentive-eligible,omitempty"` + // MinBalance MicroAlgo balance required by the account. // // The requirement grows based on asset and application usage. diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go index b866cc9f42..697e544f8d 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go @@ -139,215 +139,216 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "nUw+/v8AAAD//31f+lNw+wAA", + "H4sIAAAAAAAC/+x9/XMbN7Lgv4Livip/HEeSv7JrX229U+wkq4uTuCwle+9ZvgScaZJYDYEJgJHI+Py/", + "X6EBzGBmAHIoMXZS7/1ki4OPRqPR6C90f5jkYlUJDlyryYsPk4pKugINEv+ieS5qrjNWmL8KULlklWaC", + "T174b0RpyfhiMp0w82tF9XIynXC6graN6T+dSPi1ZhKKyQsta5hOVL6EFTUD601lWjcjrbOFyNwQp3aI", + "s1eTj1s+0KKQoNQQyh94uSGM52VdANGSckVz80mRG6aXRC+ZIq4zYZwIDkTMiV52GpM5g7JQR36Rv9Yg", + "N8Eq3eTpJX1sQcykKGEI50uxmjEOHipogGo2hGhBCphjoyXVxMxgYPUNtSAKqMyXZC7kDlAtECG8wOvV", + "5MW7iQJegMTdyoFd43/nEuA3yDSVC9CT99PY4uYaZKbZKrK0M4d9CaoutSLYFte4YNfAiel1RL6rlSYz", + "IJSTt1+/JE+ePHluFrKiWkPhiCy5qnb2cE22++TFpKAa/OchrdFyISTlRda0f/v1S5z/3C1wbCuqFMQP", + "y6n5Qs5epRbgO0ZIiHENC9yHDvWbHpFD0f48g7mQMHJPbOODbko4/2fdlZzqfFkJxnVkXwh+JfZzlIcF", + "3bfxsAaATvvKYEqaQd+dZM/ff3g0fXTy8S/vTrP/dH8+e/Jx5PJfNuPuwEC0YV5LCTzfZAsJFE/LkvIh", + "Pt46elBLUZcFWdJr3Hy6Qlbv+hLT17LOa1rWhk5YLsVpuRCKUEdGBcxpXWriJyY1Lw2bMqM5aidMkUqK", + "a1ZAMTXc92bJ8iXJqbJDYDtyw8rS0GCtoEjRWnx1Ww7TxxAlBq5b4QMX9MdFRruuHZiANXKDLC+FgkyL", + "HdeTv3EoL0h4obR3ldrvsiIXSyA4uflgL1vEHTc0XZYbonFfC0IVocRfTVPC5mQjanKDm1OyK+zvVmOw", + "tiIGabg5nXvUHN4U+gbIiCBvJkQJlCPy/LkboozP2aKWoMjNEvTS3XkSVCW4AiJm/4Jcm23/3+c/fE+E", + "JN+BUnQBb2h+RYDnooDiiJzNCRc6IA1HS4hD0zO1DgdX7JL/lxKGJlZqUdH8Kn6jl2zFIqv6jq7Zql4R", + "Xq9mIM2W+itECyJB15KnALIj7iDFFV0PJ72QNc9x/9tpO7KcoTamqpJuEGEruv77ydSBowgtS1IBLxhf", + "EL3mSTnOzL0bvEyKmhcjxBxt9jS4WFUFOZszKEgzyhZI3DS74GF8P3ha4SsAxw+SBKeZZQc4HNYRmjGn", + "23whFV1AQDJH5EfH3PCrFlfAG0Insw1+qiRcM1GrplMCRpx6uwTOhYaskjBnERo7d+gwDMa2cRx45WSg", + "XHBNGYfCMGcEWmiwzCoJUzDhdn1neIvPqIIvnqbu+PbryN2fi/6ub93xUbuNjTJ7JCNXp/nqDmxcsur0", + "H6EfhnMrtsjsz4ONZIsLc9vMWYk30b/M/nk01AqZQAcR/m5SbMGpriW8uOQPzV8kI+ea8oLKwvyysj99", + "V5eanbOF+am0P70WC5afs0UCmQ2sUYULu63sP2a8ODvW66he8VqIq7oKF5R3FNfZhpy9Sm2yHXNfwjxt", + "tN1Q8bhYe2Vk3x563WxkAsgk7ipqGl7BRoKBluZz/Gc9R3qic/mb+aeqStNbV/MYag0duysZzQfOrHBa", + "VSXLqUHiW/fZfDVMAKwiQdsWx3ihvvgQgFhJUYHUzA5KqyorRU7LTGmqcaR/kzCfvJj85bi1vxzb7uo4", + "mPy16XWOnYzIasWgjFbVHmO8MaKP2sIsDIPGT8gmLNtDoYlxu4mGlJhhwSVcU66PWpWlww+aA/zOzdTi", + "20o7Ft89FSyJcGIbzkBZCdg2vKdIgHqCaCWIVhRIF6WYNT/cP62qFoP4/bSqLD5QegSGghmsmdLqAS6f", + "ticpnOfs1RH5JhwbRXHBy425HKyoYe6Gubu13C3W2JbcGtoR7ymC2ynkkdkajwYj5h+C4lCtWIrSSD07", + "acU0/odrG5KZ+X1U5z8HiYW4TRMXKloOc1bHwV8C5eZ+j3KGhOPMPUfktN/3dmRjRokTzK1oZet+2nG3", + "4LFB4Y2klQXQfbF3KeOopNlGFtY7ctORjC4Kc3CGA1pDqG591naehygkSAo9GL4sRX71D6qWBzjzMz/W", + "8PjhNGQJtABJllQtjyYxKSM8Xu1oY46YaYgKPpkFUx01SzzU8nYsraCaBktz8MbFEot67IdMD2REd/kB", + "/0NLYj6bs21Yvx32iFwgA1P2ODsnQ2G0fasg2JlMA7RCCLKyCj4xWvdeUL5sJ4/v06g9+sraFNwOuUU0", + "O3SxZoU61DbhYKm9CgXUs1dWo9OwUhGtrVkVlZJu4mu3c41BwIWoSAnXUPZBsCwLR7MIEeuD84UvxToG", + "05diPeAJYg0H2QkzDsrVHrs74HvlIBNyN+Zx7DFINws0srxC9sBDEcjM0lqrT2dC3o4d9/gsJ60NnlAz", + "anAbTXtIwqZ1lbmzGbHj2Qa9gVq353Yu2h8+hrEOFs41/R2woMyoh8BCd6BDY0GsKlbCAUh/Gb0FZ1TB", + "k8fk/B+nzx49/vnxsy8MSVZSLCRdkdlGgyL3nbJKlN6U8GC4MlQX61LHR//iqbfcdseNjaNELXNY0Wo4", + "lLUIW5nQNiOm3RBrXTTjqhsAR3FEMFebRTuxzg4D2iumjMi5mh1kM1IIK9pZCuIgKWAnMe27vHaaTbhE", + "uZH1IXR7kFLI6NVVSaFFLsrsGqRiIuJeeuNaENfCy/tV/3cLLbmhipi50RZec5SwIpSl13w837dDX6x5", + "i5utnN+uN7I6N++Yfeki35tWFalAZnrNSQGzetFRDedSrAglBXbEO/ob0FZuYSs413RV/TCfH0Z3FjhQ", + "RIdlK1BmJmJbGKlBQS64DQ3Zoa66Ucegp48Yb7PUaQAcRs43PEfD6yGObVqTXzGOXiC14Xmg1hsYSygW", + "HbK8u/qeQoed6p6KgGPQ8Ro/o+XnFZSafi3kRSv2fSNFXR1cyOvPOXY51C3G2ZYK09cbFRhflN1wpIWB", + "/Si2xs+yoJf++Lo1IPRIka/ZYqkDPeuNFGJ+eBhjs8QAxQ9WSy1Nn6Gu+r0oDDPRtTqACNYO1nI4Q7ch", + "X6MzUWtCCRcF4ObXKi6cJQJY0HOODn8dynt6aRXPGRjqymltVltXBN3Zg/ui7ZjR3J7QDFGjEs68xgtr", + "W9npbHBEKYEWGzID4ETMnMfM+fJwkRR98dqLN040jPCLDlyVFDkoBUXmLHU7QfPt7NWht+AJAUeAm1mI", + "EmRO5Z2BvbreCecVbDKMHFHk/rc/qQefAV4tNC13IBbbxNDb2D2cW3QI9bjptxFcf/KQ7KgE4u8VogVK", + "syVoSKFwL5wk968P0WAX746Wa5DooPxdKd5PcjcCakD9nen9rtDWVSIe0qm3RsIzG8YpF16wig1WUqWz", + "XWzZNOro4GYFASeMcWIcOCF4vaZKW6c64wXaAu11gvNYIcxMkQY4qYaYkX/yGshw7Nzcg1zVqlFHVF1V", + "QmooYmvgsN4y1/ewbuYS82DsRufRgtQKdo2cwlIwvkOWXYlFENWN78lFnQwXhx4ac89voqjsANEiYhsg", + "575VgN0wJiwBCFMtoi3hMNWjnCYQbTpRWlSV4RY6q3nTL4Wmc9v6VP/Yth0SF9XtvV0IUBiK5to7yG8s", + "Zm004JIq4uAgK3plZA80g1jv/xBmcxgzxXgO2TbKRxXPtAqPwM5DWlcLSQvICijpZjjoj/YzsZ+3DYA7", + "3qq7QkNmw7rim95Sso+i2TK0wPFUTHgk+IXk5ggaVaAlENd7x8gF4Ngx5uTo6F4zFM4V3SI/Hi7bbnVk", + "RLwNr4U2O+7oAUF2HH0MwAk8NEPfHhXYOWt1z/4U/wHKTdDIEftPsgGVWkI7/l4LSNhQXcR8cF567L3H", + "gaNsM8nGdvCR1JFNGHTfUKlZzirUdb6FzcFVv/4EUb8rKUBTVkJBgg9WDazC/sQGJPXHvJ0qOMr2NgR/", + "YHyLLKdkCkWeLvBXsEGd+42NdA1MHYfQZSOjmvuJcoKA+vg5I4KHTWBNc11ujKCml7AhNyCBqHq2Ylrb", + "CPauqqtFlYUDRP0aW2Z0Xs2oT3Grm/UchwqWN9yK6cTqBNvhu+gpBh10OF2gEqIcYSEbICMKwagAGFIJ", + "s+vMBdP7cGpPSR0gHdNGl3Zz/d9THTTjCsh/iJrklKPKVWtoZBohUVBAAdLMYESwZk4X6tJiCEpYgdUk", + "8cvDh/2FP3zo9pwpMocb/wLFNOyj4+FDtOO8EUp3DtcB7KHmuJ1Frg90+JiLz2khfZ6yO9TCjTxmJ9/0", + "Bm+8ROZMKeUI1yz/zgygdzLXY9Ye0si4MBMcd5Qvp+OyH64b9/2creqS6kN4reCalpm4BilZATs5uZuY", + "Cf7VNS1/aLrh6xrIDY3mkOX4JmTkWHBh+thnJGYcxpk5wDaEdCxAcGZ7ndtOO1TMNkqPrVZQMKqh3JBK", + "Qg729YSRHFWz1CNi4yrzJeULVBikqBcusM+Ogwy/VtY0I2s+GCIqVOk1z9DIHbsAXDC3f0BjxCmgRqXr", + "W8itAnNDm/ncm6kxN3OwB32PQdRJNp0kNV6D1OtW47XI6b4CGnEZdOS9AD/txCNdKYg6I/sM8RVuizlM", + "ZnN/H5N9O3QMyuHEQahh+zEVbWjU7XJzAKHHDkQkVBIUXlGhmUrZr2Ievvhzd5jaKA2roSXfdv05cfze", + "JvVFwUvGIVsJDpvoI3fG4Tv8GD1OeE0mOqPAkurb10E68PfA6s4zhhrvil/c7f4J7Xus1NdCHsolagcc", + "Ld6P8EDudLe7KW/rJ6VlGXEtuvdAfQagpk3+ASYJVUrkDGW2s0JN7UFz3kj3eKiL/jdNlPMBzl5/3J4P", + "LXxqijZiKCtCSV4ytCALrrSsc33JKdqogqVGgp+8Mp62Wr70TeJm0ogV0w11ySkGvjWWq2jAxhwiZpqv", + "AbzxUtWLBSjd03XmAJfctWKc1JxpnGtljktmz0sFEiOQjmzLFd2QuaEJLchvIAWZ1bor/eNzN6VZWTqH", + "npmGiPklp5qUQJUm3zF+scbhvNPfH1kO+kbIqwYL8dt9ARwUU1k8SOsb+xUDit3yly64GNMT2M8+WLN9", + "fzsxy+w8uf+/9//9xbvT7D9p9ttJ9vx/HL//8PTjg4eDHx9//Pvf/1/3pycf//7g3/8ttlMe9thjLAf5", + "2SunGZ+9QvWn9QENYP9k9v8V41mUyMJojh5tkfv48NgR0IOucUwv4ZLrNTeEdE1LVhjechty6N8wg7No", + "T0ePajob0TOG+bXuqVTcgcuQCJPpscZbS1HDuMb4s0d0SrqXjHhe5jW3W+mlb/uqx8eXifm0edpqs968", + "IPjucUl9cKT78/GzLybT9r1i830ynbiv7yOUzIp17FVqAeuYrugOCB6Me4pUdKNAx7kHwh4NpbOxHeGw", + "K1jNQKolqz49p1CazeIczr+VcDanNT/jNjDenB90cW6c50TMPz3cWgIUUOllLBtGR1DDVu1uAvTCTiop", + "roFPCTuCo77NpzD6ogvqK4HOMSsDap9ijDbUnANLaJ4qAqyHCxllWInRT+9ZgLv81cHVITdwDK7+nI0/", + "0/+tBbn3zVcX5NgxTHXPPpC2QwdPWiOqtHu11QlIMtzM5gCyQt4lv+SvYI7WB8FfXPKCano8o4rl6rhW", + "IL+kJeU5HC0EeeEfgr2iml7ygaSVTNMVPMEjVT0rWU6uQoWkJU+bemU4wuXlO1ouxOXl+0FsxlB9cFNF", + "+YudIDOCsKh15hJHZBJuqIz5vlSTOABHtplhts1qhWxRWwOpT0zhxo/zPFpVqv+AeLj8qirN8gMyVO55", + "rNkyorSQXhYxAoqFBvf3e+EuBklvvF2lVqDILytavWNcvyfZZX1y8gRI50XtL+7KNzS5qWC0dSX5wLlv", + "VMGFW7US1lrSrKKLmIvt8vKdBlrh7qO8vEIbR1kS7NZ5yesD83GodgEeH+kNsHDs/SoRF3due/kkYfEl", + "4CfcQmxjxI3W8X/b/Qre9t56u3rvgwe7VOtlZs52dFXKkLjfmSZ30MIIWT4aQ7EFaqsuzdIMSL6E/Mrl", + "v4FVpTfTTncf8OMETc86mLKZkezLPMzNgQ6KGZC6KqgTxSnf9JMkKNDahxW/hSvYXIg2tcc+WRG6j/RV", + "6qAipQbSpSHW8Ni6Mfqb76LKULGvKv/WHR89erJ40dCF75M+yFbkPcAhjhFF5xF5ChFURhBhiT+Bglss", + "1Ix3J9KPLY/xHLhm15BByRZsFkvq+M+hP8zDaqjS5bFyUcjNgIqwOTGq/MxerE69l5QvwFzP5koVipY2", + "R180aMOoQK53JIWTv5iaCRrNzkWnhahGl4D9vgLMASduDGhGqRAufZl9JR+w2FrRBSTE99CBNfItesfp", + "hYPsupSj17CY92/bwWUYBdk2zsyao2QM5ouhY9S0ejGJfibrI3VuE8xK6hA2K1GGa4I3LUeksuNItGkW", + "U6DFTxdI3kpDHowuRkKKXFLlKRIT0HlGM0pA+R2zPmzL9XMWhNMFWeaaTD7+QugzkYHq6zL++DQ/PrdP", + "qPeOyNNj1A+M4I9th+AonRVQwsIu3Db2hNJmoGg3yMDxw3xeMg4ki0XmBTba4A50c4AR3h8SYt0DZPQI", + "MTIOwEbfPw5Mvhfh2eSLfYDkLoMG9WMjlwz+hvjbNhurbuQxUZn7hSVcbrnnANSFczaXay+oGIchjE+J", + "YXPXtDRszqmj7SCDlDMoU/cSzLjokwcpWXuLd8beenutyd6Tt1lNKNB5oOPS5haIZ2Kd2cetUXF8tp4Z", + "eo+G7+NT29jBtMl97ikyE2uMaMKrxYaL74AlDYcHIzA/rJlCesV+KVHDArNt2u2iXowKFZKMszU25JKS", + "dcZMnRCvUuRyP8jXcysAepaYNvm108x3atBd8WR4mbe32rTNQ+dfRsWOf+oIRXcpgb+hiajJsPOmL7FE", + "jSjdwJxucqFAvo0RvWETQw/S0E+loATUWLKOEJVdxdy6RvECvHHOfbfAsoIpjCjfPAiivSQsmNLQWvh9", + "EMfnsJ1SzJwoxDy9Ol3JuVnfWyGaa8r6OLFjZ5mffAUYLj1nUukM3SPRJZhGXyvU+L82TeOyUjeezOYZ", + "ZkWcN+C0V7DJClbWcXp18377ykz7fcMSVT1Dfsu4jaaZYV7saJTplqltIPLWBb+2C35ND7becafBNDUT", + "S0Mu3Tn+JOeix3m3sYMIAcaIY7hrSZRuYZDB6+AhdwzkpiAA4WibaXhwmAo/9s6QIv9GOXVH2ZGiawms", + "GVtXwdCHZcQSo6MH9TL6K0qcAVpVrFj3DLV21KTGTPeyxvhkfD0s4O66wXZgoBs0GI3B7iQydKGJziB1", + "jALysRHhbKyiC8QDiVqOfbBa1BItfp1IwGHWzEawG7n2b38610LSBTirbWZButMQuJx90BDkpFREM+t+", + "Ldh8DqG1Ut3G0tYBbmCTKkaQboTI4ibNmnH9xdMYGe2gnhbG3SiLU0yEFlI+rIuhVdiLVYHe2ZRVCbbm", + "Fqbd6PPWb2GT/WQ0FFJRJlUbzubMtF3+t8euX6++hQ2OvDNKzAC2Y1dQTX0LSIMxs2Dzyb7qaFSgMMEq", + "ZqTobOEeO3Ua36UDbY1LiZsm/jZmvJMytruUuxyM1qloYBmzG+dxX545PdBFfJ+Ud20CSxjjQnIMRK5w", + "KqZ8AaHhVdS83d5FuxdAS0+8uJzJx+nkbp6z2G3mRtyB6zfNBRrFM0ZmWU9KxxG+J8ppVUlxTcvM+RdT", + "l78U1+7yx+beHfmJhck4ZV98dfr6jQP/43SSl0Bl1ihjyVVhu+pPsyqbRHf7VYISi7eKWGU92Pwm82fo", + "k7xZgqv0EOj7g5TUrb85OIrORzmPB4ju5H3ONW6XuMVFDlXjIW8dJNZB3nWK02vKSu+Z8NAmgjlxcePy", + "mke5QjjAnZ3rQYxEdlB2Mzjd8dPRUtcOnoRz/YCp3OIaB3eJ3pAVOWc5Pbj09LWQHebvXvJEne2/n1hl", + "hGyLx0Rso68e1BemjogVvH5Z/GJO48OH4VF7+HBKfindhwBA/H3mfkf94uHDqKshakkwTAINBZyu4EET", + "lZzciE9rduJwM+6CPr1eNZKlSJNhQ6HWa+7RfeOwdyOZw2fhfimgBPPT7od/vU236A6BGXOCzlMvd5qg", + "rJUtWKSI4P0YRHw0ZkgLmf2KYkp267kZHiFer9DbkamS5XE/MJ8pw165DT4yjQk2ThjMzIg1S8Sy8ZoF", + "Y5lmY3IM9oAM5ogiU0XTHLa4mwl3vGvOfq2BsMJoNXMGEu+13lXnlQMcdSCQGtVzOJcb2EYRtMPfxQ4S", + "liPoy4wIxHYjSBjqNAD3VWPW9wttvGatzrRvxGQ444Bxb4l2dPThqNm+/lh2Q5bG6TFjCld6RufqIiTm", + "iBaiZCqbS/EbxG3RaMKPPBz3BRgYhgn/BjwW6dJnKY0Hqq2n2c6+a7vH68apjb+zLuwX3dR8uM1lGj/V", + "+23kbZReFU9v6pCcUsJCd2Q3lDbBWvB4BcFjmG7fhypQbs+TfTXdeZERP5Xh26djO357Kh3Mg/diJb2Z", + "0VgtAqMLGZiC7e0EVWhBfGe/Aap5E2xnJ0HEY9OW2cxLFcg2ccYwi+Mt9Ro77WiNplVgkKJC1WVqA8FK", + "JSLD1PyGclvD0fSz/Mr1VmC9oKbXjZCYN03F4z8KyNkqao69vHxX5ENff8EWzJYnrBUE9e/cQLb0q6Ui", + "V0OweenuUHM2JyfToAin242CXTPFZiVgi0e2xYwqvC4bj2TTxSwPuF4qbP54RPNlzQsJhV4qi1glSKN7", + "opDXRDHNQN8AcHKC7R49J/cxfkuxa3hgsOiEoMmLR8/R+27/OIndsq685DaWXSDP9sGNcTrGADY7hmGS", + "btR4tKKtL52+HbacJtt1zFnClu5C2X2WVpTTBcTjmVc7YLJ9cTfRo9rDC7feAFBaig1hOj4/aGr4U+KN", + "pGF/FgySi9WK6ZWL8lFiZeipLW5nJ/XD2Uqrri6Jh8t/xGC5yscK9Wxdn1iNoavEGwcMafyerqCL1imh", + "NlleydowVl8tiZz5XJxYqKWpz2JxY+YyS0dZEqNa56SSjGu0f9R6nv3NqMWS5ob9HaXAzWZfPI0UPOnW", + "BOD7Af7J8S5BgbyOo14myN7LLK4vuc8Fz1aGoxQP2jfJwalMRvXF47dSQWTbhx4r+ZpRsiS51R1yowGn", + "vhPh8S0D3pEUm/XsRY97r+yTU2Yt4+RBa7NDP7597aSMlZCxBNvtcXcShwQtGVzjC5P4Jpkx77gXshy1", + "C3eB/vOGoHiRMxDL/FmOKgKBR3Pb41Ijxf/0XZspGB2r9uVOzwYoZMTa6ex2nzjgaz+rW99/a2N28FsC", + "c6PRZsvQD7CSCNW1sbhNn0/81jhq7rV73jE4PvqFSKODoxz/8CEC/fDh1InBvzzufrbs/eHDeMLOqMnN", + "/Npi4S4aMfaN7eGXImIA89WxmoAi9544YoBMXVLmg2GCMzfUlHQrEX16KeIwj0HiAX/xU3B5+Q6/eDzg", + "H31EfGZmiRvYhjSnD3u3EluUZIrmexBqTMmXYj2WcHp3kCeePwCKEigZaZ7DlQwqzUXd9TvjRQIaNaPO", + "oBRGyQyLaIT2/D8Pns3ip1uwXbOy+KnNhdS7SCTl+TIaqDkzHX9uK8I3S7SsMpqXf0k5hzI6nNVtf/Y6", + "cERL/5cYO8+K8ZFt+5UO7XJ7i2sB74LpgfITGvQyXZoJQqx208w0z5jLhSgIztMmgW+Z47BkaFDH7Nca", + "lI4dDfxgXyuhs8swX1tGiwAv0Pp1RL7BhA8Glk6GX7Q6+dyJ3TxidVUKWkwxp+PFV6eviZ3V9rF1jW0Z", + "rwUaXbqriFrJx+dVa0oUxxMGjB9n+wtms2qls6bqViwlk2nR1gVjvdAJNMeE2Dkir6wlTHk7i52EYGZQ", + "uYIiKPJldTGkCfMfrWm+RBNT5yJLk/z4+nOeKlsDfFDMuin6gOfOwO1K0NkKdFMi9BLkDVOArzDhGrpZ", + "oJqUaM7E6bNCdZcna84tpRztIVM0JR72RbsHzgok3jcchayH+D0NDLZ8477l+M6xVzQHdb+2X89563MK", + "NUWKv3M24pxywVmOGaBjAhFmrBnnbRqRLDvuJlITd0IjhytaUbB5/+WwmKwx6BmhQ9zQcxt8NZtqqcP+", + "qWHtKs0sQCvH2aCY+sKYzq/BuAJXxMMQUcgnhYzEpkTj2Rs/+J5khMkoEoaqr823750ZEx9CXzGOBguH", + "NidmW89DqRg6GDlhmiwEKLeebkYu9c70OcLkVAWs3x+9FguWn7MFjmGjocyybejfcKhTHwjoAu9M25em", + "rUsZ3Pzcieqxk55WlZs0XTY1Xit6zZMIjoWf+HiAALnN+OFoW8htawQv3qeG0OAag4+gwnt4QBhNCdFe", + "vW6jIliKwhbEvk2K5g1kPALGa8a9Jyx+QeTRKwE3Bs9rop/KJdVWBBzF0y6Alok4dnzrZ12pdx2qnzDZ", + "oATX6OdIb2Nb/TTBOJoGreBG+Yb4Q2GoOxAmXtKyiYCN1DJFqcoJUQW+EelVN40xDsO4ff3k7gWwo2T6", + "tO2OScj3vYlSqZlmdbEAndGiiNVU+RK/Evzq3/rAGvK6qb1RVSTHTKTd1KxDanMT5YKrerVlLt/gjtMF", + "5YIj1BCWLPY7jNkVZhv8d59i9k3s697v23yga7FfPuLhe72Y1GtoOlNskY3HBN4pd0dHO/XtCL3tf1BK", + "L8WiC8jnMJImuFy4RzH+9pW5OMJ8hYMwY3u1NOkEMaRX4Hef5KJJhNXlSniVDcqroPO6KSK/3QyRLgc/", + "xcsv8aY0NHnb+9WagVMvS/PkQ2iqXUoWTclWFpRMc2FDPntG9KEnKBXmaaM8D2d8dmvditC0C+bbjsPF", + "hvq0zCLpaLmdL6Td4H2dId9epx4b+/Tk+L1fLvoKXBK5SsI1E7UPovGhrF4ltL92ii83z72j648GiH9u", + "43PSVH7hyvbZZTqd/NufrDONANdy8wcwnA82fVCIeijtWvNU24Q0FZ9GVYDq3IpjUvfHssQ72bBTCntH", + "Ie8BWb0aIw4MC3NPJ2fFXhdmrNLAxI4SO3bxMtvpRMxt8mU8YpVQrC28Fqu/PTJm/AJLaAeJpIdj+VjC", + "a8g1VttrY6QkwD5ppc1k3nb/3wmZ0+p0E1rv8jBvS748LLG3444fpCAJ0ujY8mRH41MNnzaRsPYhzw1V", + "mJhfoo27+/R19AO8+RxyTAa5NeXLP5fAg3QiU2+XQVjmQQYY1jxHwXSm+1sdW4C2ZWTZCk9QVuDO4KSe", + "I1/B5p4iHWqI1ktr3mLdJlkkYgC5Q+ZTZ6YMyS74h6mGMhALPrLTdoc2J3iy1HKQwOiWc3mSNBdHm9Ro", + "y5TxWq+j5jJd90r1hS8rUllhhqUi0/rHK6zMqVycE22STYZaOjkb1gu4cckqMUFP4zvxaStB+d98Ni47", + "S8muICwGjZ6qGyoL3yJqevFWnWzLfTRI5eLLHPaBnjczszYOf+irjmSgxicteSmMGJGl3gV1Q9+buLF7", + "ygb4tXlYEK45SFc0H+XfUijItPBx+9vg2IYKG8V4KySoZNUHC1wy3enbNp8rVr+hmN6UuuDFcIFEwooa", + "6GSQdTU95zZkv7Tf/VtqX/1kp4WpodfdZfj8CwymBkgMqX5O3G25+432bYxNjHOQmfc89VOwcpBdb0gl", + "RVHn9oIOD0ZjkBudAmULK4naafLhKns6QvDW+Qo2x1YJ8vUL/Q6GQFvJyYIepO7rbfJBzW8qBvfiIOB9", + "TsvVdFIJUWYJZ8fZMG9sn+KvWH4FBTE3hY9UTpSmJffRxt54s2+WG58ntaqAQ/HgiJBTbt+GeMd2t6pS", + "b3J+T2+bf42zFrVN5eyMakeXPB5kj0mW5R25mR9mOw9TYFjdHaeyg+zISrpO5KyV9CZSqPlorFY+dDX3", + "i+e2RGWhiMkk59Zj9RIPesxwhC/Zg5QL6MikxHm6iCpFLCTzNq/tzVBxTIWTIUAa+JhH3w0UbvAoAqLl", + "YCOn0GYwc7nLxJxIaJ3It03iNqxcG9Po+zM3s3T53VxI6NSgNb2FLLzIw1RbLJrKGdOSys1tUq0NKucO", + "rCdJLO8Mx2oisdqFtNFYQxyWpbjJkFllTW7zmGpr2qnuZexrzbT9zKmeQRDXRZUT1DZkSQuSCykhD3vE", + "n+1ZqFZCQlYKDPOKeaDn2sjdK3yrw0kpFkRUuSjA1giIU1BqrppzimITBFE1URRY2sFHn7ZPQMcjpzxU", + "2WabnMcuOrO+zETgKSiXjMdhyDYewrul5PFe2fnP5mgRYhjr0n17baXPsPAz7Fn3mZWlNxikSj+TH1WN", + "4Uj48MZM8ZSshNJOs7MjqWaoNsTrfi64lqIsu0YgKxIvnGX7O7o+zXP9WoirGc2vHqAeyYVuVlpM/bPU", + "fjBeO5PsZWQaWaP6Yhmx8+Is/tTtXYjacY6968cGYL7fzbF227hPY3W2u+vqF47nidyZWqxYHqfhP1d0", + "WzImLcYSoqmebAkn+zgfmyGjDi+HJpgBWdIQzcBptAbNKXE8zTl1kXmY/6LE2x+XzMFdEomLacgnndSS", + "5UnZqgcAQmpfjOpa2rpPoeTTcBWxsC/M0SXdB3QkF8fIn7vBZkY4OFAa7gTUINqwAfC+VfanNiWXjVyc", + "ibX//qDN2XUr4D9up/JYrfzIKW5Iy5Xy9/k9Ehwhnhl4a/wRVjX3N+juKKSmRt/IGzUAIB2X1IFhVHTS", + "vmDMKSuhyKhOXO5oE5oGmq170dKvvMqU4+Q5tRf2EogZu5bg8k1YkbpXqb2ihpRE03xoueUFrEFhMghb", + "bpoq62fw/g4obVmpnvItqqyEa+iEa7kkGDWKduwafF/VdCYFQIXev75NKhaHFN7lPUOFW3sWRLKMwW7U", + "cmERa3eK7DBLRI0oa57ZY6LGHiUD0TUratrBn9pX5Oia3cxRjqBqIJNnXm8bO82PdoS3foBT3z8mynhM", + "vB/Hh/ZmQXHUbWNAO+MSa5U69TwelhhmeGkcGjhb0Tg+LYm3fENV9IanDYBDkm/Vm5H7xAQPEPvVGnKU", + "arpxd3fHCcHBiOplb0qK4LLZ4dsbkj8LDW8l4eR4MVVDATLYrZYaTxdOYMcGWGuTG7HXSM1YQsrxf8f/", + "pmRW+4GMXm0rWoUa3CvwHjtMKN04K5xAy5oLzccXTl0+wb5SzoLI6hXdECHxH6Ov/VrTks03eEIt+L4b", + "UUtqSMi5CK3v2sUrmom3CyZTD5i3Cwg/lV03GztmMNzGjBIAba5AZ5zCzEBXEG4DuuUt58m1YTmqnq2Y", + "UnjZ9bZziAW3eJ8TYkWLUEfGzHTdOqc+V6np/T/bV1vhVD6hVFXS3NcvA6LoqmcQtzUKPXHpJay2P+sb", + "qseeBJq6hy3RSv+ct7iFcW/PyI1YrHyq3kMH7EE9uEGpizstY5/qye3L6C0PIkct5dC7MDY+ZAA0Opl9", + "Vq8d4NtsjD4D2KfAfzRpZGoZY8D/o+A9UUYvhNdWzPsEWO48+Y/Aau2qM7HOJMzVrlAIa1g1irBskwV4", + "4yTjuQSqbGzI2Q9OZWtzIjJuVEgbvdh435pRCpgz3jJLxqtaRzQATI3INwHCQvM0ojXh7ElJCUYMu6bl", + "D9cgJStSG2dOhy3jFeak9yZ51zei/Dd36nAAplrtB18SQvtSLWhmLnBb9cYGFipNeUFlETZnnOQgzb1P", + "buhG3d73YaCVtZEvdng/aCDNdN+3B34QJG0LSLlx7ss7eiYaAOkBXRQjXAsYwRpxK1ijiBYJT8IQhnha", + "BbrOSrHA92UJAnTJJ9H3Y5UVwdFga+Wh/eZR7DfYPg3m3XYHXwucdcwU28/ZD4g6VHh+5ExvPWnWmtZ/", + "8GcjMu1B8PTPF21YuN2cIf3H3mhe4COGzjvNfkV8v9c2PMTOBwlPRteCm9hFdJC7B76huXZ8PaOuDz72", + "EtTqsBnqtmpL4DeoNsiZ5i5wZ2j0GSjFFilT9452T5uQtST7eyABnq1U685Wd9ommMKMs08RqO0vZ7NK", + "VFk+JhrQpuYvnEHbQdqFMUEfgbk6se4mcEI1xSo6iU06VSv2rYOVrJqxyy9T5duU7JRBI8FBu8ZyMUde", + "hkfYmnHwjUdjvJj2Xx91DTYNkyCUSMhriQbNG7rZXVcokRL2/B+nzx49/vnxsy+IaUAKtgDVphXu1eVp", + "I8YY79tZPm2M2GB5Or4J/l26RZz3lPnnNs2muLNmua1qcwYOqhLtYwmNXACR4xipB3OrvcJx2qDvP9Z2", + "xRZ58B2LoeD33zMpyjKe1r0R3SKm/thuBcZ+I/FXIBVT2jDCrq+O6TZWVi3RHIfJPa9tnhHBc5d9vaEC", + "phPBOLGFpEItkZ/hq1/n3yCwrkrHq6xPYtu6nF5kLWIYnIHxGzMglaicKM3mJAYRvi2RwZtLZ2jE8M4g", + "erJhtjaOMkaILiY5Tnqn3GmeYk62c/tutUYd5/RmEyPihT+UtyDNlCU9/aL9NpykNaX/YfhH5In+wbhG", + "s9zfg1dE9YPbFT4eBdrwuXaEPBCAxDvMzgu6sC56m2lUWqs82u+9q7MvfnzXukB3PhhASHyHHeCFDyvb", + "dk2MuwPnM6fs/K5BSrCU9ylK6Cx/11tNz3qbiyTYImek0BqUZUtiKBYGD3HVy+Z9a0IrGTyDxSLoRjMt", + "y8jzWWs3wTMVEo5RCeQ1LT8918Dq+KeIDyjeph/NhG8oQyRbVKrbZXB7TUfNHbyXPNzU/A0+2f0nmD2K", + "3nNuKOcuHtxmaPXCktQLfyvYV8DkBse04UCPviAzl02/kpAz1XdD33jhpHkyCJLNXeglrPWON4q71vmT", + "0Hcg47mPGSHfB+4kgWa7FsL2iH5mppI4uVEqj1HfgCwi+IvxqLD65o7r4o6Z12+XECRI7bVnQpBhXdGx", + "y7NJL8ylUysYrnP0bd3BbeSibtc2NpvN6ATul5fv9GxMEpp4snXTHbPgHCTr+l4513+H/DcWR24MN2+M", + "Yn5KZUS1WT8TyXd7+1GzcmeASCeV8sfpZAEcFFOYLPhnVxzi096lHgL7Jn94VC2sd0kkYhETWWtn8mCq", + "IEnyiPzIrlskGzK+d8tryfQGC4N6Axr7OZqp55sm64PLGtL4rtzdp8UVNMWZ2xwRtfK36zeClngfWZca", + "N7eQKI/IV2u6qkpnDiZ/vzf7Kzz529Pi5Mmjv87+dvLsJIenz56fnNDnT+mj508eweO/PXt6Ao/mXzyf", + "PS4eP308e/r46RfPnudPnj6aPf3i+V/vGT5kQLaA+tzdLyb/JzstFyI7fXOWXRhgW5zQin0LZm9QV54L", + "LFxnkJrjSYQVZeXkhf/pf/kTdpSLVTu8/3XiCrBMllpX6sXx8c3NzVHY5XiBj8IzLep8eeznwXJiHXnl", + "zVkTTW7jXnBHW+sxbqojhVP89var8wty+ubsqCWYyYvJydHJ0SNXu5bTik1eTJ7gT3h6lrjvx47YJi8+", + "fJxOjpdAS8yhYv5YgZYs958k0GLj/q9u6GIB8ggfDNifrh8fe7Hi+IN7HP9x27fjMKTi+EMnh0CxoyeG", + "Axx/8BUst7fuVC90kVhBh5FQbGt2PMOqFWObggoap5eCyoY6/oDicvL3Y2fziH9EtcWeh2OfaCPesoOl", + "D3ptYN3RY82KYCU51fmyro4/4H+Qej9adlJCLOmGzaZOSdt8SpgmdCYk1jzU+dJwEF9sjamgZVgC+aww", + "x8D0emkh8LVr0b8+efFu+HQAByJ+JOQZ5kC0R7ozU8u10b45aUumN3dSp317M707yZ6///Bo+ujk41/M", + "zeP+fPbk48hXNi+bccl5c62MbPgeK5VhPCGe9McnJ569OeUhIM1jd5KDxQ2UqHaRdpOacMXhre9oIR0a", + "7raqNxBpkLGjolJv+KHwghz96Z4r3mpp6qSIxOH7JSwK4l+04tyPPt3cZ9wGSZqbw95wH6eTZ59y9Wfc", + "kDwtCbYMSmQOt/5HfsXFDfctjThSr1ZUbvwxVh2mQNxm46VHFwpdlpJdU5QCueBB3iu+mLzHDAqxV8UJ", + "fqM0vQW/OTe9/pvfdBrGS6Rb84crpxo42u1l0lSPAZ8M0AfX0uKa8tzH8bfhwbhfVuB1hNFEoNUK5nXp", + "X4xXJZvbKrVClH4iVVeV4ThzqhrKcjHJRoK1D3CboUnNc8FtLAOGf3uPDD6kRa+OumJVpwubG6py9VM5", + "gHtjiZv+aw1y0+76ihlRtN3eQbTN78nCLR4PwMK7Ax2YhT/ek43++Vf8X/vSenryt08Hgc8zccFWIGr9", + "Z700z+0NdqdL08nwNlX6sV7zY4xvPP7Q0Ujc54FG0v297R62uF6JArwKIeZzW39/2+fjD/bfYCJYVyDZ", + "CrgthOt+tTfHMZZh3Qx/3vA8+uNwHZ0Umomfj72JI6bldlt+6PzZVe7UstaFuLGVw6LyCl6ftHSVstGS", + "31gFzD3oBmize5IfquaicikrCMVKSaLWrdnGxnK7t6CNYw1vtCa8YsE4ToAeEpzFloSnwQWuwNyNaIzo", + "yUYOsu9FAUPZKHYROhg7l2FzFCIF2O98MQ4Z78f9Dgp6cqwbckhG5mOt+n8f31CmjQTl0mwiRoedNdDy", + "2NXU6f3aprEffMHc/MGP4YPW6K/HtHsuukYSs2WpjgMLSuyrsyAkGvlocv+5taaG1kkkl8Yu+e692XWs", + "nu0oqTW2vTg+xudFS6H0MUqiXUNc+PF9s9G+6GOz4ebbOhOSLRinZeaMXG1hsMnjo5PJx/8fAAD//9sE", + "6a4N/AAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go index 713e5cd7b0..49eb954d27 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go @@ -802,211 +802,212 @@ var swaggerSpec = []string{ "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", - "bwAAAP//bsB3VeaoAQA=", + "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", + "AQA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/participating/private/routes.go b/daemon/algod/api/server/v2/generated/participating/private/routes.go index c0e0c71fe2..84000d35dd 100644 --- a/daemon/algod/api/server/v2/generated/participating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/private/routes.go @@ -281,140 +281,141 @@ var swaggerSpec = []string{ "7Fe+SFh8CfgItxDfMeJG6/i/6X4Fub033q5efvBgl2q9zMzZjq5KGRL3O9PUDloYIctHYyi2QG3VlVma", "AcmXkF+7+jewqvRu2vncB/w4QdOzDqZsZSSbmYe1OdBBMQNSVwV1ojjlu36RBAVa+7Dit3ANu0vRlvY4", "pipCN0lfpQ4qUmogXRpiDY+tG6O/+S6qDBX7qvK57pj06MniRUMX/pv0QbYi7x0c4hhRdJLIU4igMoII", - "S/wJFNxgoWa8W5F+bHlGy5jZmy9SJcnzfuJeaZUnFwAWrgat7vb5CrDMmtgoMqNGbheuQphNRA+4WK3o", - "AhIScugjGpnu3fEr4SCH7r3oTSfm/QttcN9EQbYvZ2bNUUoB88SQCiozvbA/P5N1QzrPBBb+dAiblSgm", - "NfGRlulQ2fHV2UqGKdDiBAyStwKHB6OLkVCyWVLli5dhjTd/lkfJAH9gYYV95XTOg4i1oJBbUyzH89z+", - "OR1ol66ojq+k48vnhKrliFI4RsLHIPnYdgiOAlABJSzswu3LnlDaIg/tBhk4fpzPS8aBZLHgt8AMGlwz", - "bg4w8vFDQqwFnoweIUbGAdjoXseByQ8iPJt8cQyQ3BWpoH5sdMwHf0M8fcyGgxuRR1SGhbOEVyv3HIC6", - "iMnm/urF7eIwhPEpMWxuTUvD5pzG1w4yqOqCYmuvhosL8HiQEmf3OEDsxXLUmuxVdJPVhDKTBzou0O2B", - "eCa2mc0fjUq8s+3M0Hs0Qh6zWWMH09bPuafITGwxaAivFhuRfQCWNBwejEDD3zKF9IrfpW5zC8y+afdL", - "UzEqVEgyzpzXkEtKnBgzdUKCSZHL/aAkzo0A6Bk72vrSTvk9qKR2xZPhZd7eatO21JtPPood/9QRiu5S", - "An9DK0xTxOZNX2KJ2im6sS/d+j2BCBkjesMmhk6aoStIQQmoFGQdISq7jnlOjW4DeONc+M8C4wVWCaJ8", - "9yAIqJKwYEpDa0T3cRKfwzxJsTihEPP06nQl52Z9b4VorinrRsQPO8v85CvAiOQ5k0pn6IGILsG89I1C", - "pfob82pcVuqGbNlSvqyI8wac9hp2WcHKOk6vbt7vXplpf2hYoqpnyG8ZtwErMyw9HQ3k3DO1jfXdu+DX", - "dsGv6Z2td9xpMK+aiaUhl+4c/yLnosd597GDCAHGiGO4a0mU7mGQQQLukDsGclPg4z/ZZ30dHKbCj30w", - "asenAafuKDtSdC2BwWDvKhi6iYxYwnRQuXmYGZs4A7SqWLHt2ULtqEmNmR5l8PD17npYwN11gx3AQDcu", - "Lxrm3KkV6KL/nM3nFAXkUyPC2XBAF+sGErUcmxNa1BKNap1gu2FhykawG7n2736+0ELSBTjDaGZButUQ", - "uJxj0BCUfVREM+vhLNh8DqFBUN3EmNUBrm/2iTZ3GEFkcathzbj+4lmMjA5QTwvjYZTFKSZCCyk30eXQ", - "8OrFqkDvbDqXBFtzA+tpNIP0O9hlPxsNhVSUSdVGjDlLaJf/HbHr69V3sMORDwZiGcAO7AqqqW8BaTBm", - "Fmwe2cSJRgUKa5hi0YfOFh6xU2fxXbqjrXFVZ9PE34Zld6qydpdym4PR+u0MLGN24yLuLjOnB7qI75Py", - "oU1gCWNcSI6ByBVOxZTv0TO8ipr06EO0ewm09MSLy5l8nE5u55yK3WZuxAO4ftNcoFE8Y/CTdVZ0fM1H", - "opxWlRRrWmbOhZe6/KVYu8sfX/cev08sTMYp+/Lrs9dvHPgfp5O8BCqzRhlLrgrfq/5lVmXr1O6/SlBi", - "8VYRq6wHm98U1wzdfpsluGYKgb4/qPrcunSDo+jcgPN4DOZB3ue8z3aJe7zQUDVO6NZBYn3QXb8zXVNW", - "es+EhzYRL4mLG1c6PMoVwgFu7b8OwhCyO2U3g9MdPx0tdR3gSTjXj1gtLa5xcFdLDVmR80fTO5eevhGy", - "w/xdskzUn/3HiVVGyLZ4TIQP+gY9fWHqhFjB69fFr+Y0PnwYHrWHD6fk19I9CADE32fud9QvHj6Muhqi", - "lgTDJNBQwOkKHjSBv8mN+LRmJw6bcRf02XrVSJYiTYYNhVrHtEf3xmFvI5nDZ+F+KaAE89Ph3Lreplt0", - "h8CMOUEXqeSYJu5pZXsCKSJ4P8wP87IMaSGzX1Gsem49N8MjxOsVejsyVbI87gfmM2XYK7fxPeZlgi8n", - "DGZmxJolwsV4zYKxzGtjyvj1gAzmiCJTRSsJtribCXe8a85+q4Gwwmg1cwYS77XeVeeVAxx1IJAa1XM4", - "lxvYRhG0w9/GDhJW/O/LjAjEfiNIGE00APdVY9b3C228Zq3OdGxQYjjjgHHvCSh09OGo2SZYLLtRQeP0", - "mDG9IT2jc60HEnNEez0ylc2l+B3itmg04Udys32PA4aRuL9DqJ6FHc46LKXxQLUtK9vZD233eN04tfG3", - "1oX9opu2Cje5TOOn+riNvInSq+IVRB2SU0pY6I7sRqsmWAseryA+Cyva+1AFyu15sonJnaSH+KkM04tO", - "7fjtqXQwD1KySrqZ0Vi5f6MLGZiC7e0EVWhB/Md+A1STdmtnJ0FQYfMus8WNKpBtbYphocQb6jV22tEa", - "TavAIEWFqsvUBoKVSkSGqfmGctsm0Xxn+ZX7WoH1gpqvNkJiaTIVj/8oIGerqDn26updkQ99/QVbMNsB", - "sFYQtJhzA9nuqpaKXJu+JpncoeZ8Th5Ngz6XbjcKtmaKzUrANx7bN2ZU4XXZeCSbT8zygOulwtefjHh9", - "WfNCQqGXyiJWCdLonijkNVFMM9AbAE4e4XuPvyT3MX5LsTU8MFh0QtDkxeMv0ftu/3gUu2VdB8d9LLtA", - "nv13x7PjdIwBbHYMwyTdqCfRKk62hXP6dthzmuynY84SvukulMNnaUU5XUA8ZHh1ACb7Le4melR7eOHW", - "GwBKS7EjTMfnB00Nf0qkIRr2Z8EguVitmF65KB8lVoae2v5xdlI/nG1m6lp/eLj8QwyWq3ysUM/W9YnV", - "GLpKpBFgSOMPdAVdtE4JtfXoStaGsfqGROTcl7vEXihNCxSLGzOXWTrKkhjVOieVZFyj/aPW8+wvRi2W", - "NDfs7yQFbjb74lmkp0i37D4/DvBPjncJCuQ6jnqZIHsvs7hvyX0ueLYyHKV40Kb9BqcyGdUXj99KBZHt", - "H3qs5GtGyZLkVnfIjQac+laEx/cMeEtSbNZzFD0evbJPTpm1jJMHrc0O/fT2tZMyVkLGali3x91JHBK0", - "ZLDGJI74Jpkxb7kXshy1C7eB/vOGoHiRMxDL/FmOKgKBR3Nf/qaR4n/+vi3Gi45VmxzTswEKGbF2Orvd", - "Jw74Os7q1vff2pgdfJbA3Gi02U7vA6wkQnVtLG7zzSdO542ae+2edwyOj38l0ujgKMc/fIhAP3w4dWLw", - "r0+6jy17f/gwXhMzanIzv7ZYuI1GjN/G9vArETGA+QZUTUCRS9mNGCBTl5R5YJjgzA01Jd1mP59eirib", - "ZJB4wF/8FFxdvcMnHg/4Rx8Rn5lZ4ga2Ic3pw95tdhYlmaJ5HoQaU/KV2I4lnN4d5InnnwBFCZSMNM/h", - "SgbN3KLu+oPxIgGNmlFnUAqjZIZ9KkJ7/r8Ons3ip3uwXbOy+LktN9S7SCTl+TIaqDkzH/7SNl1vlmhZ", - "ZbT0/ZJyDmV0OKvb/uJ14IiW/g8xdp4V4yPf7TcTtMvtLa4FvAumB8pPaNDLdGkmCLHareTSZAqXC1EQ", - "nKets94yx2FXzqBV2G81KB07GvjAZiuhs8swX9upigAv0Pp1Qr7FmgoGlk4RXbQ6+fKE3VJddVUKWkyx", - "bOLl12eviZ3VfmNbB9tOWQs0unRXEbWSjy9d1nQBjufkjx9nf5KwWbXSWdPYKlb1yLzRtt5ivdAJNMeE", - "2Dkhr6wlTHk7i52EYPFNuYIi6KNldTGkCfMfrWm+RBNT5yJLk/z4Fm+eKlsDfNAvuumrgOfOwO26vNkm", - "b1Mi9BLkhinALExYQ7fQUlN1zJk4feGl7vJkzbmllJMjZIqmi8KxaPfAWYHE+4ajkPUQf6SBwXZIPLbj", - "3QV+FS3z3G+f13Pe+rI9TR/g752NOKdccJZjkeWYQIRFYcZ5m0bUo467idTEndDI4Yo27WvyvxwWk238", - "PCN0iBt6boOnZlMtddg/NWxdM5cFaOU4GxRT33vS+TUYV+D6ZBgiCvmkkJHYlGg8e+MHP5KMsN5DwlD1", - "jXn2gzNjYiL0NeNosHBoc2K29TyUiqGDkROmyUKAcuvpFr1S78w3J1j/qYDt+5PXYsHyC7bAMWw0lFm2", - "Df0bDnXmAwFd4J1596V511XlbX7uRPXYSc+qyk2a7kwab8e85UkEx8JPfDxAgNxm/HC0PeS2N4IX71ND", - "aLDG4COo8B4eEEbTpbPXEtuoCJai8A1ic5OipfkYj4DxmnHvCYtfEHn0SsCNwfOa+E7lkmorAo7iaZdA", - "y0QcO+b6WVfqbYfq1yQ2KME1+jnS29g2GE0wjuaFVnCjfEf8oTDUHQgTL2nZRMBG2oWiVOWEqAJzRHoN", - "RGOMwzBu36K4ewEc6Eo+bT/HOt/H3kSp6kezuliAzmhRxNqWfIVPCT71uT6whbxu2ltUFcmx2Ge3+umQ", - "2txEueCqXu2Zy79wy+mCjrwRagi7AvsdxuoKsx3+e0y/+Cb29ej8Nh/oWhxX8neYrxeTeg1NZ4otsvGY", - "wDvl9uhop74Zobff3ymll2LRBeRzGEkTXC7coxh/+9pcHGFJwEGYsb1amop9GNIr8LkvctHUmupyJbzK", - "Bh1M0Hnd9Gnfb4ZId1yf4uWXyCkNTd72frVm4FRmaZ5MhKbalWTRlOxlQckyFzbks2dEH3qCUmGeNsrz", - "7ozPbq17EZp2wXzXcbjYUJ+WWSQdLTfzhbQbfKwz5Lt1KtnYVwDH5/2OzNfg6rRVEtZM1D6IxoeyepXQ", - "/trpb9yke0fXHw0Q/9zG56Sp/NJ1xrPLdDr5dz9bZxoBruXun8BwPtj0Qa/nobRrzVPtK6RpqjSqyVLn", - "VhxTHT9WiN3Jhp1u0wd6ZQ/I6tUYcWDY+3o6OS+OujBjxfwndpTYsYt3sk7XOm7rG+MRq4RibW+zWIvr", - "kTHjl9ilOqjVPBzLxxKuIdfY0K6NkZIAx1RuNpN52/2fNY/T6nQTWu9KHe+rbzzsYnfgjh+UIAnK6NgO", - "YCfjq/meNZGwNpFnQxXWvpdo4+6mvo5OwJvPIddsfaDky9+XwINyIlNvl0FY5kEFGNako2DF0OOtji1A", - "+yqy7IUnqNx/a3BS6cjXsLunSIcaoi3JmlysmxSLRAwgd8gMiQgVizSzhmQX/MNUQxmIBR/ZaT+Htux2", - "sptxUMDohnN5kjQXR1vUaM+U8Xaqo+Yynx5V6gszK1JVYYbdGNP6xytsfqlcnBNtik2GWjo5H5bk37hi", - "lVigp/Gd+LKVoPxvvhqXnaVk1xD2W0ZP1YbKwr8RNb14q0625z4alHLxnQT7QM+bmVkbhz/0VUeKPGNK", - "S14KI0Zkqbygbuh7Ezd2T9kAv7YOC8I1B+n60qP8WwoFmRY+bn8fHPtQYaMYb4QElWysYIFLljt929Zz", - "xQYzFMubUhe8GC6QSFhRA50Mqq6m59yH7Jf2uc+l9g1GDlqYGno93OnOZ2AwNUBiSPVz4m7LwznaNzE2", - "Mc5BZt7z1C/BykF2vSGVFEWd2ws6PBiNQW50CZQ9rCRqp8mHq+zpCEGu8zXsTq0S5FsE+h0MgbaSkwU9", - "KN3X2+Q7Nb+pGNyLOwHvc1quppNKiDJLODvOh3Vj+xR/zfJrKIi5KXykcqL7K7mPNvbGm71Z7nyd1KoC", - "DsWDE0LOuM0N8Y7tbuOi3uT8nt43/xZnLWpbytkZ1U6ueDzIHossy1tyMz/Mfh6mwLC6W05lBzlQlXSb", - "qFkr6SbSC/lkrFY+dDX3+9O2RGWhiMkkF9Zj9RIPesxwhJnsQckFdGRS4jxdRJUiFpJ5k2x7M1QcU+Fk", - "CJAGPibpu4HCDR5FQLTjauQU2gpmrnaZmBMJrRP5pkXchs1hYxp9f+Zmli6/mwsJnTav5mshCy/yMNX2", - "Y6ZyxrSkcneTUmuD5rQD60kSywfDsZpIrHYhbTTWEIdlKTYZMqusqW0eU23Ne6p7Gft2Lu135lTPIIjr", - "osoJajuypAXJhZSQh1/E0/YsVCshISsFhnnFPNBzbeTuFebqcFKKBRFVLgqwPQLiFJSaq+acotgEQVRN", - "FAWWdjDp034T0PHIKe+qM7ItzmMXnVlfZiLwFJQrxuMwZF8ewrunq/BR1fnP52gRYhjr0s29ttJn2FsZ", - "jmytzMrSGwxS3ZXJT6rGcCRMvDFTPCMrobTT7OxIqhmqDfG6nwuupSjLrhHIisQLZ9n+nm7P8ly/FuJ6", - "RvPrB6hHcqGblRZTn5baD8ZrZ5K9ikwj20BfLiN2XpzFn7qjez07znF0i9YAzPeHOdZhG/dZrJV1d139", - "3uw8UTtTixXL4zT8rxXdloxJi7GEaKkn2yXJJufja8iow8uhCWZAljREM3BDsLH9cjzNOXWReZj/osTb", - "H5fMwV0SiYtpyCed1JLlSdmqBwBCajNGdS1ta6VQ8mm4iljYDHN0SfcBHcnFMfLndrCZEe4cKA23AmoQ", - "bdgAeN8q+1NbkstGLs7E1j9/0NbsuhHwH/dTeawdfeQUN6TluuX7+h4JjhCvDLw3/ggbh/sb9HAUUtMG", - "b+SNGgCQjkvqwDAqOulYMOaUlVBkVCcud7QJTQPN1mW09JubMuU4eU7thb0EYsauJbh6E1ak7jVDr6gh", - "JdG8PrTc8gK2oLAYhO3oTJX1M3h/B5S2rVRP+RZVVsIaOuFarghGjaIdW4P/VjUfkwKgQu9f3yYVi0MK", - "7/KeocKtPQsiWcZgN2q5sIi1O0UOmCWiRpQtz+wxUWOPkoFozYqadvCnjhU5umY3c5QjqBrI5JnX28ZO", - "85Md4a0f4Mx/HxNlPCbej+NDR7OgOOr2MaCDcYm1Sp16Hg9LDCu8NA4NnK1oHJ+WxFu+oSq64WkD4JDk", - "W/Vm5D4xwQPEfr2FHKWabtzd7XFCcDCietWbkiK4bHb45obkz0LDe0k4OV5M1VCADHavpcbThRPY8QVs", - "Z8mN2GukZmwh5fi/439T7MBvBzJ6te1oFWpwr8B77LCgdOOscAItay40H184dfUE+0o5CyKrV3RHhMR/", - "jL72W01LNt/hCbXg+8+IWlJDQs5FaH3XLl7RTLxfMJl6wLxdQPip7LrZ2DGD4XZmlABocwU64xRWBrqG", - "cBvQLW85T64Ny1H1bMWUwsuut51DLLjF+5oQK1qEOjJWpuu2EvW1Ss3X/3+btRVO5QtKVSXNff8yIIqu", - "egZx26PQE5dewmp/Wt9QPfYk0PQ9bIlW+nTe4gbGvSMjN2Kx8ql+Dx2wB/3gBq0ubrWMYxoUt5nRexIi", - "Ry3lrndhbHzIAGh0MvuqXgfAt9UYfQWwT4H/aNHI1DLGgP/PgvdEG70QXtsx7xNguZPyH4HV2lVnYptJ", - "mKtDoRDWsGoUYdkWC/DGScZzCVTZ2JDzH53K1tZEZNyokDZ6sfG+NaMUMGe8ZZaMV7WOaABYGpHvAoSF", - "5mlEa8LZk5ISjBi2puWPa5CSFamNM6fDtvEKa9J7k7z7NqL8N3fqcACmWu0HMwmhzVQLXjMXuO16YwML", - "laa8oLIIX2ec5CDNvU82dKdu7vsw0MrayBcHvB80kGa6+e2BHwRJ2wJS7pz78paeiQZAeocuihGuBYxg", - "jbgVrFFEi4QnYQhDvKwC3WalWGB+WYIAXfFJ9P1YZUVwNNhaeei4eRT7HfZPg3W33cHXAmcdM8X+c/Yj", - "og4Vnp8403tPmrWm9RP+bESmPQie/vmiDQu3mzOk/1iO5iUmMXTyNPtN5/1e2/AQOx8kPBldC25iF9FB", - "7hJ8Q3Pt+H5GXR98LBPU6rAZ6rZqT+A3qDbImeYucGdo9BkoxRYpU5dHe6RNyFqS/T2QAM92qnVnqztt", - "E0xhxjmmCdT+zNmsElWWj4kGtKX5C2fQdpB2YUzQR2CuTqy7CZxQTbOKTmGTTteKY/tgJbtmHPLLVPk+", - "JTtl0Ehw0K6xXMyRl+ERtmYczPFojBfTfvZR12DTMAlCiYS8lmjQ3NDd4b5CiZKwF387e/74yS9Pnn9B", - "zAukYAtQbVnhXl+eNmKM8b6d5dPGiA2Wp+Ob4PPSLeK8p8yn2zSb4s6a5baqrRk46Ep0jCU0cgFEjmOk", - "H8yN9grHaYO+/7m2K7bIO9+xGAr++D2ToizjZd0b0S1i6o/tVmDsNxJ/BVIxpQ0j7PrqmG5jZdUSzXFY", - "3HNt64wInrvq6w0VMJ0IxoktJBVqifwMs36df4PAtiodr7I+iX3rcnqRtYhhcAbGb8yAVKJyojSbkxhE", - "mFsig5xLZ2jE8M4gerJhtjaOMkaILiY5Tnpn3GmeYk72c/tut0Yd5/RmEyPihT+UNyDNlCU9ndF+E07S", - "mtL/afhHJEX/zrhGs9w/gldE9YObNT4eBdowXTtCHghAIg+zk0EX9kVvK41Ka5VH+713dfbFj+9bF+jB", - "hAGExH9wALwwsbJ9r4lxd+B85pKd3zdICZbyPkUJneUfytX0rLe5SIItckYKrUFZtiSGYmGQiKteNvmt", - "Ca1kkAaLTdCNZlqWkfRZazfBMxUSjlEJ5JqWn55rYHf8M8QHFG/TSTNhDmWIZItKdbMKbq/pqLmDfMm7", - "m5q/wZTdv4PZo+g954Zy7uLBbYZWL2xJvfC3gs0CJhsc04YDPf6CzFw1/UpCzlTfDb3xwkmTMgiSzV3o", - "JWz1gRzFQ+v8WehbkPHcx4yQHwJ3kkCzXQthe0Q/M1NJnNwolceob0AWEfzFeFTYffPAdXHLyus3KwgS", - "lPY6siDIsK/o2OXZohfm0qkVDNc5+rbu4DZyUbdrG1vNZnQB96urd3o2pghNvNi6+Ryr4NxJ1fWjaq7/", - "AfVvLI7cGG7eGMX8nKqIaqt+Jorv9vajZuXBAJFOKeWP08kCOCimsFjwL645xKe9Sz0ENid/eFQtrLcp", - "JGIRE1lrZ/JgqqBI8oj6yO6zSDVkzHfLa8n0DhuDegMa+yVaqefbpuqDqxrS+K7c3afFNTTNmdsaEbXy", - "t+u3gpZ4H1mXGje3kChPyNdbuqpKZw4mf703+w94+pdnxaOnj/9j9pdHzx/l8Oz5l48e0S+f0cdfPn0M", - "T/7y/NkjeDz/4svZk+LJsyezZ0+effH8y/zps8ezZ198+R/3DB8yIFtAfe3uF5P/nZ2VC5GdvTnPLg2w", - "LU5oxb4DszeoK88FNq4zSM3xJMKKsnLywv/0v/wJO8nFqh3e/zpxDVgmS60r9eL0dLPZnISfnC4wKTzT", - "os6Xp34ebCfWkVfenDfR5DbuBXe0tR7jpjpSOMNnb7++uCRnb85PWoKZvJg8Onl08tj1ruW0YpMXk6f4", - "E56eJe77qSO2yYsPH6eT0yXQEmuomD9WoCXL/SMJtNi5/6sNXSxAnmDCgP1p/eTUixWnH1xy/Md9z07D", - "kIrTD50aAsWBLzEc4PSD72C5/+1O90IXiRV8MBKKfa+dzrBrxdhXQQUvp5eCyoY6/YDicvL3U2fziD9E", - "tcWeh1NfaCP+ZgdLH/TWwHrgiy0rgpXkVOfLujr9gP9B6g2AtkUYT/WWn6Ln9PRDZ63u8WCt3d/bz8M3", - "1itRgAdOzOe2s+e+x6cf7L/BRLCtQDIjFmLhE/erLVB1ig2edsOfdzyP/jhcR6c4jzl3US/0W1sRnpKS", - "KR9O0K3po8Lmz+cF8mfdLxRkXvKhhHjInzx65Dmb0xsCqjx1h3jStoIfV3agX55oeOMNWdu+lX2cTp4d", - "Cehe21CnqGMEmK9oQXwOKs79+NPNfc5tWKPh9fZOQgiefToIOttHvoMd+UFo8g0qTx+nk+efcifOuRHl", - "aEnwzaDB5vCI/MSvudhw/6YRZurVisrd6OOj6UKh31OyNXWiZPMaX0zeYw0Gm5fcPWpnRTEgeivUgdJf", - "CbwdUxhbqUXl3CYt0lqZlnGzhKFSPEDVpe0z26v0ZevReOc5FwVMQmlTyxo+3pIn9AIuqNTnERsPGisx", - "0nnuW+IGoEbLVvXd0XbkoT5yiITbrs1tgPCfPOVPntLwlOePnn666S9ArlkO5BJWlZBUsnJHfuJN5PmN", - "edxZUURr/XWP/kEeN51ss1wUsACeOQaWzUSx853pOxNcg1VfB4LMqVf3OhJ/gnt6RTImrbTxkJMX72J+", - "StdGtapnJcuJNXWhrmcUmUAVa4qvdZnfNNjWAfuJFPglBSvrJhFYb4RLtBteKOR+mB6vfrMd1vEgMr0j", - "G8YLscH20gjubzUgn3fw+mkmEQCDoLthL4vWgm8AHICVmg9N/2Ows2fy1/Rmc5f02Knf3/LKOniZNsWN", - "/uvixx+CdBybQmw99JgMYkkXI3elwIjUDcUQLamhOCEvreml3BEu0Mhfq067nZM/76E/ef/tef+3TbVL", - "22hHYweNIUsK7oKTUQJvlLd/6PzpTBMTGx8Zq1FpfieULLBJ2vCCmu3I+auB9mo/618JX+3w1d6tEOH3", - "fRCPYvwJ9rJPpDELWQjdRInaRf0pZP4pZN5KcR19eMborlHLkm1dSAf62NR3IYz106Z6CMoY+9NnPb53", - "svFD21bMlmXr4UJBggc2PbuP5j9ZxJ8s4nYs4luIHEY8tY5pRIjuOFvXWIaBVTiKTsyTlzr863VJZZAR", - "d8iEfYYjxlXBP4RrfGqDXRRX1l6HgbzMRrBFNvBubXh/srw/Wd6/Dss7O8xouoLJra1e17Bb0aqxdall", - "rQuxCTzcCIuNPh36+Kzi3//7dEOZzuZCuu4KdK5BDj/WQMtT10q192vbvWzwBFuyBT+GdYyiv57SrtOy", - "6xs3rDf14cBxHnvqHMeJl3wSsX/cBtGEQSnI9ptwlHfvDctWINf+RmhjLF6cnmJViaVQ+nTycfqhF38R", - "PnzfkMeH5h5xZPIR6UJItmCclpmLbWj7QU+enDyafPx/AQAA//+o0sPPZwkBAA==", + "S/wJFNxgoWa8W5F+bHmM58A1W0MGJVuwWayo49+H/jAPq6FKV8fKRSE3AyrC5sSo8jN7sTr1XlK+AHM9", + "mytVKFraGn3RoA2jArmvIyWc/MXUTNBodi46LUQ1ugTs8xVgDTixMaAZpUK48mU2Sz5gsbWiC0iI76ED", + "a2QuesfphYMcupSj17CY92/bwWUYBdm+nJk1R8kYzBNDx6hp9WIS/UzWR+rcJliV1CFsVqIM1wRvWo5I", + "ZceRaMsspkCLny6QvJWGPBhdjIQUuaTKUyQWoPOMZpSA8gdWfdhX6+c8CKcLqsw1lXz8hdBnIgPV11X8", + "8WV+fG2fUO8dUafHqB8YwR/bDsFROiughIVduH3ZE0pbgaLdIAPHj/N5yTiQLBaZF9hogzvQzQFGeH9I", + "iHUPkNEjxMg4ABt9/zgw+UGEZ5MvjgGSuwoa1I+NXDL4G+K5bTZW3chjojL3C0u43HLPAagL52wu115Q", + "MQ5DGJ8Sw+bWtDRszqmj7SCDkjMoU/cKzLjokwcpWXuPd8beeketyd6TN1lNKNB5oOPS5h6IZ2Kb2eTW", + "qDg+284MvUfD9zHVNnYwbXGfe4rMxBYjmvBqseHiB2BJw+HBCMwPW6aQXvG7lKhhgdk37X5RL0aFCknG", + "2RobcknJOmOmTohXKXK5H9TruREAPUtMW/zaaeYHNeiueDK8zNtbbdrWofOZUbHjnzpC0V1K4G9oImoq", + "7LzpSyxRI0o3MKdbXCiQb2NEb9jE0IM09FMpKAE1lqwjRGXXMbeuUbwAb5wL/1lgWcESRpTvHgTRXhIW", + "TGloLfw+iONz2E4pVk4UYp5ena7k3KzvrRDNNWV9nPhhZ5mffAUYLj1nUukM3SPRJZiXvlGo8X9jXo3L", + "St14MltnmBVx3oDTXsMuK1hZx+nVzfvdKzPtDw1LVPUM+S3jNppmhnWxo1Gme6a2gch7F/zaLvg1vbP1", + "jjsN5lUzsTTk0p3jX+Rc9DjvPnYQIcAYcQx3LYnSPQwyyA4ecsdAbgoCEE72mYYHh6nwYx8MKfI5yqk7", + "yo4UXUtgzdi7CoY+LCOWGB096JfRX1HiDNCqYsW2Z6i1oyY1ZnqUNcYX4+thAXfXDXYAA92gwWgMdqeQ", + "oQtNdAapUxSQT40IZ2MVXSAeSNRybMJqUUu0+HUiAYdVMxvBbuTav/v5QgtJF+CstpkF6VZD4HKOQUNQ", + "k1IRzaz7tWDzOYTWSnUTS1sHuIFNqhhBuhEii5s0a8b1F89iZHSAeloYD6MsTjERWkj5sC6HVmEvVgV6", + "Z9NWJdiaG5h2o+mt38Eu+9loKKSiTKo2nM2Zabv874hdX6++gx2OfDBKzAB2YFdQTX0LSIMxs2DzyGZ1", + "NCpQWGAVK1J0tvCInTqL79IdbY0riZsm/jZmvFMytruU2xyM1qloYBmzGxdxX545PdBFfJ+UD20CSxjj", + "QnIMRK5wKqZ8A6HhVdTkbh+i3UugpSdeXM7k43RyO89Z7DZzIx7A9ZvmAo3iGSOzrCel4wg/EuW0qqRY", + "0zJz/sXU5S/F2l3++Lp3R35iYTJO2Zdfn71+48D/OJ3kJVCZNcpYclX4XvUvsypbRHf/VYISi7eKWGU9", + "2Pym8mfok9wswXV6CPT9QUnq1t8cHEXno5zHA0QP8j7nGrdL3OMih6rxkLcOEusg7zrF6Zqy0nsmPLSJ", + "YE5c3Li65lGuEA5wa+d6ECOR3Sm7GZzu+OloqesAT8K5fsRSbnGNg7tCb8iKnLOc3rn09I2QHebvMnmi", + "zvY/TqwyQrbFYyK20XcP6gtTJ8QKXr8ufjWn8eHD8Kg9fDglv5buQQAg/j5zv6N+8fBh1NUQtSQYJoGG", + "Ak5X8KCJSk5uxKc1O3HYjLugz9arRrIUaTJsKNR6zT26Nw57G8kcPgv3SwElmJ8OJ/71Nt2iOwRmzAm6", + "SGXuNEFZK9uwSBHB+zGImDRmSAuZ/YpiSXbruRkeIV6v0NuRqZLlcT8wnynDXrkNPjIvE3w5YTAzI9Ys", + "EcvGaxaMZV4bU2OwB2QwRxSZKlrmsMXdTLjjXXP2Ww2EFUarmTOQeK/1rjqvHOCoA4HUqJ7DudzANoqg", + "Hf42dpCwHUFfZkQg9htBwlCnAbivGrO+X2jjNWt1pmMjJsMZB4x7T7Sjow9HzTb7Y9kNWRqnx4xpXOkZ", + "neuLkJgj2oiSqWwuxe8Qt0WjCT+SOO4bMDAME/4deCzSpc9SGg9U20+znf3Qdo/XjVMbf2td2C+66flw", + "k8s0fqqP28ibKL0qXt7UITmlhIXuyG4obYK14PEKgsew3L4PVaDcniebNd3JyIifyjD36dSO355KB/Mg", + "X6ykmxmN9SIwupCBKdjeTlCFFsR/7DdANTnBdnYSRDw27zJbeakC2RbOGFZxvKFeY6cdrdG0CgxSVKi6", + "TG0gWKlEZJiabyi3PRzNd5Zfua8VWC+o+WojJNZNU/H4jwJytoqaY6+u3hX50NdfsAWz7QlrBUH/OzeQ", + "bf1qqcj1EGwy3R1qzufk0TRowul2o2BrptisBHzjsX1jRhVel41HsvnELA+4Xip8/cmI15c1LyQUeqks", + "YpUgje6JQl4TxTQDvQHg5BG+9/hLch/jtxRbwwODRScETV48/hK97/aPR7Fb1rWX3MeyC+TZPrgxTscY", + "wGbHMEzSjRqPVrT9pdO3w57TZD8dc5bwTXehHD5LK8rpAuLxzKsDMNlvcTfRo9rDC7feAFBaih1hOj4/", + "aGr4UyJH0rA/CwbJxWrF9MpF+SixMvTUNrezk/rhbKdV15fEw+UfYrBc5WOFerauT6zG0FUixwFDGn+g", + "K+iidUqoLZZXsjaM1XdLIue+Fic2amn6s1jcmLnM0lGWxKjWOakk4xrtH7WeZ38xarGkuWF/Jylws9kX", + "zyINT7o9AfhxgH9yvEtQINdx1MsE2XuZxX1L7nPBs5XhKMWDNic5OJXJqL54/FYqiGz/0GMlXzNKliS3", + "ukNuNODUtyI8vmfAW5Jis56j6PHolX1yyqxlnDxobXbop7evnZSxEjJWYLs97k7ikKAlgzVmmMQ3yYx5", + "y72Q5ahduA30nzcExYucgVjmz3JUEQg8mvuSS40U//P3baVgdKzazJ2eDVDIiLXT2e0+ccDXcVa3vv/W", + "xuzgswTmRqPNtqEfYCURqmtjcZtvPnGucdTca/e8Y3B8/CuRRgdHOf7hQwT64cOpE4N/fdJ9bNn7w4fx", + "gp1Rk5v5tcXCbTRi/Da2h1+JiAHMd8dqAopcPnHEAJm6pMwDwwRnbqgp6XYi+vRSxN0kg8QD/uKn4Orq", + "HT7xeMA/+oj4zMwSN7ANaU4f9m4ntijJFM3zINSYkq/Edizh9O4gTzz/BChKoGSkeQ5XMug0F3XXH4wX", + "CWjUjDqDUhglM2yiEdrz/3XwbBY/3YPtmpXFz20tpN5FIinPl9FAzZn58Je2I3yzRMsqo3X5l5RzKKPD", + "Wd32F68DR7T0f4ix86wYH/luv9OhXW5vcS3gXTA9UH5Cg16mSzNBiNVumZkmjblciILgPG0R+JY5DluG", + "Bn3MfqtB6djRwAc2WwmdXYb52jZaBHiB1q8T8i0WfDCwdCr8otXJ107s1hGrq1LQYoo1HS+/PntN7Kz2", + "G9vX2LbxWqDRpbuKqJV8fF21pkVxvGDA+HH2ZzCbVSudNV23YiWZzBttXzDWC51Ac0yInRPyylrClLez", + "2EkIVgaVKyiCJl9WF0OaMP/RmuZLNDF1LrI0yY/vP+epsjXAB82sm6YPeO4M3K4Fne1ANyVCL0FumALM", + "woQ1dKtANSXRnInTV4XqLk/WnFtKOTlCpmhaPByLdg+cFUi8bzgKWQ/xRxoYbPvGY9vxXeBX0RrU/d5+", + "PeetrynUNCn+3tmIc8oFZzlWgI4JRFixZpy3aUSx7LibSE3cCY0crmhHwSb/y2Ex2WPQM0KHuKHnNnhq", + "NtVSh/1Tw9Z1mlmAVo6zQTH1jTGdX4NxBa6JhyGikE8KGYlNicazN37wI8kIi1EkDFXfmGc/ODMmJkJf", + "M44GC4c2J2Zbz0OpGDoYOWGaLAQot55uRS71znxzgsWpCti+P3ktFiy/YAscw0ZDmWXb0L/hUGc+ENAF", + "3pl3X5p3Xcng5udOVI+d9Kyq3KTptqnxXtFbnkRwLPzExwMEyG3GD0fbQ257I3jxPjWEBmsMPoIK7+EB", + "YTQtRHv9uo2KYCkK3yA2NylaN5DxCBivGfeesPgFkUevBNwYPK+J71QuqbYi4Ciedgm0TMSxY66fdaXe", + "dqh+wWSDElyjnyO9jW330wTjaF5oBTfKd8QfCkPdgTDxkpZNBGyklylKVU6IKjBHpNfdNMY4DOP2/ZO7", + "F8CBlunT9nMsQn7sTZQqzTSriwXojBZFrKfKV/iU4FOf6wNbyOum90ZVkRwrkXZLsw6pzU2UC67q1Z65", + "/Au3nC5oFxyhhrBlsd9hrK4w2+G/xzSzb2Jfj85v84GuxXH1iIf5ejGp19B0ptgiG48JvFNuj4526psR", + "evv9nVJ6KRZdQD6HkTTB5cI9ivG3r83FEdYrHIQZ26ulKSeIIb0Cn/siF00hrC5Xwqts0F4FnddNE/n9", + "Zoh0O/gpXn6JnNLQ5G3vV2sGTmWW5slEaKpdSRZNyV4WlCxzYUM+e0b0oScoFeZpozzvzvjs1roXoWkX", + "zHcdh4sN9WmZRdLRcjNfSLvBxzpDvlunko19eXJ83m8XfQ2uiFwlYc1E7YNofCirVwntr53my026d3T9", + "0QDxz218TprKL13bPrtMp5N/97N1phHgWu7+CQzng00fNKIeSrvWPNW+QpqOT6M6QHVuxTGl+2NV4p1s", + "2GmFfaCR94CsXo0RB4aNuaeT8+KoCzPWaWBiR4kdu3ib7XQh5rb4Mh6xSijWNl6L9d8eGTN+iS20g0LS", + "w7F8LOEaco3d9toYKQlwTFlpM5m33f9ZkDmtTjeh9a4O877iy8MWewfu+EEJkqCMjm1PdjK+1PBZEwlr", + "E3k2VGFhfok27m7q6+gEvPkcciwGubfky9+XwINyIlNvl0FY5kEFGNako2A50+Otji1A+yqy7IUnaCtw", + "a3BS6cjXsLunSIcaov3SmlysmxSLRAwgd8h86cyUIdkF/zDVUAZiwUd22s+hrQmebLUcFDC64VyeJM3F", + "0RY12jNlvNfrqLnMp0eV+sLMilRVmGGryLT+8Qo7cyoX50SbYpOhlk7Oh/0CNq5YJRboaXwnvmwlKP+b", + "r8ZlZynZNYTNoNFTtaGy8G9ETS/eqpPtuY8GpVx8m8M+0PNmZtbG4Q991ZEK1JjSkpfCiBFZKi+oG/re", + "xI3dUzbAr63DgnDNQbqm+Sj/lkJBpoWP298Hxz5U2CjGGyFBJbs+WOCS5U7ftvVcsfsNxfKm1AUvhgsk", + "ElbUQCeDqqvpOfch+6V97nOpffeTgxamhl4Pt+HzGRhMDZAYUv2cuNvycI72TYxNjHOQmfc89UuwcpBd", + "b0glRVHn9oIOD0ZjkBtdAmUPK4naafLhKns6QpDrfA27U6sE+f6FfgdDoK3kZEEPSvf1NvlOzW8qBvfi", + "TsD7nJar6aQSoswSzo7zYd3YPsVfs/waCmJuCh+pnGhNS+6jjb3xZm+WO18ntaqAQ/HghJAzbnNDvGO7", + "21WpNzm/p/fNv8VZi9qWcnZGtZMrHg+yxyLL8pbczA+zn4cpMKzullPZQQ5UJd0matZKuok0aj4Zq5UP", + "Xc395rktUVkoYjLJhfVYvcSDHjMcYSZ7UHIBHZmUOE8XUaWIhWTeJNveDBXHVDgZAqSBj0n6bqBwg0cR", + "EG0HGzmFtoKZq10m5kRC60S+aRG3YefamEbfn7mZpcvv5kJCpwet+VrIwos8TLXNoqmcMS2p3N2k1Nqg", + "c+7AepLE8sFwrCYSq11IG401xGFZik2GzCprapvHVFvznupexr7XTPudOdUzCOK6qHKC2o4saUFyISXk", + "4RfxtD0L1UpIyEqBYV4xD/RcG7l7hbk6nJRiQUSViwJsj4A4BaXmqjmnKDZBEFUTRYGlHUz6tN8EdDxy", + "yrtq22yL89hFZ9aXmQg8BeWK8TgM2ZeH8O5peXxUdf7zOVqEGMa6dHOvrfQZNn6GI/s+s7L0BoNU62fy", + "k6oxHAkTb8wUz8hKKO00OzuSaoZqQ7zu54JrKcqyawSyIvHCWba/p9uzPNevhbie0fz6AeqRXOhmpcXU", + "p6X2g/HamWSvItPIHtWXy4idF2fxp+7oRtSOcxzdPzYA8/1hjnXYxn0W67PdXVe/cTxP1M7UYsXyOA3/", + "a0W3JWPSYiwhWurJtnCyyfn4GjLq8HJoghmQJQ3RDJxGe9CcEcfTnFMXmYf5L0q8/XHJHNwlkbiYhnzS", + "SS1ZnpStegAgpDZjVNfS9n0KJZ+Gq4iFzTBHl3Qf0JFcHCN/bgebGeHOgdJwK6AG0YYNgPetsj+1Jbls", + "5OJMbP3zB23NrhsB/3E/lcd65UdOcUNarpW/r++R4AjxysB744+wq7m/QQ9HITU9+kbeqAEA6bikDgyj", + "opOOBWNOWQlFRnXickeb0DTQbF1GS7/zKlOOk+fUXthLIGbsWoKrN2FF6l6n9ooaUhLN60PLLS9gCwqL", + "Qdh201RZP4P3d0Bp20r1lG9RZSWsoROu5Ypg1CjasTX4b1XzMSkAKvT+9W1SsTik8C7vGSrc2rMgkmUM", + "dqOWC4tYu1PkgFkiakTZ8sweEzX2KBmI1qyoaQd/6liRo2t2M0c5gqqBTJ55vW3sND/ZEd76Ac789zFR", + "xmPi/Tg+dDQLiqNuHwM6GJdYq9Sp5/GwxLDCS+PQwNmKxvFpSbzlG6qiG542AA5JvlVvRu4TEzxA7Ndb", + "yFGq6cbd3R4nBAcjqle9KSmCy2aHb25I/iw0vJeEk+PFVA0FyGD3Wmo8XTiBHV/AXpvciL1GasYWUo7/", + "O/43JbPaD2T0atvRKtTgXoH32GFB6cZZ4QRa1lxoPr5w6uoJ9pVyFkRWr+iOCIn/GH3tt5qWbL7DE2rB", + "958RtaSGhJyL0PquXbyimXi/YDL1gHm7gPBT2XWzsWMGw+3MKAHQ5gp0ximsDHQN4TagW95ynlwblqPq", + "2YophZddbzuHWHCL9zUhVrQIdWSsTNftc+prlZqv//82ayucyheUqkqa+/5lQBRd9QzitkehJy69hNX+", + "tL6heuxJoOl72BKt9Om8xQ2Me0dGbsRi5VP9HjpgD/rBDVpd3GoZx3RPbjOj9yREjlrKXe/C2PiQAdDo", + "ZPZVvQ6Ab6sx+gpgnwL/0aKRqWWMAf+fBe+JNnohvLZj3ifAciflPwKrtavOxDaTMFeHQiGsYdUowrIt", + "FuCNk4znEqiysSHnPzqVra2JyLhRIW30YuN9a0YpYM54yywZr2od0QCwNCLfBQgLzdOI1oSzJyUlGDFs", + "Tcsf1yAlK1IbZ06HbeMV1qT3Jnn3bUT5b+7U4QBMtdoPZhJCm6kWvGYucNv1xgYWKk15QWURvs44yUGa", + "e59s6E7d3PdhoJW1kS8OeD9oIM1089sDPwiStgWk3Dn35S09Ew2A9A5dFCNcCxjBGnErWKOIFglPwhCG", + "eFkFus1KscD8sgQBuuKT6PuxyorgaLC18tBx8yj2O+yfButuu4OvBc46Zor95+xHRB0qPD9xpveeNGtN", + "6yf82YhMexA8/fNFGxZuN2dI/7EczUtMYujkafY74vu9tuEhdj5IeDK6FtzELqKD3CX4huba8f2Muj74", + "WCao1WEz1G3VnsBvUG2QM81d4M7Q6DNQii1Spi6P9kibkLUk+3sgAZ7tVOvOVnfaJpjCjHNME6j9mbNZ", + "JaosHxMNaEvzF86g7SDtwpigj8BcnVh3EzihmmYVncImna4Vx/bBSnbNOOSXqfJ9SnbKoJHgoF1juZgj", + "L8MjbM04mOPRGC+m/eyjrsGmYRKEEgl5LdGguaG7w32FEiVhL/529vzxk1+ePP+CmBdIwRag2rLCvb48", + "bcQY4307y6eNERssT8c3weelW8R5T5lPt2k2xZ01y21VWzNw0JXoGEto5AKIHMdIP5gb7RWO0wZ9/3Nt", + "V2yRd75jMRT88XsmRVnGy7o3olvE1B/brcDYbyT+CqRiShtG2PXVMd3GyqolmuOwuOfa1hkRPHfV1xsq", + "YDoRjBNbSCrUEvkZZv06/waBbVU6XmV9EvvW5fQiaxHD4AyM35gBqUTlRGk2JzGIMLdEBjmXztCI4Z1B", + "9GTDbG0cZYwQXUxynPTOuNM8xZzs5/bdbo06zunNJkbEC38ob0CaKUt6OqP9JpykNaX/0/CPSIr+nXGN", + "Zrl/BK+I6gc3a3w8CrRhunaEPBCARB5mJ4Mu7IveVhqV1iqP9nvv6uyLH9+3LtCDCQMIif/gAHhhYmX7", + "XhPj7sD5zCU7v2+QEizlfYoSOss/lKvpWW9zkQRb5IwUWoOybEkMxcIgEVe9bPJbE1rJIA0Wm6AbzbQs", + "I+mz1m6CZyokHKMSyDUtPz3XwO74Z4gPKN6mk2bCHMoQyRaV6mYV3F7TUXMH+ZJ3NzV/gym7fwezR9F7", + "zg3l3MWD2wytXtiSeuFvBZsFTDY4pg0HevwFmblq+pWEnKm+G3rjhZMmZRAkm7vQS9jqAzmKh9b5s9C3", + "IOO5jxkhPwTuJIFmuxbC9oh+ZqaSOLlRKo9R34AsIviL8aiw++aB6+KWlddvVhAkKO11ZEGQYV/Rscuz", + "RS/MpVMrGK5z9G3dwW3kom7XNraazegC7ldX7/RsTBGaeLF18zlWwbmTqutH1Vz/A+rfWBy5Mdy8MYr5", + "OVUR1Vb9TBTf7e1HzcqDASKdUsofp5MFcFBMYbHgX1xziE97l3oIbE7+8KhaWG9TSMQiJrLWzuTBVEGR", + "5BH1kd1nkWrImO+W15LpHTYG9QY09ku0Us+3TdUHVzWk8V25u0+La2iaM7c1Imrlb9dvBS3xPrIuNW5u", + "IVGekK+3dFWVzhxM/npv9h/w9C/PikdPH//H7C+Pnj/K4dnzLx89ol8+o4+/fPoYnvzl+bNH8Hj+xZez", + "J8WTZ09mz548++L5l/nTZ49nz7748j/uGT5kQLaA+trdLyb/OzsrFyI7e3OeXRpgW5zQin0HZm9QV54L", + "bFxnkJrjSYQVZeXkhf/pf/kTdpKLVTu8/3XiGrBMllpX6sXp6WazOQk/OV1gUnimRZ0vT/082E6sI6+8", + "OW+iyW3cC+5oaz3GTXWkcIbP3n59cUnO3pyftAQzeTF5dPLo5LHrXctpxSYvJk/xJzw9S9z3U0dskxcf", + "Pk4np0ugJdZQMX+sQEuW+0cSaLFz/1cbuliAPMGEAfvT+smpFytOP7jk+I/7np2GIRWnHzo1BIoDX2I4", + "wOkH38Fy/9ud7oUuEiv4YCQU+147nWHXirGvggpeTi8FlQ11+gHF5eTvp87mEX+Iaos9D6e+0Eb8zQ6W", + "PuitgfXAF1tWBCvJqc6XdXX6Af+D1BsAbYswnuotP0XP6emHzlrd48Fau7+3n4dvrFeiAA+cmM9tZ899", + "j08/2H+DiWBbgWRGLMTCJ+5XW6DqFBs87YY/73ge/XG4jk5xHnPuol7ot7YiPCUlUz6coFvTR4XNn88L", + "5M+6XyjIvORDCfGQP3n0yHM2pzcEVHnqDvGkbQU/ruxAvzzR8MYbsrZ9K/s4nTw7EtC9tqFOUccIMF/R", + "gvgcVJz78aeb+5zbsEbD6+2dhBA8+3QQdLaPfAc78oPQ5BtUnj5OJ88/5U6ccyPK0ZLgm0GDzeER+Ylf", + "c7Hh/k0jzNSrFZW70cdH04VCv6dka+pEyeY1vpi8xxoMNi+5e9TOimJA9FaoA6W/Eng7pjC2UovKuU1a", + "pLUyLeNmCUOleICqS9tntlfpy9aj8c5zLgqYhNKmljV8vCVP6AVcUKnPIzYeNFZipPPct8QNQI2Wreq7", + "o+3IQ33kEAm3XZvbAOE/ecqfPKXhKc8fPf1001+AXLMcyCWsKiGpZOWO/MSbyPMb87izoojW+use/YM8", + "bjrZZrkoYAE8cwwsm4li5zvTdya4Bqu+DgSZU6/udST+BPf0imRMWmnjIScv3sX8lK6NalXPSpYTa+pC", + "Xc8oMoEq1hRf6zK/abCtA/YTKfBLClbWTSKw3giXaDe8UMj9MD1e/WY7rONBZHpHNowXYoPtpRHc32pA", + "Pu/g9dNMIgAGQXfDXhatBd8AOAArNR+a/sdgZ8/kr+nN5i7psVO/v+WVdfAybYob/dfFjz8E6Tg2hdh6", + "6DEZxJIuRu5KgRGpG4ohWlJDcUJeWtNLuSNcoJG/Vp12Oyd/3kN/8v7b8/5vm2qXttGOxg4aQ5YU3AUn", + "owTeKG//0PnTmSYmNj4yVqPS/E4oWWCTtOEFNduR81cD7dV+1r8Svtrhq71bIcLv+yAexfgT7GWfSGMW", + "shC6iRK1i/pTyPxTyLyV4jr68IzRXaOWJdu6kA70sanvQhjrp031EJQx9qfPenzvZOOHtq2YLcvWw4WC", + "BA9senYfzX+yiD9ZxO1YxLcQOYx4ah3TiBDdcbausQwDq3AUnZgnL3X41+uSyiAj7pAJ+wxHjKuCfwjX", + "+NQGuyiurL0OA3mZjWCLbODd2vD+ZHl/srx/HZZ3dpjRdAWTW1u9rmG3olVj61LLWhdiE3i4ERYbfTr0", + "8VnFv//36YYync2FdN0V6FyDHH6sgZanrpVq79e2e9ngCbZkC34M6xhFfz2lXadl1zduWG/qw4HjPPbU", + "OY4TL/kkYv+4DaIJg1KQ7TfhKO/eG5atQK79jdDGWLw4PcWqEkuh9Onk4/RDL/4ifPi+IY8PzT3iyOQj", + "0oWQbME4LTMX29D2g548OXk0+fj/AgAA//88dqWvBAoBAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/participating/public/routes.go b/daemon/algod/api/server/v2/generated/participating/public/routes.go index 0938833520..c4e4a8de10 100644 --- a/daemon/algod/api/server/v2/generated/participating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/public/routes.go @@ -255,146 +255,146 @@ var swaggerSpec = []string{ "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", - "/G1EEgEA", + "YKFmvFuRfmx5jKfANVtBAjlbsFmsqOPf+v4wD6uhSlfHykUh1wMqwubEqPIze7E69V5SvgBzPZsrVSia", + "2xp90aANowK5ryMlnPzFVE9Qa3YuOi1ENboE7PMCsAacuDagGaVCuPJlNks+YLGVogsYEN9DB9bIXPSW", + "0wsH2XUpR69hMe/etr3LMAqyfTkxa46SMZgnho5R0+rEJPqZrI/UuU2wKqlD2CxHGa4O3rQckcqWI9GW", + "WRwCLX66QPJGGvJgtDESUuSSKk+RWIDOM5pRAspvWPVhW62fsyCcLqgyV1fy8RdCl4n0VF9X8ceX+fG1", + "fUK9d0SdHqN+YAR/bDsER+ksgxwWduH2ZU8oTQWKZoMMHK/n85xxIEksMi+w0QZ3oJsDjPB+QIh1D5DR", + "I8TIOAAbff84MPlehGeTL/YBkrsKGtSPjVwy+BviuW02Vt3IY6I09wsbcLmlngNQF85ZX66doGIchjA+", + "JYbNrWhu2JxTR5tBeiVnUKbuFJhx0ScPhmTtLd4Ze+vttSZ7T95kNaFA54GOS5tbIJ6JdWKTW6Pi+Gw9", + "M/QeDd/HVNvYwbTFfe4pMhNrjGjCq8WGi++AZRgOD0ZgflgzhfSK3w2JGhaYbdNuF/ViVKiQZJytsSaX", + "IVlnzNQD4tUQudwP6vXcCICOJaYpfu00850adFs86V/mza02berQ+cyo2PEfOkLRXRrAX99EVFfYedOV", + "WKJGlHZgTru4UCDfxojesIm+B6nvp1KQA2osSUuISq5ibl2jeAHeOOf+s8CygiWMKN88CKK9JCyY0tBY", + "+H0Qx+ewnVKsnCjEfHh1upRzs763QtTXlPVx4oetZX7yFWC49JxJpRN0j0SXYF76RqHG/415NS4rtePJ", + "bJ1hlsV5A057BZskY3kVp1c373cvzLTf1yxRVTPkt4zbaJoZ1sWORplumdoGIm9d8Eu74Jf0ztY77jSY", + "V83E0pBLe44/yLnocN5t7CBCgDHi6O/aIEq3MMggO7jPHQO5KQhAONxmGu4dpsyPvTOkyOcoD91RdqTo", + "WgJrxtZVMPRhGbHE6OhBv4zuigbOAC1Llq07hlo76qDGTPeyxvhifB0s4O66wXZgoB00GI3BbhUydKGJ", + "ziB1hALykRHhbKyiC8QDiVqOTVjNKokWv1YkYL9qZi3YjVz7dz+eayHpApzVNrEg3WoIXM4+aAhqUiqi", + "mXW/Zmw+h9BaqW5iaWsB17NJZSNIN0JkcZNmxbj+4kmMjHZQTwPjbpTFKSZCC0M+rIu+VdiLVYHeWbdV", + "CbbmBqbdaHrrd7BJfjQaCikpk6oJZ3Nm2jb/22PXV8V3sMGRd0aJGcB27AqqqW8BaTBmFqwf2ayOWgUK", + "C6xiRYrWFu6xU6fxXbqjrXElcYeJv4kZb5WMbS/lNgejcSoaWMbsxnncl2dOD7QR3yXlXZvABoxxITkG", + "Ilc4FVO+gVD/Kqpzt3fR7gXQ3BMvLmfycTq5necsdpu5EXfg+k19gUbxjJFZ1pPScoTviXJallKsaJ44", + "/+LQ5S/Fyl3++Lp3R35iYTJO2Rdfn75848D/OJ2kOVCZ1MrY4KrwvfIPsypbRHf7VYISi7eKWGU92Py6", + "8mfok7xeguv0EOj7vZLUjb85OIrORzmPB4ju5H3ONW6XuMVFDmXtIW8cJNZB3naK0xVlufdMeGgHgjlx", + "cePqmke5QjjArZ3rQYxEcqfspne646ejoa4dPAnneo2l3OIaB3eF3pAVOWc5vXPp6RshW8zfZfJEne2/", + "nVhlhGyLx4HYRt89qCtMHRIreP28+NmcxoOD8KgdHEzJz7l7EACIv8/c76hfHBxEXQ1RS4JhEmgo4LSA", + "B3VU8uBGfFqzE4frcRf06aqoJUsxTIY1hVqvuUf3tcPetWQOn5n7JYMczE+7E/86m27RHQIz5gSdD2Xu", + "1EFZhW1YpIjg3RhETBozpIXMvqBYkt16bvpHiFcFejsSlbM07gfmM2XYK7fBR+Zlgi8PGMzMiBUbiGXj", + "FQvGMq+NqTHYATKYI4pMFS1z2OBuJtzxrjj7pQLCMqPVzBlIvNc6V51XDnDUnkBqVM/+XG5gG0XQDH8b", + "O0jYjqArMyIQ240gYahTD9wXtVnfL7T2mjU6074Rk+GMPca9JdrR0YejZpv9sWyHLI3TY8Y0rvSMzvVF", + "GJgj2oiSqWQuxa8Qt0WjCT+SOO4bMDAME/4VeCzSpctSag9U00+zmX3Xdo/XjYc2/ta6sF903fPhJpdp", + "/FTvt5E3UXpVvLypQ/KQEha6I9uhtAOsBY9XEDyG5fZ9qALl9jzZrOlWRkb8VIa5T0d2/OZUOph7+WI5", + "vZ7RWC8CowsZmILtbQVVaEH8x34DVJ0TbGcnQcRj/S6zlZdKkE3hjH4VxxvqNXba0RpNo8AgRYWqy9QG", + "guVKRIap+DXltoej+c7yK/e1AusFNV9dC4l101Q8/iODlBVRc+zl5bss7fv6M7Zgtj1hpSDof+cGsq1f", + "LRW5HoJ1prtDzdmcHE+DJpxuNzK2YorNcsA3Hto3ZlThdVl7JOtPzPKA66XC1x+NeH1Z8UxCppfKIlYJ", + "UuueKOTVUUwz0NcAnBzjew+fkfsYv6XYCh4YLDohaHLy8Bl63+0fx7Fb1rWX3MayM+TZPrgxTscYwGbH", + "MEzSjRqPVrT9pYdvhy2nyX465izhm+5C2X2WCsrpAuLxzMUOmOy3uJvoUe3ghVtvACgtxYYwHZ8fNDX8", + "aSBH0rA/CwZJRVEwXbgoHyUKQ09Nczs7qR/Odlp1fUk8XP4hBsuVPlaoY+v6xGoMLQZyHDCk8XtaQBut", + "U0JtsbycNWGsvlsSOfO1OLFRS92fxeLGzGWWjrIkRrXOSSkZ12j/qPQ8+YtRiyVNDfs7HAI3mX3xJNLw", + "pN0TgO8H+CfHuwQFchVHvRwgey+zuG/JfS54UhiOkj1ocpKDUzkY1ReP3xoKIts+9FjJ14ySDJJb1SI3", + "GnDqWxEe3zLgLUmxXs9e9Lj3yj45ZVYyTh60Mjv0w9uXTsoohIwV2G6Ou5M4JGjJYIUZJvFNMmPeci9k", + "PmoXbgP95w1B8SJnIJb5sxxVBAKP5rbkUiPF//iqqRSMjlWbudOxAQoZsXY6u90nDvjaz+rW9d/amB18", + "NoC50Wizbeh7WBkI1bWxuPU3nzjXOGrutXveMjg+/JlIo4OjHH9wgEAfHEydGPzzo/Zjy94PDuIFO6Mm", + "N/Nrg4XbaMT4bWwPvxIRA5jvjlUHFLl84ogBcuiSMg8ME5y5oaak3Yno00sRd5MMEg/4i5+Cy8t3+MTj", + "Af/oIuIzM0vcwCakefiwtzuxRUkmq58HocaUfCXWYwmncwd54vkdoGgAJSPNc7iSXqe5qLt+Z7xIQKNm", + "1BnkwiiZYRON0J7/x8GzWfx0C7Yrlmc/NrWQOheJpDxdRgM1Z+bDn5qO8PUSLauM1uVfUs4hjw5ndduf", + "vA4c0dL/KcbOUzA+8t1up0O73M7iGsDbYHqg/IQGvUznZoIQq+0yM3Uac74QGcF5miLwDXPstwwN+pj9", + "UoHSsaOBD2y2Ejq7DPO1bbQI8AytX4fkWyz4YGBpVfhFq5OvndiuI1aVuaDZFGs6Xnx9+pLYWe03tq+x", + "beO1QKNLexVRK/n4ump1i+J4wYDx42zPYDarVjqpu27FSjKZN5q+YKwTOoHmmBA7h+SFtYQpb2exkxCs", + "DCoLyIImX1YXQ5ow/9Gapks0MbUusmGSH99/zlNlY4APmlnXTR/w3Bm4XQs624FuSoRegrxmCjALE1bQ", + "rgJVl0RzJk5fFaq9PFlxbinlcA+Zom7xsC/aPXBWIPG+4ShkHcTvaWCw7Rv3bcd3jl9Fa1B3e/t1nLe+", + "plDdpPiVsxGnlAvOUqwAHROIsGLNOG/TiGLZcTeRmrgTGjlc0Y6Cdf6Xw+Jgj0HPCB3i+p7b4KnZVEsd", + "9k8Na9dpZgFaOc4G2dQ3xnR+DcYVuCYehohCPilkJDYlGs9e+8H3JCMsRjFgqPrGPPvemTExEfqKcTRY", + "OLQ5Mdt6HnLF0MHICdNkIUC59bQrcql35ptDLE6Vwfr94UuxYOk5W+AYNhrKLNuG/vWHOvWBgC7wzrz7", + "3LzrSgbXP7eieuykp2XpJh1umxrvFb3mgwiOhZ/4eIAAufX44WhbyG1rBC/ep4bQYIXBR1DiPdwjjLqF", + "aKdft1ERLEXhG8TmJkXrBjIeAeMl494TFr8g0uiVgBuD53XgO5VKqq0IOIqnXQDNB+LYMdfPulJvO1S3", + "YLJBCa7RzzG8jU330wHGUb/QCG6Ub4g/FIa6A2HiOc3rCNhIL1OUqpwQlWGOSKe7aYxxGMbt+ye3L4Ad", + "LdOnzedYhHzfm2ioNNOsyhagE5plsZ4qX+FTgk99rg+sIa3q3htlSVKsRNouzdqnNjdRKriqii1z+Rdu", + "OV3QLjhCDWHLYr/DWF1htsF/92lmX8e+7p3f5gNds/3qEffz9WJSr6HpRLFFMh4TeKfcHh3N1Dcj9Ob7", + "O6X0XCzagHwOI+kAlwv3KMbfvjYXR1ivsBdmbK+WupwghvQKfO6LXNSFsNpcCa+yXnsVdF7XTeS3myGG", + "28FP8fIbyCkNTd72frVm4KHM0nQwEZpqV5JFU7KVBQ2WubAhnx0jet8TNBTmaaM878747Na6FaHDLpjv", + "Wg4XG+rTMItBR8vNfCHNBu/rDPluNZRs7MuT4/Nuu+grcEXkSgkrJiofRONDWb1KaH9tNV+u072j648G", + "iH9u4/OgqfzCte2zy3Q6+Xc/WmcaAa7l5ndgOO9teq8RdV/ateap5hVSd3wa1QGqdSuOKd0fqxLvZMNW", + "K+wdjbx7ZPVijDjQb8w9nZxle12YsU4DEztK7NjF22wPF2Juii/jESuFYk3jtVj/7ZEx4xfYQjsoJN0f", + "y8cSriDV2G2viZGSAPuUlTaTedv9nwWZh9XpOrTe1WHeVny532Jvxx3fK0ESlNGx7ckOx5caPq0jYW0i", + "zzVVWJhfoo27nfo6OgFvPocUi0FuLfnytyXwoJzI1NtlEJZ5UAGG1ekoWM50f6tjA9C2iixb4QnaCtwa", + "nKF05CvY3FOkRQ3Rfml1LtZNikUiBpA7JL505pAh2QX/MFVTBmLBR3baz6GpCT7YajkoYHTDuTxJmouj", + "KWq0Zcp4r9dRc5lP9yr1hZkVQ1Vh+q0ih/WPF9iZU7k4J1oXmwy1dHLW7xdw7YpVYoGe2nfiy1aC8r/5", + "alx2lpxdQdgMGj1V11Rm/o2o6cVbdZIt91GvlItvc9gFel7PzJo4/L6vOlKBGlNa0lwYMSIZygtqh77X", + "cWP3lA3wa+qwIFxzkK5pPsq/uVCQaOHj9rfBsQ0VNorxRkhQg10fLHCD5U7fNvVcsfsNxfKm1AUvhgsk", + "EgpqoJNB1dXhObch+7l97nOpffeTnRamml53t+HzGRhM9ZAYUv2cuNtyd472TYxNjHOQifc8dUuwcpBt", + "b0gpRVal9oIOD0ZtkBtdAmULK4naadL+Kjs6QpDrfAWbI6sE+f6FfgdDoK3kZEEPSvd1NvlOzW8qBvfi", + "TsD7nJar6aQUIk8GnB1n/bqxXYq/YukVZMTcFD5SeaA1LbmPNvbam3293Pg6qWUJHLIHh4Sccpsb4h3b", + "7a5Kncn5Pb1t/jXOmlW2lLMzqh1e8niQPRZZlrfkZn6Y7TxMgWF1t5zKDrKjKul6oGatpNeRRs2HY7Xy", + "vqu52zy3ISoLRUwmObceq+d40GOGI8xkD0ouoCOTEufpIioXsZDMm2Tbm6HimAonQ4A08DFJ3zUUbvAo", + "AqLtYCOn0FYwc7XLxJxIaJzINy3i1u9cG9PouzPXs7T53VxIaPWgNV8LmXmRh6mmWTSVM6YllZublFrr", + "dc7tWU8GsbwzHKuOxGoW0kRj9XGY5+I6QWaV1LXNY6qteU+1L2Pfa6b5zpzqGQRxXVQ5QW1DljQjqZAS", + "0vCLeNqehaoQEpJcYJhXzAM910buLjBXh5NcLIgoU5GB7REQp6ChuSrOKYpNEETVRFFgaQeTPu03AR2P", + "nPKu2jbb4jx20Yn1ZQ4EnoJyxXgchuzLfXi3tDzeqzr/2RwtQgxjXdq511b6DBs/w559n1mee4PBUOtn", + "8oOqMBwJE2/MFE9IIZR2mp0dSdVDNSFe91PBtRR53jYCWZF44Szbr+j6NE31SyGuZjS9eoB6JBe6Xmk2", + "9Wmp3WC8ZibZqcg0skf1xTJi58VZ/KnbuxG14xx7948NwHy/m2PttnGfxvpst9fVbRzPB2pnalGwNE7D", + "f6zotsGYtBhLiJZ6si2cbHI+voaMOrwc6mAGZEl9NAOn0R40p8TxNOfUReZh/osSb3dcMgd3SQxcTH0+", + "6aSWJB2UrToAIKQ2Y1RX0vZ9CiWfmquIhc0wR5d0F9CRXBwjf24HmxnhzoHScCugetGGNYD3rbI/tSW5", + "bOTiTKz98wdNza4bAf9xO5XHeuVHTnFNWq6Vv6/vMcAR4pWBt8YfYVdzf4PujkKqe/SNvFEDAIbjklow", + "jIpO2heMOWU5ZAnVA5c72oSmgWbrMlq6nVeZcpw8pfbCXgIxY1cSXL0JK1J3OrWX1JCSqF/vW255BmtQ", + "WAzCtpumyvoZvL8DcttWqqN8izLJYQWtcC1XBKNC0Y6twH+r6o9JBlCi969rk4rFIYV3ecdQ4daeBJEs", + "Y7AbtVxYxNqdIjvMElEjypon9piosUfJQLRiWUVb+FP7ihxts5s5yhFU9WTyxOttY6f5wY7w1g9w6r+P", + "iTIeE+/H8aG9WVAcddsY0M64xEoNnXoeD0sMK7zUDg2cLasdn5bEG76hSnrNhw2AfZJv1JuR+8QEDxD7", + "9RpSlGracXe3xwnBwYjqVG8aFMFlvcM3NyR/FhreSsKD48VUDQXIYLdaajxdOIEdX8Bem9yIvUZqxhZS", + "jv87/jcls8oPZPRq29Eq1OBegPfYYUHp2lnhBFpWX2g+vnDq6gl2lXIWRFYXdEOExH+MvvZLRXM23+AJ", + "teD7z4haUkNCzkVofdcuXtFMvF0wmXrAvF1A+KnsutnYMYPhNmaUAGhzBTrjFFYGuoJwG9AtbzlPqg3L", + "UdWsYErhZdfZzj4W3OJ9TYiCZqGOjJXp2n1Ofa1S8/X/brK2wql8Qakyp6nvXwZE0aJjELc9Cj1x6SUU", + "29P6+uqxJ4G672FDtNKn82Y3MO7tGbkRi5Uf6vfQArvXD67X6uJWy9ine3KTGb0lIXLUUu56F8bGh/SA", + "Riezr+q1A3xbjdFXAPsU+I8WjRxaxhjwfy94H2ijF8JrO+Z9Aiy3Uv4jsFq76kysEwlztSsUwhpWjSIs", + "m2IB3jjJeCqBKhsbcvbaqWxNTUTGjQppoxdr71s9SgZzxhtmyXhZ6YgGgKUR+SZAWGieRrQOOHuGpAQj", + "hq1o/noFUrJsaOPM6bBtvMKa9N4k776NKP/1ndofgKlG+8FMQmgy1YLXzAVuu97YwEKlKc+ozMLXGScp", + "SHPvk2u6UTf3fRhoZWXkix3eDxpIM+389sAPgqRtAck3zn15S89EDSC9QxfFCNcCRrBG3ArWKKLFgCeh", + "D0O8rAJdJ7lYYH7ZAAG64pPo+7HKiuBosLXy0H7zKPYrbJ8G6267g68Fzjpmiu3n7DWiDhWeHzjTW0+a", + "taZ1E/5sRKY9CJ7++aIJC7eb06f/WI7mBSYxtPI0ux3x/V7b8BA7Hwx4MtoW3IFdRAe5S/ANzbXj+xm1", + "ffCxTFCrwyao26otgd+gmiBnmrrAnb7Rp6cUW6RMXR7tnjYha0n298AAeLZTrTtb7WnrYAozzj5NoLZn", + "zialKJN0TDSgLc2fOYO2g7QN4wB9BObqgXXXgROqblbRKmzS6lqxbx+swa4Zu/wyZbpNyR4yaAxw0Lax", + "XMyRl+ERtmYczPGojRfTbvZR22BTMwlCiYS0kmjQvKab3X2FBkrCnv/19OnDRz89evoFMS+QjC1ANWWF", + "O315mogxxrt2lk8bI9Zbno5vgs9Lt4jznjKfblNvijtrltuqpmZgryvRPpbQyAUQOY6RfjA32iscpwn6", + "/n1tV2yRd75jMRT89nsmRZ7Hy7rXolvE1B/brcDYbyT+EqRiShtG2PbVMd3EyqolmuOwuOfK1hkRPHXV", + "12sqYHogGCe2kKFQS+RnmPXr/BsE1mXueJX1SWxbl9OLrEUMgzMwfmMGpBSlE6XZnMQgwtwSGeRcOkMj", + "hncG0ZM1s7VxlDFCdDHJcdI75U7zFHOyndu3uzXqOKc3mxgRL/yhvAFpDlnShzPab8JJGlP674Z/RFL0", + "74xr1Mv9LXhFVD+4WePjUaD107Uj5IEADORhtjLowr7oTaVRaa3yaL/3rs6u+PGqcYHuTBhASPwHO8AL", + "Eyub9+oYdwfOZy7Z+apGSrCU90OU0Fr+rlxNz3rriyTYImek0BqUZUuiLxYGibjqeZ3fOqCV9NJgsQm6", + "0UzzPJI+a+0meKZCwjEqgVzR/NNzDeyOf4r4gOztcNJMmEMZItmiUt2sgttLOmruIF/y7qbmbzBl929g", + "9ih6z7mhnLu4d5uh1QtbUi/8rWCzgMk1jmnDgR5+QWaumn4pIWWq64a+9sJJnTIIks1d6CWs9Y4cxV3r", + "/FHoW5Dx3MeMkO8Dd5JAs10DYXNEPzNTGTi5USqPUV+PLCL4i/GosPvmjuvilpXXb1YQJCjttWdBkH5f", + "0bHLs0UvzKVTKeivc/Rt3cJt5KJu1ja2ms3oAu6Xl+/0bEwRmnixdfM5VsG5k6rre9Vc/w3q31gcuTHc", + "vDGK+XGoIqqt+jlQfLezHxXLdwaItEopf5xOFsBBMYXFgn9yzSE+7V3qIbA5+f2jamG9TSERi5jIWluT", + "B1MFRZJH1Ed2n0WqIWO+W1pJpjfYGNQb0NhP0Uo939ZVH1zVkNp35e4+La6gbs7c1IiolL9dvxU0x/vI", + "utS4uYVEfki+XtOizJ05mHx5b/bv8PgvT7Ljxw//ffaX46fHKTx5+uz4mD57Qh8+e/wQHv3l6ZNjeDj/", + "4tnsUfboyaPZk0dPvnj6LH385OHsyRfP/v2e4UMGZAuor919Mvl7cpovRHL65iy5MMA2OKEl+w7M3qCu", + "PBfYuM4gNcWTCAVl+eTE//R//Ak7TEXRDO9/nbgGLJOl1qU6OTq6vr4+DD85WmBSeKJFlS6P/DzYTqwl", + "r7w5q6PJbdwL7mhjPcZNdaRwis/efn1+QU7fnB02BDM5mRwfHh8+dL1rOS3Z5GTyGH/C07PEfT9yxDY5", + "+fBxOjlaAs2xhor5owAtWeofSaDZxv1fXdPFAuQhJgzYn1aPjrxYcfTBJcd/3PbsKAypOPrQqiGQ7fgS", + "wwGOPvgOltvfbnUvdJFYZulRR+C3oF25HGshiNRaQH+AG31KlJAup7iUTJhTNTVXZAboLcegL4kFoLWs", + "eGpdqHYK4PjfV6d/Rzfyq9O/ky/J8dQFsCtUO2LT24zZmhzOMgt2P3pPfbU5ratRNC7nycm7mCnIdaoq", + "q1nOUmKlCTxOhlYCaq9HbLgZ2v0mTSvxhjcbfnucPHv/4elfPsZkvp4EWyMpKNAQol4L34AQkVbQ9ZdD", + "KFu7iGYz7i8VyE2ziIKuJyHAfT9npGqVTzjxfVjDiL0glu8/z19/T4QkTsd9Q9OrOtnGZ1c1GWVhcpX5", + "cghid/2FQAOvCnOTuKydQi3KdgHXGs3vsWkZAoqH/tHxsed0To8ITt+RO9TBTB3jU5/QMHglMCf2U5kV", + "gTVNdb4hVAXRAxjL5xsMdlKiRJm0ArO3GjD7M7otiUa175tNHakwLjTNd8B30WnG1kKHC4QpzVW4O325", + "h4woBO9jl324tZ5G/tzd/x6725cdSCnMmWYYrdxcOf46awHpJMZ848EdKBRxSP4hKpTwjOxeaYi1osYZ", + "rOfCzenq2gThZU0qCj45OOgu/OCgCYabwzUyWcrxxS46Dg4OzU492ZOVbbUmt8rAjjo7+wzX26xXdF3H", + "ElPCBU84LKhmKyCBWvjk+OEfdoVn3EZvG5HWit4fp5Onf+AtO+NGsKE5wTftah7/YVdzDnLFUiAXUJRC", + "UsnyDfmB1+HxQafjPvv7gV9xcc09IoxWWRUFlRsnRNOa51Q86Nuylf/0KtQ0gjZyUbpQGLGCIqqVaX0V", + "O76YvP/odYCRisW2145m2Ihu7KuggpeHtRP0H6ijD2gBH/z9yLkx4w/RE2FV3CNfOy/+Zkvx+aDXBtYd", + "X6xZFqwkpTpdVuXRB/wPKqQB0Lau+pFe8yMMhjz60Fqre9xba/v35vPwjVUhMvDAifncNuvf9vjog/03", + "mAjWJUhmbhysZeh+tTVnj7Bn66b/84an0R/762jV2xz4+cjbQ2IqcfvND60/22SjlpXOxHUwC3oSrBus", + "D5l5WKnu30fXlGkjB7kyj9jkvf+xBpofuZ4unV+bMuq9J1gbPvixIzmVwtZ5aSutb+n1RSt9UNr6Cl8J", + "NDQM8dR1MmMcGU3ICBv7oH3Y14J67O9iCTYQ1rtYI2KmFmQmBc1SqrB3uOt+1FN/P95SxeqWgziLONAQ", + "TLQo9CsGGpZxuNOrguOOkSODfSFnL/yETebVby579SD6imbEFwZKyCuamw2HjJw6Cb+Fjd9abvr8gs5n", + "lkw+mSjxlT98ilCsktbSAWW8zkrQpmyM3GAURcMAFsATx4KSmcg2rpPURNJrvbZlHbrM7Yi2b4y2rZFK", + "Wqihh3dgiPx9Wx93GR3/tPX9aev70xr0p63vz93909Y30tb3pyXsT0vY/0hL2D7mr5iY6cw/w9Imtram", + "rXmt3kebFgI1i28XnGK6lsla+ZzYrYDpQ0IusOYJNbcErEDSnKRUWenKFdYqMMwSy1ZBdnLJkxYkNpjR", + "THy/+a+NIr2sjo8fAzl+0P1GaZbnIW/uf4vyLj6yiR5fksvJ5aQ3koRCrCCzWalhCWv71c5h/1c97ute", + "7XtM/8aiMr66FVHVfM5SZlGeC74gdCGaCGis4ckFPgFpgLMdhAjTU5cxwlw5UNdgvF1puy259yWAs2YL", + "d0YNdMglHjBgCG/PaIF/GxMq8D9aSr9pGafbMtKtY/e46p9c5VNwlc/OV/7oftjAtPjfUsx8cvzkD7ug", + "0BD9vdDkG4zuv5045mpLptFGSjcVtHyFFG/uayKEw4hbvEXrWNt3781FoECu/AXbBJCeHB1hyaylUPpo", + "Yq6/dnBp+PB9DfMHfzuVkq2wUy9aN4VkC8ZpnrjAzaQJEn10eDz5+P8DAAD//4x4XYfhEgEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 3eb65154b7..5663f429ba 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -525,6 +525,7 @@ func (v2 *Handlers) basicAccountInformation(ctx echo.Context, addr basics.Addres Status: record.Status.String(), RewardBase: &record.RewardsBase, Participation: apiParticipation, + IncentiveEligible: omitEmpty(record.IncentiveEligible), TotalCreatedAssets: record.TotalAssetParams, TotalCreatedApps: record.TotalAppParams, TotalAssetsOptedIn: record.TotalAssets, diff --git a/data/basics/msgp_gen.go b/data/basics/msgp_gen.go index 06190153d6..f51e6e503b 100644 --- a/data/basics/msgp_gen.go +++ b/data/basics/msgp_gen.go @@ -250,8 +250,8 @@ import ( func (z *AccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0009Len := uint32(19) - var zb0009Mask uint32 /* 20 bits */ + zb0009Len := uint32(20) + var zb0009Mask uint32 /* 21 bits */ if (*z).MicroAlgos.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x2 @@ -280,54 +280,58 @@ func (z *AccountData) MarshalMsg(b []byte) (o []byte) { zb0009Len-- zb0009Mask |= 0x80 } - if (*z).Status == 0 { + if (*z).IncentiveEligible == false { zb0009Len-- zb0009Mask |= 0x100 } - if (*z).SelectionID.MsgIsZero() { + if (*z).Status == 0 { zb0009Len-- zb0009Mask |= 0x200 } - if (*z).AuthAddr.MsgIsZero() { + if (*z).SelectionID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x400 } - if (*z).StateProofID.MsgIsZero() { + if (*z).AuthAddr.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x800 } - if (*z).TotalBoxes == 0 { + if (*z).StateProofID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x1000 } - if (*z).TotalBoxBytes == 0 { + if (*z).TotalBoxes == 0 { zb0009Len-- zb0009Mask |= 0x2000 } - if (*z).TotalExtraAppPages == 0 { + if (*z).TotalBoxBytes == 0 { zb0009Len-- zb0009Mask |= 0x4000 } - if ((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0) { + if (*z).TotalExtraAppPages == 0 { zb0009Len-- zb0009Mask |= 0x8000 } - if (*z).VoteID.MsgIsZero() { + if ((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0) { zb0009Len-- zb0009Mask |= 0x10000 } - if (*z).VoteFirstValid == 0 { + if (*z).VoteID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x20000 } - if (*z).VoteKeyDilution == 0 { + if (*z).VoteFirstValid == 0 { zb0009Len-- zb0009Mask |= 0x40000 } - if (*z).VoteLastValid == 0 { + if (*z).VoteKeyDilution == 0 { zb0009Len-- zb0009Mask |= 0x80000 } + if (*z).VoteLastValid == 0 { + zb0009Len-- + zb0009Mask |= 0x100000 + } // variable map header, size zb0009Len o = msgp.AppendMapHeader(o, zb0009Len) if zb0009Len != 0 { @@ -451,41 +455,46 @@ func (z *AccountData) MarshalMsg(b []byte) (o []byte) { o = (*z).RewardedMicroAlgos.MarshalMsg(o) } if (zb0009Mask & 0x100) == 0 { // if not empty + // string "ie" + o = append(o, 0xa2, 0x69, 0x65) + o = msgp.AppendBool(o, (*z).IncentiveEligible) + } + if (zb0009Mask & 0x200) == 0 { // if not empty // string "onl" o = append(o, 0xa3, 0x6f, 0x6e, 0x6c) o = msgp.AppendByte(o, byte((*z).Status)) } - if (zb0009Mask & 0x200) == 0 { // if not empty + if (zb0009Mask & 0x400) == 0 { // if not empty // string "sel" o = append(o, 0xa3, 0x73, 0x65, 0x6c) o = (*z).SelectionID.MarshalMsg(o) } - if (zb0009Mask & 0x400) == 0 { // if not empty + if (zb0009Mask & 0x800) == 0 { // if not empty // string "spend" o = append(o, 0xa5, 0x73, 0x70, 0x65, 0x6e, 0x64) o = (*z).AuthAddr.MarshalMsg(o) } - if (zb0009Mask & 0x800) == 0 { // if not empty + if (zb0009Mask & 0x1000) == 0 { // if not empty // string "stprf" o = append(o, 0xa5, 0x73, 0x74, 0x70, 0x72, 0x66) o = (*z).StateProofID.MarshalMsg(o) } - if (zb0009Mask & 0x1000) == 0 { // if not empty + if (zb0009Mask & 0x2000) == 0 { // if not empty // string "tbx" o = append(o, 0xa3, 0x74, 0x62, 0x78) o = msgp.AppendUint64(o, (*z).TotalBoxes) } - if (zb0009Mask & 0x2000) == 0 { // if not empty + if (zb0009Mask & 0x4000) == 0 { // if not empty // string "tbxb" o = append(o, 0xa4, 0x74, 0x62, 0x78, 0x62) o = msgp.AppendUint64(o, (*z).TotalBoxBytes) } - if (zb0009Mask & 0x4000) == 0 { // if not empty + if (zb0009Mask & 0x8000) == 0 { // if not empty // string "teap" o = append(o, 0xa4, 0x74, 0x65, 0x61, 0x70) o = msgp.AppendUint32(o, (*z).TotalExtraAppPages) } - if (zb0009Mask & 0x8000) == 0 { // if not empty + if (zb0009Mask & 0x10000) == 0 { // if not empty // string "tsch" o = append(o, 0xa4, 0x74, 0x73, 0x63, 0x68) // omitempty: check for empty values @@ -512,22 +521,22 @@ func (z *AccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).TotalAppSchema.NumUint) } } - if (zb0009Mask & 0x10000) == 0 { // if not empty + if (zb0009Mask & 0x20000) == 0 { // if not empty // string "vote" o = append(o, 0xa4, 0x76, 0x6f, 0x74, 0x65) o = (*z).VoteID.MarshalMsg(o) } - if (zb0009Mask & 0x20000) == 0 { // if not empty + if (zb0009Mask & 0x40000) == 0 { // if not empty // string "voteFst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x46, 0x73, 0x74) o = msgp.AppendUint64(o, uint64((*z).VoteFirstValid)) } - if (zb0009Mask & 0x40000) == 0 { // if not empty + if (zb0009Mask & 0x80000) == 0 { // if not empty // string "voteKD" o = append(o, 0xa6, 0x76, 0x6f, 0x74, 0x65, 0x4b, 0x44) o = msgp.AppendUint64(o, (*z).VoteKeyDilution) } - if (zb0009Mask & 0x80000) == 0 { // if not empty + if (zb0009Mask & 0x100000) == 0 { // if not empty // string "voteLst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x4c, 0x73, 0x74) o = msgp.AppendUint64(o, uint64((*z).VoteLastValid)) @@ -795,6 +804,14 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } + if zb0009 > 0 { + zb0009-- + (*z).IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "IncentiveEligible") + return + } + } if zb0009 > 0 { zb0009-- var zb0020 int @@ -1196,6 +1213,12 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "AuthAddr") return } + case "ie": + (*z).IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) + if err != nil { + err = msgp.WrapError(err, "IncentiveEligible") + return + } case "appl": var zb0035 int var zb0036 bool @@ -1391,7 +1414,7 @@ func (z *AccountData) Msgsize() (s int) { s += 0 + zb0003.Msgsize() + 1 + 2 + msgp.Uint64Size + 2 + msgp.BoolSize } } - s += 6 + (*z).AuthAddr.Msgsize() + 5 + msgp.MapHeaderSize + s += 6 + (*z).AuthAddr.Msgsize() + 3 + msgp.BoolSize + 5 + msgp.MapHeaderSize if (*z).AppLocalStates != nil { for zb0005, zb0006 := range (*z).AppLocalStates { _ = zb0005 @@ -1413,7 +1436,7 @@ func (z *AccountData) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *AccountData) MsgIsZero() bool { - return ((*z).Status == 0) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) && ((*z).RewardedMicroAlgos.MsgIsZero()) && ((*z).VoteID.MsgIsZero()) && ((*z).SelectionID.MsgIsZero()) && ((*z).StateProofID.MsgIsZero()) && ((*z).VoteFirstValid == 0) && ((*z).VoteLastValid == 0) && ((*z).VoteKeyDilution == 0) && (len((*z).AssetParams) == 0) && (len((*z).Assets) == 0) && ((*z).AuthAddr.MsgIsZero()) && (len((*z).AppLocalStates) == 0) && (len((*z).AppParams) == 0) && (((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0)) && ((*z).TotalExtraAppPages == 0) && ((*z).TotalBoxes == 0) && ((*z).TotalBoxBytes == 0) + return ((*z).Status == 0) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) && ((*z).RewardedMicroAlgos.MsgIsZero()) && ((*z).VoteID.MsgIsZero()) && ((*z).SelectionID.MsgIsZero()) && ((*z).StateProofID.MsgIsZero()) && ((*z).VoteFirstValid == 0) && ((*z).VoteLastValid == 0) && ((*z).VoteKeyDilution == 0) && (len((*z).AssetParams) == 0) && (len((*z).Assets) == 0) && ((*z).AuthAddr.MsgIsZero()) && ((*z).IncentiveEligible == false) && (len((*z).AppLocalStates) == 0) && (len((*z).AppParams) == 0) && (((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0)) && ((*z).TotalExtraAppPages == 0) && ((*z).TotalBoxes == 0) && ((*z).TotalBoxBytes == 0) } // MaxSize returns a maximum valid message size for this message type @@ -1431,7 +1454,7 @@ func AccountDataMaxSize() (s int) { // Adding size of map values for z.Assets s += encodedMaxAssetsPerAccount * (1) s += 2 + msgp.Uint64Size + 2 + msgp.BoolSize - s += 6 + AddressMaxSize() + 5 + s += 6 + AddressMaxSize() + 3 + msgp.BoolSize + 5 s += msgp.MapHeaderSize // Adding size of map keys for z.AppLocalStates s += EncodedMaxAppLocalStates * (AppIndexMaxSize()) @@ -3199,8 +3222,8 @@ func AssetParamsMaxSize() (s int) { func (z *BalanceRecord) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0009Len := uint32(20) - var zb0009Mask uint32 /* 22 bits */ + zb0009Len := uint32(21) + var zb0009Mask uint32 /* 23 bits */ if (*z).Addr.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x4 @@ -3233,54 +3256,58 @@ func (z *BalanceRecord) MarshalMsg(b []byte) (o []byte) { zb0009Len-- zb0009Mask |= 0x200 } - if (*z).AccountData.Status == 0 { + if (*z).AccountData.IncentiveEligible == false { zb0009Len-- zb0009Mask |= 0x400 } - if (*z).AccountData.SelectionID.MsgIsZero() { + if (*z).AccountData.Status == 0 { zb0009Len-- zb0009Mask |= 0x800 } - if (*z).AccountData.AuthAddr.MsgIsZero() { + if (*z).AccountData.SelectionID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x1000 } - if (*z).AccountData.StateProofID.MsgIsZero() { + if (*z).AccountData.AuthAddr.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x2000 } - if (*z).AccountData.TotalBoxes == 0 { + if (*z).AccountData.StateProofID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x4000 } - if (*z).AccountData.TotalBoxBytes == 0 { + if (*z).AccountData.TotalBoxes == 0 { zb0009Len-- zb0009Mask |= 0x8000 } - if (*z).AccountData.TotalExtraAppPages == 0 { + if (*z).AccountData.TotalBoxBytes == 0 { zb0009Len-- zb0009Mask |= 0x10000 } - if ((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0) { + if (*z).AccountData.TotalExtraAppPages == 0 { zb0009Len-- zb0009Mask |= 0x20000 } - if (*z).AccountData.VoteID.MsgIsZero() { + if ((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0) { zb0009Len-- zb0009Mask |= 0x40000 } - if (*z).AccountData.VoteFirstValid == 0 { + if (*z).AccountData.VoteID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x80000 } - if (*z).AccountData.VoteKeyDilution == 0 { + if (*z).AccountData.VoteFirstValid == 0 { zb0009Len-- zb0009Mask |= 0x100000 } - if (*z).AccountData.VoteLastValid == 0 { + if (*z).AccountData.VoteKeyDilution == 0 { zb0009Len-- zb0009Mask |= 0x200000 } + if (*z).AccountData.VoteLastValid == 0 { + zb0009Len-- + zb0009Mask |= 0x400000 + } // variable map header, size zb0009Len o = msgp.AppendMapHeader(o, zb0009Len) if zb0009Len != 0 { @@ -3409,41 +3436,46 @@ func (z *BalanceRecord) MarshalMsg(b []byte) (o []byte) { o = (*z).AccountData.RewardedMicroAlgos.MarshalMsg(o) } if (zb0009Mask & 0x400) == 0 { // if not empty + // string "ie" + o = append(o, 0xa2, 0x69, 0x65) + o = msgp.AppendBool(o, (*z).AccountData.IncentiveEligible) + } + if (zb0009Mask & 0x800) == 0 { // if not empty // string "onl" o = append(o, 0xa3, 0x6f, 0x6e, 0x6c) o = msgp.AppendByte(o, byte((*z).AccountData.Status)) } - if (zb0009Mask & 0x800) == 0 { // if not empty + if (zb0009Mask & 0x1000) == 0 { // if not empty // string "sel" o = append(o, 0xa3, 0x73, 0x65, 0x6c) o = (*z).AccountData.SelectionID.MarshalMsg(o) } - if (zb0009Mask & 0x1000) == 0 { // if not empty + if (zb0009Mask & 0x2000) == 0 { // if not empty // string "spend" o = append(o, 0xa5, 0x73, 0x70, 0x65, 0x6e, 0x64) o = (*z).AccountData.AuthAddr.MarshalMsg(o) } - if (zb0009Mask & 0x2000) == 0 { // if not empty + if (zb0009Mask & 0x4000) == 0 { // if not empty // string "stprf" o = append(o, 0xa5, 0x73, 0x74, 0x70, 0x72, 0x66) o = (*z).AccountData.StateProofID.MarshalMsg(o) } - if (zb0009Mask & 0x4000) == 0 { // if not empty + if (zb0009Mask & 0x8000) == 0 { // if not empty // string "tbx" o = append(o, 0xa3, 0x74, 0x62, 0x78) o = msgp.AppendUint64(o, (*z).AccountData.TotalBoxes) } - if (zb0009Mask & 0x8000) == 0 { // if not empty + if (zb0009Mask & 0x10000) == 0 { // if not empty // string "tbxb" o = append(o, 0xa4, 0x74, 0x62, 0x78, 0x62) o = msgp.AppendUint64(o, (*z).AccountData.TotalBoxBytes) } - if (zb0009Mask & 0x10000) == 0 { // if not empty + if (zb0009Mask & 0x20000) == 0 { // if not empty // string "teap" o = append(o, 0xa4, 0x74, 0x65, 0x61, 0x70) o = msgp.AppendUint32(o, (*z).AccountData.TotalExtraAppPages) } - if (zb0009Mask & 0x20000) == 0 { // if not empty + if (zb0009Mask & 0x40000) == 0 { // if not empty // string "tsch" o = append(o, 0xa4, 0x74, 0x73, 0x63, 0x68) // omitempty: check for empty values @@ -3470,22 +3502,22 @@ func (z *BalanceRecord) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).AccountData.TotalAppSchema.NumUint) } } - if (zb0009Mask & 0x40000) == 0 { // if not empty + if (zb0009Mask & 0x80000) == 0 { // if not empty // string "vote" o = append(o, 0xa4, 0x76, 0x6f, 0x74, 0x65) o = (*z).AccountData.VoteID.MarshalMsg(o) } - if (zb0009Mask & 0x80000) == 0 { // if not empty + if (zb0009Mask & 0x100000) == 0 { // if not empty // string "voteFst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x46, 0x73, 0x74) o = msgp.AppendUint64(o, uint64((*z).AccountData.VoteFirstValid)) } - if (zb0009Mask & 0x100000) == 0 { // if not empty + if (zb0009Mask & 0x200000) == 0 { // if not empty // string "voteKD" o = append(o, 0xa6, 0x76, 0x6f, 0x74, 0x65, 0x4b, 0x44) o = msgp.AppendUint64(o, (*z).AccountData.VoteKeyDilution) } - if (zb0009Mask & 0x200000) == 0 { // if not empty + if (zb0009Mask & 0x400000) == 0 { // if not empty // string "voteLst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x4c, 0x73, 0x74) o = msgp.AppendUint64(o, uint64((*z).AccountData.VoteLastValid)) @@ -3761,6 +3793,14 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState return } } + if zb0009 > 0 { + zb0009-- + (*z).AccountData.IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "IncentiveEligible") + return + } + } if zb0009 > 0 { zb0009-- var zb0020 int @@ -4168,6 +4208,12 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState err = msgp.WrapError(err, "AuthAddr") return } + case "ie": + (*z).AccountData.IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) + if err != nil { + err = msgp.WrapError(err, "IncentiveEligible") + return + } case "appl": var zb0035 int var zb0036 bool @@ -4363,7 +4409,7 @@ func (z *BalanceRecord) Msgsize() (s int) { s += 0 + zb0003.Msgsize() + 1 + 2 + msgp.Uint64Size + 2 + msgp.BoolSize } } - s += 6 + (*z).AccountData.AuthAddr.Msgsize() + 5 + msgp.MapHeaderSize + s += 6 + (*z).AccountData.AuthAddr.Msgsize() + 3 + msgp.BoolSize + 5 + msgp.MapHeaderSize if (*z).AccountData.AppLocalStates != nil { for zb0005, zb0006 := range (*z).AccountData.AppLocalStates { _ = zb0005 @@ -4385,7 +4431,7 @@ func (z *BalanceRecord) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *BalanceRecord) MsgIsZero() bool { - return ((*z).Addr.MsgIsZero()) && ((*z).AccountData.Status == 0) && ((*z).AccountData.MicroAlgos.MsgIsZero()) && ((*z).AccountData.RewardsBase == 0) && ((*z).AccountData.RewardedMicroAlgos.MsgIsZero()) && ((*z).AccountData.VoteID.MsgIsZero()) && ((*z).AccountData.SelectionID.MsgIsZero()) && ((*z).AccountData.StateProofID.MsgIsZero()) && ((*z).AccountData.VoteFirstValid == 0) && ((*z).AccountData.VoteLastValid == 0) && ((*z).AccountData.VoteKeyDilution == 0) && (len((*z).AccountData.AssetParams) == 0) && (len((*z).AccountData.Assets) == 0) && ((*z).AccountData.AuthAddr.MsgIsZero()) && (len((*z).AccountData.AppLocalStates) == 0) && (len((*z).AccountData.AppParams) == 0) && (((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0)) && ((*z).AccountData.TotalExtraAppPages == 0) && ((*z).AccountData.TotalBoxes == 0) && ((*z).AccountData.TotalBoxBytes == 0) + return ((*z).Addr.MsgIsZero()) && ((*z).AccountData.Status == 0) && ((*z).AccountData.MicroAlgos.MsgIsZero()) && ((*z).AccountData.RewardsBase == 0) && ((*z).AccountData.RewardedMicroAlgos.MsgIsZero()) && ((*z).AccountData.VoteID.MsgIsZero()) && ((*z).AccountData.SelectionID.MsgIsZero()) && ((*z).AccountData.StateProofID.MsgIsZero()) && ((*z).AccountData.VoteFirstValid == 0) && ((*z).AccountData.VoteLastValid == 0) && ((*z).AccountData.VoteKeyDilution == 0) && (len((*z).AccountData.AssetParams) == 0) && (len((*z).AccountData.Assets) == 0) && ((*z).AccountData.AuthAddr.MsgIsZero()) && ((*z).AccountData.IncentiveEligible == false) && (len((*z).AccountData.AppLocalStates) == 0) && (len((*z).AccountData.AppParams) == 0) && (((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0)) && ((*z).AccountData.TotalExtraAppPages == 0) && ((*z).AccountData.TotalBoxes == 0) && ((*z).AccountData.TotalBoxBytes == 0) } // MaxSize returns a maximum valid message size for this message type @@ -4403,7 +4449,7 @@ func BalanceRecordMaxSize() (s int) { // Adding size of map values for z.AccountData.Assets s += encodedMaxAssetsPerAccount * (1) s += 2 + msgp.Uint64Size + 2 + msgp.BoolSize - s += 6 + AddressMaxSize() + 5 + s += 6 + AddressMaxSize() + 3 + msgp.BoolSize + 5 s += msgp.MapHeaderSize // Adding size of map keys for z.AccountData.AppLocalStates s += EncodedMaxAppLocalStates * (AppIndexMaxSize()) diff --git a/data/basics/userBalance.go b/data/basics/userBalance.go index 398147bb6d..cea8a9bc2c 100644 --- a/data/basics/userBalance.go +++ b/data/basics/userBalance.go @@ -209,6 +209,8 @@ type AccountData struct { // This allows key rotation, changing the members in a multisig, etc. AuthAddr Address `codec:"spend"` + IncentiveEligible bool `codec:"ie"` + // AppLocalStates stores the local states associated with any applications // that this account has opted in to. AppLocalStates map[AppIndex]AppLocalState `codec:"appl,allocbound=EncodedMaxAppLocalStates"` diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 3685d16909..7fde54c0aa 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -39,6 +39,7 @@ type AccountBaseData struct { RewardsBase uint64 RewardedMicroAlgos basics.MicroAlgos AuthAddr basics.Address + IncentiveEligible bool TotalAppSchema basics.StateSchema // Totals across created globals, and opted in locals. TotalExtraAppPages uint32 // Total number of extra pages across all created apps @@ -75,8 +76,8 @@ func ToAccountData(acct basics.AccountData) AccountData { MicroAlgos: acct.MicroAlgos, RewardsBase: acct.RewardsBase, RewardedMicroAlgos: acct.RewardedMicroAlgos, - - AuthAddr: acct.AuthAddr, + AuthAddr: acct.AuthAddr, + IncentiveEligible: acct.IncentiveEligible, TotalAppSchema: acct.TotalAppSchema, TotalExtraAppPages: acct.TotalExtraAppPages, @@ -105,6 +106,8 @@ func AssignAccountData(a *basics.AccountData, acct AccountData) { a.MicroAlgos = acct.MicroAlgos a.RewardsBase = acct.RewardsBase a.RewardedMicroAlgos = acct.RewardedMicroAlgos + a.AuthAddr = acct.AuthAddr + a.IncentiveEligible = acct.IncentiveEligible a.VoteID = acct.VoteID a.SelectionID = acct.SelectionID @@ -113,7 +116,6 @@ func AssignAccountData(a *basics.AccountData, acct AccountData) { a.VoteLastValid = acct.VoteLastValid a.VoteKeyDilution = acct.VoteKeyDilution - a.AuthAddr = acct.AuthAddr a.TotalAppSchema = acct.TotalAppSchema a.TotalExtraAppPages = acct.TotalExtraAppPages a.TotalBoxes = acct.TotalBoxes diff --git a/ledger/store/trackerdb/data.go b/ledger/store/trackerdb/data.go index fc243b2b92..2e9ee4778e 100644 --- a/ledger/store/trackerdb/data.go +++ b/ledger/store/trackerdb/data.go @@ -47,6 +47,7 @@ type BaseAccountData struct { TotalAppLocalStates uint64 `codec:"l"` TotalBoxes uint64 `codec:"m"` TotalBoxBytes uint64 `codec:"n"` + IncentiveEligible bool `codec:"o"` BaseVotingData @@ -286,6 +287,7 @@ func (ba *BaseAccountData) SetCoreAccountData(ad *ledgercore.AccountData) { ba.TotalAppLocalStates = ad.TotalAppLocalStates ba.TotalBoxes = ad.TotalBoxes ba.TotalBoxBytes = ad.TotalBoxBytes + ba.IncentiveEligible = ad.IncentiveEligible ba.BaseVotingData.SetCoreAccountData(ad) } @@ -306,6 +308,7 @@ func (ba *BaseAccountData) SetAccountData(ad *basics.AccountData) { ba.TotalAppLocalStates = uint64(len(ad.AppLocalStates)) ba.TotalBoxes = ad.TotalBoxes ba.TotalBoxBytes = ad.TotalBoxBytes + ba.IncentiveEligible = ad.IncentiveEligible ba.BaseVotingData.VoteID = ad.VoteID ba.BaseVotingData.SelectionID = ad.SelectionID @@ -342,6 +345,7 @@ func (ba *BaseAccountData) GetLedgerCoreAccountBaseData() ledgercore.AccountBase TotalAssets: ba.TotalAssets, TotalBoxes: ba.TotalBoxes, TotalBoxBytes: ba.TotalBoxBytes, + IncentiveEligible: ba.IncentiveEligible, } } @@ -365,6 +369,7 @@ func (ba *BaseAccountData) GetAccountData() basics.AccountData { RewardsBase: ba.RewardsBase, RewardedMicroAlgos: ba.RewardedMicroAlgos, AuthAddr: ba.AuthAddr, + IncentiveEligible: ba.IncentiveEligible, TotalAppSchema: basics.StateSchema{ NumUint: ba.TotalAppSchemaNumUint, NumByteSlice: ba.TotalAppSchemaNumByteSlice, @@ -389,6 +394,7 @@ func (ba *BaseAccountData) IsEmpty() bool { ba.RewardsBase == 0 && ba.RewardedMicroAlgos.Raw == 0 && ba.AuthAddr.IsZero() && + !ba.IncentiveEligible && ba.TotalAppSchemaNumUint == 0 && ba.TotalAppSchemaNumByteSlice == 0 && ba.TotalExtraAppPages == 0 && diff --git a/ledger/store/trackerdb/data_test.go b/ledger/store/trackerdb/data_test.go index e329a84e74..8c91b028de 100644 --- a/ledger/store/trackerdb/data_test.go +++ b/ledger/store/trackerdb/data_test.go @@ -1105,7 +1105,7 @@ func TestBaseAccountDataIsEmpty(t *testing.T) { structureTesting := func(t *testing.T) { encoding, err := json.Marshal(&empty) zeros32 := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - expectedEncoding := `{"Status":0,"MicroAlgos":{"Raw":0},"RewardsBase":0,"RewardedMicroAlgos":{"Raw":0},"AuthAddr":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ","TotalAppSchemaNumUint":0,"TotalAppSchemaNumByteSlice":0,"TotalExtraAppPages":0,"TotalAssetParams":0,"TotalAssets":0,"TotalAppParams":0,"TotalAppLocalStates":0,"TotalBoxes":0,"TotalBoxBytes":0,"VoteID":[` + zeros32 + `],"SelectionID":[` + zeros32 + `],"VoteFirstValid":0,"VoteLastValid":0,"VoteKeyDilution":0,"StateProofID":[` + zeros32 + `,` + zeros32 + `],"UpdateRound":0}` + expectedEncoding := `{"Status":0,"MicroAlgos":{"Raw":0},"RewardsBase":0,"RewardedMicroAlgos":{"Raw":0},"AuthAddr":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ","TotalAppSchemaNumUint":0,"TotalAppSchemaNumByteSlice":0,"TotalExtraAppPages":0,"TotalAssetParams":0,"TotalAssets":0,"TotalAppParams":0,"TotalAppLocalStates":0,"TotalBoxes":0,"TotalBoxBytes":0,"IncentiveEligible":false,"VoteID":[` + zeros32 + `],"SelectionID":[` + zeros32 + `],"VoteFirstValid":0,"VoteLastValid":0,"VoteKeyDilution":0,"StateProofID":[` + zeros32 + `,` + zeros32 + `],"UpdateRound":0}` require.NoError(t, err) require.Equal(t, expectedEncoding, string(encoding)) } diff --git a/ledger/store/trackerdb/msgp_gen.go b/ledger/store/trackerdb/msgp_gen.go index a13469c0ab..8b0092f4c1 100644 --- a/ledger/store/trackerdb/msgp_gen.go +++ b/ledger/store/trackerdb/msgp_gen.go @@ -100,8 +100,8 @@ import ( func (z *BaseAccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0001Len := uint32(21) - var zb0001Mask uint32 /* 23 bits */ + zb0001Len := uint32(22) + var zb0001Mask uint32 /* 24 bits */ if (*z).BaseVotingData.VoteID.MsgIsZero() { zb0001Len-- zb0001Mask |= 0x1 @@ -182,10 +182,14 @@ func (z *BaseAccountData) MarshalMsg(b []byte) (o []byte) { zb0001Len-- zb0001Mask |= 0x200000 } - if (*z).UpdateRound == 0 { + if (*z).IncentiveEligible == false { zb0001Len-- zb0001Mask |= 0x400000 } + if (*z).UpdateRound == 0 { + zb0001Len-- + zb0001Mask |= 0x800000 + } // variable map header, size zb0001Len o = msgp.AppendMapHeader(o, zb0001Len) if zb0001Len != 0 { @@ -290,6 +294,11 @@ func (z *BaseAccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).TotalBoxBytes) } if (zb0001Mask & 0x400000) == 0 { // if not empty + // string "o" + o = append(o, 0xa1, 0x6f) + o = msgp.AppendBool(o, (*z).IncentiveEligible) + } + if (zb0001Mask & 0x800000) == 0 { // if not empty // string "z" o = append(o, 0xa1, 0x7a) o = msgp.AppendUint64(o, (*z).UpdateRound) @@ -433,6 +442,14 @@ func (z *BaseAccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalSta return } } + if zb0001 > 0 { + zb0001-- + (*z).IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "IncentiveEligible") + return + } + } if zb0001 > 0 { zb0001-- bts, err = (*z).BaseVotingData.VoteID.UnmarshalMsgWithState(bts, st) @@ -596,6 +613,12 @@ func (z *BaseAccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalSta err = msgp.WrapError(err, "TotalBoxBytes") return } + case "o": + (*z).IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) + if err != nil { + err = msgp.WrapError(err, "IncentiveEligible") + return + } case "A": bts, err = (*z).BaseVotingData.VoteID.UnmarshalMsgWithState(bts, st) if err != nil { @@ -661,18 +684,18 @@ func (_ *BaseAccountData) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *BaseAccountData) Msgsize() (s int) { - s = 3 + 2 + (*z).Status.Msgsize() + 2 + (*z).MicroAlgos.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).RewardedMicroAlgos.Msgsize() + 2 + (*z).AuthAddr.Msgsize() + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint32Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + (*z).BaseVotingData.VoteID.Msgsize() + 2 + (*z).BaseVotingData.SelectionID.Msgsize() + 2 + (*z).BaseVotingData.VoteFirstValid.Msgsize() + 2 + (*z).BaseVotingData.VoteLastValid.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).BaseVotingData.StateProofID.Msgsize() + 2 + msgp.Uint64Size + s = 3 + 2 + (*z).Status.Msgsize() + 2 + (*z).MicroAlgos.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).RewardedMicroAlgos.Msgsize() + 2 + (*z).AuthAddr.Msgsize() + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint32Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.BoolSize + 2 + (*z).BaseVotingData.VoteID.Msgsize() + 2 + (*z).BaseVotingData.SelectionID.Msgsize() + 2 + (*z).BaseVotingData.VoteFirstValid.Msgsize() + 2 + (*z).BaseVotingData.VoteLastValid.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).BaseVotingData.StateProofID.Msgsize() + 2 + msgp.Uint64Size return } // MsgIsZero returns whether this is a zero value func (z *BaseAccountData) MsgIsZero() bool { - return ((*z).Status.MsgIsZero()) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) && ((*z).RewardedMicroAlgos.MsgIsZero()) && ((*z).AuthAddr.MsgIsZero()) && ((*z).TotalAppSchemaNumUint == 0) && ((*z).TotalAppSchemaNumByteSlice == 0) && ((*z).TotalExtraAppPages == 0) && ((*z).TotalAssetParams == 0) && ((*z).TotalAssets == 0) && ((*z).TotalAppParams == 0) && ((*z).TotalAppLocalStates == 0) && ((*z).TotalBoxes == 0) && ((*z).TotalBoxBytes == 0) && ((*z).BaseVotingData.VoteID.MsgIsZero()) && ((*z).BaseVotingData.SelectionID.MsgIsZero()) && ((*z).BaseVotingData.VoteFirstValid.MsgIsZero()) && ((*z).BaseVotingData.VoteLastValid.MsgIsZero()) && ((*z).BaseVotingData.VoteKeyDilution == 0) && ((*z).BaseVotingData.StateProofID.MsgIsZero()) && ((*z).UpdateRound == 0) + return ((*z).Status.MsgIsZero()) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) && ((*z).RewardedMicroAlgos.MsgIsZero()) && ((*z).AuthAddr.MsgIsZero()) && ((*z).TotalAppSchemaNumUint == 0) && ((*z).TotalAppSchemaNumByteSlice == 0) && ((*z).TotalExtraAppPages == 0) && ((*z).TotalAssetParams == 0) && ((*z).TotalAssets == 0) && ((*z).TotalAppParams == 0) && ((*z).TotalAppLocalStates == 0) && ((*z).TotalBoxes == 0) && ((*z).TotalBoxBytes == 0) && ((*z).IncentiveEligible == false) && ((*z).BaseVotingData.VoteID.MsgIsZero()) && ((*z).BaseVotingData.SelectionID.MsgIsZero()) && ((*z).BaseVotingData.VoteFirstValid.MsgIsZero()) && ((*z).BaseVotingData.VoteLastValid.MsgIsZero()) && ((*z).BaseVotingData.VoteKeyDilution == 0) && ((*z).BaseVotingData.StateProofID.MsgIsZero()) && ((*z).UpdateRound == 0) } // MaxSize returns a maximum valid message size for this message type func BaseAccountDataMaxSize() (s int) { - s = 3 + 2 + basics.StatusMaxSize() + 2 + basics.MicroAlgosMaxSize() + 2 + msgp.Uint64Size + 2 + basics.MicroAlgosMaxSize() + 2 + basics.AddressMaxSize() + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint32Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + crypto.OneTimeSignatureVerifierMaxSize() + 2 + crypto.VRFVerifierMaxSize() + 2 + basics.RoundMaxSize() + 2 + basics.RoundMaxSize() + 2 + msgp.Uint64Size + 2 + merklesignature.CommitmentMaxSize() + 2 + msgp.Uint64Size + s = 3 + 2 + basics.StatusMaxSize() + 2 + basics.MicroAlgosMaxSize() + 2 + msgp.Uint64Size + 2 + basics.MicroAlgosMaxSize() + 2 + basics.AddressMaxSize() + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint32Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.BoolSize + 2 + crypto.OneTimeSignatureVerifierMaxSize() + 2 + crypto.VRFVerifierMaxSize() + 2 + basics.RoundMaxSize() + 2 + basics.RoundMaxSize() + 2 + msgp.Uint64Size + 2 + merklesignature.CommitmentMaxSize() + 2 + msgp.Uint64Size return } diff --git a/ledger/testing/randomAccounts.go b/ledger/testing/randomAccounts.go index 2d5f79c82a..99a5879301 100644 --- a/ledger/testing/randomAccounts.go +++ b/ledger/testing/randomAccounts.go @@ -78,6 +78,7 @@ func RandomAccountData(rewardsBase uint64) basics.AccountData { data.VoteFirstValid = 0 data.RewardsBase = rewardsBase + data.IncentiveEligible = crypto.RandUint64()%5 == 0 return data } @@ -99,6 +100,10 @@ func RandomAssetParams() basics.AssetParams { Decimals: uint32(crypto.RandUint64() % 20), DefaultFrozen: crypto.RandUint64()%2 == 0, } + // Since 0 and 1 Total assets seem extra interesting, make them more often. + if crypto.RandUint64()%5 != 0 { + ap.Total = crypto.RandUint64() % 2 + } if crypto.RandUint64()%5 != 0 { ap.UnitName = fmt.Sprintf("un%x", uint32(crypto.RandUint64()%0x7fffff)) } diff --git a/ledger/testing/randomAccounts_test.go b/ledger/testing/randomAccounts_test.go index 9f69321aaa..f59f704129 100644 --- a/ledger/testing/randomAccounts_test.go +++ b/ledger/testing/randomAccounts_test.go @@ -87,7 +87,6 @@ func TestAccounts(t *testing.T) { zeroValueExceptions := []reflectionhelpers.TypePath{ reflectionhelpers.TypePath{}.AddField("MicroAlgos").AddField("Raw"), reflectionhelpers.TypePath{}.AddField("AssetParams").AddMapKey(), - reflectionhelpers.TypePath{}.AddField("AssetParams").AddValue().AddField("Total"), reflectionhelpers.TypePath{}.AddField("Assets").AddMapKey(), reflectionhelpers.TypePath{}.AddField("AppLocalStates").AddMapKey(), reflectionhelpers.TypePath{}.AddField("AppLocalStates").AddValue().AddField("KeyValue").AddValue().AddField("Type"), From 1993a9fbaf9eaae84f712d21c641ab0b230d22e8 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 30 Nov 2023 15:18:19 -0500 Subject: [PATCH 005/117] Maintain and use the acct.Incentiveligible flag --- daemon/algod/api/server/v2/account.go | 7 ++ daemon/algod/api/server/v2/account_test.go | 21 ++++++ data/basics/units.go | 18 +++++ data/basics/userBalance.go | 3 + data/txntest/txn.go | 3 + ledger/apply/keyreg.go | 12 ++- ledger/apptxn_test.go | 11 ++- ledger/eval/eval.go | 14 ++-- ledger/eval_simple_test.go | 88 +++++++++++++++++++++- ledger/testing/randomAccounts.go | 6 +- test/scripts/e2e_subs/eligible.py | 51 +++++++++++++ test/scripts/e2e_subs/goal/goal.py | 24 +++--- test/scripts/e2e_subs/mining.py | 3 +- 13 files changed, 232 insertions(+), 29 deletions(-) create mode 100755 test/scripts/e2e_subs/eligible.py diff --git a/daemon/algod/api/server/v2/account.go b/daemon/algod/api/server/v2/account.go index 0d659320bf..732d85133c 100644 --- a/daemon/algod/api/server/v2/account.go +++ b/daemon/algod/api/server/v2/account.go @@ -24,6 +24,7 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" + "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/basics" "golang.org/x/exp/slices" @@ -200,12 +201,16 @@ func AccountToAccountData(a *model.Account) (basics.AccountData, error) { var voteFirstValid basics.Round var voteLastValid basics.Round var voteKeyDilution uint64 + var stateProofID merklesignature.Commitment if a.Participation != nil { copy(voteID[:], a.Participation.VoteParticipationKey) copy(selID[:], a.Participation.SelectionParticipationKey) voteFirstValid = basics.Round(a.Participation.VoteFirstValid) voteLastValid = basics.Round(a.Participation.VoteLastValid) voteKeyDilution = a.Participation.VoteKeyDilution + if a.Participation.StateProofKey != nil { + copy(stateProofID[:], *a.Participation.StateProofKey) + } } var rewardsBase uint64 @@ -351,11 +356,13 @@ func AccountToAccountData(a *model.Account) (basics.AccountData, error) { MicroAlgos: basics.MicroAlgos{Raw: a.Amount}, RewardsBase: rewardsBase, RewardedMicroAlgos: basics.MicroAlgos{Raw: a.Rewards}, + IncentiveEligible: nilToZero(a.IncentiveEligible), VoteID: voteID, SelectionID: selID, VoteFirstValid: voteFirstValid, VoteLastValid: voteLastValid, VoteKeyDilution: voteKeyDilution, + StateProofID: stateProofID, Assets: assets, AppLocalStates: appLocalStates, AppParams: appParams, diff --git a/daemon/algod/api/server/v2/account_test.go b/daemon/algod/api/server/v2/account_test.go index cd3c67499a..29d668f6e2 100644 --- a/daemon/algod/api/server/v2/account_test.go +++ b/daemon/algod/api/server/v2/account_test.go @@ -25,12 +25,15 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/basics" + ledgertesting "github.com/algorand/go-algorand/ledger/testing" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" ) func TestAccount(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() + proto := config.Consensus[protocol.ConsensusFuture] appIdx1 := basics.AppIndex(1) appIdx2 := basics.AppIndex(2) @@ -203,3 +206,21 @@ func TestAccount(t *testing.T) { } }) } + +func TestAccountRandomRoundTrip(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + for _, simple := range []bool{true, false} { + accts := ledgertesting.RandomAccounts(20, simple) + for addr, acct := range accts { + round := basics.Round(2) + proto := config.Consensus[protocol.ConsensusFuture] + conv, err := AccountDataToAccount(addr.String(), &acct, round, &proto, acct.MicroAlgos) + require.NoError(t, err) + c, err := AccountToAccountData(&conv) + require.NoError(t, err) + require.Equal(t, acct, c) + } + } +} diff --git a/data/basics/units.go b/data/basics/units.go index af8743ee40..09bee7b8a8 100644 --- a/data/basics/units.go +++ b/data/basics/units.go @@ -17,6 +17,8 @@ package basics import ( + "math" + "github.com/algorand/go-codec/codec" "github.com/algorand/msgp/msgp" @@ -43,6 +45,11 @@ func (a MicroAlgos) GreaterThan(b MicroAlgos) bool { return a.Raw > b.Raw } +// GTE implements arithmetic comparison for MicroAlgos +func (a MicroAlgos) GTE(b MicroAlgos) bool { + return a.Raw >= b.Raw +} + // IsZero implements arithmetic comparison for MicroAlgos func (a MicroAlgos) IsZero() bool { return a.Raw == 0 @@ -122,6 +129,17 @@ func MicroAlgosMaxSize() (s int) { return msgp.Uint64Size } +// Algos is a convenience function so that whole Algos can be written easily. It +// panics on overflow because it should only be used constants - things that are +// best human-readable in source code - not used on arbitrary values from, say, +// transactions. +func Algos(algos uint64) MicroAlgos { + if algos > math.MaxUint64/1_000_000 { + panic(algos) + } + return MicroAlgos{Raw: algos * 1_000_000} +} + // Round represents a protocol round index type Round uint64 diff --git a/data/basics/userBalance.go b/data/basics/userBalance.go index cea8a9bc2c..48af4348e2 100644 --- a/data/basics/userBalance.go +++ b/data/basics/userBalance.go @@ -209,6 +209,9 @@ type AccountData struct { // This allows key rotation, changing the members in a multisig, etc. AuthAddr Address `codec:"spend"` + // IncentiveEligible indicates whether the account came online with the + // extra fee required to be eligible for block incentives. At proposal time, + // balance limits must also be met to receive incentives. IncentiveEligible bool `codec:"ie"` // AppLocalStates stores the local states associated with any applications diff --git a/data/txntest/txn.go b/data/txntest/txn.go index 515c9df458..26553d6b97 100644 --- a/data/txntest/txn.go +++ b/data/txntest/txn.go @@ -23,6 +23,7 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" + "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/crypto/stateproof" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/stateproofmsg" @@ -55,6 +56,7 @@ type Txn struct { VoteLast basics.Round VoteKeyDilution uint64 Nonparticipation bool + StateProofPK merklesignature.Commitment Receiver basics.Address Amount uint64 @@ -228,6 +230,7 @@ func (tx Txn) Txn() transactions.Transaction { VoteLast: tx.VoteLast, VoteKeyDilution: tx.VoteKeyDilution, Nonparticipation: tx.Nonparticipation, + StateProofPK: tx.StateProofPK, }, PaymentTxnFields: transactions.PaymentTxnFields{ Receiver: tx.Receiver, diff --git a/ledger/apply/keyreg.go b/ledger/apply/keyreg.go index 4fe0f0a326..6a83eab316 100644 --- a/ledger/apply/keyreg.go +++ b/ledger/apply/keyreg.go @@ -31,7 +31,7 @@ var errKeyregGoingOnlineFirstVotingInFuture = errors.New("transaction tries to m // Keyreg applies a KeyRegistration transaction using the Balances interface. func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, balances Balances, spec transactions.SpecialAddresses, ad *transactions.ApplyData, round basics.Round) error { if header.Sender == spec.FeeSink { - return fmt.Errorf("cannot register participation key for fee sink's address %v ", header.Sender) + return fmt.Errorf("cannot register participation key for fee sink's address %v", header.Sender) } // Get the user's balance entry @@ -67,6 +67,7 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal record.VoteFirstValid = 0 record.VoteLastValid = 0 record.VoteKeyDilution = 0 + record.IncentiveEligible = false } else { if params.EnableKeyregCoherencyCheck { if keyreg.VoteLast <= round { @@ -80,6 +81,9 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal record.VoteFirstValid = keyreg.VoteFirst record.VoteLastValid = keyreg.VoteLast record.VoteKeyDilution = keyreg.VoteKeyDilution + if header.Fee.GTE(incentiveFeeForEligibility) && params.EnableMining { + record.IncentiveEligible = true + } } // Write the updated entry @@ -90,3 +94,9 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal return nil } + +// incentiveFeeForEligibility imparts a small cost on moving from offline to +// online. This will impose a cost to running unreliable nodes that get +// suspended and then come back online. Becomes a consensus param if ever +// changed. +var incentiveFeeForEligibility = basics.Algos(2) diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index 8126322875..b21833b08c 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" + "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" @@ -60,11 +61,19 @@ func TestPayAction(t *testing.T) { // We're going to test some mining effects here too, so that we have an inner transaction example. proposer := basics.Address{0x01, 0x02, 0x03} - dl.txn(&txntest.Txn{ + dl.txns(&txntest.Txn{ Type: "pay", Sender: addrs[7], Receiver: proposer, Amount: 1_000_000 * 1_000_000, // 1 million algos is surely an eligible amount + }, &txntest.Txn{ + Type: "keyreg", + Sender: proposer, + Fee: 3_000_000, + VotePK: crypto.OneTimeSignatureVerifier{0x01}, + SelectionPK: crypto.VRFVerifier{0x02}, + StateProofPK: merklesignature.Commitment{0x03}, + VoteFirst: 1, VoteLast: 1000, }) payout1 := txntest.Txn{ diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index bdf351c953..fd4f65fa77 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -804,7 +804,7 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return eval, nil } -const ( +var ( // these would become ConsensusParameters if we ever wanted to change them // incentiveMinBalance is the minimum balance an account must have to be @@ -817,7 +817,7 @@ const ( // that assurance, it is difficult to model their behaviour - might many // participants join for the hope of easy financial rewards, but without // caring enough to run a high-quality node? - incentiveMinBalance = 100_000 * 1_000_000 // 100K algos + incentiveMinBalance = basics.Algos(100_000) // incentiveMaxBalance is the maximum balance an account might have to be // eligible for incentives. It encourages large accounts to split their @@ -825,7 +825,7 @@ const ( // nothing in protocol can prevent such accounts from running nodes that // share fate (same machine, same data center, etc), but this serves as a // gentle reminder. - incentiveMaxBalance = 100_000_000 * 1_000_000 // 100M algos + incentiveMaxBalance = basics.Algos(100_000_000) ) func (eval *BlockEvaluator) eligibleForIncentives(proposer basics.Address) bool { @@ -833,15 +833,13 @@ func (eval *BlockEvaluator) eligibleForIncentives(proposer basics.Address) bool if err != nil { return false } - if proposerState.MicroAlgos.Raw < incentiveMinBalance { + if proposerState.MicroAlgos.LessThan(incentiveMinBalance) { return false } - if proposerState.MicroAlgos.Raw > incentiveMaxBalance { + if proposerState.MicroAlgos.GreaterThan(incentiveMaxBalance) { return false } - // We'll also need a flag on the account, set to true if the account - // properly key-regged for incentives by including the "entry fee". - return true + return proposerState.IncentiveEligible } // hotfix for testnet stall 08/26/2019; move some algos from testnet bank to rewards pool to give it enough time until protocol upgrade occur. diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 9f9e15912f..01bc751f65 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -29,6 +29,7 @@ import ( "github.com/algorand/go-algorand/agreement" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" + "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/transactions" @@ -229,14 +230,31 @@ func TestMiningFees(t *testing.T) { smallest := basics.Address{0x01, 0x033} biggest := basics.Address{0x01, 0x044} - dl.txns(&txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: tooBig, Amount: 100_000_000*1_000_000 + 1}, - &txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: tooSmall, Amount: 100_000*1_000_000 - 1}, - &txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: smallest, Amount: 100_000 * 1_000_000}, - &txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: biggest, Amount: 100_000_000 * 1_000_000}, + const eFee = 3_000_000 + dl.txns( + &txntest.Txn{Type: "pay", Sender: addrs[1], + Receiver: tooBig, Amount: eFee + 100_000_000*1_000_000 + 1}, + &txntest.Txn{Type: "pay", Sender: addrs[1], + Receiver: tooSmall, Amount: eFee + 100_000*1_000_000 - 1}, + &txntest.Txn{Type: "pay", Sender: addrs[1], + Receiver: smallest, Amount: eFee + 100_000*1_000_000}, + &txntest.Txn{Type: "pay", Sender: addrs[1], + Receiver: biggest, Amount: eFee + 100_000_000*1_000_000}, ) for _, proposer := range []basics.Address{tooBig, tooSmall, smallest, biggest} { t.Log(proposer) + + dl.txn(&txntest.Txn{ + Type: "keyreg", + Sender: proposer, + Fee: eFee, + VotePK: crypto.OneTimeSignatureVerifier{0x01}, + SelectionPK: crypto.VRFVerifier{0x02}, + StateProofPK: merklesignature.Commitment{0x03}, + VoteFirst: 1, VoteLast: 1000, + }) + dl.fullBlock() // start with an empty block, so no mining fees are paid at start of next one presink := micros(dl.t, dl.generator, genBalances.FeeSink) @@ -293,6 +311,68 @@ func TestMiningFees(t *testing.T) { }) } +// TestIncentiveEligible checks that keyreg with extra fee turns on the incentive eligible flag +func TestIncentiveEligible(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + genBalances, addrs, _ := ledgertesting.NewTestGenesis() + // Incentive-eligible appears in v39. Start checking in v38 to test that is unchanged. + ledgertesting.TestConsensusRange(t, 38, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { + dl := NewDoubleLedger(t, genBalances, cv, cfg) + defer dl.Close() + + tooSmall := basics.Address{0x01, 0x011} + smallest := basics.Address{0x01, 0x022} + + // They begin ineligible + for _, addr := range []basics.Address{tooSmall, smallest} { + acct, _, _, err := dl.generator.LookupLatest(addr) + require.NoError(t, err) + require.False(t, acct.IncentiveEligible) + } + + // Fund everyone + dl.txns(&txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: tooSmall, Amount: 10_000_000}, + &txntest.Txn{Type: "pay", Sender: addrs[1], Receiver: smallest, Amount: 10_000_000}, + ) + + // Keyreg (but offline) with various fees. No effect on incentive eligible + dl.txns(&txntest.Txn{Type: "keyreg", Sender: tooSmall, Fee: 2_000_000 - 1}, + &txntest.Txn{Type: "keyreg", Sender: smallest, Fee: 2_000_000}, + ) + + for _, addr := range []basics.Address{tooSmall, smallest} { + acct, _, _, err := dl.generator.LookupLatest(addr) + require.NoError(t, err) + require.False(t, acct.IncentiveEligible) + } + + // Keyreg to get online with various fees. Sufficient fee gets `smallest` eligible + keyreg := txntest.Txn{ + Type: "keyreg", + VotePK: crypto.OneTimeSignatureVerifier{0x01}, + SelectionPK: crypto.VRFVerifier{0x02}, + StateProofPK: merklesignature.Commitment{0x03}, + VoteFirst: 1, VoteLast: 1000, + } + tooSmallKR := keyreg + tooSmallKR.Sender = tooSmall + tooSmallKR.Fee = 2_000_000 - 1 + + smallKR := keyreg + smallKR.Sender = smallest + smallKR.Fee = 2_000_000 + dl.txns(&tooSmallKR, &smallKR) + a, _, _, err := dl.generator.LookupLatest(tooSmall) + require.NoError(t, err) + require.False(t, a.IncentiveEligible) + a, _, _, err = dl.generator.LookupLatest(smallest) + require.NoError(t, err) + require.Equal(t, a.IncentiveEligible, ver > 38) + }) +} + // TestHoldingGet tests some of the corner cases for the asset_holding_get // opcode: the asset doesn't exist, the account doesn't exist, account not opted // in, vs it has none of the asset. This is tested here, even though it should diff --git a/ledger/testing/randomAccounts.go b/ledger/testing/randomAccounts.go index 99a5879301..bd079605a3 100644 --- a/ledger/testing/randomAccounts.go +++ b/ledger/testing/randomAccounts.go @@ -68,17 +68,17 @@ func RandomAccountData(rewardsBase uint64) basics.AccountData { switch crypto.RandUint64() % 3 { case 0: data.Status = basics.Online + data.VoteID = crypto.OneTimeSignatureVerifier{0x01} + data.IncentiveEligible = crypto.RandUint64()%5 == 0 + data.VoteFirstValid = 1 data.VoteLastValid = 10000 case 1: data.Status = basics.Offline - data.VoteLastValid = 0 default: data.Status = basics.NotParticipating } - data.VoteFirstValid = 0 data.RewardsBase = rewardsBase - data.IncentiveEligible = crypto.RandUint64()%5 == 0 return data } diff --git a/test/scripts/e2e_subs/eligible.py b/test/scripts/e2e_subs/eligible.py new file mode 100755 index 0000000000..ddac9a3515 --- /dev/null +++ b/test/scripts/e2e_subs/eligible.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +import base64 +import os +import sys +from goal import Goal +import algosdk.encoding as enc + +from datetime import datetime + +stamp = datetime.now().strftime("%Y%m%d_%H%M%S") +print(f"{os.path.basename(sys.argv[0])} start {stamp}") + +goal = Goal(sys.argv[1], autosend=True) + +joe = goal.new_account() + +txinfo, err = goal.pay(goal.account, joe, amt=10_000_000) +assert not err, err + +# Joe is a brand new account, it is not incentive eligible +joe_info = goal.algod.account_info(joe) +assert "incentive-eligible" not in joe_info, joe_info + +# Go online, but without paying enough to be incentive eligible +txinfo, err = goal.keyreg(joe, votekey=base64.b64encode(b'1'*32), + selkey=base64.b64encode(b'1'*32), + sprfkey=base64.b64encode(b'1'*64), + votekd=1, + votefst=1, votelst=2000) +assert not err, err + +# No extra fee paid, so not eligible +joe_info = goal.algod.account_info(joe) +assert "incentive-eligible" not in joe_info, joe_info + +# Pay the extra fee to become eligible +txinfo, err = goal.keyreg(joe, fee=3_000_000, + votekey=base64.b64encode(b'1'*32), + selkey=base64.b64encode(b'1'*32), + sprfkey=base64.b64encode(b'1'*64), + votekd=2, + votefst=1, votelst=2000) +assert not err, err +joe_info = goal.algod.account_info(joe) +assert joe_info.get("incentive-eligible", None) == True, joe_info + + + +stamp = datetime.now().strftime("%Y%m%d_%H%M%S") +print(f"{os.path.basename(sys.argv[0])} OK {stamp}") diff --git a/test/scripts/e2e_subs/goal/goal.py b/test/scripts/e2e_subs/goal/goal.py index a366f1b69e..2a1bb4cd10 100755 --- a/test/scripts/e2e_subs/goal/goal.py +++ b/test/scripts/e2e_subs/goal/goal.py @@ -227,21 +227,21 @@ def finish(self, tx, send): return tx def keyreg(self, sender, votekey=None, selkey=None, votefst=None, - votelst=None, votekd=None, + votelst=None, votekd=None, sprfkey=None, send=None, **kwargs): - params = self.params(kwargs.pop("lifetime", 1000)) + params = self.params(kwargs.pop("lifetime", 1000), kwargs.pop("fee", None)) tx = txn.KeyregTxn(sender, params, - votekey, selkey, votefst, votelst, votekd, + votekey, selkey, votefst, votelst, votekd, sprfkey=sprfkey, **kwargs) return self.finish(tx, send) def pay(self, sender, receiver, amt: int, send=None, **kwargs): - params = self.params(kwargs.pop("lifetime", 1000)) + params = self.params(kwargs.pop("lifetime", 1000), kwargs.pop("fee", None)) tx = txn.PaymentTxn(sender, params, receiver, amt, **kwargs) return self.finish(tx, send) def acfg(self, sender, send=None, **kwargs): - params = self.params(kwargs.pop("lifetime", 1000)) + params = self.params(kwargs.pop("lifetime", 1000), kwargs.pop("fee", None)) tx = txn.AssetConfigTxn( sender, params, **kwargs, strict_empty_address_check=False ) @@ -252,7 +252,7 @@ def asset_create(self, sender, **kwargs): return self.acfg(sender, **kwargs) def axfer(self, sender, receiver, amt: int, index: int, send=None, **kwargs): - params = self.params(kwargs.pop("lifetime", 1000)) + params = self.params(kwargs.pop("lifetime", 1000), kwargs.pop("fee", None)) tx = txn.AssetTransferTxn( sender, params, receiver, amt, index, **kwargs ) @@ -263,7 +263,7 @@ def asset_optin(self, sender, index: int, **kwargs): return self.axfer(sender, sender, 0, index, **kwargs) def afrz(self, sender, index: int, target, frozen, send=None, **kwargs): - params = self.params(kwargs.pop("lifetime", 1000)) + params = self.params(kwargs.pop("lifetime", 1000), kwargs.pop("fee", None)) tx = txn.AssetFreezeTxn(sender, params, index, target, frozen, **kwargs) return self.finish(tx, send) @@ -275,14 +275,18 @@ def coerce_schema(self, values): return txn.StateSchema(num_uints=values[0], num_byte_slices=values[1]) - def params(self, lifetime): + def params(self, lifetime=None, fee=None): params = self.algod.suggested_params() - params.last = params.first + lifetime + if lifetime is not None: + params.last = params.first + lifetime + if fee is not None: + params.flat_fee = True + params.fee = fee return params def appl(self, sender, index: int, on_complete=txn.OnComplete.NoOpOC, send=None, **kwargs): - params = self.params(kwargs.pop("lifetime", 1000)) + params = self.params(kwargs.pop("lifetime", 1000), kwargs.pop("fee", None)) local_schema = self.coerce_schema(kwargs.pop("local_schema", None)) global_schema = self.coerce_schema(kwargs.pop("global_schema", None)) tx = txn.ApplicationCallTxn( diff --git a/test/scripts/e2e_subs/mining.py b/test/scripts/e2e_subs/mining.py index 04f10e4b7f..3777645073 100755 --- a/test/scripts/e2e_subs/mining.py +++ b/test/scripts/e2e_subs/mining.py @@ -39,7 +39,7 @@ # During construction, the app examines an arbitrary round, a little before the latest. -examined = max(goal.params(1).first-5, 1) +examined = max(goal.params().first-5, 1) txinfo, err = goal.app_create(joe, goal.assemble(get_proposer), app_args=[examined], lifetime=50) assert not err, err getter = txinfo['application-index'] @@ -82,4 +82,3 @@ stamp = datetime.now().strftime("%Y%m%d_%H%M%S") print(f"{os.path.basename(sys.argv[0])} OK {stamp}") - From b717df62d6a644627bde0b7649820169560054e2 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 21 Dec 2023 11:32:19 -0500 Subject: [PATCH 006/117] Test assembly of new block fields --- data/transactions/logic/assembler_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 3f609fd7ee..0d2ae828ce 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -1707,6 +1707,10 @@ block BlkSeed global AssetCreateMinBalance global AssetOptInMinBalance global GenesisHash +pushint 1 +block BlkProposer +pushint 1 +block BlkFeesCollected `, AssemblerMaxVersion) for _, names := range [][]string{GlobalFieldNames[:], TxnFieldNames[:], blockFieldNames[:]} { for _, f := range names { From 00de01045746365fda33691e744f21b21da0fd71 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 9 Jan 2024 14:11:33 -0500 Subject: [PATCH 007/117] year --- data/basics/fraction.go | 2 +- data/basics/fraction_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/basics/fraction.go b/data/basics/fraction.go index 271da85265..8a643f2520 100644 --- a/data/basics/fraction.go +++ b/data/basics/fraction.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2023 Algorand, Inc. +// Copyright (C) 2019-2024 Algorand, Inc. // This file is part of go-algorand // // go-algorand is free software: you can redistribute it and/or modify diff --git a/data/basics/fraction_test.go b/data/basics/fraction_test.go index 9fad8dfa19..bc6a639c29 100644 --- a/data/basics/fraction_test.go +++ b/data/basics/fraction_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2023 Algorand, Inc. +// Copyright (C) 2019-2024 Algorand, Inc. // This file is part of go-algorand // // go-algorand is free software: you can redistribute it and/or modify From 1aa9072e37becf3241d60ca4ab6772160e9b2133 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 9 Jan 2024 14:30:46 -0500 Subject: [PATCH 008/117] it's v11 now --- ledger/apptxn_test.go | 4 ++-- ledger/eval_simple_test.go | 17 ++++++++--------- test/scripts/e2e_subs/mining.py | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index b21833b08c..0e193e3eb2 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -89,7 +89,7 @@ func TestPayAction(t *testing.T) { dl.txns(&payout1) vb := dl.endBlock(proposer) // First MiningPct > 0 - if ver >= 39 { + if ver >= 40 { require.True(t, dl.generator.GenesisProto().EnableMining) require.EqualValues(t, proposer, vb.Block().BlockHeader.Proposer) require.EqualValues(t, 2000, vb.Block().BlockHeader.FeesCollected.Raw) @@ -110,7 +110,7 @@ func TestPayAction(t *testing.T) { postsink = micros(dl.t, dl.generator, genBalances.FeeSink) postprop = micros(dl.t, dl.generator, proposer) // First MiningPct > 0 - if ver >= 39 { + if ver >= 40 { require.EqualValues(t, 500, postsink-presink) // based on 75% in config/consensus.go require.EqualValues(t, 1500, postprop-preprop) } else { diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 01bc751f65..ee9a097b06 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -212,16 +212,15 @@ func TestBlockEvaluator(t *testing.T) { require.Equal(t, bal2new.MicroAlgos.Raw, bal2.MicroAlgos.Raw-minFee.Raw) } -// TestMiningFees ensures that the proper portion of tx fees go to the proposer, -// starting in v39. +// TestMiningFees ensures that the proper portion of tx fees go to the proposer func TestMiningFees(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() // Lots of balance checks that would be messed up by rewards genBalances, addrs, _ := ledgertesting.NewTestGenesis(ledgertesting.TurnOffRewards) - // Mining begins in v39. Start checking in v38 to test that is unchanged. - ledgertesting.TestConsensusRange(t, 38, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { + miningBegins := 40 + ledgertesting.TestConsensusRange(t, miningBegins-1, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { dl := NewDoubleLedger(t, genBalances, cv, cfg) defer dl.Close() @@ -271,7 +270,7 @@ func TestMiningFees(t *testing.T) { dl.txns(&pay, pay.Args("again")) vb := dl.endBlock(proposer) - if ver >= 39 { + if ver >= miningBegins { require.True(t, dl.generator.GenesisProto().EnableMining) // version sanity check require.NotZero(t, dl.generator.GenesisProto().MiningPercent) // version sanity check // new fields are in the header @@ -299,7 +298,7 @@ func TestMiningFees(t *testing.T) { postsink = micros(dl.t, dl.generator, genBalances.FeeSink) postprop = micros(dl.t, dl.generator, proposer) - if ver >= 39 && (proposer == smallest || proposer == biggest) { + if ver >= miningBegins && (proposer == smallest || proposer == biggest) { require.EqualValues(t, 500, postsink-presink) // based on 75% in config/consensus.go require.EqualValues(t, 1500, postprop-preprop) } else { @@ -317,8 +316,8 @@ func TestIncentiveEligible(t *testing.T) { t.Parallel() genBalances, addrs, _ := ledgertesting.NewTestGenesis() - // Incentive-eligible appears in v39. Start checking in v38 to test that is unchanged. - ledgertesting.TestConsensusRange(t, 38, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { + miningBegins := 40 + ledgertesting.TestConsensusRange(t, miningBegins-1, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { dl := NewDoubleLedger(t, genBalances, cv, cfg) defer dl.Close() @@ -369,7 +368,7 @@ func TestIncentiveEligible(t *testing.T) { require.False(t, a.IncentiveEligible) a, _, _, err = dl.generator.LookupLatest(smallest) require.NoError(t, err) - require.Equal(t, a.IncentiveEligible, ver > 38) + require.Equal(t, a.IncentiveEligible, ver >= miningBegins) }) } diff --git a/test/scripts/e2e_subs/mining.py b/test/scripts/e2e_subs/mining.py index 3777645073..216ecc93ef 100755 --- a/test/scripts/e2e_subs/mining.py +++ b/test/scripts/e2e_subs/mining.py @@ -23,7 +23,7 @@ assert not err, err get_proposer = """ -#pragma version 10 +#pragma version 11 txn ApplicationArgs 0; btoi block BlkProposer; global ZeroAddress; !=; assert From 854d1499c108d9a33d249d6996b8abef2c65ea50 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 26 Sep 2023 14:07:10 -0400 Subject: [PATCH 009/117] Adds LastProposed fields, and logic for checking absenteeism Make migrate use VoteID.IsEmpty() Improve random accounts, especially for basics.Suspended Validate proposed absents, and suspend them. --- agreement/msgp_gen.go | 1567 ++++++++++------- cmd/goal/accountsList.go | 2 + cmd/incorporate/incorporate.go | 2 + config/consensus.go | 25 +- crypto/onetimesig.go | 5 + crypto/vrf.go | 5 + daemon/algod/api/Makefile | 1 + daemon/algod/api/algod.oas2.json | 8 + daemon/algod/api/algod.oas3.yml | 8 + daemon/algod/api/server/v2/account.go | 16 +- .../api/server/v2/generated/data/routes.go | 257 +-- .../v2/generated/experimental/routes.go | 257 +-- .../api/server/v2/generated/model/types.go | 6 + .../nonparticipating/private/routes.go | 419 ++--- .../nonparticipating/public/routes.go | 411 ++--- .../generated/participating/private/routes.go | 269 +-- .../generated/participating/public/routes.go | 437 ++--- daemon/algod/api/server/v2/handlers.go | 4 +- data/basics/msgp_gen.go | 768 ++++---- data/basics/userBalance.go | 25 +- data/bookkeeping/block.go | 4 + data/bookkeeping/msgp_gen.go | 1101 +++++++----- data/transactions/transaction.go | 8 +- data/txntest/txn.go | 47 +- ledger/acctdeltas.go | 6 +- ledger/acctdeltas_test.go | 2 +- ledger/acctupdates_test.go | 21 +- ledger/apply/keyreg.go | 6 +- ledger/eval/eval.go | 161 +- ledger/eval_simple_test.go | 127 ++ ledger/ledgercore/accountdata.go | 15 + ledger/ledgercore/totals.go | 2 +- ledger/simple_test.go | 3 + ledger/store/trackerdb/data.go | 18 +- ledger/store/trackerdb/data_test.go | 2 +- ledger/store/trackerdb/msgp_gen.go | 58 +- ledger/store/trackerdb/sqlitedriver/schema.go | 5 +- .../trackerdb/sqlitedriver/schema_test.go | 8 +- ledger/testing/randomAccounts.go | 60 +- ledger/testing/testGenesis.go | 20 +- protocol/tags.go | 2 +- test/scripts/e2e_subs/absentee.py | 35 + 42 files changed, 3642 insertions(+), 2561 deletions(-) create mode 100755 test/scripts/e2e_subs/absentee.py diff --git a/agreement/msgp_gen.go b/agreement/msgp_gen.go index 91f877042f..2906e73048 100644 --- a/agreement/msgp_gen.go +++ b/agreement/msgp_gen.go @@ -4493,196 +4493,212 @@ func PlayerMaxSize() (s int) { func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(31) - var zb0004Mask uint64 /* 40 bits */ + zb0005Len := uint32(32) + var zb0005Mask uint64 /* 41 bits */ if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x40 + zb0005Len-- + zb0005Mask |= 0x40 } if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x80 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x100 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x4000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x8000 } if (*z).unauthenticatedProposal.OriginalPeriod == 0 { - zb0004Len-- - zb0004Mask |= 0x10000 + zb0005Len-- + zb0005Mask |= 0x10000 } if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x20000 + } + if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + zb0005Len-- + zb0005Mask |= 0x40000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x80000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x100000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x200000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000 + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x800000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000000 + zb0005Len-- + zb0005Mask |= 0x4000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x10000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x40000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x80000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000000 + zb0005Len-- + zb0005Mask |= 0x200000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000000 + zb0005Len-- + zb0005Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000000 + zb0005Len-- + zb0005Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800000000 + zb0005Len-- + zb0005Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000000000 + zb0005Len-- + zb0005Mask |= 0x2000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x2000000000 + zb0005Len-- + zb0005Mask |= 0x4000000000 } - // variable map header, size zb0004Len - o = msgp.AppendMapHeader(o, zb0004Len) - if zb0004Len != 0 { - if (zb0004Mask & 0x40) == 0 { // if not empty + // variable map header, size zb0005Len + o = msgp.AppendMapHeader(o, zb0005Len) + if zb0005Len != 0 { + if (zb0005Mask & 0x40) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel) } - if (zb0004Mask & 0x80) == 0 { // if not empty + if (zb0005Mask & 0x80) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MarshalMsg(o) } - if (zb0004Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x100) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "oper" o = append(o, 0xa4, 0x6f, 0x70, 0x65, 0x72) o = msgp.AppendUint64(o, uint64((*z).unauthenticatedProposal.OriginalPeriod)) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "oprop" o = append(o, 0xa5, 0x6f, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.OriginalProposer.MarshalMsg(o) } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty + // string "partupdabs" + o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) + if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts))) + } + for zb0004 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) + } + } + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -4694,52 +4710,52 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MarshalMsg(o) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0004Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o) } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { @@ -4759,42 +4775,42 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter) } - if (zb0004Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp) } - if (zb0004Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o) } - if (zb0004Mask & 0x800000000) == 0 { // if not empty + if (zb0005Mask & 0x1000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x1000000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x2000000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -4817,73 +4833,73 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o st.AllowableDepth-- var field []byte _ = field - var zb0004 int - var zb0005 bool - zb0004, zb0005, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0005 int + var zb0006 bool + zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0004, zb0005, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0005, zb0006, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err) return } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Round.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Round") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Branch") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Seed") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NativeSha512_256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Sha256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp, bts, err = msgp.ReadInt64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TimeStamp") return } } - if zb0004 > 0 { - zb0004-- - var zb0006 int - zb0006, err = msgp.ReadBytesBytesHeader(bts) + if zb0005 > 0 { + zb0005-- + var zb0007 int + zb0007, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisID") return } - if zb0006 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0006), uint64(config.MaxGenesisIDLen)) + if zb0007 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0007), uint64(config.MaxGenesisIDLen)) return } (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -4892,173 +4908,173 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisHash") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Proposer") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeesCollected") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeeSink") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsPool") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsLevel") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRate") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsResidue") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRecalculationRound") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "CurrentProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolApprovals") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolVoteBefore") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolSwitchOn") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradePropose") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeDelay") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeApprove") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TxnCounter") return } } - if zb0004 > 0 { - zb0004-- - var zb0007 int - var zb0008 bool - zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0008 int + var zb0009 bool + zb0008, zb0009, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0007 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0007), uint64(protocol.NumStateProofTypes)) + if zb0008 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0008), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0008 { + if zb0009 { (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = nil } else if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { - (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0007) + (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0008) } - for zb0007 > 0 { + for zb0008 > 0 { var zb0001 protocol.StateProofType var zb0002 bookkeeping.StateProofTrackingData - zb0007-- + zb0008-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") @@ -5072,26 +5088,26 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking[zb0001] = zb0002 } } - if zb0004 > 0 { - zb0004-- - var zb0009 int - var zb0010 bool - zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0010 int + var zb0011 bool + zb0010, zb0011, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0009 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0009), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0010 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0010), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0010 { + if zb0011 { (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0009 { - (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0009] + } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0010 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0010] } else { - (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0009) + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0010) } for zb0003 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -5101,44 +5117,73 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o } } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- + var zb0012 int + var zb0013 bool + zb0012, zb0013, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0012 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0013 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) >= zb0012 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts)[:zb0012] + } else { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0012) + } + for zb0004 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts", zb0004) + return + } + } + } + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.Payset.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Payset") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.SeedProof.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "SeedProof") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- { - var zb0011 uint64 - zb0011, bts, err = msgp.ReadUint64Bytes(bts) + var zb0014 uint64 + zb0014, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "OriginalPeriod") return } - (*z).unauthenticatedProposal.OriginalPeriod = period(zb0011) + (*z).unauthenticatedProposal.OriginalPeriod = period(zb0014) } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.OriginalProposer.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "OriginalProposer") return } } - if zb0004 > 0 { - err = msgp.ErrTooManyArrayFields(zb0004) + if zb0005 > 0 { + err = msgp.ErrTooManyArrayFields(zb0005) if err != nil { err = msgp.WrapError(err, "struct-from-array") return @@ -5149,11 +5194,11 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o err = msgp.WrapError(err) return } - if zb0005 { + if zb0006 { (*z) = proposal{} } - for zb0004 > 0 { - zb0004-- + for zb0005 > 0 { + zb0005-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err) @@ -5197,14 +5242,14 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o return } case "gen": - var zb0012 int - zb0012, err = msgp.ReadBytesBytesHeader(bts) + var zb0015 int + zb0015, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "GenesisID") return } - if zb0012 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxGenesisIDLen)) + if zb0015 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0015), uint64(config.MaxGenesisIDLen)) return } (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -5321,27 +5366,27 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o return } case "spt": - var zb0013 int - var zb0014 bool - zb0013, zb0014, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0016 int + var zb0017 bool + zb0016, zb0017, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "StateProofTracking") return } - if zb0013 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0013), uint64(protocol.NumStateProofTypes)) + if zb0016 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0016), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "StateProofTracking") return } - if zb0014 { + if zb0017 { (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = nil } else if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { - (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0013) + (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0016) } - for zb0013 > 0 { + for zb0016 > 0 { var zb0001 protocol.StateProofType var zb0002 bookkeeping.StateProofTrackingData - zb0013-- + zb0016-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "StateProofTracking") @@ -5355,24 +5400,24 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking[zb0001] = zb0002 } case "partupdrmv": - var zb0015 int - var zb0016 bool - zb0015, zb0016, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0018 int + var zb0019 bool + zb0018, zb0019, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0015 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0015), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0018 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0018), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0016 { + if zb0019 { (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0015 { - (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0015] + } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0018 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0018] } else { - (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0015) + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0018) } for zb0003 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -5381,6 +5426,33 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o return } } + case "partupdabs": + var zb0020 int + var zb0021 bool + zb0020, zb0021, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0020 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0021 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) >= zb0020 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts)[:zb0020] + } else { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0020) + } + for zb0004 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts", zb0004) + return + } + } case "txns": bts, err = (*z).unauthenticatedProposal.Block.Payset.UnmarshalMsgWithState(bts, st) if err != nil { @@ -5395,13 +5467,13 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o } case "oper": { - var zb0017 uint64 - zb0017, bts, err = msgp.ReadUint64Bytes(bts) + var zb0022 uint64 + zb0022, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "OriginalPeriod") return } - (*z).unauthenticatedProposal.OriginalPeriod = period(zb0017) + (*z).unauthenticatedProposal.OriginalPeriod = period(zb0022) } case "oprop": bts, err = (*z).unauthenticatedProposal.OriginalProposer.UnmarshalMsgWithState(bts, st) @@ -5444,13 +5516,17 @@ func (z *proposal) Msgsize() (s int) { for zb0003 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { s += (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].Msgsize() } + s += 11 + msgp.ArrayHeaderSize + for zb0004 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + s += (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].Msgsize() + } s += 5 + (*z).unauthenticatedProposal.Block.Payset.Msgsize() + 5 + (*z).unauthenticatedProposal.SeedProof.Msgsize() + 5 + msgp.Uint64Size + 6 + (*z).unauthenticatedProposal.OriginalProposer.Msgsize() return } // MsgIsZero returns whether this is a zero value func (z *proposal) MsgIsZero() bool { - return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) + return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type @@ -5464,6 +5540,9 @@ func ProposalMaxSize() (s int) { s += 11 // Calculating size of slice: z.unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) + s += 11 + // Calculating size of slice: z.unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts + s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.unauthenticatedProposal.Block.Payset s += config.MaxTxnBytesPerBlock @@ -8902,200 +8981,216 @@ func ThresholdEventMaxSize() (s int) { func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(32) - var zb0004Mask uint64 /* 40 bits */ + zb0005Len := uint32(33) + var zb0005Mask uint64 /* 41 bits */ if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x80 } if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x100 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x4000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x8000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x10000 + zb0005Len-- + zb0005Mask |= 0x10000 } if (*z).unauthenticatedProposal.OriginalPeriod == 0 { - zb0004Len-- - zb0004Mask |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x20000 } if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x40000 + } + if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + zb0005Len-- + zb0005Mask |= 0x80000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x100000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x200000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000 + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x800000 } if (*z).PriorVote.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800000 + zb0005Len-- + zb0005Mask |= 0x1000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x10000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x80000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x100000000 + zb0005Len-- + zb0005Mask |= 0x200000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x200000000 + zb0005Len-- + zb0005Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000000 + zb0005Len-- + zb0005Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800000000 + zb0005Len-- + zb0005Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000000000 + zb0005Len-- + zb0005Mask |= 0x2000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000000000 + zb0005Len-- + zb0005Mask |= 0x4000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000000000 + zb0005Len-- + zb0005Mask |= 0x8000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x8000000000 + zb0005Len-- + zb0005Mask |= 0x10000000000 } - // variable map header, size zb0004Len - o = msgp.AppendMapHeader(o, zb0004Len) - if zb0004Len != 0 { - if (zb0004Mask & 0x80) == 0 { // if not empty + // variable map header, size zb0005Len + o = msgp.AppendMapHeader(o, zb0005Len) + if zb0005Len != 0 { + if (zb0005Mask & 0x80) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel) } - if (zb0004Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x100) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MarshalMsg(o) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "oper" o = append(o, 0xa4, 0x6f, 0x70, 0x65, 0x72) o = msgp.AppendUint64(o, uint64((*z).unauthenticatedProposal.OriginalPeriod)) } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty // string "oprop" o = append(o, 0xa5, 0x6f, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.OriginalProposer.MarshalMsg(o) } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x80000) == 0 { // if not empty + // string "partupdabs" + o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) + if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts))) + } + for zb0004 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) + } + } + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -9107,57 +9202,57 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0004Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MarshalMsg(o) } - if (zb0004Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "pv" o = append(o, 0xa2, 0x70, 0x76) o = (*z).PriorVote.MarshalMsg(o) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o) } - if (zb0004Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0004Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { @@ -9177,42 +9272,42 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter) } - if (zb0004Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp) } - if (zb0004Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x800000000) == 0 { // if not empty + if (zb0005Mask & 0x1000000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x1000000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o) } - if (zb0004Mask & 0x2000000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x4000000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x8000000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -9235,73 +9330,73 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal st.AllowableDepth-- var field []byte _ = field - var zb0004 int - var zb0005 bool - zb0004, zb0005, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0005 int + var zb0006 bool + zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0004, zb0005, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0005, zb0006, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err) return } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Round.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Round") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Branch") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Seed") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NativeSha512_256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Sha256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp, bts, err = msgp.ReadInt64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TimeStamp") return } } - if zb0004 > 0 { - zb0004-- - var zb0006 int - zb0006, err = msgp.ReadBytesBytesHeader(bts) + if zb0005 > 0 { + zb0005-- + var zb0007 int + zb0007, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisID") return } - if zb0006 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0006), uint64(config.MaxGenesisIDLen)) + if zb0007 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0007), uint64(config.MaxGenesisIDLen)) return } (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -9310,173 +9405,173 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisHash") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Proposer") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeesCollected") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeeSink") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsPool") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsLevel") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRate") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsResidue") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRecalculationRound") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "CurrentProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolApprovals") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolVoteBefore") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolSwitchOn") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradePropose") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeDelay") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeApprove") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TxnCounter") return } } - if zb0004 > 0 { - zb0004-- - var zb0007 int - var zb0008 bool - zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0008 int + var zb0009 bool + zb0008, zb0009, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0007 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0007), uint64(protocol.NumStateProofTypes)) + if zb0008 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0008), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0008 { + if zb0009 { (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = nil } else if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { - (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0007) + (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0008) } - for zb0007 > 0 { + for zb0008 > 0 { var zb0001 protocol.StateProofType var zb0002 bookkeeping.StateProofTrackingData - zb0007-- + zb0008-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") @@ -9490,26 +9585,26 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking[zb0001] = zb0002 } } - if zb0004 > 0 { - zb0004-- - var zb0009 int - var zb0010 bool - zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0010 int + var zb0011 bool + zb0010, zb0011, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0009 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0009), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0010 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0010), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0010 { + if zb0011 { (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0009 { - (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0009] + } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0010 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0010] } else { - (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0009) + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0010) } for zb0003 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -9519,52 +9614,81 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal } } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- + var zb0012 int + var zb0013 bool + zb0012, zb0013, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0012 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0013 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) >= zb0012 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts)[:zb0012] + } else { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0012) + } + for zb0004 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts", zb0004) + return + } + } + } + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.Block.Payset.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Payset") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.SeedProof.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "SeedProof") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- { - var zb0011 uint64 - zb0011, bts, err = msgp.ReadUint64Bytes(bts) + var zb0014 uint64 + zb0014, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "OriginalPeriod") return } - (*z).unauthenticatedProposal.OriginalPeriod = period(zb0011) + (*z).unauthenticatedProposal.OriginalPeriod = period(zb0014) } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).unauthenticatedProposal.OriginalProposer.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "OriginalProposer") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).PriorVote.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "PriorVote") return } } - if zb0004 > 0 { - err = msgp.ErrTooManyArrayFields(zb0004) + if zb0005 > 0 { + err = msgp.ErrTooManyArrayFields(zb0005) if err != nil { err = msgp.WrapError(err, "struct-from-array") return @@ -9575,11 +9699,11 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal err = msgp.WrapError(err) return } - if zb0005 { + if zb0006 { (*z) = transmittedPayload{} } - for zb0004 > 0 { - zb0004-- + for zb0005 > 0 { + zb0005-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err) @@ -9623,14 +9747,14 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal return } case "gen": - var zb0012 int - zb0012, err = msgp.ReadBytesBytesHeader(bts) + var zb0015 int + zb0015, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "GenesisID") return } - if zb0012 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxGenesisIDLen)) + if zb0015 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0015), uint64(config.MaxGenesisIDLen)) return } (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -9747,27 +9871,27 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal return } case "spt": - var zb0013 int - var zb0014 bool - zb0013, zb0014, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0016 int + var zb0017 bool + zb0016, zb0017, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "StateProofTracking") return } - if zb0013 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0013), uint64(protocol.NumStateProofTypes)) + if zb0016 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0016), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "StateProofTracking") return } - if zb0014 { + if zb0017 { (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = nil } else if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { - (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0013) + (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0016) } - for zb0013 > 0 { + for zb0016 > 0 { var zb0001 protocol.StateProofType var zb0002 bookkeeping.StateProofTrackingData - zb0013-- + zb0016-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "StateProofTracking") @@ -9781,24 +9905,24 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking[zb0001] = zb0002 } case "partupdrmv": - var zb0015 int - var zb0016 bool - zb0015, zb0016, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0018 int + var zb0019 bool + zb0018, zb0019, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0015 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0015), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0018 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0018), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0016 { + if zb0019 { (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0015 { - (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0015] + } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0018 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0018] } else { - (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0015) + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0018) } for zb0003 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -9807,6 +9931,33 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal return } } + case "partupdabs": + var zb0020 int + var zb0021 bool + zb0020, zb0021, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0020 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0021 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) >= zb0020 { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = ((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts)[:zb0020] + } else { + (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0020) + } + for zb0004 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts", zb0004) + return + } + } case "txns": bts, err = (*z).unauthenticatedProposal.Block.Payset.UnmarshalMsgWithState(bts, st) if err != nil { @@ -9821,13 +9972,13 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal } case "oper": { - var zb0017 uint64 - zb0017, bts, err = msgp.ReadUint64Bytes(bts) + var zb0022 uint64 + zb0022, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "OriginalPeriod") return } - (*z).unauthenticatedProposal.OriginalPeriod = period(zb0017) + (*z).unauthenticatedProposal.OriginalPeriod = period(zb0022) } case "oprop": bts, err = (*z).unauthenticatedProposal.OriginalProposer.UnmarshalMsgWithState(bts, st) @@ -9876,13 +10027,17 @@ func (z *transmittedPayload) Msgsize() (s int) { for zb0003 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { s += (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].Msgsize() } + s += 11 + msgp.ArrayHeaderSize + for zb0004 := range (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + s += (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].Msgsize() + } s += 5 + (*z).unauthenticatedProposal.Block.Payset.Msgsize() + 5 + (*z).unauthenticatedProposal.SeedProof.Msgsize() + 5 + msgp.Uint64Size + 6 + (*z).unauthenticatedProposal.OriginalProposer.Msgsize() + 3 + (*z).PriorVote.Msgsize() return } // MsgIsZero returns whether this is a zero value func (z *transmittedPayload) MsgIsZero() bool { - return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) && ((*z).PriorVote.MsgIsZero()) + return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) && ((*z).PriorVote.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type @@ -9896,6 +10051,9 @@ func TransmittedPayloadMaxSize() (s int) { s += 11 // Calculating size of slice: z.unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) + s += 11 + // Calculating size of slice: z.unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts + s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.unauthenticatedProposal.Block.Payset s += config.MaxTxnBytesPerBlock @@ -10608,196 +10766,212 @@ func UnauthenticatedEquivocationVoteMaxSize() (s int) { func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(31) - var zb0004Mask uint64 /* 38 bits */ + zb0005Len := uint32(32) + var zb0005Mask uint64 /* 39 bits */ if (*z).Block.BlockHeader.RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x40 + zb0005Len-- + zb0005Mask |= 0x40 } if (*z).Block.BlockHeader.FeesCollected.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x80 } if (*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x100 } if (*z).Block.BlockHeader.RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).Block.BlockHeader.GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).Block.BlockHeader.GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x4000 } if (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x8000 } if (*z).OriginalPeriod == 0 { - zb0004Len-- - zb0004Mask |= 0x10000 + zb0005Len-- + zb0005Mask |= 0x10000 } if (*z).OriginalProposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x20000 + } + if len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + zb0005Len-- + zb0005Mask |= 0x40000 } if len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x80000 } if (*z).Block.BlockHeader.Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x100000 } if (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x200000 } if (*z).Block.BlockHeader.Proposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000 + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).Block.BlockHeader.RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x800000 } if (*z).Block.BlockHeader.Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000000 + zb0005Len-- + zb0005Mask |= 0x4000000 } if (*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).SeedProof.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x10000000 } if (*z).Block.BlockHeader.Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if len((*z).Block.BlockHeader.StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).Block.BlockHeader.TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x40000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if (*z).Block.BlockHeader.TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x80000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } if (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000000 + zb0005Len-- + zb0005Mask |= 0x200000000 } if (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000000 + zb0005Len-- + zb0005Mask |= 0x400000000 } if (*z).Block.Payset.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000000 + zb0005Len-- + zb0005Mask |= 0x800000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800000000 + zb0005Len-- + zb0005Mask |= 0x1000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000000000 + zb0005Len-- + zb0005Mask |= 0x2000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x2000000000 + zb0005Len-- + zb0005Mask |= 0x4000000000 } - // variable map header, size zb0004Len - o = msgp.AppendMapHeader(o, zb0004Len) - if zb0004Len != 0 { - if (zb0004Mask & 0x40) == 0 { // if not empty + // variable map header, size zb0005Len + o = msgp.AppendMapHeader(o, zb0005Len) + if zb0005Len != 0 { + if (zb0005Mask & 0x40) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsLevel) } - if (zb0004Mask & 0x80) == 0 { // if not empty + if (zb0005Mask & 0x80) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).Block.BlockHeader.FeesCollected.MarshalMsg(o) } - if (zb0004Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x100) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).Block.BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsResidue) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).Block.BlockHeader.GenesisID) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).Block.BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).Block.BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "oper" o = append(o, 0xa4, 0x6f, 0x70, 0x65, 0x72) o = msgp.AppendUint64(o, uint64((*z).OriginalPeriod)) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "oprop" o = append(o, 0xa5, 0x6f, 0x70, 0x72, 0x6f, 0x70) o = (*z).OriginalProposer.MarshalMsg(o) } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty + // string "partupdabs" + o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) + if (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts))) + } + for zb0004 := range (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + o = (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) + } + } + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -10809,52 +10983,52 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).Block.BlockHeader.Proposer.MarshalMsg(o) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0004Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).SeedProof.MarshalMsg(o) } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).Block.BlockHeader.StateProofTracking == nil { @@ -10874,42 +11048,42 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.TxnCounter) } - if (zb0004Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).Block.BlockHeader.TimeStamp) } - if (zb0004Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).Block.Payset.MarshalMsg(o) } - if (zb0004Mask & 0x800000000) == 0 { // if not empty + if (zb0005Mask & 0x1000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x1000000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x2000000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -10932,73 +11106,73 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma st.AllowableDepth-- var field []byte _ = field - var zb0004 int - var zb0005 bool - zb0004, zb0005, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0005 int + var zb0006 bool + zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0004, zb0005, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0005, zb0006, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err) return } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.Round.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Round") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.Branch.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Branch") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.Seed.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Seed") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NativeSha512_256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Sha256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).Block.BlockHeader.TimeStamp, bts, err = msgp.ReadInt64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TimeStamp") return } } - if zb0004 > 0 { - zb0004-- - var zb0006 int - zb0006, err = msgp.ReadBytesBytesHeader(bts) + if zb0005 > 0 { + zb0005-- + var zb0007 int + zb0007, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisID") return } - if zb0006 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0006), uint64(config.MaxGenesisIDLen)) + if zb0007 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0007), uint64(config.MaxGenesisIDLen)) return } (*z).Block.BlockHeader.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -11007,173 +11181,173 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.GenesisHash.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisHash") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Proposer") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeesCollected") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeeSink") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.RewardsState.RewardsPool.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsPool") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).Block.BlockHeader.RewardsState.RewardsLevel, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsLevel") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).Block.BlockHeader.RewardsState.RewardsRate, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRate") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).Block.BlockHeader.RewardsState.RewardsResidue, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsResidue") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRecalculationRound") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "CurrentProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.UpgradeState.NextProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolApprovals") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolVoteBefore") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolSwitchOn") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradePropose") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeDelay") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeApprove") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).Block.BlockHeader.TxnCounter, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TxnCounter") return } } - if zb0004 > 0 { - zb0004-- - var zb0007 int - var zb0008 bool - zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0008 int + var zb0009 bool + zb0008, zb0009, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0007 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0007), uint64(protocol.NumStateProofTypes)) + if zb0008 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0008), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0008 { + if zb0009 { (*z).Block.BlockHeader.StateProofTracking = nil } else if (*z).Block.BlockHeader.StateProofTracking == nil { - (*z).Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0007) + (*z).Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0008) } - for zb0007 > 0 { + for zb0008 > 0 { var zb0001 protocol.StateProofType var zb0002 bookkeeping.StateProofTrackingData - zb0007-- + zb0008-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") @@ -11187,26 +11361,26 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma (*z).Block.BlockHeader.StateProofTracking[zb0001] = zb0002 } } - if zb0004 > 0 { - zb0004-- - var zb0009 int - var zb0010 bool - zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0010 int + var zb0011 bool + zb0010, zb0011, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0009 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0009), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0010 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0010), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0010 { + if zb0011 { (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0009 { - (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0009] + } else if (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0010 { + (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0010] } else { - (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0009) + (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0010) } for zb0003 := range (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -11216,44 +11390,73 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma } } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- + var zb0012 int + var zb0013 bool + zb0012, zb0013, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0012 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0013 { + (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) >= zb0012 { + (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = ((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts)[:zb0012] + } else { + (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0012) + } + for zb0004 := range (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts", zb0004) + return + } + } + } + if zb0005 > 0 { + zb0005-- bts, err = (*z).Block.Payset.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Payset") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).SeedProof.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "SeedProof") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- { - var zb0011 uint64 - zb0011, bts, err = msgp.ReadUint64Bytes(bts) + var zb0014 uint64 + zb0014, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "OriginalPeriod") return } - (*z).OriginalPeriod = period(zb0011) + (*z).OriginalPeriod = period(zb0014) } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).OriginalProposer.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "OriginalProposer") return } } - if zb0004 > 0 { - err = msgp.ErrTooManyArrayFields(zb0004) + if zb0005 > 0 { + err = msgp.ErrTooManyArrayFields(zb0005) if err != nil { err = msgp.WrapError(err, "struct-from-array") return @@ -11264,11 +11467,11 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma err = msgp.WrapError(err) return } - if zb0005 { + if zb0006 { (*z) = unauthenticatedProposal{} } - for zb0004 > 0 { - zb0004-- + for zb0005 > 0 { + zb0005-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err) @@ -11312,14 +11515,14 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma return } case "gen": - var zb0012 int - zb0012, err = msgp.ReadBytesBytesHeader(bts) + var zb0015 int + zb0015, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "GenesisID") return } - if zb0012 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxGenesisIDLen)) + if zb0015 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0015), uint64(config.MaxGenesisIDLen)) return } (*z).Block.BlockHeader.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -11436,27 +11639,27 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma return } case "spt": - var zb0013 int - var zb0014 bool - zb0013, zb0014, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0016 int + var zb0017 bool + zb0016, zb0017, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "StateProofTracking") return } - if zb0013 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0013), uint64(protocol.NumStateProofTypes)) + if zb0016 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0016), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "StateProofTracking") return } - if zb0014 { + if zb0017 { (*z).Block.BlockHeader.StateProofTracking = nil } else if (*z).Block.BlockHeader.StateProofTracking == nil { - (*z).Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0013) + (*z).Block.BlockHeader.StateProofTracking = make(map[protocol.StateProofType]bookkeeping.StateProofTrackingData, zb0016) } - for zb0013 > 0 { + for zb0016 > 0 { var zb0001 protocol.StateProofType var zb0002 bookkeeping.StateProofTrackingData - zb0013-- + zb0016-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "StateProofTracking") @@ -11470,24 +11673,24 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma (*z).Block.BlockHeader.StateProofTracking[zb0001] = zb0002 } case "partupdrmv": - var zb0015 int - var zb0016 bool - zb0015, zb0016, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0018 int + var zb0019 bool + zb0018, zb0019, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0015 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0015), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0018 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0018), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0016 { + if zb0019 { (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0015 { - (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0015] + } else if (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0018 { + (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0018] } else { - (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0015) + (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0018) } for zb0003 := range (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -11496,6 +11699,33 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma return } } + case "partupdabs": + var zb0020 int + var zb0021 bool + zb0020, zb0021, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0020 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0021 { + (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) >= zb0020 { + (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = ((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts)[:zb0020] + } else { + (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0020) + } + for zb0004 := range (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts", zb0004) + return + } + } case "txns": bts, err = (*z).Block.Payset.UnmarshalMsgWithState(bts, st) if err != nil { @@ -11510,13 +11740,13 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma } case "oper": { - var zb0017 uint64 - zb0017, bts, err = msgp.ReadUint64Bytes(bts) + var zb0022 uint64 + zb0022, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "OriginalPeriod") return } - (*z).OriginalPeriod = period(zb0017) + (*z).OriginalPeriod = period(zb0022) } case "oprop": bts, err = (*z).OriginalProposer.UnmarshalMsgWithState(bts, st) @@ -11559,13 +11789,17 @@ func (z *unauthenticatedProposal) Msgsize() (s int) { for zb0003 := range (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { s += (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].Msgsize() } + s += 11 + msgp.ArrayHeaderSize + for zb0004 := range (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + s += (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].Msgsize() + } s += 5 + (*z).Block.Payset.Msgsize() + 5 + (*z).SeedProof.Msgsize() + 5 + msgp.Uint64Size + 6 + (*z).OriginalProposer.Msgsize() return } // MsgIsZero returns whether this is a zero value func (z *unauthenticatedProposal) MsgIsZero() bool { - return ((*z).Block.BlockHeader.Round.MsgIsZero()) && ((*z).Block.BlockHeader.Branch.MsgIsZero()) && ((*z).Block.BlockHeader.Seed.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TimeStamp == 0) && ((*z).Block.BlockHeader.GenesisID == "") && ((*z).Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).Block.BlockHeader.TxnCounter == 0) && (len((*z).Block.BlockHeader.StateProofTracking) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).Block.Payset.MsgIsZero()) && ((*z).SeedProof.MsgIsZero()) && ((*z).OriginalPeriod == 0) && ((*z).OriginalProposer.MsgIsZero()) + return ((*z).Block.BlockHeader.Round.MsgIsZero()) && ((*z).Block.BlockHeader.Branch.MsgIsZero()) && ((*z).Block.BlockHeader.Seed.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TimeStamp == 0) && ((*z).Block.BlockHeader.GenesisID == "") && ((*z).Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).Block.BlockHeader.TxnCounter == 0) && (len((*z).Block.BlockHeader.StateProofTracking) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Block.Payset.MsgIsZero()) && ((*z).SeedProof.MsgIsZero()) && ((*z).OriginalPeriod == 0) && ((*z).OriginalProposer.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type @@ -11579,6 +11813,9 @@ func UnauthenticatedProposalMaxSize() (s int) { s += 11 // Calculating size of slice: z.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) + s += 11 + // Calculating size of slice: z.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts + s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.Block.Payset s += config.MaxTxnBytesPerBlock diff --git a/cmd/goal/accountsList.go b/cmd/goal/accountsList.go index 5e4a2e6fa8..7a98920393 100644 --- a/cmd/goal/accountsList.go +++ b/cmd/goal/accountsList.go @@ -216,6 +216,8 @@ func (accountList *AccountsList) outputAccount(addr string, acctInfo model.Accou status = "offline" case basics.NotParticipating.String(): status = "excluded" + case basics.Suspended.String(): + status = "suspended" default: panic(fmt.Sprintf("unexpected account status: %v", acctInfo.Status)) } diff --git a/cmd/incorporate/incorporate.go b/cmd/incorporate/incorporate.go index b9f7dd55b1..ed885915e1 100644 --- a/cmd/incorporate/incorporate.go +++ b/cmd/incorporate/incorporate.go @@ -235,6 +235,8 @@ func parseRecord(cols []string) (rec record) { rec.Status = basics.Offline case "NotParticipating": rec.Status = basics.NotParticipating + case "Suspended": + rec.Status = basics.Suspended default: log.Fatalf("unknown status: %s", cols[3]) } diff --git a/config/consensus.go b/config/consensus.go index 81c6afc84a..191196b3af 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -447,10 +447,14 @@ type ConsensusParams struct { EnableExtraPagesOnAppUpdate bool - // MaxProposedExpiredOnlineAccounts is the maximum number of online accounts, which need - // to be taken offline, that would be proposed to be taken offline. + // MaxProposedExpiredOnlineAccounts is the maximum number of online accounts + // that a proposer can take offline for having expired voting keys. MaxProposedExpiredOnlineAccounts int + // MaxProposedAbsentOnlineAccounts is the maximum number of online accounts, + // that a proposer can suspend for not proposing "lately" (TBD) + MaxProposedAbsentOnlineAccounts int + // EnableAccountDataResourceSeparation enables the support for extended application and asset storage // in a separate table. EnableAccountDataResourceSeparation bool @@ -538,6 +542,12 @@ type ConsensusParams struct { MiningPercent uint64 } +// EnableAbsenteeTracking returns true if the suspension mechanism of absentee +// accounts is enabled. +func (cp ConsensusParams) EnableAbsenteeTracking() bool { + return cp.MaxProposedAbsentOnlineAccounts > 0 +} + // PaysetCommitType enumerates possible ways for the block header to commit to // the set of transactions in the block. type PaysetCommitType int @@ -610,10 +620,14 @@ var MaxExtraAppProgramLen int // supported supported by any of the consensus protocols. used for decoding purposes. var MaxAvailableAppProgramLen int -// MaxProposedExpiredOnlineAccounts is the maximum number of online accounts, which need -// to be taken offline, that would be proposed to be taken offline. +// MaxProposedExpiredOnlineAccounts is the maximum number of online accounts +// that a proposer can take offline for having expired voting keys. var MaxProposedExpiredOnlineAccounts int +// MaxProposedAbsentOnlineAccounts is the maximum number of online accounts, +// that a proposer can suspend for not proposing "lately" (TBD) +var MaxProposedAbsentOnlineAccounts int + // MaxAppTotalArgLen is the maximum number of bytes across all arguments of an application // max sum([len(arg) for arg in txn.ApplicationArgs]) var MaxAppTotalArgLen int @@ -687,6 +701,7 @@ func checkSetAllocBounds(p ConsensusParams) { checkSetMax(p.MaxAppProgramLen, &MaxLogCalls) checkSetMax(p.MaxInnerTransactions*p.MaxTxGroupSize, &MaxInnerTransactionsPerDelta) checkSetMax(p.MaxProposedExpiredOnlineAccounts, &MaxProposedExpiredOnlineAccounts) + checkSetMax(p.MaxProposedAbsentOnlineAccounts, &MaxProposedAbsentOnlineAccounts) // These bounds are exported to make them available to the msgp generator for calculating // maximum valid message size for each message going across the wire. @@ -1422,6 +1437,8 @@ func initConsensusProtocols() { vFuture.EnableMining = true vFuture.MiningPercent = 75 + vFuture.MaxProposedAbsentOnlineAccounts = 32 + Consensus[protocol.ConsensusFuture] = vFuture // vAlphaX versions are an separate series of consensus parameters and versions for alphanet diff --git a/crypto/onetimesig.go b/crypto/onetimesig.go index d9c94da866..ca72204f63 100644 --- a/crypto/onetimesig.go +++ b/crypto/onetimesig.go @@ -304,6 +304,11 @@ func (s *OneTimeSignatureSecrets) Sign(id OneTimeSignatureIdentifier, message Ha return OneTimeSignature{} } +// IsEmpty returns true is the verifier is empty/zero'd. +func (v OneTimeSignatureVerifier) IsEmpty() bool { + return v == OneTimeSignatureVerifier{} +} + // Verify verifies that some Hashable signature was signed under some // OneTimeSignatureVerifier and some OneTimeSignatureIdentifier. // diff --git a/crypto/vrf.go b/crypto/vrf.go index 3c74225893..0c519298a5 100644 --- a/crypto/vrf.go +++ b/crypto/vrf.go @@ -134,6 +134,11 @@ func (pk VrfPubkey) verifyBytes(proof VrfProof, msg []byte) (bool, VrfOutput) { return ret == 0, out } +// IsEmpty returns true is the key is empty/zero'd. +func (pk VrfPubkey) IsEmpty() bool { + return pk == VrfPubkey{} +} + // Verify checks a VRF proof of a given Hashable. If the proof is valid the pseudorandom VrfOutput will be returned. // For a given public key and message, there are potentially multiple valid proofs. // However, given a public key and message, all valid proofs will yield the same output. diff --git a/daemon/algod/api/Makefile b/daemon/algod/api/Makefile index c4104d9e80..cb280c815e 100644 --- a/daemon/algod/api/Makefile +++ b/daemon/algod/api/Makefile @@ -30,6 +30,7 @@ server/v2/generated/model/types.go: algod.oas3.yml $(GOPATH1)/bin/oapi-codegen -config ./server/v2/generated/model/model_types.yml algod.oas3.yml algod.oas3.yml: algod.oas2.json + jq < algod.oas2.json > /dev/null # fail with a nice explantion if json is malformed curl -s -X POST "https://converter.swagger.io/api/convert" -H "accept: application/json" -H "Content-Type: application/json" -d @./algod.oas2.json -o .3tmp.json python3 jsoncanon.py < .3tmp.json > algod.oas3.yml rm -f .3tmp.json diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index 23f835e908..b88daf40f1 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -3020,6 +3020,14 @@ "description": "\\[spend\\] the address against which signing should be checked. If empty, the address of the current account is used. This field can be updated in any transaction by setting the RekeyTo field.", "type": "string", "x-algorand-format": "Address" + }, + "last-proposed": { + "description": "The round in which this account last proposed the block.", + "type": "integer" + }, + "last-heartbeat": { + "description": "The round in which this account last went online, or explicitly renewed their online status.", + "type": "integer" } } }, diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index 96e4a670d9..96be615f79 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -1071,6 +1071,14 @@ "description": "Whether or not the account can receive block incentives if its balance is in range at proposal time.", "type": "boolean" }, + "last-heartbeat": { + "description": "The round in which this account last went online, or explicitly renewed their online status.", + "type": "integer" + }, + "last-proposed": { + "description": "The round in which this account last proposed the block.", + "type": "integer" + }, "min-balance": { "description": "MicroAlgo balance required by the account.\n\nThe requirement grows based on asset and application usage.", "type": "integer" diff --git a/daemon/algod/api/server/v2/account.go b/daemon/algod/api/server/v2/account.go index 732d85133c..10ec183919 100644 --- a/daemon/algod/api/server/v2/account.go +++ b/daemon/algod/api/server/v2/account.go @@ -68,7 +68,7 @@ func AccountDataToAccount( }) var apiParticipation *model.AccountParticipation - if record.VoteID != (crypto.OneTimeSignatureVerifier{}) { + if !record.VoteID.IsEmpty() { apiParticipation = &model.AccountParticipation{ VoteParticipationKey: record.VoteID[:], SelectionParticipationKey: record.SelectionID[:], @@ -139,6 +139,8 @@ func AccountDataToAccount( TotalBoxes: omitEmpty(record.TotalBoxes), TotalBoxBytes: omitEmpty(record.TotalBoxBytes), MinBalance: minBalance.Raw, + LastProposed: omitEmpty(uint64(record.LastProposed)), + LastHeartbeat: omitEmpty(uint64(record.LastHeartbeat)), }, nil } @@ -346,6 +348,16 @@ func AccountToAccountData(a *model.Account) (basics.AccountData, error) { totalBoxBytes = *a.TotalBoxBytes } + var lastProposed uint64 + if a.LastProposed != nil { + lastProposed = *a.LastProposed + } + + var lastHeartbeat uint64 + if a.LastHeartbeat != nil { + lastHeartbeat = *a.LastHeartbeat + } + status, err := basics.UnmarshalStatus(a.Status) if err != nil { return basics.AccountData{}, err @@ -370,6 +382,8 @@ func AccountToAccountData(a *model.Account) (basics.AccountData, error) { TotalExtraAppPages: totalExtraPages, TotalBoxes: totalBoxes, TotalBoxBytes: totalBoxBytes, + LastProposed: basics.Round(lastProposed), + LastHeartbeat: basics.Round(lastHeartbeat), } if a.AuthAddr != nil { diff --git a/daemon/algod/api/server/v2/generated/data/routes.go b/daemon/algod/api/server/v2/generated/data/routes.go index 0507c856e2..caabafbfd7 100644 --- a/daemon/algod/api/server/v2/generated/data/routes.go +++ b/daemon/algod/api/server/v2/generated/data/routes.go @@ -193,134 +193,135 @@ var swaggerSpec = []string{ "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", - "r0Zkc775AAA=", + "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", + "AAAA////ElqSu/oAAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/experimental/routes.go b/daemon/algod/api/server/v2/generated/experimental/routes.go index 760f86cc7a..b0e581df69 100644 --- a/daemon/algod/api/server/v2/generated/experimental/routes.go +++ b/daemon/algod/api/server/v2/generated/experimental/routes.go @@ -169,134 +169,135 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/model/types.go b/daemon/algod/api/server/v2/generated/model/types.go index 2565f762a9..0ea0473989 100644 --- a/daemon/algod/api/server/v2/generated/model/types.go +++ b/daemon/algod/api/server/v2/generated/model/types.go @@ -188,6 +188,12 @@ type Account struct { // IncentiveEligible Whether or not the account can receive block incentives if its balance is in range at proposal time. IncentiveEligible *bool `json:"incentive-eligible,omitempty"` + // LastHeartbeat The round in which this account last went online, or explicitly renewed their online status. + LastHeartbeat *uint64 `json:"last-heartbeat,omitempty"` + + // LastProposed The round in which this account last proposed the block. + LastProposed *uint64 `json:"last-proposed,omitempty"` + // MinBalance MicroAlgo balance required by the account. // // The requirement grows based on asset and application usage. diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go index 697e544f8d..38fa807a82 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/private/routes.go @@ -140,215 +140,216 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL var swaggerSpec = []string{ "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", - "6a4N/AAA", + "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", + "AAD//yBzl+wK/QAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go index 49eb954d27..c89afa7516 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go @@ -803,211 +803,212 @@ var swaggerSpec = []string{ "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", - "AQA=", + "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", + "9SMOgKoBAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/participating/private/routes.go b/daemon/algod/api/server/v2/generated/participating/private/routes.go index 84000d35dd..ba583b859a 100644 --- a/daemon/algod/api/server/v2/generated/participating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/private/routes.go @@ -282,140 +282,141 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/participating/public/routes.go b/daemon/algod/api/server/v2/generated/participating/public/routes.go index c4e4a8de10..8f2add21ae 100644 --- a/daemon/algod/api/server/v2/generated/participating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/public/routes.go @@ -177,224 +177,225 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/handlers.go b/daemon/algod/api/server/v2/handlers.go index 5663f429ba..82bbbe6134 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -495,7 +495,7 @@ func (v2 *Handlers) basicAccountInformation(ctx echo.Context, addr basics.Addres } var apiParticipation *model.AccountParticipation - if record.VoteID != (crypto.OneTimeSignatureVerifier{}) { + if !record.VoteID.IsEmpty() { apiParticipation = &model.AccountParticipation{ VoteParticipationKey: record.VoteID[:], SelectionParticipationKey: record.SelectionID[:], @@ -539,6 +539,8 @@ func (v2 *Handlers) basicAccountInformation(ctx echo.Context, addr basics.Addres TotalBoxes: omitEmpty(record.TotalBoxes), TotalBoxBytes: omitEmpty(record.TotalBoxBytes), MinBalance: record.MinBalance(&consensus).Raw, + LastProposed: omitEmpty(uint64(record.LastProposed)), + LastHeartbeat: omitEmpty(uint64(record.LastHeartbeat)), } response := model.AccountResponse(account) return ctx.JSON(http.StatusOK, response) diff --git a/data/basics/msgp_gen.go b/data/basics/msgp_gen.go index f51e6e503b..de460960bc 100644 --- a/data/basics/msgp_gen.go +++ b/data/basics/msgp_gen.go @@ -250,8 +250,8 @@ import ( func (z *AccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0009Len := uint32(20) - var zb0009Mask uint32 /* 21 bits */ + zb0009Len := uint32(22) + var zb0009Mask uint32 /* 23 bits */ if (*z).MicroAlgos.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x2 @@ -284,54 +284,62 @@ func (z *AccountData) MarshalMsg(b []byte) (o []byte) { zb0009Len-- zb0009Mask |= 0x100 } - if (*z).Status == 0 { + if (*z).LastHeartbeat == 0 { zb0009Len-- zb0009Mask |= 0x200 } - if (*z).SelectionID.MsgIsZero() { + if (*z).LastProposed == 0 { zb0009Len-- zb0009Mask |= 0x400 } - if (*z).AuthAddr.MsgIsZero() { + if (*z).Status == 0 { zb0009Len-- zb0009Mask |= 0x800 } - if (*z).StateProofID.MsgIsZero() { + if (*z).SelectionID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x1000 } - if (*z).TotalBoxes == 0 { + if (*z).AuthAddr.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x2000 } - if (*z).TotalBoxBytes == 0 { + if (*z).StateProofID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x4000 } - if (*z).TotalExtraAppPages == 0 { + if (*z).TotalBoxes == 0 { zb0009Len-- zb0009Mask |= 0x8000 } - if ((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0) { + if (*z).TotalBoxBytes == 0 { zb0009Len-- zb0009Mask |= 0x10000 } - if (*z).VoteID.MsgIsZero() { + if (*z).TotalExtraAppPages == 0 { zb0009Len-- zb0009Mask |= 0x20000 } - if (*z).VoteFirstValid == 0 { + if ((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0) { zb0009Len-- zb0009Mask |= 0x40000 } - if (*z).VoteKeyDilution == 0 { + if (*z).VoteID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x80000 } - if (*z).VoteLastValid == 0 { + if (*z).VoteFirstValid == 0 { zb0009Len-- zb0009Mask |= 0x100000 } + if (*z).VoteKeyDilution == 0 { + zb0009Len-- + zb0009Mask |= 0x200000 + } + if (*z).VoteLastValid == 0 { + zb0009Len-- + zb0009Mask |= 0x400000 + } // variable map header, size zb0009Len o = msgp.AppendMapHeader(o, zb0009Len) if zb0009Len != 0 { @@ -460,41 +468,51 @@ func (z *AccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendBool(o, (*z).IncentiveEligible) } if (zb0009Mask & 0x200) == 0 { // if not empty + // string "lhb" + o = append(o, 0xa3, 0x6c, 0x68, 0x62) + o = msgp.AppendUint64(o, uint64((*z).LastHeartbeat)) + } + if (zb0009Mask & 0x400) == 0 { // if not empty + // string "lpr" + o = append(o, 0xa3, 0x6c, 0x70, 0x72) + o = msgp.AppendUint64(o, uint64((*z).LastProposed)) + } + if (zb0009Mask & 0x800) == 0 { // if not empty // string "onl" o = append(o, 0xa3, 0x6f, 0x6e, 0x6c) o = msgp.AppendByte(o, byte((*z).Status)) } - if (zb0009Mask & 0x400) == 0 { // if not empty + if (zb0009Mask & 0x1000) == 0 { // if not empty // string "sel" o = append(o, 0xa3, 0x73, 0x65, 0x6c) o = (*z).SelectionID.MarshalMsg(o) } - if (zb0009Mask & 0x800) == 0 { // if not empty + if (zb0009Mask & 0x2000) == 0 { // if not empty // string "spend" o = append(o, 0xa5, 0x73, 0x70, 0x65, 0x6e, 0x64) o = (*z).AuthAddr.MarshalMsg(o) } - if (zb0009Mask & 0x1000) == 0 { // if not empty + if (zb0009Mask & 0x4000) == 0 { // if not empty // string "stprf" o = append(o, 0xa5, 0x73, 0x74, 0x70, 0x72, 0x66) o = (*z).StateProofID.MarshalMsg(o) } - if (zb0009Mask & 0x2000) == 0 { // if not empty + if (zb0009Mask & 0x8000) == 0 { // if not empty // string "tbx" o = append(o, 0xa3, 0x74, 0x62, 0x78) o = msgp.AppendUint64(o, (*z).TotalBoxes) } - if (zb0009Mask & 0x4000) == 0 { // if not empty + if (zb0009Mask & 0x10000) == 0 { // if not empty // string "tbxb" o = append(o, 0xa4, 0x74, 0x62, 0x78, 0x62) o = msgp.AppendUint64(o, (*z).TotalBoxBytes) } - if (zb0009Mask & 0x8000) == 0 { // if not empty + if (zb0009Mask & 0x20000) == 0 { // if not empty // string "teap" o = append(o, 0xa4, 0x74, 0x65, 0x61, 0x70) o = msgp.AppendUint32(o, (*z).TotalExtraAppPages) } - if (zb0009Mask & 0x10000) == 0 { // if not empty + if (zb0009Mask & 0x40000) == 0 { // if not empty // string "tsch" o = append(o, 0xa4, 0x74, 0x73, 0x63, 0x68) // omitempty: check for empty values @@ -521,22 +539,22 @@ func (z *AccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).TotalAppSchema.NumUint) } } - if (zb0009Mask & 0x20000) == 0 { // if not empty + if (zb0009Mask & 0x80000) == 0 { // if not empty // string "vote" o = append(o, 0xa4, 0x76, 0x6f, 0x74, 0x65) o = (*z).VoteID.MarshalMsg(o) } - if (zb0009Mask & 0x40000) == 0 { // if not empty + if (zb0009Mask & 0x100000) == 0 { // if not empty // string "voteFst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x46, 0x73, 0x74) o = msgp.AppendUint64(o, uint64((*z).VoteFirstValid)) } - if (zb0009Mask & 0x80000) == 0 { // if not empty + if (zb0009Mask & 0x200000) == 0 { // if not empty // string "voteKD" o = append(o, 0xa6, 0x76, 0x6f, 0x74, 0x65, 0x4b, 0x44) o = msgp.AppendUint64(o, (*z).VoteKeyDilution) } - if (zb0009Mask & 0x100000) == 0 { // if not empty + if (zb0009Mask & 0x400000) == 0 { // if not empty // string "voteLst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x4c, 0x73, 0x74) o = msgp.AppendUint64(o, uint64((*z).VoteLastValid)) @@ -662,27 +680,51 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } if zb0009 > 0 { zb0009-- - var zb0014 int - var zb0015 bool - zb0014, zb0015, bts, err = msgp.ReadMapHeaderBytes(bts) + { + var zb0014 uint64 + zb0014, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "LastProposed") + return + } + (*z).LastProposed = Round(zb0014) + } + } + if zb0009 > 0 { + zb0009-- + { + var zb0015 uint64 + zb0015, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "LastHeartbeat") + return + } + (*z).LastHeartbeat = Round(zb0015) + } + } + if zb0009 > 0 { + zb0009-- + var zb0016 int + var zb0017 bool + zb0016, zb0017, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetParams") return } - if zb0014 > encodedMaxAssetsPerAccount { - err = msgp.ErrOverflow(uint64(zb0014), uint64(encodedMaxAssetsPerAccount)) + if zb0016 > encodedMaxAssetsPerAccount { + err = msgp.ErrOverflow(uint64(zb0016), uint64(encodedMaxAssetsPerAccount)) err = msgp.WrapError(err, "struct-from-array", "AssetParams") return } - if zb0015 { + if zb0017 { (*z).AssetParams = nil } else if (*z).AssetParams == nil { - (*z).AssetParams = make(map[AssetIndex]AssetParams, zb0014) + (*z).AssetParams = make(map[AssetIndex]AssetParams, zb0016) } - for zb0014 > 0 { + for zb0016 > 0 { var zb0001 AssetIndex var zb0002 AssetParams - zb0014-- + zb0016-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetParams") @@ -698,59 +740,59 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } if zb0009 > 0 { zb0009-- - var zb0016 int - var zb0017 bool - zb0016, zb0017, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0018 int + var zb0019 bool + zb0018, zb0019, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets") return } - if zb0016 > encodedMaxAssetsPerAccount { - err = msgp.ErrOverflow(uint64(zb0016), uint64(encodedMaxAssetsPerAccount)) + if zb0018 > encodedMaxAssetsPerAccount { + err = msgp.ErrOverflow(uint64(zb0018), uint64(encodedMaxAssetsPerAccount)) err = msgp.WrapError(err, "struct-from-array", "Assets") return } - if zb0017 { + if zb0019 { (*z).Assets = nil } else if (*z).Assets == nil { - (*z).Assets = make(map[AssetIndex]AssetHolding, zb0016) + (*z).Assets = make(map[AssetIndex]AssetHolding, zb0018) } - for zb0016 > 0 { + for zb0018 > 0 { var zb0003 AssetIndex var zb0004 AssetHolding - zb0016-- + zb0018-- bts, err = zb0003.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets") return } - var zb0018 int - var zb0019 bool - zb0018, zb0019, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0020 int + var zb0021 bool + zb0020, zb0021, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0018, zb0019, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0020, zb0021, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003) return } - if zb0018 > 0 { - zb0018-- + if zb0020 > 0 { + zb0020-- zb0004.Amount, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003, "struct-from-array", "Amount") return } } - if zb0018 > 0 { - zb0018-- + if zb0020 > 0 { + zb0020-- zb0004.Frozen, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003, "struct-from-array", "Frozen") return } } - if zb0018 > 0 { - err = msgp.ErrTooManyArrayFields(zb0018) + if zb0020 > 0 { + err = msgp.ErrTooManyArrayFields(zb0020) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003, "struct-from-array") return @@ -761,11 +803,11 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003) return } - if zb0019 { + if zb0021 { zb0004 = AssetHolding{} } - for zb0018 > 0 { - zb0018-- + for zb0020 > 0 { + zb0020-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003) @@ -814,27 +856,27 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } if zb0009 > 0 { zb0009-- - var zb0020 int - var zb0021 bool - zb0020, zb0021, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0022 int + var zb0023 bool + zb0022, zb0023, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AppLocalStates") return } - if zb0020 > EncodedMaxAppLocalStates { - err = msgp.ErrOverflow(uint64(zb0020), uint64(EncodedMaxAppLocalStates)) + if zb0022 > EncodedMaxAppLocalStates { + err = msgp.ErrOverflow(uint64(zb0022), uint64(EncodedMaxAppLocalStates)) err = msgp.WrapError(err, "struct-from-array", "AppLocalStates") return } - if zb0021 { + if zb0023 { (*z).AppLocalStates = nil } else if (*z).AppLocalStates == nil { - (*z).AppLocalStates = make(map[AppIndex]AppLocalState, zb0020) + (*z).AppLocalStates = make(map[AppIndex]AppLocalState, zb0022) } - for zb0020 > 0 { + for zb0022 > 0 { var zb0005 AppIndex var zb0006 AppLocalState - zb0020-- + zb0022-- bts, err = zb0005.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AppLocalStates") @@ -850,27 +892,27 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } if zb0009 > 0 { zb0009-- - var zb0022 int - var zb0023 bool - zb0022, zb0023, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0024 int + var zb0025 bool + zb0024, zb0025, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AppParams") return } - if zb0022 > EncodedMaxAppParams { - err = msgp.ErrOverflow(uint64(zb0022), uint64(EncodedMaxAppParams)) + if zb0024 > EncodedMaxAppParams { + err = msgp.ErrOverflow(uint64(zb0024), uint64(EncodedMaxAppParams)) err = msgp.WrapError(err, "struct-from-array", "AppParams") return } - if zb0023 { + if zb0025 { (*z).AppParams = nil } else if (*z).AppParams == nil { - (*z).AppParams = make(map[AppIndex]AppParams, zb0022) + (*z).AppParams = make(map[AppIndex]AppParams, zb0024) } - for zb0022 > 0 { + for zb0024 > 0 { var zb0007 AppIndex var zb0008 AppParams - zb0022-- + zb0024-- bts, err = zb0007.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AppParams") @@ -886,33 +928,33 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } if zb0009 > 0 { zb0009-- - var zb0024 int - var zb0025 bool - zb0024, zb0025, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0026 int + var zb0027 bool + zb0026, zb0027, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0024, zb0025, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0026, zb0027, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema") return } - if zb0024 > 0 { - zb0024-- + if zb0026 > 0 { + zb0026-- (*z).TotalAppSchema.NumUint, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema", "struct-from-array", "NumUint") return } } - if zb0024 > 0 { - zb0024-- + if zb0026 > 0 { + zb0026-- (*z).TotalAppSchema.NumByteSlice, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema", "struct-from-array", "NumByteSlice") return } } - if zb0024 > 0 { - err = msgp.ErrTooManyArrayFields(zb0024) + if zb0026 > 0 { + err = msgp.ErrTooManyArrayFields(zb0026) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema", "struct-from-array") return @@ -923,11 +965,11 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema") return } - if zb0025 { + if zb0027 { (*z).TotalAppSchema = StateSchema{} } - for zb0024 > 0 { - zb0024-- + for zb0026 > 0 { + zb0026-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema") @@ -1005,13 +1047,13 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) switch string(field) { case "onl": { - var zb0026 byte - zb0026, bts, err = msgp.ReadByteBytes(bts) + var zb0028 byte + zb0028, bts, err = msgp.ReadByteBytes(bts) if err != nil { err = msgp.WrapError(err, "Status") return } - (*z).Status = Status(zb0026) + (*z).Status = Status(zb0028) } case "algo": bts, err = (*z).MicroAlgos.UnmarshalMsgWithState(bts, st) @@ -1051,23 +1093,23 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } case "voteFst": { - var zb0027 uint64 - zb0027, bts, err = msgp.ReadUint64Bytes(bts) + var zb0029 uint64 + zb0029, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "VoteFirstValid") return } - (*z).VoteFirstValid = Round(zb0027) + (*z).VoteFirstValid = Round(zb0029) } case "voteLst": { - var zb0028 uint64 - zb0028, bts, err = msgp.ReadUint64Bytes(bts) + var zb0030 uint64 + zb0030, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "VoteLastValid") return } - (*z).VoteLastValid = Round(zb0028) + (*z).VoteLastValid = Round(zb0030) } case "voteKD": (*z).VoteKeyDilution, bts, err = msgp.ReadUint64Bytes(bts) @@ -1075,28 +1117,48 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "VoteKeyDilution") return } + case "lpr": + { + var zb0031 uint64 + zb0031, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "LastProposed") + return + } + (*z).LastProposed = Round(zb0031) + } + case "lhb": + { + var zb0032 uint64 + zb0032, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "LastHeartbeat") + return + } + (*z).LastHeartbeat = Round(zb0032) + } case "apar": - var zb0029 int - var zb0030 bool - zb0029, zb0030, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0033 int + var zb0034 bool + zb0033, zb0034, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "AssetParams") return } - if zb0029 > encodedMaxAssetsPerAccount { - err = msgp.ErrOverflow(uint64(zb0029), uint64(encodedMaxAssetsPerAccount)) + if zb0033 > encodedMaxAssetsPerAccount { + err = msgp.ErrOverflow(uint64(zb0033), uint64(encodedMaxAssetsPerAccount)) err = msgp.WrapError(err, "AssetParams") return } - if zb0030 { + if zb0034 { (*z).AssetParams = nil } else if (*z).AssetParams == nil { - (*z).AssetParams = make(map[AssetIndex]AssetParams, zb0029) + (*z).AssetParams = make(map[AssetIndex]AssetParams, zb0033) } - for zb0029 > 0 { + for zb0033 > 0 { var zb0001 AssetIndex var zb0002 AssetParams - zb0029-- + zb0033-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "AssetParams") @@ -1110,59 +1172,59 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (*z).AssetParams[zb0001] = zb0002 } case "asset": - var zb0031 int - var zb0032 bool - zb0031, zb0032, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0035 int + var zb0036 bool + zb0035, zb0036, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "Assets") return } - if zb0031 > encodedMaxAssetsPerAccount { - err = msgp.ErrOverflow(uint64(zb0031), uint64(encodedMaxAssetsPerAccount)) + if zb0035 > encodedMaxAssetsPerAccount { + err = msgp.ErrOverflow(uint64(zb0035), uint64(encodedMaxAssetsPerAccount)) err = msgp.WrapError(err, "Assets") return } - if zb0032 { + if zb0036 { (*z).Assets = nil } else if (*z).Assets == nil { - (*z).Assets = make(map[AssetIndex]AssetHolding, zb0031) + (*z).Assets = make(map[AssetIndex]AssetHolding, zb0035) } - for zb0031 > 0 { + for zb0035 > 0 { var zb0003 AssetIndex var zb0004 AssetHolding - zb0031-- + zb0035-- bts, err = zb0003.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "Assets") return } - var zb0033 int - var zb0034 bool - zb0033, zb0034, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0037 int + var zb0038 bool + zb0037, zb0038, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0033, zb0034, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0037, zb0038, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "Assets", zb0003) return } - if zb0033 > 0 { - zb0033-- + if zb0037 > 0 { + zb0037-- zb0004.Amount, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "Assets", zb0003, "struct-from-array", "Amount") return } } - if zb0033 > 0 { - zb0033-- + if zb0037 > 0 { + zb0037-- zb0004.Frozen, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "Assets", zb0003, "struct-from-array", "Frozen") return } } - if zb0033 > 0 { - err = msgp.ErrTooManyArrayFields(zb0033) + if zb0037 > 0 { + err = msgp.ErrTooManyArrayFields(zb0037) if err != nil { err = msgp.WrapError(err, "Assets", zb0003, "struct-from-array") return @@ -1173,11 +1235,11 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "Assets", zb0003) return } - if zb0034 { + if zb0038 { zb0004 = AssetHolding{} } - for zb0033 > 0 { - zb0033-- + for zb0037 > 0 { + zb0037-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err, "Assets", zb0003) @@ -1220,27 +1282,27 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } case "appl": - var zb0035 int - var zb0036 bool - zb0035, zb0036, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0039 int + var zb0040 bool + zb0039, zb0040, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "AppLocalStates") return } - if zb0035 > EncodedMaxAppLocalStates { - err = msgp.ErrOverflow(uint64(zb0035), uint64(EncodedMaxAppLocalStates)) + if zb0039 > EncodedMaxAppLocalStates { + err = msgp.ErrOverflow(uint64(zb0039), uint64(EncodedMaxAppLocalStates)) err = msgp.WrapError(err, "AppLocalStates") return } - if zb0036 { + if zb0040 { (*z).AppLocalStates = nil } else if (*z).AppLocalStates == nil { - (*z).AppLocalStates = make(map[AppIndex]AppLocalState, zb0035) + (*z).AppLocalStates = make(map[AppIndex]AppLocalState, zb0039) } - for zb0035 > 0 { + for zb0039 > 0 { var zb0005 AppIndex var zb0006 AppLocalState - zb0035-- + zb0039-- bts, err = zb0005.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "AppLocalStates") @@ -1254,27 +1316,27 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (*z).AppLocalStates[zb0005] = zb0006 } case "appp": - var zb0037 int - var zb0038 bool - zb0037, zb0038, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0041 int + var zb0042 bool + zb0041, zb0042, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "AppParams") return } - if zb0037 > EncodedMaxAppParams { - err = msgp.ErrOverflow(uint64(zb0037), uint64(EncodedMaxAppParams)) + if zb0041 > EncodedMaxAppParams { + err = msgp.ErrOverflow(uint64(zb0041), uint64(EncodedMaxAppParams)) err = msgp.WrapError(err, "AppParams") return } - if zb0038 { + if zb0042 { (*z).AppParams = nil } else if (*z).AppParams == nil { - (*z).AppParams = make(map[AppIndex]AppParams, zb0037) + (*z).AppParams = make(map[AppIndex]AppParams, zb0041) } - for zb0037 > 0 { + for zb0041 > 0 { var zb0007 AppIndex var zb0008 AppParams - zb0037-- + zb0041-- bts, err = zb0007.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "AppParams") @@ -1288,33 +1350,33 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (*z).AppParams[zb0007] = zb0008 } case "tsch": - var zb0039 int - var zb0040 bool - zb0039, zb0040, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0043 int + var zb0044 bool + zb0043, zb0044, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0039, zb0040, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0043, zb0044, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "TotalAppSchema") return } - if zb0039 > 0 { - zb0039-- + if zb0043 > 0 { + zb0043-- (*z).TotalAppSchema.NumUint, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "TotalAppSchema", "struct-from-array", "NumUint") return } } - if zb0039 > 0 { - zb0039-- + if zb0043 > 0 { + zb0043-- (*z).TotalAppSchema.NumByteSlice, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "TotalAppSchema", "struct-from-array", "NumByteSlice") return } } - if zb0039 > 0 { - err = msgp.ErrTooManyArrayFields(zb0039) + if zb0043 > 0 { + err = msgp.ErrTooManyArrayFields(zb0043) if err != nil { err = msgp.WrapError(err, "TotalAppSchema", "struct-from-array") return @@ -1325,11 +1387,11 @@ func (z *AccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "TotalAppSchema") return } - if zb0040 { + if zb0044 { (*z).TotalAppSchema = StateSchema{} } - for zb0039 > 0 { - zb0039-- + for zb0043 > 0 { + zb0043-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err, "TotalAppSchema") @@ -1398,7 +1460,7 @@ func (_ *AccountData) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *AccountData) Msgsize() (s int) { - s = 3 + 4 + msgp.ByteSize + 5 + (*z).MicroAlgos.Msgsize() + 6 + msgp.Uint64Size + 4 + (*z).RewardedMicroAlgos.Msgsize() + 5 + (*z).VoteID.Msgsize() + 4 + (*z).SelectionID.Msgsize() + 6 + (*z).StateProofID.Msgsize() + 8 + msgp.Uint64Size + 8 + msgp.Uint64Size + 7 + msgp.Uint64Size + 5 + msgp.MapHeaderSize + s = 3 + 4 + msgp.ByteSize + 5 + (*z).MicroAlgos.Msgsize() + 6 + msgp.Uint64Size + 4 + (*z).RewardedMicroAlgos.Msgsize() + 5 + (*z).VoteID.Msgsize() + 4 + (*z).SelectionID.Msgsize() + 6 + (*z).StateProofID.Msgsize() + 8 + msgp.Uint64Size + 8 + msgp.Uint64Size + 7 + msgp.Uint64Size + 4 + msgp.Uint64Size + 4 + msgp.Uint64Size + 5 + msgp.MapHeaderSize if (*z).AssetParams != nil { for zb0001, zb0002 := range (*z).AssetParams { _ = zb0001 @@ -1436,12 +1498,12 @@ func (z *AccountData) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *AccountData) MsgIsZero() bool { - return ((*z).Status == 0) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) && ((*z).RewardedMicroAlgos.MsgIsZero()) && ((*z).VoteID.MsgIsZero()) && ((*z).SelectionID.MsgIsZero()) && ((*z).StateProofID.MsgIsZero()) && ((*z).VoteFirstValid == 0) && ((*z).VoteLastValid == 0) && ((*z).VoteKeyDilution == 0) && (len((*z).AssetParams) == 0) && (len((*z).Assets) == 0) && ((*z).AuthAddr.MsgIsZero()) && ((*z).IncentiveEligible == false) && (len((*z).AppLocalStates) == 0) && (len((*z).AppParams) == 0) && (((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0)) && ((*z).TotalExtraAppPages == 0) && ((*z).TotalBoxes == 0) && ((*z).TotalBoxBytes == 0) + return ((*z).Status == 0) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) && ((*z).RewardedMicroAlgos.MsgIsZero()) && ((*z).VoteID.MsgIsZero()) && ((*z).SelectionID.MsgIsZero()) && ((*z).StateProofID.MsgIsZero()) && ((*z).VoteFirstValid == 0) && ((*z).VoteLastValid == 0) && ((*z).VoteKeyDilution == 0) && ((*z).LastProposed == 0) && ((*z).LastHeartbeat == 0) && (len((*z).AssetParams) == 0) && (len((*z).Assets) == 0) && ((*z).AuthAddr.MsgIsZero()) && ((*z).IncentiveEligible == false) && (len((*z).AppLocalStates) == 0) && (len((*z).AppParams) == 0) && (((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0)) && ((*z).TotalExtraAppPages == 0) && ((*z).TotalBoxes == 0) && ((*z).TotalBoxBytes == 0) } // MaxSize returns a maximum valid message size for this message type func AccountDataMaxSize() (s int) { - s = 3 + 4 + msgp.ByteSize + 5 + MicroAlgosMaxSize() + 6 + msgp.Uint64Size + 4 + MicroAlgosMaxSize() + 5 + crypto.OneTimeSignatureVerifierMaxSize() + 4 + crypto.VRFVerifierMaxSize() + 6 + merklesignature.CommitmentMaxSize() + 8 + msgp.Uint64Size + 8 + msgp.Uint64Size + 7 + msgp.Uint64Size + 5 + s = 3 + 4 + msgp.ByteSize + 5 + MicroAlgosMaxSize() + 6 + msgp.Uint64Size + 4 + MicroAlgosMaxSize() + 5 + crypto.OneTimeSignatureVerifierMaxSize() + 4 + crypto.VRFVerifierMaxSize() + 6 + merklesignature.CommitmentMaxSize() + 8 + msgp.Uint64Size + 8 + msgp.Uint64Size + 7 + msgp.Uint64Size + 4 + msgp.Uint64Size + 4 + msgp.Uint64Size + 5 s += msgp.MapHeaderSize // Adding size of map keys for z.AssetParams s += encodedMaxAssetsPerAccount * (AssetIndexMaxSize()) @@ -3222,8 +3284,8 @@ func AssetParamsMaxSize() (s int) { func (z *BalanceRecord) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0009Len := uint32(21) - var zb0009Mask uint32 /* 23 bits */ + zb0009Len := uint32(23) + var zb0009Mask uint32 /* 25 bits */ if (*z).Addr.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x4 @@ -3260,54 +3322,62 @@ func (z *BalanceRecord) MarshalMsg(b []byte) (o []byte) { zb0009Len-- zb0009Mask |= 0x400 } - if (*z).AccountData.Status == 0 { + if (*z).AccountData.LastHeartbeat == 0 { zb0009Len-- zb0009Mask |= 0x800 } - if (*z).AccountData.SelectionID.MsgIsZero() { + if (*z).AccountData.LastProposed == 0 { zb0009Len-- zb0009Mask |= 0x1000 } - if (*z).AccountData.AuthAddr.MsgIsZero() { + if (*z).AccountData.Status == 0 { zb0009Len-- zb0009Mask |= 0x2000 } - if (*z).AccountData.StateProofID.MsgIsZero() { + if (*z).AccountData.SelectionID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x4000 } - if (*z).AccountData.TotalBoxes == 0 { + if (*z).AccountData.AuthAddr.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x8000 } - if (*z).AccountData.TotalBoxBytes == 0 { + if (*z).AccountData.StateProofID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x10000 } - if (*z).AccountData.TotalExtraAppPages == 0 { + if (*z).AccountData.TotalBoxes == 0 { zb0009Len-- zb0009Mask |= 0x20000 } - if ((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0) { + if (*z).AccountData.TotalBoxBytes == 0 { zb0009Len-- zb0009Mask |= 0x40000 } - if (*z).AccountData.VoteID.MsgIsZero() { + if (*z).AccountData.TotalExtraAppPages == 0 { zb0009Len-- zb0009Mask |= 0x80000 } - if (*z).AccountData.VoteFirstValid == 0 { + if ((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0) { zb0009Len-- zb0009Mask |= 0x100000 } - if (*z).AccountData.VoteKeyDilution == 0 { + if (*z).AccountData.VoteID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x200000 } - if (*z).AccountData.VoteLastValid == 0 { + if (*z).AccountData.VoteFirstValid == 0 { zb0009Len-- zb0009Mask |= 0x400000 } + if (*z).AccountData.VoteKeyDilution == 0 { + zb0009Len-- + zb0009Mask |= 0x800000 + } + if (*z).AccountData.VoteLastValid == 0 { + zb0009Len-- + zb0009Mask |= 0x1000000 + } // variable map header, size zb0009Len o = msgp.AppendMapHeader(o, zb0009Len) if zb0009Len != 0 { @@ -3441,41 +3511,51 @@ func (z *BalanceRecord) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendBool(o, (*z).AccountData.IncentiveEligible) } if (zb0009Mask & 0x800) == 0 { // if not empty + // string "lhb" + o = append(o, 0xa3, 0x6c, 0x68, 0x62) + o = msgp.AppendUint64(o, uint64((*z).AccountData.LastHeartbeat)) + } + if (zb0009Mask & 0x1000) == 0 { // if not empty + // string "lpr" + o = append(o, 0xa3, 0x6c, 0x70, 0x72) + o = msgp.AppendUint64(o, uint64((*z).AccountData.LastProposed)) + } + if (zb0009Mask & 0x2000) == 0 { // if not empty // string "onl" o = append(o, 0xa3, 0x6f, 0x6e, 0x6c) o = msgp.AppendByte(o, byte((*z).AccountData.Status)) } - if (zb0009Mask & 0x1000) == 0 { // if not empty + if (zb0009Mask & 0x4000) == 0 { // if not empty // string "sel" o = append(o, 0xa3, 0x73, 0x65, 0x6c) o = (*z).AccountData.SelectionID.MarshalMsg(o) } - if (zb0009Mask & 0x2000) == 0 { // if not empty + if (zb0009Mask & 0x8000) == 0 { // if not empty // string "spend" o = append(o, 0xa5, 0x73, 0x70, 0x65, 0x6e, 0x64) o = (*z).AccountData.AuthAddr.MarshalMsg(o) } - if (zb0009Mask & 0x4000) == 0 { // if not empty + if (zb0009Mask & 0x10000) == 0 { // if not empty // string "stprf" o = append(o, 0xa5, 0x73, 0x74, 0x70, 0x72, 0x66) o = (*z).AccountData.StateProofID.MarshalMsg(o) } - if (zb0009Mask & 0x8000) == 0 { // if not empty + if (zb0009Mask & 0x20000) == 0 { // if not empty // string "tbx" o = append(o, 0xa3, 0x74, 0x62, 0x78) o = msgp.AppendUint64(o, (*z).AccountData.TotalBoxes) } - if (zb0009Mask & 0x10000) == 0 { // if not empty + if (zb0009Mask & 0x40000) == 0 { // if not empty // string "tbxb" o = append(o, 0xa4, 0x74, 0x62, 0x78, 0x62) o = msgp.AppendUint64(o, (*z).AccountData.TotalBoxBytes) } - if (zb0009Mask & 0x20000) == 0 { // if not empty + if (zb0009Mask & 0x80000) == 0 { // if not empty // string "teap" o = append(o, 0xa4, 0x74, 0x65, 0x61, 0x70) o = msgp.AppendUint32(o, (*z).AccountData.TotalExtraAppPages) } - if (zb0009Mask & 0x40000) == 0 { // if not empty + if (zb0009Mask & 0x100000) == 0 { // if not empty // string "tsch" o = append(o, 0xa4, 0x74, 0x73, 0x63, 0x68) // omitempty: check for empty values @@ -3502,22 +3582,22 @@ func (z *BalanceRecord) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendUint64(o, (*z).AccountData.TotalAppSchema.NumUint) } } - if (zb0009Mask & 0x80000) == 0 { // if not empty + if (zb0009Mask & 0x200000) == 0 { // if not empty // string "vote" o = append(o, 0xa4, 0x76, 0x6f, 0x74, 0x65) o = (*z).AccountData.VoteID.MarshalMsg(o) } - if (zb0009Mask & 0x100000) == 0 { // if not empty + if (zb0009Mask & 0x400000) == 0 { // if not empty // string "voteFst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x46, 0x73, 0x74) o = msgp.AppendUint64(o, uint64((*z).AccountData.VoteFirstValid)) } - if (zb0009Mask & 0x200000) == 0 { // if not empty + if (zb0009Mask & 0x800000) == 0 { // if not empty // string "voteKD" o = append(o, 0xa6, 0x76, 0x6f, 0x74, 0x65, 0x4b, 0x44) o = msgp.AppendUint64(o, (*z).AccountData.VoteKeyDilution) } - if (zb0009Mask & 0x400000) == 0 { // if not empty + if (zb0009Mask & 0x1000000) == 0 { // if not empty // string "voteLst" o = append(o, 0xa7, 0x76, 0x6f, 0x74, 0x65, 0x4c, 0x73, 0x74) o = msgp.AppendUint64(o, uint64((*z).AccountData.VoteLastValid)) @@ -3651,27 +3731,51 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState } if zb0009 > 0 { zb0009-- - var zb0014 int - var zb0015 bool - zb0014, zb0015, bts, err = msgp.ReadMapHeaderBytes(bts) + { + var zb0014 uint64 + zb0014, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "LastProposed") + return + } + (*z).AccountData.LastProposed = Round(zb0014) + } + } + if zb0009 > 0 { + zb0009-- + { + var zb0015 uint64 + zb0015, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "LastHeartbeat") + return + } + (*z).AccountData.LastHeartbeat = Round(zb0015) + } + } + if zb0009 > 0 { + zb0009-- + var zb0016 int + var zb0017 bool + zb0016, zb0017, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetParams") return } - if zb0014 > encodedMaxAssetsPerAccount { - err = msgp.ErrOverflow(uint64(zb0014), uint64(encodedMaxAssetsPerAccount)) + if zb0016 > encodedMaxAssetsPerAccount { + err = msgp.ErrOverflow(uint64(zb0016), uint64(encodedMaxAssetsPerAccount)) err = msgp.WrapError(err, "struct-from-array", "AssetParams") return } - if zb0015 { + if zb0017 { (*z).AccountData.AssetParams = nil } else if (*z).AccountData.AssetParams == nil { - (*z).AccountData.AssetParams = make(map[AssetIndex]AssetParams, zb0014) + (*z).AccountData.AssetParams = make(map[AssetIndex]AssetParams, zb0016) } - for zb0014 > 0 { + for zb0016 > 0 { var zb0001 AssetIndex var zb0002 AssetParams - zb0014-- + zb0016-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AssetParams") @@ -3687,59 +3791,59 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState } if zb0009 > 0 { zb0009-- - var zb0016 int - var zb0017 bool - zb0016, zb0017, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0018 int + var zb0019 bool + zb0018, zb0019, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets") return } - if zb0016 > encodedMaxAssetsPerAccount { - err = msgp.ErrOverflow(uint64(zb0016), uint64(encodedMaxAssetsPerAccount)) + if zb0018 > encodedMaxAssetsPerAccount { + err = msgp.ErrOverflow(uint64(zb0018), uint64(encodedMaxAssetsPerAccount)) err = msgp.WrapError(err, "struct-from-array", "Assets") return } - if zb0017 { + if zb0019 { (*z).AccountData.Assets = nil } else if (*z).AccountData.Assets == nil { - (*z).AccountData.Assets = make(map[AssetIndex]AssetHolding, zb0016) + (*z).AccountData.Assets = make(map[AssetIndex]AssetHolding, zb0018) } - for zb0016 > 0 { + for zb0018 > 0 { var zb0003 AssetIndex var zb0004 AssetHolding - zb0016-- + zb0018-- bts, err = zb0003.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets") return } - var zb0018 int - var zb0019 bool - zb0018, zb0019, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0020 int + var zb0021 bool + zb0020, zb0021, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0018, zb0019, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0020, zb0021, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003) return } - if zb0018 > 0 { - zb0018-- + if zb0020 > 0 { + zb0020-- zb0004.Amount, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003, "struct-from-array", "Amount") return } } - if zb0018 > 0 { - zb0018-- + if zb0020 > 0 { + zb0020-- zb0004.Frozen, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003, "struct-from-array", "Frozen") return } } - if zb0018 > 0 { - err = msgp.ErrTooManyArrayFields(zb0018) + if zb0020 > 0 { + err = msgp.ErrTooManyArrayFields(zb0020) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003, "struct-from-array") return @@ -3750,11 +3854,11 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003) return } - if zb0019 { + if zb0021 { zb0004 = AssetHolding{} } - for zb0018 > 0 { - zb0018-- + for zb0020 > 0 { + zb0020-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Assets", zb0003) @@ -3803,27 +3907,27 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState } if zb0009 > 0 { zb0009-- - var zb0020 int - var zb0021 bool - zb0020, zb0021, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0022 int + var zb0023 bool + zb0022, zb0023, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AppLocalStates") return } - if zb0020 > EncodedMaxAppLocalStates { - err = msgp.ErrOverflow(uint64(zb0020), uint64(EncodedMaxAppLocalStates)) + if zb0022 > EncodedMaxAppLocalStates { + err = msgp.ErrOverflow(uint64(zb0022), uint64(EncodedMaxAppLocalStates)) err = msgp.WrapError(err, "struct-from-array", "AppLocalStates") return } - if zb0021 { + if zb0023 { (*z).AccountData.AppLocalStates = nil } else if (*z).AccountData.AppLocalStates == nil { - (*z).AccountData.AppLocalStates = make(map[AppIndex]AppLocalState, zb0020) + (*z).AccountData.AppLocalStates = make(map[AppIndex]AppLocalState, zb0022) } - for zb0020 > 0 { + for zb0022 > 0 { var zb0005 AppIndex var zb0006 AppLocalState - zb0020-- + zb0022-- bts, err = zb0005.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AppLocalStates") @@ -3839,27 +3943,27 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState } if zb0009 > 0 { zb0009-- - var zb0022 int - var zb0023 bool - zb0022, zb0023, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0024 int + var zb0025 bool + zb0024, zb0025, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AppParams") return } - if zb0022 > EncodedMaxAppParams { - err = msgp.ErrOverflow(uint64(zb0022), uint64(EncodedMaxAppParams)) + if zb0024 > EncodedMaxAppParams { + err = msgp.ErrOverflow(uint64(zb0024), uint64(EncodedMaxAppParams)) err = msgp.WrapError(err, "struct-from-array", "AppParams") return } - if zb0023 { + if zb0025 { (*z).AccountData.AppParams = nil } else if (*z).AccountData.AppParams == nil { - (*z).AccountData.AppParams = make(map[AppIndex]AppParams, zb0022) + (*z).AccountData.AppParams = make(map[AppIndex]AppParams, zb0024) } - for zb0022 > 0 { + for zb0024 > 0 { var zb0007 AppIndex var zb0008 AppParams - zb0022-- + zb0024-- bts, err = zb0007.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "AppParams") @@ -3875,33 +3979,33 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState } if zb0009 > 0 { zb0009-- - var zb0024 int - var zb0025 bool - zb0024, zb0025, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0026 int + var zb0027 bool + zb0026, zb0027, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0024, zb0025, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0026, zb0027, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema") return } - if zb0024 > 0 { - zb0024-- + if zb0026 > 0 { + zb0026-- (*z).AccountData.TotalAppSchema.NumUint, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema", "struct-from-array", "NumUint") return } } - if zb0024 > 0 { - zb0024-- + if zb0026 > 0 { + zb0026-- (*z).AccountData.TotalAppSchema.NumByteSlice, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema", "struct-from-array", "NumByteSlice") return } } - if zb0024 > 0 { - err = msgp.ErrTooManyArrayFields(zb0024) + if zb0026 > 0 { + err = msgp.ErrTooManyArrayFields(zb0026) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema", "struct-from-array") return @@ -3912,11 +4016,11 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema") return } - if zb0025 { + if zb0027 { (*z).AccountData.TotalAppSchema = StateSchema{} } - for zb0024 > 0 { - zb0024-- + for zb0026 > 0 { + zb0026-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TotalAppSchema") @@ -4000,13 +4104,13 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState } case "onl": { - var zb0026 byte - zb0026, bts, err = msgp.ReadByteBytes(bts) + var zb0028 byte + zb0028, bts, err = msgp.ReadByteBytes(bts) if err != nil { err = msgp.WrapError(err, "Status") return } - (*z).AccountData.Status = Status(zb0026) + (*z).AccountData.Status = Status(zb0028) } case "algo": bts, err = (*z).AccountData.MicroAlgos.UnmarshalMsgWithState(bts, st) @@ -4046,23 +4150,23 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState } case "voteFst": { - var zb0027 uint64 - zb0027, bts, err = msgp.ReadUint64Bytes(bts) + var zb0029 uint64 + zb0029, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "VoteFirstValid") return } - (*z).AccountData.VoteFirstValid = Round(zb0027) + (*z).AccountData.VoteFirstValid = Round(zb0029) } case "voteLst": { - var zb0028 uint64 - zb0028, bts, err = msgp.ReadUint64Bytes(bts) + var zb0030 uint64 + zb0030, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "VoteLastValid") return } - (*z).AccountData.VoteLastValid = Round(zb0028) + (*z).AccountData.VoteLastValid = Round(zb0030) } case "voteKD": (*z).AccountData.VoteKeyDilution, bts, err = msgp.ReadUint64Bytes(bts) @@ -4070,28 +4174,48 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState err = msgp.WrapError(err, "VoteKeyDilution") return } + case "lpr": + { + var zb0031 uint64 + zb0031, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "LastProposed") + return + } + (*z).AccountData.LastProposed = Round(zb0031) + } + case "lhb": + { + var zb0032 uint64 + zb0032, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "LastHeartbeat") + return + } + (*z).AccountData.LastHeartbeat = Round(zb0032) + } case "apar": - var zb0029 int - var zb0030 bool - zb0029, zb0030, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0033 int + var zb0034 bool + zb0033, zb0034, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "AssetParams") return } - if zb0029 > encodedMaxAssetsPerAccount { - err = msgp.ErrOverflow(uint64(zb0029), uint64(encodedMaxAssetsPerAccount)) + if zb0033 > encodedMaxAssetsPerAccount { + err = msgp.ErrOverflow(uint64(zb0033), uint64(encodedMaxAssetsPerAccount)) err = msgp.WrapError(err, "AssetParams") return } - if zb0030 { + if zb0034 { (*z).AccountData.AssetParams = nil } else if (*z).AccountData.AssetParams == nil { - (*z).AccountData.AssetParams = make(map[AssetIndex]AssetParams, zb0029) + (*z).AccountData.AssetParams = make(map[AssetIndex]AssetParams, zb0033) } - for zb0029 > 0 { + for zb0033 > 0 { var zb0001 AssetIndex var zb0002 AssetParams - zb0029-- + zb0033-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "AssetParams") @@ -4105,59 +4229,59 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState (*z).AccountData.AssetParams[zb0001] = zb0002 } case "asset": - var zb0031 int - var zb0032 bool - zb0031, zb0032, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0035 int + var zb0036 bool + zb0035, zb0036, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "Assets") return } - if zb0031 > encodedMaxAssetsPerAccount { - err = msgp.ErrOverflow(uint64(zb0031), uint64(encodedMaxAssetsPerAccount)) + if zb0035 > encodedMaxAssetsPerAccount { + err = msgp.ErrOverflow(uint64(zb0035), uint64(encodedMaxAssetsPerAccount)) err = msgp.WrapError(err, "Assets") return } - if zb0032 { + if zb0036 { (*z).AccountData.Assets = nil } else if (*z).AccountData.Assets == nil { - (*z).AccountData.Assets = make(map[AssetIndex]AssetHolding, zb0031) + (*z).AccountData.Assets = make(map[AssetIndex]AssetHolding, zb0035) } - for zb0031 > 0 { + for zb0035 > 0 { var zb0003 AssetIndex var zb0004 AssetHolding - zb0031-- + zb0035-- bts, err = zb0003.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "Assets") return } - var zb0033 int - var zb0034 bool - zb0033, zb0034, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0037 int + var zb0038 bool + zb0037, zb0038, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0033, zb0034, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0037, zb0038, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "Assets", zb0003) return } - if zb0033 > 0 { - zb0033-- + if zb0037 > 0 { + zb0037-- zb0004.Amount, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "Assets", zb0003, "struct-from-array", "Amount") return } } - if zb0033 > 0 { - zb0033-- + if zb0037 > 0 { + zb0037-- zb0004.Frozen, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "Assets", zb0003, "struct-from-array", "Frozen") return } } - if zb0033 > 0 { - err = msgp.ErrTooManyArrayFields(zb0033) + if zb0037 > 0 { + err = msgp.ErrTooManyArrayFields(zb0037) if err != nil { err = msgp.WrapError(err, "Assets", zb0003, "struct-from-array") return @@ -4168,11 +4292,11 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState err = msgp.WrapError(err, "Assets", zb0003) return } - if zb0034 { + if zb0038 { zb0004 = AssetHolding{} } - for zb0033 > 0 { - zb0033-- + for zb0037 > 0 { + zb0037-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err, "Assets", zb0003) @@ -4215,27 +4339,27 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState return } case "appl": - var zb0035 int - var zb0036 bool - zb0035, zb0036, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0039 int + var zb0040 bool + zb0039, zb0040, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "AppLocalStates") return } - if zb0035 > EncodedMaxAppLocalStates { - err = msgp.ErrOverflow(uint64(zb0035), uint64(EncodedMaxAppLocalStates)) + if zb0039 > EncodedMaxAppLocalStates { + err = msgp.ErrOverflow(uint64(zb0039), uint64(EncodedMaxAppLocalStates)) err = msgp.WrapError(err, "AppLocalStates") return } - if zb0036 { + if zb0040 { (*z).AccountData.AppLocalStates = nil } else if (*z).AccountData.AppLocalStates == nil { - (*z).AccountData.AppLocalStates = make(map[AppIndex]AppLocalState, zb0035) + (*z).AccountData.AppLocalStates = make(map[AppIndex]AppLocalState, zb0039) } - for zb0035 > 0 { + for zb0039 > 0 { var zb0005 AppIndex var zb0006 AppLocalState - zb0035-- + zb0039-- bts, err = zb0005.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "AppLocalStates") @@ -4249,27 +4373,27 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState (*z).AccountData.AppLocalStates[zb0005] = zb0006 } case "appp": - var zb0037 int - var zb0038 bool - zb0037, zb0038, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0041 int + var zb0042 bool + zb0041, zb0042, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "AppParams") return } - if zb0037 > EncodedMaxAppParams { - err = msgp.ErrOverflow(uint64(zb0037), uint64(EncodedMaxAppParams)) + if zb0041 > EncodedMaxAppParams { + err = msgp.ErrOverflow(uint64(zb0041), uint64(EncodedMaxAppParams)) err = msgp.WrapError(err, "AppParams") return } - if zb0038 { + if zb0042 { (*z).AccountData.AppParams = nil } else if (*z).AccountData.AppParams == nil { - (*z).AccountData.AppParams = make(map[AppIndex]AppParams, zb0037) + (*z).AccountData.AppParams = make(map[AppIndex]AppParams, zb0041) } - for zb0037 > 0 { + for zb0041 > 0 { var zb0007 AppIndex var zb0008 AppParams - zb0037-- + zb0041-- bts, err = zb0007.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "AppParams") @@ -4283,33 +4407,33 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState (*z).AccountData.AppParams[zb0007] = zb0008 } case "tsch": - var zb0039 int - var zb0040 bool - zb0039, zb0040, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0043 int + var zb0044 bool + zb0043, zb0044, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0039, zb0040, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0043, zb0044, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "TotalAppSchema") return } - if zb0039 > 0 { - zb0039-- + if zb0043 > 0 { + zb0043-- (*z).AccountData.TotalAppSchema.NumUint, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "TotalAppSchema", "struct-from-array", "NumUint") return } } - if zb0039 > 0 { - zb0039-- + if zb0043 > 0 { + zb0043-- (*z).AccountData.TotalAppSchema.NumByteSlice, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "TotalAppSchema", "struct-from-array", "NumByteSlice") return } } - if zb0039 > 0 { - err = msgp.ErrTooManyArrayFields(zb0039) + if zb0043 > 0 { + err = msgp.ErrTooManyArrayFields(zb0043) if err != nil { err = msgp.WrapError(err, "TotalAppSchema", "struct-from-array") return @@ -4320,11 +4444,11 @@ func (z *BalanceRecord) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState err = msgp.WrapError(err, "TotalAppSchema") return } - if zb0040 { + if zb0044 { (*z).AccountData.TotalAppSchema = StateSchema{} } - for zb0039 > 0 { - zb0039-- + for zb0043 > 0 { + zb0043-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err, "TotalAppSchema") @@ -4393,7 +4517,7 @@ func (_ *BalanceRecord) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *BalanceRecord) Msgsize() (s int) { - s = 3 + 5 + (*z).Addr.Msgsize() + 4 + msgp.ByteSize + 5 + (*z).AccountData.MicroAlgos.Msgsize() + 6 + msgp.Uint64Size + 4 + (*z).AccountData.RewardedMicroAlgos.Msgsize() + 5 + (*z).AccountData.VoteID.Msgsize() + 4 + (*z).AccountData.SelectionID.Msgsize() + 6 + (*z).AccountData.StateProofID.Msgsize() + 8 + msgp.Uint64Size + 8 + msgp.Uint64Size + 7 + msgp.Uint64Size + 5 + msgp.MapHeaderSize + s = 3 + 5 + (*z).Addr.Msgsize() + 4 + msgp.ByteSize + 5 + (*z).AccountData.MicroAlgos.Msgsize() + 6 + msgp.Uint64Size + 4 + (*z).AccountData.RewardedMicroAlgos.Msgsize() + 5 + (*z).AccountData.VoteID.Msgsize() + 4 + (*z).AccountData.SelectionID.Msgsize() + 6 + (*z).AccountData.StateProofID.Msgsize() + 8 + msgp.Uint64Size + 8 + msgp.Uint64Size + 7 + msgp.Uint64Size + 4 + msgp.Uint64Size + 4 + msgp.Uint64Size + 5 + msgp.MapHeaderSize if (*z).AccountData.AssetParams != nil { for zb0001, zb0002 := range (*z).AccountData.AssetParams { _ = zb0001 @@ -4431,12 +4555,12 @@ func (z *BalanceRecord) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *BalanceRecord) MsgIsZero() bool { - return ((*z).Addr.MsgIsZero()) && ((*z).AccountData.Status == 0) && ((*z).AccountData.MicroAlgos.MsgIsZero()) && ((*z).AccountData.RewardsBase == 0) && ((*z).AccountData.RewardedMicroAlgos.MsgIsZero()) && ((*z).AccountData.VoteID.MsgIsZero()) && ((*z).AccountData.SelectionID.MsgIsZero()) && ((*z).AccountData.StateProofID.MsgIsZero()) && ((*z).AccountData.VoteFirstValid == 0) && ((*z).AccountData.VoteLastValid == 0) && ((*z).AccountData.VoteKeyDilution == 0) && (len((*z).AccountData.AssetParams) == 0) && (len((*z).AccountData.Assets) == 0) && ((*z).AccountData.AuthAddr.MsgIsZero()) && ((*z).AccountData.IncentiveEligible == false) && (len((*z).AccountData.AppLocalStates) == 0) && (len((*z).AccountData.AppParams) == 0) && (((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0)) && ((*z).AccountData.TotalExtraAppPages == 0) && ((*z).AccountData.TotalBoxes == 0) && ((*z).AccountData.TotalBoxBytes == 0) + return ((*z).Addr.MsgIsZero()) && ((*z).AccountData.Status == 0) && ((*z).AccountData.MicroAlgos.MsgIsZero()) && ((*z).AccountData.RewardsBase == 0) && ((*z).AccountData.RewardedMicroAlgos.MsgIsZero()) && ((*z).AccountData.VoteID.MsgIsZero()) && ((*z).AccountData.SelectionID.MsgIsZero()) && ((*z).AccountData.StateProofID.MsgIsZero()) && ((*z).AccountData.VoteFirstValid == 0) && ((*z).AccountData.VoteLastValid == 0) && ((*z).AccountData.VoteKeyDilution == 0) && ((*z).AccountData.LastProposed == 0) && ((*z).AccountData.LastHeartbeat == 0) && (len((*z).AccountData.AssetParams) == 0) && (len((*z).AccountData.Assets) == 0) && ((*z).AccountData.AuthAddr.MsgIsZero()) && ((*z).AccountData.IncentiveEligible == false) && (len((*z).AccountData.AppLocalStates) == 0) && (len((*z).AccountData.AppParams) == 0) && (((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0)) && ((*z).AccountData.TotalExtraAppPages == 0) && ((*z).AccountData.TotalBoxes == 0) && ((*z).AccountData.TotalBoxBytes == 0) } // MaxSize returns a maximum valid message size for this message type func BalanceRecordMaxSize() (s int) { - s = 3 + 5 + AddressMaxSize() + 4 + msgp.ByteSize + 5 + MicroAlgosMaxSize() + 6 + msgp.Uint64Size + 4 + MicroAlgosMaxSize() + 5 + crypto.OneTimeSignatureVerifierMaxSize() + 4 + crypto.VRFVerifierMaxSize() + 6 + merklesignature.CommitmentMaxSize() + 8 + msgp.Uint64Size + 8 + msgp.Uint64Size + 7 + msgp.Uint64Size + 5 + s = 3 + 5 + AddressMaxSize() + 4 + msgp.ByteSize + 5 + MicroAlgosMaxSize() + 6 + msgp.Uint64Size + 4 + MicroAlgosMaxSize() + 5 + crypto.OneTimeSignatureVerifierMaxSize() + 4 + crypto.VRFVerifierMaxSize() + 6 + merklesignature.CommitmentMaxSize() + 8 + msgp.Uint64Size + 8 + msgp.Uint64Size + 7 + msgp.Uint64Size + 4 + msgp.Uint64Size + 4 + msgp.Uint64Size + 5 s += msgp.MapHeaderSize // Adding size of map keys for z.AccountData.AssetParams s += encodedMaxAssetsPerAccount * (AssetIndexMaxSize()) diff --git a/data/basics/userBalance.go b/data/basics/userBalance.go index 48af4348e2..ab2332b7f1 100644 --- a/data/basics/userBalance.go +++ b/data/basics/userBalance.go @@ -42,6 +42,11 @@ const ( // Two special accounts that are defined as NotParticipating are the incentive pool (also know as rewards pool) and the fee sink. // These two accounts also have additional Algo transfer restrictions. NotParticipating + // Suspended indicates that an account has registered keys, intending to + // particpate, but appears to have gone dark. Their balance should no longer + // be considered online, but their voting keys should be retained so they + // can easily get back online by sending a "heartbeat". + Suspended // encodedMaxAssetsPerAccount is the decoder limit of number of assets stored per account. // it's being verified by the unit test TestEncodedAccountAllocationBounds to align @@ -75,6 +80,8 @@ func (s Status) String() string { return "Online" case NotParticipating: return "Not Participating" + case Suspended: + return "Suspended" } return "" } @@ -88,6 +95,8 @@ func UnmarshalStatus(value string) (s Status, err error) { s = Online case "Not Participating": s = NotParticipating + case "Suspended": + s = Suspended default: err = fmt.Errorf("unknown account status: %v", value) } @@ -115,8 +124,13 @@ type OnlineAccountData struct { // AccountData contains the data associated with a given address. // -// This includes the account balance, cryptographic public keys, -// consensus delegation status, asset data, and application data. +// This includes the account balance, cryptographic public keys, consensus +// status, asset params (for assets made by this account), asset holdings (for +// assets the account is opted into), and application data (globals if account +// created, locals if opted-in). This can be thought of as the fully "hydrated" +// structure and could take an arbitrary number of db queries to fill. As such, +// it is mostly used only for shuttling complete accounts into the ledger +// (genesis, catchpoints, REST API). And a lot of legacy tests. type AccountData struct { _struct struct{} `codec:",omitempty,omitemptyarray"` @@ -172,6 +186,13 @@ type AccountData struct { VoteLastValid Round `codec:"voteLst"` VoteKeyDilution uint64 `codec:"voteKD"` + // LastProposed is the last round that the account is known to have + // proposed. It is updated at the start of the _next_ round. + LastProposed Round `codec:"lpr"` + // LastHeartbeat is the last round an account has indicated it is ready to + // vote by sending a heartbeat transaction, signed by its partkey. + LastHeartbeat Round `codec:"lhb"` + // If this account created an asset, AssetParams stores // the parameters defining that asset. The params are indexed // by the Index of the AssetID; the Creator is this account's address. diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 987b44e867..4d3cf8a5bc 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -155,6 +155,10 @@ type ( // that needs to be converted to offline since their // participation key expired. ExpiredParticipationAccounts []basics.Address `codec:"partupdrmv,allocbound=config.MaxProposedExpiredOnlineAccounts"` + + // AbsentParticipationAccounts contains a list of online accounts that + // needs to be converted to offline since they are not proposing. + AbsentParticipationAccounts []basics.Address `codec:"partupdabs,allocbound=config.MaxProposedAbsentOnlineAccounts"` } // RewardsState represents the global parameters controlling the rate diff --git a/data/bookkeeping/msgp_gen.go b/data/bookkeeping/msgp_gen.go index e020a7c7bf..bebbcff38b 100644 --- a/data/bookkeeping/msgp_gen.go +++ b/data/bookkeeping/msgp_gen.go @@ -143,174 +143,190 @@ import ( func (z *Block) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(28) - var zb0004Mask uint64 /* 33 bits */ + zb0005Len := uint32(29) + var zb0005Mask uint64 /* 34 bits */ if (*z).BlockHeader.RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x20 + zb0005Len-- + zb0005Mask |= 0x20 } if (*z).BlockHeader.FeesCollected.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40 + zb0005Len-- + zb0005Mask |= 0x40 } if (*z).BlockHeader.RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x80 } if (*z).BlockHeader.RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x100 } if (*z).BlockHeader.GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).BlockHeader.GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x4000 + } + if len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + zb0005Len-- + zb0005Mask |= 0x8000 } if len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x10000 } if (*z).BlockHeader.Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000 + zb0005Len-- + zb0005Mask |= 0x20000 } if (*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x40000 } if (*z).BlockHeader.Proposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x80000 } if (*z).BlockHeader.RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x100000 } if (*z).BlockHeader.Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x200000 } if (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000 + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x800000 } if (*z).BlockHeader.Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800000 + zb0005Len-- + zb0005Mask |= 0x1000000 } if len((*z).BlockHeader.StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).BlockHeader.TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x2000000 + zb0005Len-- + zb0005Mask |= 0x4000000 } if (*z).BlockHeader.TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x10000000 } if (*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if (*z).Payset.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if (*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } if (*z).BlockHeader.UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x100000000 + zb0005Len-- + zb0005Mask |= 0x200000000 } - // variable map header, size zb0004Len - o = msgp.AppendMapHeader(o, zb0004Len) - if zb0004Len != 0 { - if (zb0004Mask & 0x20) == 0 { // if not empty + // variable map header, size zb0005Len + o = msgp.AppendMapHeader(o, zb0005Len) + if zb0005Len != 0 { + if (zb0005Mask & 0x20) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsLevel) } - if (zb0004Mask & 0x40) == 0 { // if not empty + if (zb0005Mask & 0x40) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).BlockHeader.FeesCollected.MarshalMsg(o) } - if (zb0004Mask & 0x80) == 0 { // if not empty + if (zb0005Mask & 0x80) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x100) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsResidue) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).BlockHeader.GenesisID) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty + // string "partupdabs" + o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) + if (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts))) + } + for zb0004 := range (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + o = (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) + } + } + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -322,47 +338,47 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).BlockHeader.Branch.MarshalMsg(o) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).BlockHeader.Proposer.MarshalMsg(o) } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsRate) } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).BlockHeader.Round.MarshalMsg(o) } - if (zb0004Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).BlockHeader.Seed.MarshalMsg(o) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).BlockHeader.StateProofTracking == nil { @@ -382,42 +398,42 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).BlockHeader.TxnCounter) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).BlockHeader.TimeStamp) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).Payset.MarshalMsg(o) } - if (zb0004Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).BlockHeader.UpgradeVote.UpgradeApprove) @@ -440,73 +456,73 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b st.AllowableDepth-- var field []byte _ = field - var zb0004 int - var zb0005 bool - zb0004, zb0005, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0005 int + var zb0006 bool + zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0004, zb0005, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0005, zb0006, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err) return } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.Round.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Round") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.Branch.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Branch") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.Seed.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Seed") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NativeSha512_256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.TxnCommitments.Sha256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Sha256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).BlockHeader.TimeStamp, bts, err = msgp.ReadInt64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TimeStamp") return } } - if zb0004 > 0 { - zb0004-- - var zb0006 int - zb0006, err = msgp.ReadBytesBytesHeader(bts) + if zb0005 > 0 { + zb0005-- + var zb0007 int + zb0007, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisID") return } - if zb0006 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0006), uint64(config.MaxGenesisIDLen)) + if zb0007 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0007), uint64(config.MaxGenesisIDLen)) return } (*z).BlockHeader.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -515,173 +531,173 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.GenesisHash.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisHash") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.Proposer.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Proposer") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeesCollected") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeeSink") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.RewardsState.RewardsPool.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsPool") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).BlockHeader.RewardsState.RewardsLevel, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsLevel") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).BlockHeader.RewardsState.RewardsRate, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRate") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).BlockHeader.RewardsState.RewardsResidue, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsResidue") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.RewardsState.RewardsRecalculationRound.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRecalculationRound") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.UpgradeState.CurrentProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "CurrentProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.UpgradeState.NextProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).BlockHeader.UpgradeState.NextProtocolApprovals, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolApprovals") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolVoteBefore") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolSwitchOn") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.UpgradeVote.UpgradePropose.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradePropose") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).BlockHeader.UpgradeVote.UpgradeDelay.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeDelay") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).BlockHeader.UpgradeVote.UpgradeApprove, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeApprove") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).BlockHeader.TxnCounter, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TxnCounter") return } } - if zb0004 > 0 { - zb0004-- - var zb0007 int - var zb0008 bool - zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0008 int + var zb0009 bool + zb0008, zb0009, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0007 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0007), uint64(protocol.NumStateProofTypes)) + if zb0008 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0008), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0008 { + if zb0009 { (*z).BlockHeader.StateProofTracking = nil } else if (*z).BlockHeader.StateProofTracking == nil { - (*z).BlockHeader.StateProofTracking = make(map[protocol.StateProofType]StateProofTrackingData, zb0007) + (*z).BlockHeader.StateProofTracking = make(map[protocol.StateProofType]StateProofTrackingData, zb0008) } - for zb0007 > 0 { + for zb0008 > 0 { var zb0001 protocol.StateProofType var zb0002 StateProofTrackingData - zb0007-- + zb0008-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") @@ -695,26 +711,26 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b (*z).BlockHeader.StateProofTracking[zb0001] = zb0002 } } - if zb0004 > 0 { - zb0004-- - var zb0009 int - var zb0010 bool - zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0010 int + var zb0011 bool + zb0010, zb0011, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0009 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0009), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0010 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0010), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0010 { + if zb0011 { (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0009 { - (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0009] + } else if (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0010 { + (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0010] } else { - (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0009) + (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0010) } for zb0003 := range (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -724,16 +740,45 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b } } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- + var zb0012 int + var zb0013 bool + zb0012, zb0013, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0012 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0013 { + (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) >= zb0012 { + (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = ((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts)[:zb0012] + } else { + (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0012) + } + for zb0004 := range (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts", zb0004) + return + } + } + } + if zb0005 > 0 { + zb0005-- bts, err = (*z).Payset.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Payset") return } } - if zb0004 > 0 { - err = msgp.ErrTooManyArrayFields(zb0004) + if zb0005 > 0 { + err = msgp.ErrTooManyArrayFields(zb0005) if err != nil { err = msgp.WrapError(err, "struct-from-array") return @@ -744,11 +789,11 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b err = msgp.WrapError(err) return } - if zb0005 { + if zb0006 { (*z) = Block{} } - for zb0004 > 0 { - zb0004-- + for zb0005 > 0 { + zb0005-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err) @@ -792,14 +837,14 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b return } case "gen": - var zb0011 int - zb0011, err = msgp.ReadBytesBytesHeader(bts) + var zb0014 int + zb0014, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "GenesisID") return } - if zb0011 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0011), uint64(config.MaxGenesisIDLen)) + if zb0014 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0014), uint64(config.MaxGenesisIDLen)) return } (*z).BlockHeader.GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -916,27 +961,27 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b return } case "spt": - var zb0012 int - var zb0013 bool - zb0012, zb0013, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0015 int + var zb0016 bool + zb0015, zb0016, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "StateProofTracking") return } - if zb0012 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0012), uint64(protocol.NumStateProofTypes)) + if zb0015 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0015), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "StateProofTracking") return } - if zb0013 { + if zb0016 { (*z).BlockHeader.StateProofTracking = nil } else if (*z).BlockHeader.StateProofTracking == nil { - (*z).BlockHeader.StateProofTracking = make(map[protocol.StateProofType]StateProofTrackingData, zb0012) + (*z).BlockHeader.StateProofTracking = make(map[protocol.StateProofType]StateProofTrackingData, zb0015) } - for zb0012 > 0 { + for zb0015 > 0 { var zb0001 protocol.StateProofType var zb0002 StateProofTrackingData - zb0012-- + zb0015-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "StateProofTracking") @@ -950,24 +995,24 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b (*z).BlockHeader.StateProofTracking[zb0001] = zb0002 } case "partupdrmv": - var zb0014 int - var zb0015 bool - zb0014, zb0015, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0017 int + var zb0018 bool + zb0017, zb0018, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0014 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0014), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0017 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0017), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0015 { + if zb0018 { (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0014 { - (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0014] + } else if (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) >= zb0017 { + (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = ((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts)[:zb0017] } else { - (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0014) + (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0017) } for zb0003 := range (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -976,6 +1021,33 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b return } } + case "partupdabs": + var zb0019 int + var zb0020 bool + zb0019, zb0020, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0019 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0019), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0020 { + (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) >= zb0019 { + (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = ((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts)[:zb0019] + } else { + (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0019) + } + for zb0004 := range (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts", zb0004) + return + } + } case "txns": bts, err = (*z).Payset.UnmarshalMsgWithState(bts, st) if err != nil { @@ -1017,13 +1089,17 @@ func (z *Block) Msgsize() (s int) { for zb0003 := range (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts { s += (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].Msgsize() } + s += 11 + msgp.ArrayHeaderSize + for zb0004 := range (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts { + s += (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].Msgsize() + } s += 5 + (*z).Payset.Msgsize() return } // MsgIsZero returns whether this is a zero value func (z *Block) MsgIsZero() bool { - return ((*z).BlockHeader.Round.MsgIsZero()) && ((*z).BlockHeader.Branch.MsgIsZero()) && ((*z).BlockHeader.Seed.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).BlockHeader.TimeStamp == 0) && ((*z).BlockHeader.GenesisID == "") && ((*z).BlockHeader.GenesisHash.MsgIsZero()) && ((*z).BlockHeader.Proposer.MsgIsZero()) && ((*z).BlockHeader.FeesCollected.MsgIsZero()) && ((*z).BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).BlockHeader.RewardsState.RewardsRate == 0) && ((*z).BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).BlockHeader.TxnCounter == 0) && (len((*z).BlockHeader.StateProofTracking) == 0) && (len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && ((*z).Payset.MsgIsZero()) + return ((*z).BlockHeader.Round.MsgIsZero()) && ((*z).BlockHeader.Branch.MsgIsZero()) && ((*z).BlockHeader.Seed.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).BlockHeader.TimeStamp == 0) && ((*z).BlockHeader.GenesisID == "") && ((*z).BlockHeader.GenesisHash.MsgIsZero()) && ((*z).BlockHeader.Proposer.MsgIsZero()) && ((*z).BlockHeader.FeesCollected.MsgIsZero()) && ((*z).BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).BlockHeader.RewardsState.RewardsRate == 0) && ((*z).BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).BlockHeader.TxnCounter == 0) && (len((*z).BlockHeader.StateProofTracking) == 0) && (len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Payset.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type @@ -1037,6 +1113,9 @@ func BlockMaxSize() (s int) { s += 11 // Calculating size of slice: z.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) + s += 11 + // Calculating size of slice: z.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts + s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.Payset s += config.MaxTxnBytesPerBlock @@ -1083,170 +1162,186 @@ func BlockHashMaxSize() int { func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0004Len := uint32(27) - var zb0004Mask uint32 /* 32 bits */ + zb0005Len := uint32(28) + var zb0005Mask uint64 /* 33 bits */ if (*z).RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x20 + zb0005Len-- + zb0005Mask |= 0x20 } if (*z).FeesCollected.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40 + zb0005Len-- + zb0005Mask |= 0x40 } if (*z).RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x80 } if (*z).RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x100 } if (*z).GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x4000 + } + if len((*z).ParticipationUpdates.AbsentParticipationAccounts) == 0 { + zb0005Len-- + zb0005Mask |= 0x8000 } if len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0 { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x10000 } if (*z).Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000 + zb0005Len-- + zb0005Mask |= 0x20000 } if (*z).UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x40000 } if (*z).Proposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x80000 } if (*z).RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x100000 } if (*z).Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x200000 } if (*z).RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000 + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x800000 } if (*z).Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800000 + zb0005Len-- + zb0005Mask |= 0x1000000 } if len((*z).StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x2000000 + zb0005Len-- + zb0005Mask |= 0x4000000 } if (*z).TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x10000000 } if (*z).TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if (*z).UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if (*z).UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x80000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } - // variable map header, size zb0004Len - o = msgp.AppendMapHeader(o, zb0004Len) - if zb0004Len != 0 { - if (zb0004Mask & 0x20) == 0 { // if not empty + // variable map header, size zb0005Len + o = msgp.AppendMapHeader(o, zb0005Len) + if zb0005Len != 0 { + if (zb0005Mask & 0x20) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).RewardsState.RewardsLevel) } - if (zb0004Mask & 0x40) == 0 { // if not empty + if (zb0005Mask & 0x40) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).FeesCollected.MarshalMsg(o) } - if (zb0004Mask & 0x80) == 0 { // if not empty + if (zb0005Mask & 0x80) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).RewardsState.FeeSink.MarshalMsg(o) } - if (zb0004Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x100) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).RewardsState.RewardsResidue) } - if (zb0004Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).GenesisID) } - if (zb0004Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).GenesisHash.MarshalMsg(o) } - if (zb0004Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0004Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0004Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).UpgradeState.NextProtocolApprovals) } - if (zb0004Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty + // string "partupdabs" + o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) + if (*z).ParticipationUpdates.AbsentParticipationAccounts == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len((*z).ParticipationUpdates.AbsentParticipationAccounts))) + } + for zb0004 := range (*z).ParticipationUpdates.AbsentParticipationAccounts { + o = (*z).ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) + } + } + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -1258,47 +1353,47 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = (*z).ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).Branch.MarshalMsg(o) } - if (zb0004Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0004Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).Proposer.MarshalMsg(o) } - if (zb0004Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).RewardsState.RewardsRate) } - if (zb0004Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).Round.MarshalMsg(o) } - if (zb0004Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0004Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).Seed.MarshalMsg(o) } - if (zb0004Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).StateProofTracking == nil { @@ -1318,37 +1413,37 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).TxnCounter) } - if (zb0004Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).TimeStamp) } - if (zb0004Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0004Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0004Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0004Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).UpgradeVote.UpgradeApprove) @@ -1371,73 +1466,73 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) st.AllowableDepth-- var field []byte _ = field - var zb0004 int - var zb0005 bool - zb0004, zb0005, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0005 int + var zb0006 bool + zb0005, zb0006, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0004, zb0005, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0005, zb0006, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err) return } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Round.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Round") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Branch.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Branch") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Seed.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Seed") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).TxnCommitments.NativeSha512_256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NativeSha512_256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).TxnCommitments.Sha256Commitment.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Sha256Commitment") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).TimeStamp, bts, err = msgp.ReadInt64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TimeStamp") return } } - if zb0004 > 0 { - zb0004-- - var zb0006 int - zb0006, err = msgp.ReadBytesBytesHeader(bts) + if zb0005 > 0 { + zb0005-- + var zb0007 int + zb0007, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisID") return } - if zb0006 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0006), uint64(config.MaxGenesisIDLen)) + if zb0007 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0007), uint64(config.MaxGenesisIDLen)) return } (*z).GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -1446,173 +1541,173 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).GenesisHash.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "GenesisHash") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).Proposer.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "Proposer") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).FeesCollected.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeesCollected") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "FeeSink") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).RewardsState.RewardsPool.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsPool") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).RewardsState.RewardsLevel, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsLevel") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).RewardsState.RewardsRate, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRate") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).RewardsState.RewardsResidue, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsResidue") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).RewardsState.RewardsRecalculationRound.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "RewardsRecalculationRound") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).UpgradeState.CurrentProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "CurrentProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).UpgradeState.NextProtocol.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocol") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).UpgradeState.NextProtocolApprovals, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolApprovals") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).UpgradeState.NextProtocolVoteBefore.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolVoteBefore") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).UpgradeState.NextProtocolSwitchOn.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "NextProtocolSwitchOn") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).UpgradeVote.UpgradePropose.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradePropose") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- bts, err = (*z).UpgradeVote.UpgradeDelay.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeDelay") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).UpgradeVote.UpgradeApprove, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "UpgradeApprove") return } } - if zb0004 > 0 { - zb0004-- + if zb0005 > 0 { + zb0005-- (*z).TxnCounter, bts, err = msgp.ReadUint64Bytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "TxnCounter") return } } - if zb0004 > 0 { - zb0004-- - var zb0007 int - var zb0008 bool - zb0007, zb0008, bts, err = msgp.ReadMapHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0008 int + var zb0009 bool + zb0008, zb0009, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0007 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0007), uint64(protocol.NumStateProofTypes)) + if zb0008 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0008), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") return } - if zb0008 { + if zb0009 { (*z).StateProofTracking = nil } else if (*z).StateProofTracking == nil { - (*z).StateProofTracking = make(map[protocol.StateProofType]StateProofTrackingData, zb0007) + (*z).StateProofTracking = make(map[protocol.StateProofType]StateProofTrackingData, zb0008) } - for zb0007 > 0 { + for zb0008 > 0 { var zb0001 protocol.StateProofType var zb0002 StateProofTrackingData - zb0007-- + zb0008-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "struct-from-array", "StateProofTracking") @@ -1626,26 +1721,26 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (*z).StateProofTracking[zb0001] = zb0002 } } - if zb0004 > 0 { - zb0004-- - var zb0009 int - var zb0010 bool - zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0005 > 0 { + zb0005-- + var zb0010 int + var zb0011 bool + zb0010, zb0011, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0009 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0009), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0010 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0010), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0010 { + if zb0011 { (*z).ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).ParticipationUpdates.ExpiredParticipationAccounts) >= zb0009 { - (*z).ParticipationUpdates.ExpiredParticipationAccounts = ((*z).ParticipationUpdates.ExpiredParticipationAccounts)[:zb0009] + } else if (*z).ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).ParticipationUpdates.ExpiredParticipationAccounts) >= zb0010 { + (*z).ParticipationUpdates.ExpiredParticipationAccounts = ((*z).ParticipationUpdates.ExpiredParticipationAccounts)[:zb0010] } else { - (*z).ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0009) + (*z).ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0010) } for zb0003 := range (*z).ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -1655,8 +1750,37 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) } } } - if zb0004 > 0 { - err = msgp.ErrTooManyArrayFields(zb0004) + if zb0005 > 0 { + zb0005-- + var zb0012 int + var zb0013 bool + zb0012, zb0013, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0012 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0013 { + (*z).ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).ParticipationUpdates.AbsentParticipationAccounts) >= zb0012 { + (*z).ParticipationUpdates.AbsentParticipationAccounts = ((*z).ParticipationUpdates.AbsentParticipationAccounts)[:zb0012] + } else { + (*z).ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0012) + } + for zb0004 := range (*z).ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts", zb0004) + return + } + } + } + if zb0005 > 0 { + err = msgp.ErrTooManyArrayFields(zb0005) if err != nil { err = msgp.WrapError(err, "struct-from-array") return @@ -1667,11 +1791,11 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err) return } - if zb0005 { + if zb0006 { (*z) = BlockHeader{} } - for zb0004 > 0 { - zb0004-- + for zb0005 > 0 { + zb0005-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err) @@ -1715,14 +1839,14 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } case "gen": - var zb0011 int - zb0011, err = msgp.ReadBytesBytesHeader(bts) + var zb0014 int + zb0014, err = msgp.ReadBytesBytesHeader(bts) if err != nil { err = msgp.WrapError(err, "GenesisID") return } - if zb0011 > config.MaxGenesisIDLen { - err = msgp.ErrOverflow(uint64(zb0011), uint64(config.MaxGenesisIDLen)) + if zb0014 > config.MaxGenesisIDLen { + err = msgp.ErrOverflow(uint64(zb0014), uint64(config.MaxGenesisIDLen)) return } (*z).GenesisID, bts, err = msgp.ReadStringBytes(bts) @@ -1839,27 +1963,27 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } case "spt": - var zb0012 int - var zb0013 bool - zb0012, zb0013, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0015 int + var zb0016 bool + zb0015, zb0016, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "StateProofTracking") return } - if zb0012 > protocol.NumStateProofTypes { - err = msgp.ErrOverflow(uint64(zb0012), uint64(protocol.NumStateProofTypes)) + if zb0015 > protocol.NumStateProofTypes { + err = msgp.ErrOverflow(uint64(zb0015), uint64(protocol.NumStateProofTypes)) err = msgp.WrapError(err, "StateProofTracking") return } - if zb0013 { + if zb0016 { (*z).StateProofTracking = nil } else if (*z).StateProofTracking == nil { - (*z).StateProofTracking = make(map[protocol.StateProofType]StateProofTrackingData, zb0012) + (*z).StateProofTracking = make(map[protocol.StateProofType]StateProofTrackingData, zb0015) } - for zb0012 > 0 { + for zb0015 > 0 { var zb0001 protocol.StateProofType var zb0002 StateProofTrackingData - zb0012-- + zb0015-- bts, err = zb0001.UnmarshalMsgWithState(bts, st) if err != nil { err = msgp.WrapError(err, "StateProofTracking") @@ -1873,24 +1997,24 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (*z).StateProofTracking[zb0001] = zb0002 } case "partupdrmv": - var zb0014 int - var zb0015 bool - zb0014, zb0015, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0017 int + var zb0018 bool + zb0017, zb0018, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0014 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0014), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0017 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0017), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0015 { + if zb0018 { (*z).ParticipationUpdates.ExpiredParticipationAccounts = nil - } else if (*z).ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).ParticipationUpdates.ExpiredParticipationAccounts) >= zb0014 { - (*z).ParticipationUpdates.ExpiredParticipationAccounts = ((*z).ParticipationUpdates.ExpiredParticipationAccounts)[:zb0014] + } else if (*z).ParticipationUpdates.ExpiredParticipationAccounts != nil && cap((*z).ParticipationUpdates.ExpiredParticipationAccounts) >= zb0017 { + (*z).ParticipationUpdates.ExpiredParticipationAccounts = ((*z).ParticipationUpdates.ExpiredParticipationAccounts)[:zb0017] } else { - (*z).ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0014) + (*z).ParticipationUpdates.ExpiredParticipationAccounts = make([]basics.Address, zb0017) } for zb0003 := range (*z).ParticipationUpdates.ExpiredParticipationAccounts { bts, err = (*z).ParticipationUpdates.ExpiredParticipationAccounts[zb0003].UnmarshalMsgWithState(bts, st) @@ -1899,6 +2023,33 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } + case "partupdabs": + var zb0019 int + var zb0020 bool + zb0019, zb0020, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0019 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0019), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0020 { + (*z).ParticipationUpdates.AbsentParticipationAccounts = nil + } else if (*z).ParticipationUpdates.AbsentParticipationAccounts != nil && cap((*z).ParticipationUpdates.AbsentParticipationAccounts) >= zb0019 { + (*z).ParticipationUpdates.AbsentParticipationAccounts = ((*z).ParticipationUpdates.AbsentParticipationAccounts)[:zb0019] + } else { + (*z).ParticipationUpdates.AbsentParticipationAccounts = make([]basics.Address, zb0019) + } + for zb0004 := range (*z).ParticipationUpdates.AbsentParticipationAccounts { + bts, err = (*z).ParticipationUpdates.AbsentParticipationAccounts[zb0004].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts", zb0004) + return + } + } default: err = msgp.ErrNoField(string(field)) if err != nil { @@ -1934,12 +2085,16 @@ func (z *BlockHeader) Msgsize() (s int) { for zb0003 := range (*z).ParticipationUpdates.ExpiredParticipationAccounts { s += (*z).ParticipationUpdates.ExpiredParticipationAccounts[zb0003].Msgsize() } + s += 11 + msgp.ArrayHeaderSize + for zb0004 := range (*z).ParticipationUpdates.AbsentParticipationAccounts { + s += (*z).ParticipationUpdates.AbsentParticipationAccounts[zb0004].Msgsize() + } return } // MsgIsZero returns whether this is a zero value func (z *BlockHeader) MsgIsZero() bool { - return ((*z).Round.MsgIsZero()) && ((*z).Branch.MsgIsZero()) && ((*z).Seed.MsgIsZero()) && ((*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).TimeStamp == 0) && ((*z).GenesisID == "") && ((*z).GenesisHash.MsgIsZero()) && ((*z).Proposer.MsgIsZero()) && ((*z).FeesCollected.MsgIsZero()) && ((*z).RewardsState.FeeSink.MsgIsZero()) && ((*z).RewardsState.RewardsPool.MsgIsZero()) && ((*z).RewardsState.RewardsLevel == 0) && ((*z).RewardsState.RewardsRate == 0) && ((*z).RewardsState.RewardsResidue == 0) && ((*z).RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocolApprovals == 0) && ((*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).UpgradeVote.UpgradeApprove == false) && ((*z).TxnCounter == 0) && (len((*z).StateProofTracking) == 0) && (len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0) + return ((*z).Round.MsgIsZero()) && ((*z).Branch.MsgIsZero()) && ((*z).Seed.MsgIsZero()) && ((*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).TimeStamp == 0) && ((*z).GenesisID == "") && ((*z).GenesisHash.MsgIsZero()) && ((*z).Proposer.MsgIsZero()) && ((*z).FeesCollected.MsgIsZero()) && ((*z).RewardsState.FeeSink.MsgIsZero()) && ((*z).RewardsState.RewardsPool.MsgIsZero()) && ((*z).RewardsState.RewardsLevel == 0) && ((*z).RewardsState.RewardsRate == 0) && ((*z).RewardsState.RewardsResidue == 0) && ((*z).RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocolApprovals == 0) && ((*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).UpgradeVote.UpgradeApprove == false) && ((*z).TxnCounter == 0) && (len((*z).StateProofTracking) == 0) && (len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).ParticipationUpdates.AbsentParticipationAccounts) == 0) } // MaxSize returns a maximum valid message size for this message type @@ -1953,6 +2108,9 @@ func BlockHeaderMaxSize() (s int) { s += 11 // Calculating size of slice: z.ParticipationUpdates.ExpiredParticipationAccounts s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) + s += 11 + // Calculating size of slice: z.ParticipationUpdates.AbsentParticipationAccounts + s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) return } @@ -2967,16 +3125,32 @@ func LightBlockHeaderMaxSize() (s int) { func (z *ParticipationUpdates) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0002Len := uint32(1) - var zb0002Mask uint8 /* 2 bits */ - if len((*z).ExpiredParticipationAccounts) == 0 { - zb0002Len-- - zb0002Mask |= 0x2 + zb0003Len := uint32(2) + var zb0003Mask uint8 /* 3 bits */ + if len((*z).AbsentParticipationAccounts) == 0 { + zb0003Len-- + zb0003Mask |= 0x2 } - // variable map header, size zb0002Len - o = append(o, 0x80|uint8(zb0002Len)) - if zb0002Len != 0 { - if (zb0002Mask & 0x2) == 0 { // if not empty + if len((*z).ExpiredParticipationAccounts) == 0 { + zb0003Len-- + zb0003Mask |= 0x4 + } + // variable map header, size zb0003Len + o = append(o, 0x80|uint8(zb0003Len)) + if zb0003Len != 0 { + if (zb0003Mask & 0x2) == 0 { // if not empty + // string "partupdabs" + o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) + if (*z).AbsentParticipationAccounts == nil { + o = msgp.AppendNil(o) + } else { + o = msgp.AppendArrayHeader(o, uint32(len((*z).AbsentParticipationAccounts))) + } + for zb0002 := range (*z).AbsentParticipationAccounts { + o = (*z).AbsentParticipationAccounts[zb0002].MarshalMsg(o) + } + } + if (zb0003Mask & 0x4) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).ExpiredParticipationAccounts == nil { @@ -3006,35 +3180,35 @@ func (z *ParticipationUpdates) UnmarshalMsgWithState(bts []byte, st msgp.Unmarsh st.AllowableDepth-- var field []byte _ = field - var zb0002 int - var zb0003 bool - zb0002, zb0003, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0003 int + var zb0004 bool + zb0003, zb0004, bts, err = msgp.ReadMapHeaderBytes(bts) if _, ok := err.(msgp.TypeError); ok { - zb0002, zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) + zb0003, zb0004, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err) return } - if zb0002 > 0 { - zb0002-- - var zb0004 int - var zb0005 bool - zb0004, zb0005, bts, err = msgp.ReadArrayHeaderBytes(bts) + if zb0003 > 0 { + zb0003-- + var zb0005 int + var zb0006 bool + zb0005, zb0006, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0004 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0004), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0005 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0005), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "struct-from-array", "ExpiredParticipationAccounts") return } - if zb0005 { + if zb0006 { (*z).ExpiredParticipationAccounts = nil - } else if (*z).ExpiredParticipationAccounts != nil && cap((*z).ExpiredParticipationAccounts) >= zb0004 { - (*z).ExpiredParticipationAccounts = ((*z).ExpiredParticipationAccounts)[:zb0004] + } else if (*z).ExpiredParticipationAccounts != nil && cap((*z).ExpiredParticipationAccounts) >= zb0005 { + (*z).ExpiredParticipationAccounts = ((*z).ExpiredParticipationAccounts)[:zb0005] } else { - (*z).ExpiredParticipationAccounts = make([]basics.Address, zb0004) + (*z).ExpiredParticipationAccounts = make([]basics.Address, zb0005) } for zb0001 := range (*z).ExpiredParticipationAccounts { bts, err = (*z).ExpiredParticipationAccounts[zb0001].UnmarshalMsgWithState(bts, st) @@ -3044,8 +3218,37 @@ func (z *ParticipationUpdates) UnmarshalMsgWithState(bts []byte, st msgp.Unmarsh } } } - if zb0002 > 0 { - err = msgp.ErrTooManyArrayFields(zb0002) + if zb0003 > 0 { + zb0003-- + var zb0007 int + var zb0008 bool + zb0007, zb0008, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0007 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0007), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") + return + } + if zb0008 { + (*z).AbsentParticipationAccounts = nil + } else if (*z).AbsentParticipationAccounts != nil && cap((*z).AbsentParticipationAccounts) >= zb0007 { + (*z).AbsentParticipationAccounts = ((*z).AbsentParticipationAccounts)[:zb0007] + } else { + (*z).AbsentParticipationAccounts = make([]basics.Address, zb0007) + } + for zb0002 := range (*z).AbsentParticipationAccounts { + bts, err = (*z).AbsentParticipationAccounts[zb0002].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts", zb0002) + return + } + } + } + if zb0003 > 0 { + err = msgp.ErrTooManyArrayFields(zb0003) if err != nil { err = msgp.WrapError(err, "struct-from-array") return @@ -3056,11 +3259,11 @@ func (z *ParticipationUpdates) UnmarshalMsgWithState(bts []byte, st msgp.Unmarsh err = msgp.WrapError(err) return } - if zb0003 { + if zb0004 { (*z) = ParticipationUpdates{} } - for zb0002 > 0 { - zb0002-- + for zb0003 > 0 { + zb0003-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { err = msgp.WrapError(err) @@ -3068,24 +3271,24 @@ func (z *ParticipationUpdates) UnmarshalMsgWithState(bts []byte, st msgp.Unmarsh } switch string(field) { case "partupdrmv": - var zb0006 int - var zb0007 bool - zb0006, zb0007, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0009 int + var zb0010 bool + zb0009, zb0010, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0006 > config.MaxProposedExpiredOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0006), uint64(config.MaxProposedExpiredOnlineAccounts)) + if zb0009 > config.MaxProposedExpiredOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0009), uint64(config.MaxProposedExpiredOnlineAccounts)) err = msgp.WrapError(err, "ExpiredParticipationAccounts") return } - if zb0007 { + if zb0010 { (*z).ExpiredParticipationAccounts = nil - } else if (*z).ExpiredParticipationAccounts != nil && cap((*z).ExpiredParticipationAccounts) >= zb0006 { - (*z).ExpiredParticipationAccounts = ((*z).ExpiredParticipationAccounts)[:zb0006] + } else if (*z).ExpiredParticipationAccounts != nil && cap((*z).ExpiredParticipationAccounts) >= zb0009 { + (*z).ExpiredParticipationAccounts = ((*z).ExpiredParticipationAccounts)[:zb0009] } else { - (*z).ExpiredParticipationAccounts = make([]basics.Address, zb0006) + (*z).ExpiredParticipationAccounts = make([]basics.Address, zb0009) } for zb0001 := range (*z).ExpiredParticipationAccounts { bts, err = (*z).ExpiredParticipationAccounts[zb0001].UnmarshalMsgWithState(bts, st) @@ -3094,6 +3297,33 @@ func (z *ParticipationUpdates) UnmarshalMsgWithState(bts []byte, st msgp.Unmarsh return } } + case "partupdabs": + var zb0011 int + var zb0012 bool + zb0011, zb0012, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0011 > config.MaxProposedAbsentOnlineAccounts { + err = msgp.ErrOverflow(uint64(zb0011), uint64(config.MaxProposedAbsentOnlineAccounts)) + err = msgp.WrapError(err, "AbsentParticipationAccounts") + return + } + if zb0012 { + (*z).AbsentParticipationAccounts = nil + } else if (*z).AbsentParticipationAccounts != nil && cap((*z).AbsentParticipationAccounts) >= zb0011 { + (*z).AbsentParticipationAccounts = ((*z).AbsentParticipationAccounts)[:zb0011] + } else { + (*z).AbsentParticipationAccounts = make([]basics.Address, zb0011) + } + for zb0002 := range (*z).AbsentParticipationAccounts { + bts, err = (*z).AbsentParticipationAccounts[zb0002].UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "AbsentParticipationAccounts", zb0002) + return + } + } default: err = msgp.ErrNoField(string(field)) if err != nil { @@ -3121,12 +3351,16 @@ func (z *ParticipationUpdates) Msgsize() (s int) { for zb0001 := range (*z).ExpiredParticipationAccounts { s += (*z).ExpiredParticipationAccounts[zb0001].Msgsize() } + s += 11 + msgp.ArrayHeaderSize + for zb0002 := range (*z).AbsentParticipationAccounts { + s += (*z).AbsentParticipationAccounts[zb0002].Msgsize() + } return } // MsgIsZero returns whether this is a zero value func (z *ParticipationUpdates) MsgIsZero() bool { - return (len((*z).ExpiredParticipationAccounts) == 0) + return (len((*z).ExpiredParticipationAccounts) == 0) && (len((*z).AbsentParticipationAccounts) == 0) } // MaxSize returns a maximum valid message size for this message type @@ -3134,6 +3368,9 @@ func ParticipationUpdatesMaxSize() (s int) { s = 1 + 11 // Calculating size of slice: z.ExpiredParticipationAccounts s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) + s += 11 + // Calculating size of slice: z.AbsentParticipationAccounts + s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) return } diff --git a/data/transactions/transaction.go b/data/transactions/transaction.go index b0bb7c413b..06ae38c0d6 100644 --- a/data/transactions/transaction.go +++ b/data/transactions/transaction.go @@ -365,8 +365,8 @@ func (tx Transaction) WellFormed(spec SpecialAddresses, proto config.ConsensusPa } // The trio of [VotePK, SelectionPK, VoteKeyDilution] needs to be all zeros or all non-zero for the transaction to be valid. - if !((tx.KeyregTxnFields.VotePK == crypto.OneTimeSignatureVerifier{} && tx.KeyregTxnFields.SelectionPK == crypto.VRFVerifier{} && tx.KeyregTxnFields.VoteKeyDilution == 0) || - (tx.KeyregTxnFields.VotePK != crypto.OneTimeSignatureVerifier{} && tx.KeyregTxnFields.SelectionPK != crypto.VRFVerifier{} && tx.KeyregTxnFields.VoteKeyDilution != 0)) { + if !((tx.KeyregTxnFields.VotePK.IsEmpty() && tx.KeyregTxnFields.SelectionPK.IsEmpty() && tx.KeyregTxnFields.VoteKeyDilution == 0) || + (!tx.KeyregTxnFields.VotePK.IsEmpty() && !tx.KeyregTxnFields.SelectionPK.IsEmpty() && tx.KeyregTxnFields.VoteKeyDilution != 0)) { return errKeyregTxnNonCoherentVotingKeys } @@ -395,7 +395,7 @@ func (tx Transaction) WellFormed(spec SpecialAddresses, proto config.ConsensusPa // that type of transaction, it is invalid. return errKeyregTxnUnsupportedSwitchToNonParticipating } - suppliesNullKeys := tx.KeyregTxnFields.VotePK == crypto.OneTimeSignatureVerifier{} || tx.KeyregTxnFields.SelectionPK == crypto.VRFVerifier{} + suppliesNullKeys := tx.KeyregTxnFields.VotePK.IsEmpty() || tx.KeyregTxnFields.SelectionPK.IsEmpty() if !suppliesNullKeys { return errKeyregTxnGoingOnlineWithNonParticipating } @@ -673,7 +673,7 @@ func (tx Transaction) stateProofPKWellFormed(proto config.ConsensusParams) error return nil } - if tx.VotePK == (crypto.OneTimeSignatureVerifier{}) || tx.SelectionPK == (crypto.VRFVerifier{}) { + if tx.VotePK.IsEmpty() || tx.SelectionPK.IsEmpty() { if !isEmpty { return errKeyregTxnOfflineShouldBeEmptyStateProofPK } diff --git a/data/txntest/txn.go b/data/txntest/txn.go index 26553d6b97..aea4de005b 100644 --- a/data/txntest/txn.go +++ b/data/txntest/txn.go @@ -149,30 +149,39 @@ func (tx *Txn) FillDefaults(params config.ConsensusParams) { tx.LastValid = tx.FirstValid + basics.Round(params.MaxTxnLife) } - if tx.Type == protocol.ApplicationCallTx && - (tx.ApplicationID == 0 || tx.OnCompletion == transactions.UpdateApplicationOC) { - - switch program := tx.ApprovalProgram.(type) { - case nil: - tx.ApprovalProgram = fmt.Sprintf("#pragma version %d\nint 1", params.LogicSigVersion) - case string: - if program != "" && !strings.Contains(program, "#pragma version") { - pragma := fmt.Sprintf("#pragma version %d\n", params.LogicSigVersion) - tx.ApprovalProgram = pragma + program + switch tx.Type { + case protocol.KeyRegistrationTx: + if !tx.VotePK.MsgIsZero() && !tx.SelectionPK.MsgIsZero() { + if tx.VoteLast == 0 { + tx.VoteLast = tx.VoteFirst + 1_000_000 } - case []byte: } + case protocol.ApplicationCallTx: + // fill in empty programs + if tx.ApplicationID == 0 || tx.OnCompletion == transactions.UpdateApplicationOC { + switch program := tx.ApprovalProgram.(type) { + case nil: + tx.ApprovalProgram = fmt.Sprintf("#pragma version %d\nint 1", params.LogicSigVersion) + case string: + if program != "" && !strings.Contains(program, "#pragma version") { + pragma := fmt.Sprintf("#pragma version %d\n", params.LogicSigVersion) + tx.ApprovalProgram = pragma + program + } + case []byte: + } - switch program := tx.ClearStateProgram.(type) { - case nil: - tx.ClearStateProgram = tx.ApprovalProgram - case string: - if program != "" && !strings.Contains(program, "#pragma version") { - pragma := fmt.Sprintf("#pragma version %d\n", params.LogicSigVersion) - tx.ClearStateProgram = pragma + program + switch program := tx.ClearStateProgram.(type) { + case nil: + tx.ClearStateProgram = tx.ApprovalProgram + case string: + if program != "" && !strings.Contains(program, "#pragma version") { + pragma := fmt.Sprintf("#pragma version %d\n", params.LogicSigVersion) + tx.ClearStateProgram = pragma + program + } + case []byte: } - case []byte: } + } } diff --git a/ledger/acctdeltas.go b/ledger/acctdeltas.go index d6bb91881f..6a0764b105 100644 --- a/ledger/acctdeltas.go +++ b/ledger/acctdeltas.go @@ -1013,15 +1013,15 @@ func onlineAccountsNewRoundImpl( } updatedAccounts = append(updatedAccounts, updated) prevAcct = updated - } else if !newAcct.IsVotingEmpty() { + } else if !newAcct.IsVotingEmpty() && newStatus != basics.Suspended { err = fmt.Errorf("non-empty voting data for non-online account %s: %v", data.address.String(), newAcct) return nil, err } } } else { // non-zero rowid means we had a previous value. - if newAcct.IsVotingEmpty() { - // new value is zero then go offline + if newAcct.IsVotingEmpty() || newStatus == basics.Suspended { + // new value is zero, or the account is suspended then go offline if newStatus == basics.Online { err = fmt.Errorf("empty voting data but online account %s: %v", data.address.String(), newAcct) return nil, err diff --git a/ledger/acctdeltas_test.go b/ledger/acctdeltas_test.go index ec00269dbe..e39c88836e 100644 --- a/ledger/acctdeltas_test.go +++ b/ledger/acctdeltas_test.go @@ -74,7 +74,7 @@ func checkAccounts(t *testing.T, tx trackerdb.TransactionScope, rnd basics.Round switch d.Status { case basics.Online: totalOnline += d.MicroAlgos.Raw - case basics.Offline: + case basics.Offline, basics.Suspended: totalOffline += d.MicroAlgos.Raw case basics.NotParticipating: totalNotPart += d.MicroAlgos.Raw diff --git a/ledger/acctupdates_test.go b/ledger/acctupdates_test.go index 1cf080d15e..7e4ed0c3d1 100644 --- a/ledger/acctupdates_test.go +++ b/ledger/acctupdates_test.go @@ -385,18 +385,23 @@ func checkAcctUpdates(t *testing.T, au *accountUpdates, ao *onlineAccounts, base // TODO: make lookupOnlineAccountData returning extended version of ledgercore.VotingData ? od, err := ao.lookupOnlineAccountData(rnd, addr) require.NoError(t, err) - require.Equal(t, od.VoteID, data.VoteID) - require.Equal(t, od.SelectionID, data.SelectionID) - require.Equal(t, od.VoteFirstValid, data.VoteFirstValid) - require.Equal(t, od.VoteLastValid, data.VoteLastValid) - require.Equal(t, od.VoteKeyDilution, data.VoteKeyDilution) + // Unless suspended an account's voting data should agree with + // the online account tracker. (Vaccuously true, when account is + // offline or non-part). + if data.Status != basics.Suspended { + require.Equal(t, od.VoteID, data.VoteID) + require.Equal(t, od.SelectionID, data.SelectionID) + require.Equal(t, od.VoteFirstValid, data.VoteFirstValid) + require.Equal(t, od.VoteLastValid, data.VoteLastValid) + require.Equal(t, od.VoteKeyDilution, data.VoteKeyDilution) + } rewardsDelta := rewards[rnd] - d.RewardsBase switch d.Status { case basics.Online: totalOnline += d.MicroAlgos.Raw totalOnline += (d.MicroAlgos.Raw / proto.RewardUnit) * rewardsDelta - case basics.Offline: + case basics.Offline, basics.Suspended: totalOffline += d.MicroAlgos.Raw totalOffline += (d.MicroAlgos.Raw / proto.RewardUnit) * rewardsDelta case basics.NotParticipating: @@ -504,6 +509,10 @@ func checkOnlineAcctUpdatesConsistency(t *testing.T, ao *onlineAccounts, rnd bas for i := 0; i < latest.Len(); i++ { addr, acct := latest.GetByIdx(i) od, err := ao.lookupOnlineAccountData(rnd, addr) + if acct.Status == basics.Suspended { + // suspended accounts will not match, since they have vote info but not in online accounts + continue + } require.NoError(t, err) require.Equal(t, acct.VoteID, od.VoteID) require.Equal(t, acct.SelectionID, od.SelectionID) diff --git a/ledger/apply/keyreg.go b/ledger/apply/keyreg.go index 6a83eab316..f54d146ba4 100644 --- a/ledger/apply/keyreg.go +++ b/ledger/apply/keyreg.go @@ -20,7 +20,6 @@ import ( "errors" "fmt" - "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" ) @@ -54,7 +53,7 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal if params.EnableStateProofKeyregCheck { record.StateProofID = keyreg.StateProofPK } - if (keyreg.VotePK == crypto.OneTimeSignatureVerifier{} || keyreg.SelectionPK == crypto.VRFVerifier{}) { + if keyreg.VotePK.IsEmpty() || keyreg.SelectionPK.IsEmpty() { if keyreg.Nonparticipation { if params.SupportBecomeNonParticipatingTransactions { record.Status = basics.NotParticipating @@ -78,6 +77,9 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal } } record.Status = basics.Online + if params.EnableAbsenteeTracking() { + record.LastHeartbeat = header.FirstValid + } record.VoteFirstValid = keyreg.VoteFirst record.VoteLastValid = keyreg.VoteLast record.VoteKeyDilution = keyreg.VoteKeyDilution diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index fd4f65fa77..ff96dd1b21 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -797,6 +797,17 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts } } + // Note their proposal + prp, err := eval.state.Get(prevHeader.Proposer, false) + if err != nil { + return nil, err + } + prp.LastProposed = hdr.Round - 1 + err = eval.state.Put(prevHeader.Proposer, prp) + if err != nil { + return nil, err + } + if eval.Tracer != nil { eval.Tracer.BeforeBlock(&eval.block.BlockHeader) } @@ -1338,7 +1349,7 @@ func (eval *BlockEvaluator) endOfBlock() error { eval.block.FeesCollected = eval.state.feesCollected } - eval.generateExpiredOnlineAccountsList() + eval.generateKnockOfflineAccountsList() if eval.proto.StateProofInterval > 0 { var basicStateProof bookkeeping.StateProofTrackingData @@ -1354,13 +1365,17 @@ func (eval *BlockEvaluator) endOfBlock() error { } } - err := eval.validateExpiredOnlineAccounts() - if err != nil { + if err := eval.validateExpiredOnlineAccounts(); err != nil { + return err + } + if err := eval.resetExpiredOnlineAccountsParticipationKeys(); err != nil { return err } - err = eval.resetExpiredOnlineAccountsParticipationKeys() - if err != nil { + if err := eval.validateAbsentOnlineAccounts(); err != nil { + return err + } + if err := eval.suspendAbsentAccounts(); err != nil { return err } @@ -1432,8 +1447,7 @@ func (eval *BlockEvaluator) endOfBlock() error { } } - err = eval.state.CalculateTotals() - if err != nil { + if err := eval.state.CalculateTotals(); err != nil { return err } @@ -1444,9 +1458,10 @@ func (eval *BlockEvaluator) endOfBlock() error { return nil } -// generateExpiredOnlineAccountsList creates the list of the expired participation accounts by traversing over the -// modified accounts in the state deltas and testing if any of them needs to be reset. -func (eval *BlockEvaluator) generateExpiredOnlineAccountsList() { +// generateKnockOfflineAccountsList creates the lists of expired or absent +// participation accounts by traversing over the modified accounts in the state +// deltas and testing if any of them needs to be reset/suspended. +func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { if !eval.generate { return } @@ -1455,32 +1470,59 @@ func (eval *BlockEvaluator) generateExpiredOnlineAccountsList() { // Then we are going to go through each modified account and // see if it meets the criteria for adding it to the expired // participation accounts list. - modifiedAccounts := eval.state.modifiedAccounts() currentRound := eval.Round() expectedMaxNumberOfExpiredAccounts := eval.proto.MaxProposedExpiredOnlineAccounts + expectedMaxNumberOfAbsentAccounts := eval.proto.MaxProposedAbsentOnlineAccounts - for i := 0; i < len(modifiedAccounts) && len(eval.block.ParticipationUpdates.ExpiredParticipationAccounts) < expectedMaxNumberOfExpiredAccounts; i++ { - accountAddr := modifiedAccounts[i] + updates := &eval.block.ParticipationUpdates + + for _, accountAddr := range eval.state.modifiedAccounts() { acctDelta, found := eval.state.mods.Accts.GetData(accountAddr) if !found { continue } - // true if the account is online - isOnline := acctDelta.Status == basics.Online - // true if the accounts last valid round has passed - pastCurrentRound := acctDelta.VoteLastValid < currentRound + if acctDelta.Status == basics.Online || acctDelta.Status == basics.Suspended { + expiresBeforeCurrent := acctDelta.VoteLastValid < currentRound + if expiresBeforeCurrent && + len(updates.ExpiredParticipationAccounts) < expectedMaxNumberOfExpiredAccounts { + updates.ExpiredParticipationAccounts = append( + updates.ExpiredParticipationAccounts, + accountAddr, + ) + continue // if marking expired, do not also suspend + } + } - if isOnline && pastCurrentRound { - eval.block.ParticipationUpdates.ExpiredParticipationAccounts = append( - eval.block.ParticipationUpdates.ExpiredParticipationAccounts, - accountAddr, - ) + if acctDelta.Status == basics.Online { + lastSeen := max(acctDelta.LastHeartbeat, acctDelta.LastHeartbeat) + if isAbsent(eval.state.prevTotals.Online.Money, acctDelta.MicroAlgos, lastSeen, currentRound) && + len(updates.AbsentParticipationAccounts) < expectedMaxNumberOfAbsentAccounts { + updates.AbsentParticipationAccounts = append( + updates.AbsentParticipationAccounts, + accountAddr, + ) + } } } } +// delete me in Go 1.21 +func max(a, b basics.Round) basics.Round { + if a > b { + return a + } + return b +} + +func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, lastSeen basics.Round, current basics.Round) bool { + // See if the account has exceeded 10x their expected observation interval. + allowableLag := basics.Round(10 * totalOnlineStake.Raw / acctStake.Raw) + fmt.Printf("%d / %d -> %d \n", acctStake, totalOnlineStake, allowableLag) + return lastSeen+allowableLag < current +} + // validateExpiredOnlineAccounts tests the expired online accounts specified in ExpiredParticipationAccounts, and verify // that they have all expired and need to be reset. func (eval *BlockEvaluator) validateExpiredOnlineAccounts() error { @@ -1501,7 +1543,7 @@ func (eval *BlockEvaluator) validateExpiredOnlineAccounts() error { // are unique. We make this map to keep track of previously seen address addressSet := make(map[basics.Address]bool, lengthOfExpiredParticipationAccounts) - // Validate that all expired accounts meet the current criteria + // Validate that all proposed accounts have expired keys currentRound := eval.Round() for _, accountAddr := range eval.block.ParticipationUpdates.ExpiredParticipationAccounts { @@ -1518,22 +1560,63 @@ func (eval *BlockEvaluator) validateExpiredOnlineAccounts() error { return fmt.Errorf("endOfBlock was unable to retrieve account %v : %w", accountAddr, err) } - // true if the account is online - isOnline := acctData.Status == basics.Online - // true if the accounts last valid round has passed - pastCurrentRound := acctData.VoteLastValid < currentRound - - if !isOnline { + if acctData.Status != basics.Online && acctData.Status != basics.Suspended { return fmt.Errorf("endOfBlock found %v was not online but %v", accountAddr, acctData.Status) } - if !pastCurrentRound { + if acctData.VoteLastValid >= currentRound { return fmt.Errorf("endOfBlock found %v round (%d) was not less than current round (%d)", accountAddr, acctData.VoteLastValid, currentRound) } } return nil } +// validateAbsentOnlineAccounts tests the accounts specified in +// AbsentParticipationAccounts, and verifies that they need to be reset. +func (eval *BlockEvaluator) validateAbsentOnlineAccounts() error { + if !eval.validate { + return nil + } + expectedMaxNumberOfAbsentAccounts := eval.proto.MaxProposedAbsentOnlineAccounts + lengthOfAbsentParticipationAccounts := len(eval.block.ParticipationUpdates.AbsentParticipationAccounts) + + // If the length of the array is strictly greater than our max then we have an error. + // This works when the expected number of accounts is zero (i.e. it is disabled) as well + if lengthOfAbsentParticipationAccounts > expectedMaxNumberOfAbsentAccounts { + return fmt.Errorf("length of absent accounts (%d) was greater than expected (%d)", + lengthOfAbsentParticipationAccounts, expectedMaxNumberOfAbsentAccounts) + } + + // For consistency with expired account handling, we preclude duplicates + addressSet := make(map[basics.Address]bool, lengthOfAbsentParticipationAccounts) + + // Validate that all accounts have been absent + currentRound := eval.Round() + for _, accountAddr := range eval.block.ParticipationUpdates.AbsentParticipationAccounts { + + if _, exists := addressSet[accountAddr]; exists { + return fmt.Errorf("duplicate address found: %v", accountAddr) + } + addressSet[accountAddr] = true + + acctData, err := eval.state.lookup(accountAddr) + if err != nil { + return fmt.Errorf("unable to retrieve proposed absent account %v : %w", accountAddr, err) + } + + if acctData.Status != basics.Online { + return fmt.Errorf("proposed absent acct %v was not online but %v", accountAddr, acctData.Status) + } + + lastSeen := max(acctData.LastHeartbeat, acctData.LastHeartbeat) + if !isAbsent(eval.state.prevTotals.Online.Money, acctData.MicroAlgos, lastSeen, currentRound) { + return fmt.Errorf("proposed absent account %v is not absent in %d, %d", + accountAddr, acctData.LastProposed, acctData.LastHeartbeat) + } + } + return nil +} + // resetExpiredOnlineAccountsParticipationKeys after all transactions and rewards are processed, modify the accounts so that their status is offline func (eval *BlockEvaluator) resetExpiredOnlineAccountsParticipationKeys() error { expectedMaxNumberOfExpiredAccounts := eval.proto.MaxProposedExpiredOnlineAccounts @@ -1564,6 +1647,24 @@ func (eval *BlockEvaluator) resetExpiredOnlineAccountsParticipationKeys() error return nil } +// suspendAbsentAccounts suspends the proposed list of absent accounts. +func (eval *BlockEvaluator) suspendAbsentAccounts() error { + for _, addr := range eval.block.ParticipationUpdates.AbsentParticipationAccounts { + acct, err := eval.state.lookup(addr) + if err != nil { + return err + } + + acct.Suspend() + + err = eval.state.putAccount(addr, acct) + if err != nil { + return err + } + } + return nil +} + // GenerateBlock produces a complete block from the BlockEvaluator. This is // used during proposal to get an actual block that will be proposed, after // feeding in tentative transactions into this block evaluator. diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index ee9a097b06..76decea684 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -372,6 +372,133 @@ func TestIncentiveEligible(t *testing.T) { }) } +// TestAbsentTracking checks that LastProposed and LastHeartbeat are updated +// properly. +func TestAbsentTracking(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + genBalances, addrs, _ := ledgertesting.NewTestGenesis(func(cfg *ledgertesting.GenesisCfg) { + cfg.OnlineCount = 2 // So we know proposer should propose every 2 rounds, on average + }) + // Absentee checking begins in v39. Start checking in v38 to test that is unchanged. + ledgertesting.TestConsensusRange(t, 38, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { + dl := NewDoubleLedger(t, genBalances, cv, cfg) + defer dl.Close() + + totals, err := dl.generator.Totals(0) + require.NoError(t, err) + require.NotZero(t, totals.Online.Money.Raw) + for i, addr := range addrs { + fmt.Printf("addrs[%d] == %v\n", i, addr) + } + require.True(t, lookup(t, dl.generator, addrs[0]).Status == basics.Online) + require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) + require.False(t, lookup(t, dl.generator, addrs[2]).Status == basics.Online) + + dl.fullBlock() + + proposer := addrs[7] + dl.beginBlock() + dl.txns(&txntest.Txn{ + Type: "pay", + Sender: addrs[1], + Receiver: addrs[2], + Amount: 100_000, + }) + dl.endBlock(proposer) + + newtotals, err := dl.generator.Totals(dl.generator.Latest()) + require.NoError(t, err) + // payment and fee left the online account + require.Equal(t, totals.Online.Money.Raw-100_000-1000, newtotals.Online.Money.Raw) + totals = newtotals + + dl.fullBlock() + + prp := lookup(t, dl.validator, proposer) + + if ver >= 39 { + // version sanity check + require.True(t, dl.generator.GenesisProto().EnableAbsenteeTracking()) + require.NotZero(t, prp.LastProposed) + require.Zero(t, prp.LastHeartbeat) // genesis participants have never hb + } else { + require.False(t, dl.generator.GenesisProto().EnableAbsenteeTracking()) + require.Zero(t, prp.LastProposed) + require.Zero(t, prp.LastHeartbeat) + } + + // addrs[2] was already offline + dl.txns(&txntest.Txn{Type: "keyreg", Sender: addrs[2]}) // OFFLINE keyreg + regger := lookup(t, dl.validator, addrs[2]) + + newtotals, err = dl.generator.Totals(dl.generator.Latest()) + require.NoError(t, err) + require.Equal(t, totals.Online.Money.Raw, newtotals.Online.Money.Raw) + + // offline transaction records nothing + require.Zero(t, regger.LastProposed) + require.Zero(t, regger.LastHeartbeat) + + // ONLINE keyreg + dl.txns(&txntest.Txn{ + Type: "keyreg", + Sender: addrs[2], + VotePK: [32]byte{1}, + SelectionPK: [32]byte{1}, + }) + newtotals, err = dl.generator.Totals(dl.generator.Latest()) + require.NoError(t, err) + require.Greater(t, newtotals.Online.Money.Raw, totals.Online.Money.Raw) + + regger = lookup(t, dl.validator, addrs[2]) + require.Zero(t, regger.LastProposed) + require.True(t, regger.Status == basics.Online) + + if ver >= 39 { + require.NotZero(t, regger.LastHeartbeat) // online keyreg caused update + } else { + require.Zero(t, regger.LastHeartbeat) + } + + for i := 0; i < 5; i++ { + dl.fullBlock() + require.True(t, lookup(t, dl.generator, addrs[0]).Status == basics.Online) + require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) + require.True(t, lookup(t, dl.generator, addrs[2]).Status == basics.Online) + } + + // all are still online after a few blocks + require.True(t, lookup(t, dl.generator, addrs[0]).Status == basics.Online) + require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) + require.True(t, lookup(t, dl.generator, addrs[2]).Status == basics.Online) + + for i := 0; i < 30; i++ { + dl.fullBlock() + } + + // addrs 0-2 all have about 1/3 of stake, so become eligible for + // suspension after 30 rounds. We're at about 35. But, since blocks are + // empty, nobody's susspendible account is noticed. + require.True(t, lookup(t, dl.generator, addrs[0]).Status == basics.Online) + require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) + require.True(t, lookup(t, dl.generator, addrs[2]).Status == basics.Online) + + // when 2 pays 0, they both get noticed and get suspended + dl.txns(&txntest.Txn{ + Type: "pay", + Sender: addrs[2], + Receiver: addrs[0], + Amount: 0, + }) + require.Equal(t, ver < 39, lookup(t, dl.generator, addrs[0]).Status == basics.Online) + require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) + require.Equal(t, ver < 39, lookup(t, dl.generator, addrs[2]).Status == basics.Online) + + }) +} + // TestHoldingGet tests some of the corner cases for the asset_holding_get // opcode: the asset doesn't exist, the account doesn't exist, account not opted // in, vs it has none of the asset. This is tested here, even though it should diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 7fde54c0aa..11982250d3 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -49,6 +49,9 @@ type AccountBaseData struct { TotalAssets uint64 // Total of asset creations and optins (i.e. number of holdings) TotalBoxes uint64 // Total number of boxes associated to this account TotalBoxBytes uint64 // Total bytes for this account's boxes. keys _and_ values count + + LastProposed basics.Round // The last round that this account proposed the winning block. + LastHeartbeat basics.Round // The last round that this account sent a heartbeat to show it was online. } // VotingData holds participation information @@ -87,6 +90,9 @@ func ToAccountData(acct basics.AccountData) AccountData { TotalAppLocalStates: uint64(len(acct.AppLocalStates)), TotalBoxes: acct.TotalBoxes, TotalBoxBytes: acct.TotalBoxBytes, + + LastProposed: acct.LastProposed, + LastHeartbeat: acct.LastHeartbeat, }, VotingData: VotingData{ VoteID: acct.VoteID, @@ -120,6 +126,9 @@ func AssignAccountData(a *basics.AccountData, acct AccountData) { a.TotalExtraAppPages = acct.TotalExtraAppPages a.TotalBoxes = acct.TotalBoxes a.TotalBoxBytes = acct.TotalBoxBytes + + a.LastProposed = acct.LastProposed + a.LastHeartbeat = acct.LastHeartbeat } // WithUpdatedRewards calls basics account data WithUpdatedRewards @@ -136,6 +145,12 @@ func (u *AccountData) ClearOnlineState() { u.VotingData = VotingData{} } +// Suspend sets the status to Suspended, but does _not_ clear voting keys, so +// that a heartbeat can bring the account back Online +func (u *AccountData) Suspend() { + u.Status = basics.Suspended +} + // MinBalance computes the minimum balance requirements for an account based on // some consensus parameters. MinBalance should correspond roughly to how much // storage the account is allowed to store on disk. diff --git a/ledger/ledgercore/totals.go b/ledger/ledgercore/totals.go index 22708586b1..f4fac018a0 100644 --- a/ledger/ledgercore/totals.go +++ b/ledger/ledgercore/totals.go @@ -66,7 +66,7 @@ func (at *AccountTotals) statusField(status basics.Status) *AlgoCount { switch status { case basics.Online: return &at.Online - case basics.Offline: + case basics.Offline, basics.Suspended: return &at.Offline case basics.NotParticipating: return &at.NotParticipating diff --git a/ledger/simple_test.go b/ledger/simple_test.go index b42d3a53f2..d3ad9f4ed2 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -101,6 +101,9 @@ func fillDefaults(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, txn * if txn.FirstValid == 0 { txn.FirstValid = eval.Round() } + if txn.Type == protocol.KeyRegistrationTx && txn.VoteFirst == 0 { + txn.VoteFirst = eval.Round() + } txn.FillDefaults(ledger.GenesisProto()) } diff --git a/ledger/store/trackerdb/data.go b/ledger/store/trackerdb/data.go index 2e9ee4778e..adb8f4e0e5 100644 --- a/ledger/store/trackerdb/data.go +++ b/ledger/store/trackerdb/data.go @@ -48,6 +48,8 @@ type BaseAccountData struct { TotalBoxes uint64 `codec:"m"` TotalBoxBytes uint64 `codec:"n"` IncentiveEligible bool `codec:"o"` + LastProposed basics.Round `codec:"p"` + LastHeartbeat basics.Round `codec:"q"` BaseVotingData @@ -289,6 +291,9 @@ func (ba *BaseAccountData) SetCoreAccountData(ad *ledgercore.AccountData) { ba.TotalBoxBytes = ad.TotalBoxBytes ba.IncentiveEligible = ad.IncentiveEligible + ba.LastProposed = ad.LastProposed + ba.LastHeartbeat = ad.LastHeartbeat + ba.BaseVotingData.SetCoreAccountData(ad) } @@ -310,6 +315,9 @@ func (ba *BaseAccountData) SetAccountData(ad *basics.AccountData) { ba.TotalBoxBytes = ad.TotalBoxBytes ba.IncentiveEligible = ad.IncentiveEligible + ba.LastProposed = ad.LastProposed + ba.LastHeartbeat = ad.LastHeartbeat + ba.BaseVotingData.VoteID = ad.VoteID ba.BaseVotingData.SelectionID = ad.SelectionID ba.BaseVotingData.StateProofID = ad.StateProofID @@ -346,6 +354,9 @@ func (ba *BaseAccountData) GetLedgerCoreAccountBaseData() ledgercore.AccountBase TotalBoxes: ba.TotalBoxes, TotalBoxBytes: ba.TotalBoxBytes, IncentiveEligible: ba.IncentiveEligible, + + LastProposed: ba.LastProposed, + LastHeartbeat: ba.LastHeartbeat, } } @@ -384,6 +395,9 @@ func (ba *BaseAccountData) GetAccountData() basics.AccountData { VoteFirstValid: ba.VoteFirstValid, VoteLastValid: ba.VoteLastValid, VoteKeyDilution: ba.VoteKeyDilution, + + LastProposed: ba.LastProposed, + LastHeartbeat: ba.LastHeartbeat, } } @@ -404,6 +418,8 @@ func (ba *BaseAccountData) IsEmpty() bool { ba.TotalAppLocalStates == 0 && ba.TotalBoxes == 0 && ba.TotalBoxBytes == 0 && + ba.LastProposed == 0 && + ba.LastHeartbeat == 0 && ba.BaseVotingData.IsEmpty() } @@ -427,7 +443,7 @@ func (bo *BaseOnlineAccountData) IsVotingEmpty() bool { return bo.BaseVotingData.IsEmpty() } -// IsEmpty return true if any of the fields are non-zero. +// IsEmpty return true if all of the fields are zero. func (bo *BaseOnlineAccountData) IsEmpty() bool { return bo.IsVotingEmpty() && bo.MicroAlgos.Raw == 0 && diff --git a/ledger/store/trackerdb/data_test.go b/ledger/store/trackerdb/data_test.go index 8c91b028de..f15a43b216 100644 --- a/ledger/store/trackerdb/data_test.go +++ b/ledger/store/trackerdb/data_test.go @@ -1105,7 +1105,7 @@ func TestBaseAccountDataIsEmpty(t *testing.T) { structureTesting := func(t *testing.T) { encoding, err := json.Marshal(&empty) zeros32 := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - expectedEncoding := `{"Status":0,"MicroAlgos":{"Raw":0},"RewardsBase":0,"RewardedMicroAlgos":{"Raw":0},"AuthAddr":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ","TotalAppSchemaNumUint":0,"TotalAppSchemaNumByteSlice":0,"TotalExtraAppPages":0,"TotalAssetParams":0,"TotalAssets":0,"TotalAppParams":0,"TotalAppLocalStates":0,"TotalBoxes":0,"TotalBoxBytes":0,"IncentiveEligible":false,"VoteID":[` + zeros32 + `],"SelectionID":[` + zeros32 + `],"VoteFirstValid":0,"VoteLastValid":0,"VoteKeyDilution":0,"StateProofID":[` + zeros32 + `,` + zeros32 + `],"UpdateRound":0}` + expectedEncoding := `{"Status":0,"MicroAlgos":{"Raw":0},"RewardsBase":0,"RewardedMicroAlgos":{"Raw":0},"AuthAddr":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ","TotalAppSchemaNumUint":0,"TotalAppSchemaNumByteSlice":0,"TotalExtraAppPages":0,"TotalAssetParams":0,"TotalAssets":0,"TotalAppParams":0,"TotalAppLocalStates":0,"TotalBoxes":0,"TotalBoxBytes":0,"IncentiveEligible":false,"LastProposed":0,"LastHeartbeat":0,"VoteID":[` + zeros32 + `],"SelectionID":[` + zeros32 + `],"VoteFirstValid":0,"VoteLastValid":0,"VoteKeyDilution":0,"StateProofID":[` + zeros32 + `,` + zeros32 + `],"UpdateRound":0}` require.NoError(t, err) require.Equal(t, expectedEncoding, string(encoding)) } diff --git a/ledger/store/trackerdb/msgp_gen.go b/ledger/store/trackerdb/msgp_gen.go index 8b0092f4c1..285b0e5b44 100644 --- a/ledger/store/trackerdb/msgp_gen.go +++ b/ledger/store/trackerdb/msgp_gen.go @@ -100,8 +100,8 @@ import ( func (z *BaseAccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0001Len := uint32(22) - var zb0001Mask uint32 /* 24 bits */ + zb0001Len := uint32(24) + var zb0001Mask uint32 /* 26 bits */ if (*z).BaseVotingData.VoteID.MsgIsZero() { zb0001Len-- zb0001Mask |= 0x1 @@ -186,10 +186,18 @@ func (z *BaseAccountData) MarshalMsg(b []byte) (o []byte) { zb0001Len-- zb0001Mask |= 0x400000 } - if (*z).UpdateRound == 0 { + if (*z).LastProposed.MsgIsZero() { zb0001Len-- zb0001Mask |= 0x800000 } + if (*z).LastHeartbeat.MsgIsZero() { + zb0001Len-- + zb0001Mask |= 0x1000000 + } + if (*z).UpdateRound == 0 { + zb0001Len-- + zb0001Mask |= 0x2000000 + } // variable map header, size zb0001Len o = msgp.AppendMapHeader(o, zb0001Len) if zb0001Len != 0 { @@ -299,6 +307,16 @@ func (z *BaseAccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.AppendBool(o, (*z).IncentiveEligible) } if (zb0001Mask & 0x800000) == 0 { // if not empty + // string "p" + o = append(o, 0xa1, 0x70) + o = (*z).LastProposed.MarshalMsg(o) + } + if (zb0001Mask & 0x1000000) == 0 { // if not empty + // string "q" + o = append(o, 0xa1, 0x71) + o = (*z).LastHeartbeat.MarshalMsg(o) + } + if (zb0001Mask & 0x2000000) == 0 { // if not empty // string "z" o = append(o, 0xa1, 0x7a) o = msgp.AppendUint64(o, (*z).UpdateRound) @@ -450,6 +468,22 @@ func (z *BaseAccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalSta return } } + if zb0001 > 0 { + zb0001-- + bts, err = (*z).LastProposed.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "LastProposed") + return + } + } + if zb0001 > 0 { + zb0001-- + bts, err = (*z).LastHeartbeat.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "LastHeartbeat") + return + } + } if zb0001 > 0 { zb0001-- bts, err = (*z).BaseVotingData.VoteID.UnmarshalMsgWithState(bts, st) @@ -619,6 +653,18 @@ func (z *BaseAccountData) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalSta err = msgp.WrapError(err, "IncentiveEligible") return } + case "p": + bts, err = (*z).LastProposed.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "LastProposed") + return + } + case "q": + bts, err = (*z).LastHeartbeat.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "LastHeartbeat") + return + } case "A": bts, err = (*z).BaseVotingData.VoteID.UnmarshalMsgWithState(bts, st) if err != nil { @@ -684,18 +730,18 @@ func (_ *BaseAccountData) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *BaseAccountData) Msgsize() (s int) { - s = 3 + 2 + (*z).Status.Msgsize() + 2 + (*z).MicroAlgos.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).RewardedMicroAlgos.Msgsize() + 2 + (*z).AuthAddr.Msgsize() + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint32Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.BoolSize + 2 + (*z).BaseVotingData.VoteID.Msgsize() + 2 + (*z).BaseVotingData.SelectionID.Msgsize() + 2 + (*z).BaseVotingData.VoteFirstValid.Msgsize() + 2 + (*z).BaseVotingData.VoteLastValid.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).BaseVotingData.StateProofID.Msgsize() + 2 + msgp.Uint64Size + s = 3 + 2 + (*z).Status.Msgsize() + 2 + (*z).MicroAlgos.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).RewardedMicroAlgos.Msgsize() + 2 + (*z).AuthAddr.Msgsize() + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint32Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.BoolSize + 2 + (*z).LastProposed.Msgsize() + 2 + (*z).LastHeartbeat.Msgsize() + 2 + (*z).BaseVotingData.VoteID.Msgsize() + 2 + (*z).BaseVotingData.SelectionID.Msgsize() + 2 + (*z).BaseVotingData.VoteFirstValid.Msgsize() + 2 + (*z).BaseVotingData.VoteLastValid.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).BaseVotingData.StateProofID.Msgsize() + 2 + msgp.Uint64Size return } // MsgIsZero returns whether this is a zero value func (z *BaseAccountData) MsgIsZero() bool { - return ((*z).Status.MsgIsZero()) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) && ((*z).RewardedMicroAlgos.MsgIsZero()) && ((*z).AuthAddr.MsgIsZero()) && ((*z).TotalAppSchemaNumUint == 0) && ((*z).TotalAppSchemaNumByteSlice == 0) && ((*z).TotalExtraAppPages == 0) && ((*z).TotalAssetParams == 0) && ((*z).TotalAssets == 0) && ((*z).TotalAppParams == 0) && ((*z).TotalAppLocalStates == 0) && ((*z).TotalBoxes == 0) && ((*z).TotalBoxBytes == 0) && ((*z).IncentiveEligible == false) && ((*z).BaseVotingData.VoteID.MsgIsZero()) && ((*z).BaseVotingData.SelectionID.MsgIsZero()) && ((*z).BaseVotingData.VoteFirstValid.MsgIsZero()) && ((*z).BaseVotingData.VoteLastValid.MsgIsZero()) && ((*z).BaseVotingData.VoteKeyDilution == 0) && ((*z).BaseVotingData.StateProofID.MsgIsZero()) && ((*z).UpdateRound == 0) + return ((*z).Status.MsgIsZero()) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) && ((*z).RewardedMicroAlgos.MsgIsZero()) && ((*z).AuthAddr.MsgIsZero()) && ((*z).TotalAppSchemaNumUint == 0) && ((*z).TotalAppSchemaNumByteSlice == 0) && ((*z).TotalExtraAppPages == 0) && ((*z).TotalAssetParams == 0) && ((*z).TotalAssets == 0) && ((*z).TotalAppParams == 0) && ((*z).TotalAppLocalStates == 0) && ((*z).TotalBoxes == 0) && ((*z).TotalBoxBytes == 0) && ((*z).IncentiveEligible == false) && ((*z).LastProposed.MsgIsZero()) && ((*z).LastHeartbeat.MsgIsZero()) && ((*z).BaseVotingData.VoteID.MsgIsZero()) && ((*z).BaseVotingData.SelectionID.MsgIsZero()) && ((*z).BaseVotingData.VoteFirstValid.MsgIsZero()) && ((*z).BaseVotingData.VoteLastValid.MsgIsZero()) && ((*z).BaseVotingData.VoteKeyDilution == 0) && ((*z).BaseVotingData.StateProofID.MsgIsZero()) && ((*z).UpdateRound == 0) } // MaxSize returns a maximum valid message size for this message type func BaseAccountDataMaxSize() (s int) { - s = 3 + 2 + basics.StatusMaxSize() + 2 + basics.MicroAlgosMaxSize() + 2 + msgp.Uint64Size + 2 + basics.MicroAlgosMaxSize() + 2 + basics.AddressMaxSize() + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint32Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.BoolSize + 2 + crypto.OneTimeSignatureVerifierMaxSize() + 2 + crypto.VRFVerifierMaxSize() + 2 + basics.RoundMaxSize() + 2 + basics.RoundMaxSize() + 2 + msgp.Uint64Size + 2 + merklesignature.CommitmentMaxSize() + 2 + msgp.Uint64Size + s = 3 + 2 + basics.StatusMaxSize() + 2 + basics.MicroAlgosMaxSize() + 2 + msgp.Uint64Size + 2 + basics.MicroAlgosMaxSize() + 2 + basics.AddressMaxSize() + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint32Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.Uint64Size + 2 + msgp.BoolSize + 2 + basics.RoundMaxSize() + 2 + basics.RoundMaxSize() + 2 + crypto.OneTimeSignatureVerifierMaxSize() + 2 + crypto.VRFVerifierMaxSize() + 2 + basics.RoundMaxSize() + 2 + basics.RoundMaxSize() + 2 + msgp.Uint64Size + 2 + merklesignature.CommitmentMaxSize() + 2 + msgp.Uint64Size return } diff --git a/ledger/store/trackerdb/sqlitedriver/schema.go b/ledger/store/trackerdb/sqlitedriver/schema.go index 369ff264e0..04f316e06a 100644 --- a/ledger/store/trackerdb/sqlitedriver/schema.go +++ b/ledger/store/trackerdb/sqlitedriver/schema.go @@ -734,8 +734,9 @@ func performOnlineAccountsTableMigration(ctx context.Context, e db.Executable, p } } - // remove stateproofID field for offline accounts - if ba.Status != basics.Online && !ba.StateProofID.IsEmpty() { + // We had a bug that didn't remove StateProofIDs when going offline. + // Tidy up such accounts. + if ba.VoteID.IsEmpty() && !ba.StateProofID.IsEmpty() { // store old data for account hash update state := acctState{old: ba, oldEnc: encodedAcctData} ba.StateProofID = merklesignature.Commitment{} diff --git a/ledger/store/trackerdb/sqlitedriver/schema_test.go b/ledger/store/trackerdb/sqlitedriver/schema_test.go index e7aee0b2a9..37c719cb8a 100644 --- a/ledger/store/trackerdb/sqlitedriver/schema_test.go +++ b/ledger/store/trackerdb/sqlitedriver/schema_test.go @@ -176,7 +176,7 @@ func TestRemoveOfflineStateProofID(t *testing.T) { accts[addr] = acct expectedAcct := acct - if acct.Status != basics.Online { + if acct.Status != basics.Online && acct.Status != basics.Suspended { expectedAcct.StateProofID = merklesignature.Commitment{} } expectedAccts[addr] = expectedAcct @@ -211,7 +211,7 @@ func TestRemoveOfflineStateProofID(t *testing.T) { defer dbs.Close() defer tx.Rollback() - // make second copy of DB to prepare exepected/fixed merkle trie + // make second copy of DB to prepare expected/fixed merkle trie expectedDBs, expectedTx := buildDB(expectedAccts) defer expectedDBs.Close() defer expectedTx.Rollback() @@ -237,7 +237,7 @@ func TestRemoveOfflineStateProofID(t *testing.T) { var ba trackerdb.BaseAccountData err = protocol.Decode(encodedAcctData, &ba) require.NoError(t, err) - if expected && ba.Status != basics.Online { + if expected && (ba.Status != basics.Online && ba.Status != basics.Suspended) { require.Equal(t, merklesignature.Commitment{}, ba.StateProofID) } addHash := trackerdb.AccountHashBuilderV6(addr, &ba, encodedAcctData) @@ -287,7 +287,7 @@ func TestRemoveOfflineStateProofID(t *testing.T) { var ba trackerdb.BaseAccountData err = protocol.Decode(encodedAcctData, &ba) require.NoError(t, err) - if ba.Status != basics.Online { + if ba.Status != basics.Online && ba.Status != basics.Suspended { require.True(t, ba.StateProofID.IsEmpty()) } } diff --git a/ledger/testing/randomAccounts.go b/ledger/testing/randomAccounts.go index bd079605a3..e9dd8b4d0a 100644 --- a/ledger/testing/randomAccounts.go +++ b/ledger/testing/randomAccounts.go @@ -58,14 +58,18 @@ func RandomNote() []byte { return note[:] } -// RandomAccountData generates a random AccountData +// RandomAccountData generates a random AccountData with no associated resources. func RandomAccountData(rewardsBase uint64) basics.AccountData { var data basics.AccountData // Avoid overflowing totals data.MicroAlgos.Raw = crypto.RandUint64() % (1 << 32) + // 0 is an invalid round, but would be right if never proposed + data.LastProposed = basics.Round(crypto.RandUint64() % 10) + // 0 is an invalid round, but would be right if never needed a heartbeat + data.LastHeartbeat = basics.Round(crypto.RandUint64() % 10) - switch crypto.RandUint64() % 3 { + switch crypto.RandUint64() % 4 { case 0: data.Status = basics.Online data.VoteID = crypto.OneTimeSignatureVerifier{0x01} @@ -74,8 +78,27 @@ func RandomAccountData(rewardsBase uint64) basics.AccountData { data.VoteLastValid = 10000 case 1: data.Status = basics.Offline - default: + case 2: data.Status = basics.NotParticipating + case 3: + data.Status = basics.Suspended + } + + switch data.Status { + case basics.Online, basics.Suspended: + crypto.RandBytes(data.VoteID[:]) + crypto.RandBytes(data.SelectionID[:]) + crypto.RandBytes(data.StateProofID[:]) + data.VoteFirstValid = basics.Round(crypto.RandUint64()) + data.VoteLastValid = basics.Round(crypto.RandUint64() % uint64(math.MaxInt64)) // int64 is the max sqlite can store + data.VoteKeyDilution = crypto.RandUint64() + case basics.Offline, basics.NotParticipating: + data.VoteID = crypto.OneTimeSignatureVerifier{} + data.SelectionID = crypto.VRFVerifier{} + data.StateProofID = merklesignature.Commitment{} + data.VoteFirstValid = 0 + data.VoteLastValid = 0 + data.VoteKeyDilution = 0 } data.RewardsBase = rewardsBase @@ -84,16 +107,15 @@ func RandomAccountData(rewardsBase uint64) basics.AccountData { // RandomOnlineAccountData is similar to RandomAccountData but always creates online account func RandomOnlineAccountData(rewardsBase uint64) basics.AccountData { - var data basics.AccountData - data.MicroAlgos.Raw = crypto.RandUint64() % (1 << 32) - data.Status = basics.Online - data.VoteLastValid = 1000 - data.VoteFirstValid = 0 - data.RewardsBase = rewardsBase - return data + for { + data := RandomAccountData(rewardsBase) + if data.Status == basics.Online { + return data + } + } } -// RandomAssetParams creates a randim basics.AssetParams +// RandomAssetParams creates a random basics.AssetParams func RandomAssetParams() basics.AssetParams { ap := basics.AssetParams{ Total: crypto.RandUint64(), @@ -219,6 +241,7 @@ func RandomAppParams() basics.AppParams { if len(ap.GlobalState) == 0 { ap.GlobalState = nil } + ap.ExtraProgramPages = uint32(crypto.RandUint64() % 4) return ap } @@ -273,21 +296,6 @@ func RandomAppLocalState() basics.AppLocalState { func RandomFullAccountData(rewardsLevel uint64, lastCreatableID *basics.CreatableIndex, assets map[basics.AssetIndex]struct{}, apps map[basics.AppIndex]struct{}) basics.AccountData { data := RandomAccountData(rewardsLevel) - if data.Status == basics.Online { - crypto.RandBytes(data.VoteID[:]) - crypto.RandBytes(data.SelectionID[:]) - crypto.RandBytes(data.StateProofID[:]) - data.VoteFirstValid = basics.Round(crypto.RandUint64()) - data.VoteLastValid = basics.Round(crypto.RandUint64() % uint64(math.MaxInt64)) // int64 is the max sqlite can store - data.VoteKeyDilution = crypto.RandUint64() - } else { - data.VoteID = crypto.OneTimeSignatureVerifier{} - data.SelectionID = crypto.VRFVerifier{} - data.StateProofID = merklesignature.Commitment{} - data.VoteFirstValid = 0 - data.VoteLastValid = 0 - data.VoteKeyDilution = 0 - } if (crypto.RandUint64() % 2) == 1 { // if account has created assets, have these defined. createdAssetsCount := crypto.RandUint64()%20 + 1 diff --git a/ledger/testing/testGenesis.go b/ledger/testing/testGenesis.go index 06b50be977..71004373f3 100644 --- a/ledger/testing/testGenesis.go +++ b/ledger/testing/testGenesis.go @@ -25,24 +25,25 @@ import ( "github.com/algorand/go-algorand/protocol" ) -// testGenesisCfg provides a configuration object for NewTestGenesis. -type testGenesisCfg struct { +// GenesisCfg provides a configuration object for NewTestGenesis. +type GenesisCfg struct { rewardsPoolAmount basics.MicroAlgos + OnlineCount int } // TestGenesisOption provides functional options for testGenesisCfg. -type TestGenesisOption func(*testGenesisCfg) +type TestGenesisOption func(*GenesisCfg) // TurnOffRewards turns off the rewards pool for tests that are sensistive to // "surprise" balance changes. -var TurnOffRewards = func(cfg *testGenesisCfg) { cfg.rewardsPoolAmount = basics.MicroAlgos{Raw: 100_000} } +var TurnOffRewards = func(cfg *GenesisCfg) { cfg.rewardsPoolAmount = basics.MicroAlgos{Raw: 100_000} } // NewTestGenesis creates a bunch of accounts, splits up 10B algos // between them and the rewardspool and feesink, and gives out the // addresses and secrets it creates to enable tests. For special // scenarios, manipulate these return values before using newTestLedger. func NewTestGenesis(opts ...TestGenesisOption) (bookkeeping.GenesisBalances, []basics.Address, []*crypto.SignatureSecrets) { - var cfg testGenesisCfg + var cfg GenesisCfg for _, opt := range opts { opt(&cfg) } @@ -74,6 +75,15 @@ func NewTestGenesis(opts ...TestGenesisOption) (bookkeeping.GenesisBalances, []b adata := basics.AccountData{ MicroAlgos: basics.MicroAlgos{Raw: amount}, + Status: basics.Offline, + } + if i < cfg.OnlineCount { + adata.Status = basics.Online + adata.VoteFirstValid = 0 + adata.VoteLastValid = 1_000_000 + crypto.RandBytes(adata.VoteID[:]) + crypto.RandBytes(adata.SelectionID[:]) + crypto.RandBytes(adata.StateProofID[:]) } accts[addrs[i]] = adata } diff --git a/protocol/tags.go b/protocol/tags.go index 1e2ef3b148..6a51626f70 100644 --- a/protocol/tags.go +++ b/protocol/tags.go @@ -73,7 +73,7 @@ const PingReplyTagMaxSize = 8 // ProposalPayloadTagMaxSize is the maximum size of a ProposalPayloadTag message // This value is dominated by the MaxTxnBytesPerBlock -const ProposalPayloadTagMaxSize = 5248065 +const ProposalPayloadTagMaxSize = 5250289 // StateProofSigTagMaxSize is the maximum size of a StateProofSigTag message const StateProofSigTagMaxSize = 6378 diff --git a/test/scripts/e2e_subs/absentee.py b/test/scripts/e2e_subs/absentee.py new file mode 100755 index 0000000000..f889a4ff35 --- /dev/null +++ b/test/scripts/e2e_subs/absentee.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import base64 +import os +import sys +from goal import Goal +import algosdk.encoding as enc + +from datetime import datetime + +stamp = datetime.now().strftime("%Y%m%d_%H%M%S") +print(f"{os.path.basename(sys.argv[0])} start {stamp}") + +goal = Goal(sys.argv[1], autosend=True) + +joe = goal.new_account() + +txinfo, err = goal.pay(goal.account, joe, amt=500_000) +assert not err, err + +# Joe is a brand new account, it has neither proposed nor heartbeat +joe_info = goal.algod.account_info(joe) +assert "last-proposed" not in joe_info, joe_info +assert "last-heartbeat" not in joe_info, joe_info + +# Find info on the proposer of the pay block +pblock = goal.algod.block_info(txinfo['confirmed-round'])['block'] +assert pblock["prp"] != "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ" +prp_info = goal.algod.account_info(pblock["prp"]) +assert "last-proposed" in prp_info, prp_info # they just did! +assert prp_info["last-proposed"] > 0 +assert "last-heartbeat" not in prp_info, prp_info # was a genesis account + +stamp = datetime.now().strftime("%Y%m%d_%H%M%S") +print(f"{os.path.basename(sys.argv[0])} OK {stamp}") From cd40d7477c90566a57da550f879157ef0d95ff30 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 9 Jan 2024 12:52:35 -0500 Subject: [PATCH 010/117] Remove basics.Suspended as an explicit account state v40 Use SelectionID to detect when to zero out StateProofID Simplify online account delta handling Fix expiration test --- cmd/goal/accountsList.go | 2 - cmd/incorporate/incorporate.go | 2 - crypto/onetimesig.go | 2 +- crypto/vrf.go | 2 +- data/basics/userBalance.go | 9 --- ledger/acctdeltas.go | 74 ++++++++----------- ledger/acctdeltas_test.go | 23 +++--- ledger/acctupdates_test.go | 14 ++-- ledger/eval/eval.go | 18 +++-- ledger/eval/eval_test.go | 25 +++++-- ledger/eval_simple_test.go | 12 +-- ledger/ledgercore/accountdata.go | 4 +- ledger/ledgercore/totals.go | 2 +- ledger/store/trackerdb/sqlitedriver/schema.go | 7 +- .../trackerdb/sqlitedriver/schema_test.go | 13 ++-- ledger/testing/randomAccounts.go | 19 +---- 16 files changed, 104 insertions(+), 124 deletions(-) diff --git a/cmd/goal/accountsList.go b/cmd/goal/accountsList.go index 7a98920393..5e4a2e6fa8 100644 --- a/cmd/goal/accountsList.go +++ b/cmd/goal/accountsList.go @@ -216,8 +216,6 @@ func (accountList *AccountsList) outputAccount(addr string, acctInfo model.Accou status = "offline" case basics.NotParticipating.String(): status = "excluded" - case basics.Suspended.String(): - status = "suspended" default: panic(fmt.Sprintf("unexpected account status: %v", acctInfo.Status)) } diff --git a/cmd/incorporate/incorporate.go b/cmd/incorporate/incorporate.go index ed885915e1..b9f7dd55b1 100644 --- a/cmd/incorporate/incorporate.go +++ b/cmd/incorporate/incorporate.go @@ -235,8 +235,6 @@ func parseRecord(cols []string) (rec record) { rec.Status = basics.Offline case "NotParticipating": rec.Status = basics.NotParticipating - case "Suspended": - rec.Status = basics.Suspended default: log.Fatalf("unknown status: %s", cols[3]) } diff --git a/crypto/onetimesig.go b/crypto/onetimesig.go index ca72204f63..bc11070125 100644 --- a/crypto/onetimesig.go +++ b/crypto/onetimesig.go @@ -304,7 +304,7 @@ func (s *OneTimeSignatureSecrets) Sign(id OneTimeSignatureIdentifier, message Ha return OneTimeSignature{} } -// IsEmpty returns true is the verifier is empty/zero'd. +// IsEmpty returns true if the verifier is empty/zero'd. func (v OneTimeSignatureVerifier) IsEmpty() bool { return v == OneTimeSignatureVerifier{} } diff --git a/crypto/vrf.go b/crypto/vrf.go index 0c519298a5..0002a7b2f7 100644 --- a/crypto/vrf.go +++ b/crypto/vrf.go @@ -134,7 +134,7 @@ func (pk VrfPubkey) verifyBytes(proof VrfProof, msg []byte) (bool, VrfOutput) { return ret == 0, out } -// IsEmpty returns true is the key is empty/zero'd. +// IsEmpty returns true if the key is empty/zero'd. func (pk VrfPubkey) IsEmpty() bool { return pk == VrfPubkey{} } diff --git a/data/basics/userBalance.go b/data/basics/userBalance.go index ab2332b7f1..f8c06a816e 100644 --- a/data/basics/userBalance.go +++ b/data/basics/userBalance.go @@ -42,11 +42,6 @@ const ( // Two special accounts that are defined as NotParticipating are the incentive pool (also know as rewards pool) and the fee sink. // These two accounts also have additional Algo transfer restrictions. NotParticipating - // Suspended indicates that an account has registered keys, intending to - // particpate, but appears to have gone dark. Their balance should no longer - // be considered online, but their voting keys should be retained so they - // can easily get back online by sending a "heartbeat". - Suspended // encodedMaxAssetsPerAccount is the decoder limit of number of assets stored per account. // it's being verified by the unit test TestEncodedAccountAllocationBounds to align @@ -80,8 +75,6 @@ func (s Status) String() string { return "Online" case NotParticipating: return "Not Participating" - case Suspended: - return "Suspended" } return "" } @@ -95,8 +88,6 @@ func UnmarshalStatus(value string) (s Status, err error) { s = Online case "Not Participating": s = NotParticipating - case "Suspended": - s = Suspended default: err = fmt.Errorf("unknown account status: %v", value) } diff --git a/ledger/acctdeltas.go b/ledger/acctdeltas.go index 6a0764b105..ad0be650b7 100644 --- a/ledger/acctdeltas.go +++ b/ledger/acctdeltas.go @@ -986,18 +986,34 @@ func onlineAccountsNewRoundImpl( newAcct := data.newAcct[j] updRound := data.updRound[j] newStatus := data.newStatus[j] - if prevAcct.Ref == nil { - // zero rowid means we don't have a previous value. - if newAcct.IsEmpty() { - // IsEmpty means we don't have a previous value. - // if we didn't had it before, and we don't have anything now, just skip it. - } else { - if newStatus == basics.Online { - if newAcct.IsVotingEmpty() { - err = fmt.Errorf("empty voting data for online account %s: %v", data.address.String(), newAcct) - return nil, err - } - // create a new entry. + if newStatus == basics.Online && newAcct.IsVotingEmpty() { + return nil, fmt.Errorf("empty voting data for online account %s: %v", data.address, newAcct) + } + if prevAcct.Ref == nil { // zero rowid (nil Ref) means we don't have a previous value. + if newStatus != basics.Online { + continue // didn't exist, and not going online, we don't care. + } + + // create a new entry. + var ref trackerdb.OnlineAccountRef + normBalance := newAcct.NormalizedOnlineBalance(proto) + ref, err = writer.InsertOnlineAccount(data.address, normBalance, newAcct, updRound, uint64(newAcct.VoteLastValid)) + if err != nil { + return nil, err + } + updated := trackerdb.PersistedOnlineAccountData{ + Addr: data.address, + AccountData: newAcct, + Round: lastUpdateRound, + Ref: ref, + UpdRound: basics.Round(updRound), + } + updatedAccounts = append(updatedAccounts, updated) + prevAcct = updated + } else { // non-zero rowid (non-nil Ref) means we had a previous value. + if newStatus == basics.Online { + // was already online, so create an update only if something changed + if prevAcct.AccountData != newAcct { var ref trackerdb.OnlineAccountRef normBalance := newAcct.NormalizedOnlineBalance(proto) ref, err = writer.InsertOnlineAccount(data.address, normBalance, newAcct, updRound, uint64(newAcct.VoteLastValid)) @@ -1011,21 +1027,12 @@ func onlineAccountsNewRoundImpl( Ref: ref, UpdRound: basics.Round(updRound), } + updatedAccounts = append(updatedAccounts, updated) prevAcct = updated - } else if !newAcct.IsVotingEmpty() && newStatus != basics.Suspended { - err = fmt.Errorf("non-empty voting data for non-online account %s: %v", data.address.String(), newAcct) - return nil, err - } - } - } else { - // non-zero rowid means we had a previous value. - if newAcct.IsVotingEmpty() || newStatus == basics.Suspended { - // new value is zero, or the account is suspended then go offline - if newStatus == basics.Online { - err = fmt.Errorf("empty voting data but online account %s: %v", data.address.String(), newAcct) - return nil, err } + } else { + // "delete" by inserting a zero entry var ref trackerdb.OnlineAccountRef ref, err = writer.InsertOnlineAccount(data.address, 0, trackerdb.BaseOnlineAccountData{}, updRound, 0) if err != nil { @@ -1041,25 +1048,6 @@ func onlineAccountsNewRoundImpl( updatedAccounts = append(updatedAccounts, updated) prevAcct = updated - } else { - if prevAcct.AccountData != newAcct { - var ref trackerdb.OnlineAccountRef - normBalance := newAcct.NormalizedOnlineBalance(proto) - ref, err = writer.InsertOnlineAccount(data.address, normBalance, newAcct, updRound, uint64(newAcct.VoteLastValid)) - if err != nil { - return nil, err - } - updated := trackerdb.PersistedOnlineAccountData{ - Addr: data.address, - AccountData: newAcct, - Round: lastUpdateRound, - Ref: ref, - UpdRound: basics.Round(updRound), - } - - updatedAccounts = append(updatedAccounts, updated) - prevAcct = updated - } } } } diff --git a/ledger/acctdeltas_test.go b/ledger/acctdeltas_test.go index e39c88836e..a3e5bd49db 100644 --- a/ledger/acctdeltas_test.go +++ b/ledger/acctdeltas_test.go @@ -74,7 +74,7 @@ func checkAccounts(t *testing.T, tx trackerdb.TransactionScope, rnd basics.Round switch d.Status { case basics.Online: totalOnline += d.MicroAlgos.Raw - case basics.Offline, basics.Suspended: + case basics.Offline: totalOffline += d.MicroAlgos.Raw case basics.NotParticipating: totalNotPart += d.MicroAlgos.Raw @@ -2621,20 +2621,21 @@ func TestAccountOnlineAccountsNewRound(t *testing.T) { deltaC.newAcct[0].VoteFirstValid = 0 updates.deltas = []onlineAccountDelta{deltaC} _, err = onlineAccountsNewRoundImpl(writer, updates, proto, lastUpdateRound) - require.Error(t, err) + require.ErrorContains(t, err, "empty voting data for online account") - // check errors: new non-online with non-empty voting data + // It used to be an error to go offline with non-empty voting data, but + // account suspension makes it legal. deltaB.newStatus[0] = basics.Offline deltaB.newAcct[0].VoteFirstValid = 1 updates.deltas = []onlineAccountDelta{deltaB} _, err = onlineAccountsNewRoundImpl(writer, updates, proto, lastUpdateRound) - require.Error(t, err) + require.NoError(t, err) // check errors: new online with empty voting data deltaD.newStatus[0] = basics.Online updates.deltas = []onlineAccountDelta{deltaD} _, err = onlineAccountsNewRoundImpl(writer, updates, proto, lastUpdateRound) - require.Error(t, err) + require.ErrorContains(t, err, "empty voting data for online account") } func TestAccountOnlineAccountsNewRoundFlip(t *testing.T) { @@ -2937,8 +2938,7 @@ func TestOnlineAccountsNewRoundError(t *testing.T) { updates.deltas = append(updates.deltas, deltaA) lastUpdateRound := basics.Round(1) updated, err := onlineAccountsNewRoundImpl(writer, updates, proto, lastUpdateRound) - require.Error(t, err) - require.Equal(t, errMockOnlineAccountsErrorWriter, err) + require.ErrorIs(t, err, errMockOnlineAccountsErrorWriter) require.Empty(t, updated) // update acct A => exercise "update" @@ -2965,8 +2965,7 @@ func TestOnlineAccountsNewRoundError(t *testing.T) { updates.deltas = append(updates.deltas, deltaA2) lastUpdateRound = basics.Round(3) updated, err = onlineAccountsNewRoundImpl(writer, updates, proto, lastUpdateRound) - require.Error(t, err) - require.Equal(t, errMockOnlineAccountsErrorWriter, err) + require.ErrorIs(t, err, errMockOnlineAccountsErrorWriter) require.Empty(t, updated) // make acct A offline => exercise "deletion" @@ -2993,8 +2992,7 @@ func TestOnlineAccountsNewRoundError(t *testing.T) { updates.deltas = append(updates.deltas, deltaA3) lastUpdateRound = basics.Round(4) updated, err = onlineAccountsNewRoundImpl(writer, updates, proto, lastUpdateRound) - require.Error(t, err) - require.Equal(t, errMockOnlineAccountsErrorWriter, err) + require.ErrorIs(t, err, errMockOnlineAccountsErrorWriter) require.Empty(t, updated) } @@ -3200,8 +3198,7 @@ func TestAccountsNewRoundError(t *testing.T) { } lastUpdateRound := basics.Round(i + 1) updatedAcct, updatedResources, updatedKvs, err := accountsNewRoundImpl(writer, updates, resources, kvs, creatables, proto, lastUpdateRound) - require.Error(t, err) - require.Equal(t, test.expErr, err) + require.ErrorIs(t, err, test.expErr) require.Empty(t, updatedAcct) require.Empty(t, updatedResources) require.Empty(t, updatedKvs) diff --git a/ledger/acctupdates_test.go b/ledger/acctupdates_test.go index 7e4ed0c3d1..34ffb2f32a 100644 --- a/ledger/acctupdates_test.go +++ b/ledger/acctupdates_test.go @@ -385,10 +385,9 @@ func checkAcctUpdates(t *testing.T, au *accountUpdates, ao *onlineAccounts, base // TODO: make lookupOnlineAccountData returning extended version of ledgercore.VotingData ? od, err := ao.lookupOnlineAccountData(rnd, addr) require.NoError(t, err) - // Unless suspended an account's voting data should agree with - // the online account tracker. (Vaccuously true, when account is - // offline or non-part). - if data.Status != basics.Suspended { + + // If lookupOnlineAccountData returned something, it should agree with `data`. + if !od.VoteID.IsEmpty() { require.Equal(t, od.VoteID, data.VoteID) require.Equal(t, od.SelectionID, data.SelectionID) require.Equal(t, od.VoteFirstValid, data.VoteFirstValid) @@ -401,7 +400,7 @@ func checkAcctUpdates(t *testing.T, au *accountUpdates, ao *onlineAccounts, base case basics.Online: totalOnline += d.MicroAlgos.Raw totalOnline += (d.MicroAlgos.Raw / proto.RewardUnit) * rewardsDelta - case basics.Offline, basics.Suspended: + case basics.Offline: totalOffline += d.MicroAlgos.Raw totalOffline += (d.MicroAlgos.Raw / proto.RewardUnit) * rewardsDelta case basics.NotParticipating: @@ -509,8 +508,9 @@ func checkOnlineAcctUpdatesConsistency(t *testing.T, ao *onlineAccounts, rnd bas for i := 0; i < latest.Len(); i++ { addr, acct := latest.GetByIdx(i) od, err := ao.lookupOnlineAccountData(rnd, addr) - if acct.Status == basics.Suspended { - // suspended accounts will not match, since they have vote info but not in online accounts + if od.VoteID.IsEmpty() { + // suspended accounts will be in `latest` (from ao.deltas), but + // `lookupOnlineAccountData` will return {}. continue } require.NoError(t, err) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index ff96dd1b21..a418edf65c 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1460,7 +1460,9 @@ func (eval *BlockEvaluator) endOfBlock() error { // generateKnockOfflineAccountsList creates the lists of expired or absent // participation accounts by traversing over the modified accounts in the state -// deltas and testing if any of them needs to be reset/suspended. +// deltas and testing if any of them needs to be reset/suspended. Expiration +// takes precedence - if an account is expired, it should be knocked offline and +// key material deleted. If it is only suspended, the key material will remain. func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { if !eval.generate { return @@ -1483,7 +1485,10 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { continue } - if acctDelta.Status == basics.Online || acctDelta.Status == basics.Suspended { + // Regardless of being online or suspended, if voting data exists, the + // account can be expired to remove it. This means an offline account + // can be expired (because it was already suspended). + if !acctDelta.VoteID.IsEmpty() { expiresBeforeCurrent := acctDelta.VoteLastValid < currentRound if expiresBeforeCurrent && len(updates.ExpiredParticipationAccounts) < expectedMaxNumberOfExpiredAccounts { @@ -1496,7 +1501,7 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { } if acctDelta.Status == basics.Online { - lastSeen := max(acctDelta.LastHeartbeat, acctDelta.LastHeartbeat) + lastSeen := max(acctDelta.LastProposed, acctDelta.LastHeartbeat) if isAbsent(eval.state.prevTotals.Online.Money, acctDelta.MicroAlgos, lastSeen, currentRound) && len(updates.AbsentParticipationAccounts) < expectedMaxNumberOfAbsentAccounts { updates.AbsentParticipationAccounts = append( @@ -1519,7 +1524,6 @@ func max(a, b basics.Round) basics.Round { func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, lastSeen basics.Round, current basics.Round) bool { // See if the account has exceeded 10x their expected observation interval. allowableLag := basics.Round(10 * totalOnlineStake.Raw / acctStake.Raw) - fmt.Printf("%d / %d -> %d \n", acctStake, totalOnlineStake, allowableLag) return lastSeen+allowableLag < current } @@ -1560,8 +1564,8 @@ func (eval *BlockEvaluator) validateExpiredOnlineAccounts() error { return fmt.Errorf("endOfBlock was unable to retrieve account %v : %w", accountAddr, err) } - if acctData.Status != basics.Online && acctData.Status != basics.Suspended { - return fmt.Errorf("endOfBlock found %v was not online but %v", accountAddr, acctData.Status) + if acctData.VoteID.IsEmpty() { + return fmt.Errorf("endOfBlock found expiration candidate %v had no vote key", accountAddr) } if acctData.VoteLastValid >= currentRound { @@ -1608,7 +1612,7 @@ func (eval *BlockEvaluator) validateAbsentOnlineAccounts() error { return fmt.Errorf("proposed absent acct %v was not online but %v", accountAddr, acctData.Status) } - lastSeen := max(acctData.LastHeartbeat, acctData.LastHeartbeat) + lastSeen := max(acctData.LastProposed, acctData.LastHeartbeat) if !isAbsent(eval.state.prevTotals.Online.Money, acctData.MicroAlgos, lastSeen, currentRound) { return fmt.Errorf("proposed absent account %v is not absent in %d, %d", accountAddr, acctData.LastProposed, acctData.LastHeartbeat) diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 7884a7f894..1bca8b1705 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -1142,6 +1142,7 @@ func TestEvalFunctionForExpiredAccounts(t *testing.T) { tmp.Status = basics.Online crypto.RandBytes(tmp.StateProofID[:]) crypto.RandBytes(tmp.SelectionID[:]) + crypto.RandBytes(tmp.VoteID[:]) genesisInitState.Accounts[addr] = tmp } @@ -1165,10 +1166,16 @@ func TestEvalFunctionForExpiredAccounts(t *testing.T) { blkEval, err := l.StartEvaluator(newBlock.BlockHeader, 0, 0, nil) require.NoError(t, err) - // Advance the evaluator a couple rounds... + // Advance the evaluator a couple rounds, watching for lack of expiration for i := uint64(0); i < uint64(targetRound); i++ { - l.endBlock(t, blkEval) + vb := l.endBlock(t, blkEval) blkEval = l.nextBlock(t) + for _, acct := range vb.Block().ExpiredParticipationAccounts { + if acct == recvAddr { + // won't happen, because recvAddr didn't appear in block + require.Fail(t, "premature expiration") + } + } } require.Greater(t, uint64(blkEval.Round()), uint64(recvAddrLastValidRound)) @@ -1199,14 +1206,22 @@ func TestEvalFunctionForExpiredAccounts(t *testing.T) { validatedBlock, err := blkEval.GenerateBlock() require.NoError(t, err) + expired := false + for _, acct := range validatedBlock.Block().ExpiredParticipationAccounts { + if acct == recvAddr { + expired = true + } + } + require.True(t, expired) + _, err = Eval(context.Background(), l, validatedBlock.Block(), false, nil, nil, l.tracer) require.NoError(t, err) acctData, _ := blkEval.state.lookup(recvAddr) - require.Equal(t, merklesignature.Verifier{}.Commitment, acctData.StateProofID) - require.Equal(t, crypto.VRFVerifier{}, acctData.SelectionID) - + require.Zero(t, acctData.StateProofID) + require.Zero(t, acctData.SelectionID) + require.Zero(t, acctData.VoteID) badBlock := *validatedBlock // First validate that bad block is fine if we dont touch it... diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 76decea684..e897ff34c5 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -381,8 +381,8 @@ func TestAbsentTracking(t *testing.T) { genBalances, addrs, _ := ledgertesting.NewTestGenesis(func(cfg *ledgertesting.GenesisCfg) { cfg.OnlineCount = 2 // So we know proposer should propose every 2 rounds, on average }) - // Absentee checking begins in v39. Start checking in v38 to test that is unchanged. - ledgertesting.TestConsensusRange(t, 38, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { + checkingBegins := 40 + ledgertesting.TestConsensusRange(t, checkingBegins-1, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { dl := NewDoubleLedger(t, genBalances, cv, cfg) defer dl.Close() @@ -418,7 +418,7 @@ func TestAbsentTracking(t *testing.T) { prp := lookup(t, dl.validator, proposer) - if ver >= 39 { + if ver >= checkingBegins { // version sanity check require.True(t, dl.generator.GenesisProto().EnableAbsenteeTracking()) require.NotZero(t, prp.LastProposed) @@ -456,7 +456,7 @@ func TestAbsentTracking(t *testing.T) { require.Zero(t, regger.LastProposed) require.True(t, regger.Status == basics.Online) - if ver >= 39 { + if ver >= checkingBegins { require.NotZero(t, regger.LastHeartbeat) // online keyreg caused update } else { require.Zero(t, regger.LastHeartbeat) @@ -492,9 +492,9 @@ func TestAbsentTracking(t *testing.T) { Receiver: addrs[0], Amount: 0, }) - require.Equal(t, ver < 39, lookup(t, dl.generator, addrs[0]).Status == basics.Online) + require.Equal(t, ver < checkingBegins, lookup(t, dl.generator, addrs[0]).Status == basics.Online) require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) - require.Equal(t, ver < 39, lookup(t, dl.generator, addrs[2]).Status == basics.Online) + require.Equal(t, ver < checkingBegins, lookup(t, dl.generator, addrs[2]).Status == basics.Online) }) } diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 11982250d3..227421c9c5 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -145,10 +145,10 @@ func (u *AccountData) ClearOnlineState() { u.VotingData = VotingData{} } -// Suspend sets the status to Suspended, but does _not_ clear voting keys, so +// Suspend sets the status to Offline, but does _not_ clear voting keys, so // that a heartbeat can bring the account back Online func (u *AccountData) Suspend() { - u.Status = basics.Suspended + u.Status = basics.Offline } // MinBalance computes the minimum balance requirements for an account based on diff --git a/ledger/ledgercore/totals.go b/ledger/ledgercore/totals.go index f4fac018a0..22708586b1 100644 --- a/ledger/ledgercore/totals.go +++ b/ledger/ledgercore/totals.go @@ -66,7 +66,7 @@ func (at *AccountTotals) statusField(status basics.Status) *AlgoCount { switch status { case basics.Online: return &at.Online - case basics.Offline, basics.Suspended: + case basics.Offline: return &at.Offline case basics.NotParticipating: return &at.NotParticipating diff --git a/ledger/store/trackerdb/sqlitedriver/schema.go b/ledger/store/trackerdb/sqlitedriver/schema.go index 04f316e06a..a36dd2cd90 100644 --- a/ledger/store/trackerdb/sqlitedriver/schema.go +++ b/ledger/store/trackerdb/sqlitedriver/schema.go @@ -735,8 +735,11 @@ func performOnlineAccountsTableMigration(ctx context.Context, e db.Executable, p } // We had a bug that didn't remove StateProofIDs when going offline. - // Tidy up such accounts. - if ba.VoteID.IsEmpty() && !ba.StateProofID.IsEmpty() { + // Tidy up such accounts. We don't zero it out based on + // `!basics.Online` because accounts can be suspended, in which case + // they are Offline, but retain their voting material. But it remains + // illegal to have a StateProofID without a SelectionID. + if ba.SelectionID.IsEmpty() && !ba.StateProofID.IsEmpty() { // store old data for account hash update state := acctState{old: ba, oldEnc: encodedAcctData} ba.StateProofID = merklesignature.Commitment{} diff --git a/ledger/store/trackerdb/sqlitedriver/schema_test.go b/ledger/store/trackerdb/sqlitedriver/schema_test.go index 37c719cb8a..9143eba9c1 100644 --- a/ledger/store/trackerdb/sqlitedriver/schema_test.go +++ b/ledger/store/trackerdb/sqlitedriver/schema_test.go @@ -166,7 +166,7 @@ func TestAccountDBTxTailLoad(t *testing.T) { } } -func TestRemoveOfflineStateProofID(t *testing.T) { +func TestRemoveStrayStateProofID(t *testing.T) { partitiontest.PartitionTest(t) accts := ledgertesting.RandomAccounts(20, true) @@ -176,11 +176,10 @@ func TestRemoveOfflineStateProofID(t *testing.T) { accts[addr] = acct expectedAcct := acct - if acct.Status != basics.Online && acct.Status != basics.Suspended { + if acct.SelectionID.IsEmpty() { expectedAcct.StateProofID = merklesignature.Commitment{} } expectedAccts[addr] = expectedAcct - } buildDB := func(accounts map[basics.Address]basics.AccountData) (db.Pair, *sql.Tx) { @@ -237,8 +236,8 @@ func TestRemoveOfflineStateProofID(t *testing.T) { var ba trackerdb.BaseAccountData err = protocol.Decode(encodedAcctData, &ba) require.NoError(t, err) - if expected && (ba.Status != basics.Online && ba.Status != basics.Suspended) { - require.Equal(t, merklesignature.Commitment{}, ba.StateProofID) + if expected && ba.SelectionID.IsEmpty() { + require.Zero(t, ba.StateProofID) } addHash := trackerdb.AccountHashBuilderV6(addr, &ba, encodedAcctData) added, err := trie.Add(addHash) @@ -287,8 +286,8 @@ func TestRemoveOfflineStateProofID(t *testing.T) { var ba trackerdb.BaseAccountData err = protocol.Decode(encodedAcctData, &ba) require.NoError(t, err) - if ba.Status != basics.Online && ba.Status != basics.Suspended { - require.True(t, ba.StateProofID.IsEmpty()) + if ba.SelectionID.IsEmpty() { + require.Zero(t, ba.StateProofID) } } } diff --git a/ledger/testing/randomAccounts.go b/ledger/testing/randomAccounts.go index e9dd8b4d0a..7b99453e2c 100644 --- a/ledger/testing/randomAccounts.go +++ b/ledger/testing/randomAccounts.go @@ -22,7 +22,6 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" - "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/protocol" @@ -69,36 +68,24 @@ func RandomAccountData(rewardsBase uint64) basics.AccountData { // 0 is an invalid round, but would be right if never needed a heartbeat data.LastHeartbeat = basics.Round(crypto.RandUint64() % 10) - switch crypto.RandUint64() % 4 { + switch crypto.RandUint64() % 3 { case 0: data.Status = basics.Online - data.VoteID = crypto.OneTimeSignatureVerifier{0x01} data.IncentiveEligible = crypto.RandUint64()%5 == 0 - data.VoteFirstValid = 1 - data.VoteLastValid = 10000 case 1: data.Status = basics.Offline case 2: data.Status = basics.NotParticipating - case 3: - data.Status = basics.Suspended } - switch data.Status { - case basics.Online, basics.Suspended: + // Give online accounts voting data, and some of the offline too. They are "suspended". + if data.Status == basics.Online || (data.Status == basics.Offline && crypto.RandUint64()%5 == 1) { crypto.RandBytes(data.VoteID[:]) crypto.RandBytes(data.SelectionID[:]) crypto.RandBytes(data.StateProofID[:]) data.VoteFirstValid = basics.Round(crypto.RandUint64()) data.VoteLastValid = basics.Round(crypto.RandUint64() % uint64(math.MaxInt64)) // int64 is the max sqlite can store data.VoteKeyDilution = crypto.RandUint64() - case basics.Offline, basics.NotParticipating: - data.VoteID = crypto.OneTimeSignatureVerifier{} - data.SelectionID = crypto.VRFVerifier{} - data.StateProofID = merklesignature.Commitment{} - data.VoteFirstValid = 0 - data.VoteLastValid = 0 - data.VoteKeyDilution = 0 } data.RewardsBase = rewardsBase From 8a6bc20dca24c6a8fc0bc6db77ad866fb70459c3 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Sun, 14 Jan 2024 13:20:45 -0500 Subject: [PATCH 011/117] Go e2e test showing suspension and proposal to come back online --- .../features/suspension/suspension_test.go | 118 ++++++++++++++++++ .../features/transactions/sendReceive_test.go | 2 +- .../upgrades/application_support_test.go | 4 +- test/e2e-go/upgrades/rekey_support_test.go | 2 +- test/framework/fixtures/restClientFixture.go | 8 +- test/testdata/nettemplates/Suspension.json | 24 ++++ 6 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 test/e2e-go/features/suspension/suspension_test.go create mode 100644 test/testdata/nettemplates/Suspension.json diff --git a/test/e2e-go/features/suspension/suspension_test.go b/test/e2e-go/features/suspension/suspension_test.go new file mode 100644 index 0000000000..2afae04251 --- /dev/null +++ b/test/e2e-go/features/suspension/suspension_test.go @@ -0,0 +1,118 @@ +// Copyright (C) 2019-2024 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package suspension + +import ( + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/test/framework/fixtures" + "github.com/algorand/go-algorand/test/partitiontest" +) + +const roundTime = 4 * time.Second + +// TestBasicSuspension confirms that accounts that don't propose get suspended +// (when a tx naming them occurs) +func TestBasicSuspension(t *testing.T) { + partitiontest.PartitionTest(t) + defer fixtures.ShutdownSynchronizedTest(t) + + if testing.Short() { + t.Skip() + } + t.Parallel() + a := require.New(fixtures.SynchronizedTest(t)) + + // Overview of this test: + // Start a three-node network (84,15,1) + // Stop the 15% node + // Let it run for less than 10*100/15 + // check not suspended, send a tx, still not suspended + // Let it run two more, during which the node can't propose, so it is ready for suspension + // check not suspended, send a tx, NOW suspended + + var fixture fixtures.RestClientFixture + fixture.Setup(t, filepath.Join("nettemplates", "Suspension.json")) + defer fixture.Shutdown() + + richAccount, err := fixture.GetRichestAccount() + a.NoError(err) + + // get Node15's address + n15c := fixture.GetLibGoalClientForNamedNode("Node15") + accounts, err := fixture.GetNodeWalletsSortedByBalance(n15c) + a.NoError(err) + a.Len(accounts, 1) + a.Equal(accounts[0].Status, basics.Online.String()) + address := accounts[0].Address + + // turn off Node15 + n15, err := fixture.GetNodeController("Node15") + a.NoError(err) + a.NoError(n15.FullStop()) + + // Proceed 60 rounds + err = fixture.WaitForRound(60, 60*roundTime) + a.NoError(err) + + // n15account is still online (the node is off, but the account is marked online) + account, err := fixture.LibGoalClient.AccountData(address) + a.NoError(err) + a.Equal(basics.Online, account.Status) + + // Proceed to round 70 + err = fixture.WaitForRound(70, 10*roundTime) + a.NoError(err) + + // n15's account is still online, but only because it has gone "unnoticed" + account, err = fixture.LibGoalClient.AccountData(address) + a.NoError(err) + a.Equal(basics.Online, account.Status) + + fixture.SendMoneyAndWait(70, 1000, 1000, richAccount.Address, address, "") + + // n15's account is now offline, but has voting key material (suspended) + account, err = fixture.LibGoalClient.AccountData(address) + a.NoError(err) + a.Equal(basics.Offline, account.Status) + a.NotZero(account.VoteID) + + // Use the fixture to start the node again. Since we're only a bit past the + // suspension round, it will still be voting. It should get a chance to + // propose soon (15/100 of blocks) which will put it back online. + lg, err := fixture.StartNode(n15.GetDataDir()) + a.NoError(err) + + // Wait for newly restarted node to start. Presumably it'll catchup in + // seconds, and propose by round 90 + _, err = lg.Status() + a.NoError(err) + + // Proceed to round 90 + err = fixture.WaitForRound(90, 20*roundTime) + a.NoError(err) + // n15's account is back online, but has voting key material (suspended) + account, err = fixture.LibGoalClient.AccountData(address) + a.NoError(err) + a.Equal(basics.Online, account.Status) + a.NotZero(account.VoteID) +} diff --git a/test/e2e-go/features/transactions/sendReceive_test.go b/test/e2e-go/features/transactions/sendReceive_test.go index 9d909beedc..22dc94173a 100644 --- a/test/e2e-go/features/transactions/sendReceive_test.go +++ b/test/e2e-go/features/transactions/sendReceive_test.go @@ -81,7 +81,7 @@ func testAccountsCanSendMoney(t *testing.T, templatePath string, numberOfSends i pingAccount := pingAccountList[0].Address pongClient := fixture.GetLibGoalClientForNamedNode("Node") - pongAccounts, err := fixture.GetNodeWalletsSortedByBalance(pongClient.DataDir()) + pongAccounts, err := fixture.GetNodeWalletsSortedByBalance(pongClient) a.NoError(err) var pongAccount string for _, acct := range pongAccounts { diff --git a/test/e2e-go/upgrades/application_support_test.go b/test/e2e-go/upgrades/application_support_test.go index 49635a43ac..bd5372244b 100644 --- a/test/e2e-go/upgrades/application_support_test.go +++ b/test/e2e-go/upgrades/application_support_test.go @@ -85,7 +85,7 @@ func TestApplicationsUpgradeOverREST(t *testing.T) { a := require.New(fixtures.SynchronizedTest(t)) client := fixture.GetLibGoalClientForNamedNode("Node") - accountList, err := fixture.GetNodeWalletsSortedByBalance(client.DataDir()) + accountList, err := fixture.GetNodeWalletsSortedByBalance(client) a.NoError(err) creator := accountList[0].Address @@ -328,7 +328,7 @@ func TestApplicationsUpgradeOverGossip(t *testing.T) { defer fixture.Shutdown() - accountList, err := fixture.GetNodeWalletsSortedByBalance(client.DataDir()) + accountList, err := fixture.GetNodeWalletsSortedByBalance(client) a.NoError(err) creator := accountList[0].Address diff --git a/test/e2e-go/upgrades/rekey_support_test.go b/test/e2e-go/upgrades/rekey_support_test.go index 30226c795f..0dcec41545 100644 --- a/test/e2e-go/upgrades/rekey_support_test.go +++ b/test/e2e-go/upgrades/rekey_support_test.go @@ -46,7 +46,7 @@ func TestRekeyUpgrade(t *testing.T) { defer fixture.Shutdown() client := fixture.GetLibGoalClientForNamedNode("Node") - accountList, err := fixture.GetNodeWalletsSortedByBalance(client.DataDir()) + accountList, err := fixture.GetNodeWalletsSortedByBalance(client) a.NoError(err) accountA := accountList[0].Address diff --git a/test/framework/fixtures/restClientFixture.go b/test/framework/fixtures/restClientFixture.go index 40e12c408b..be8ac2c252 100644 --- a/test/framework/fixtures/restClientFixture.go +++ b/test/framework/fixtures/restClientFixture.go @@ -196,16 +196,12 @@ func (f *RestClientFixture) GetBalanceAndRound(account string) (balance uint64, // GetWalletsSortedByBalance returns the Primary node's accounts sorted DESC by balance // the richest account will be at accounts[0] func (f *RestClientFixture) GetWalletsSortedByBalance() (accounts []model.Account, err error) { - return f.getNodeWalletsSortedByBalance(f.LibGoalClient) + return f.GetNodeWalletsSortedByBalance(f.LibGoalClient) } // GetNodeWalletsSortedByBalance returns the specified node's accounts sorted DESC by balance // the richest account will be at accounts[0] -func (f *RestClientFixture) GetNodeWalletsSortedByBalance(nodeDataDir string) (accounts []model.Account, err error) { - return f.getNodeWalletsSortedByBalance(f.GetLibGoalClientFromDataDir(nodeDataDir)) -} - -func (f *RestClientFixture) getNodeWalletsSortedByBalance(client libgoal.Client) (accounts []model.Account, err error) { +func (f *RestClientFixture) GetNodeWalletsSortedByBalance(client libgoal.Client) (accounts []model.Account, err error) { wh, err := client.GetUnencryptedWalletHandle() if err != nil { return nil, fmt.Errorf("unable to retrieve wallet handle : %v", err) diff --git a/test/testdata/nettemplates/Suspension.json b/test/testdata/nettemplates/Suspension.json new file mode 100644 index 0000000000..7dae62aa3e --- /dev/null +++ b/test/testdata/nettemplates/Suspension.json @@ -0,0 +1,24 @@ +{ + "Genesis": { + "NetworkName": "tbd", + "ConsensusProtocol": "future", + "LastPartKeyRound": 500, + "Wallets": [ + { "Name": "Relay", "Stake": 84, "Online": true }, + { "Name": "Wallet15", "Stake": 15, "Online": true }, + { "Name": "Wallet1", "Stake": 1, "Online": true } + ] + }, + "Nodes": [ + { + "Name": "Relay", + "Wallets": [{ "Name": "Relay", "ParticipationOnly": false }], + "IsRelay": true + }, + { + "Name": "Node15", + "Wallets": [{ "Name": "Wallet15", "ParticipationOnly": false }] + }, + { "Name": "Node1", "Wallets": [{ "Name": "Wallet1", "ParticipationOnly": false }] } + ] +} From 49c24de6d66440db2eb27267fdf39180aabb4b15 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 16 Jan 2024 12:56:32 -0500 Subject: [PATCH 012/117] If a suspended account proposes, bring it back online. --- ledger/eval/eval.go | 24 ++++++++----- ledger/eval_simple_test.go | 36 ++++++++++++------- ledger/ledgercore/accountdata.go | 5 +++ .../features/suspension/suspension_test.go | 4 ++- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index a418edf65c..fa62521c61 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -797,15 +797,21 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts } } - // Note their proposal - prp, err := eval.state.Get(prevHeader.Proposer, false) - if err != nil { - return nil, err - } - prp.LastProposed = hdr.Round - 1 - err = eval.state.Put(prevHeader.Proposer, prp) - if err != nil { - return nil, err + // Note their proposal if MiningEnabled. + if proto.EnableMining { + prp, err := eval.state.Get(prevHeader.Proposer, false) + if err != nil { + return nil, err + } + prp.LastProposed = hdr.Round - 1 + // An account could propose, even while suspended, because of the 320 round lookback. + if prp.Suspended() { + prp.Status = basics.Online + } + err = eval.state.Put(prevHeader.Proposer, prp) + if err != nil { + return nil, err + } } if eval.Tracer != nil { diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index e897ff34c5..de05c857f7 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -389,15 +389,15 @@ func TestAbsentTracking(t *testing.T) { totals, err := dl.generator.Totals(0) require.NoError(t, err) require.NotZero(t, totals.Online.Money.Raw) - for i, addr := range addrs { - fmt.Printf("addrs[%d] == %v\n", i, addr) - } + + // as configured above, the first two accounts should be online require.True(t, lookup(t, dl.generator, addrs[0]).Status == basics.Online) require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) require.False(t, lookup(t, dl.generator, addrs[2]).Status == basics.Online) dl.fullBlock() + // although it's not even online, we'll use addrs[7] as the proposer proposer := addrs[7] dl.beginBlock() dl.txns(&txntest.Txn{ @@ -406,8 +406,9 @@ func TestAbsentTracking(t *testing.T) { Receiver: addrs[2], Amount: 100_000, }) - dl.endBlock(proposer) + vb := dl.endBlock(proposer) + // addr[1] is spent to an offline account, so Online totals decrease newtotals, err := dl.generator.Totals(dl.generator.Latest()) require.NoError(t, err) // payment and fee left the online account @@ -421,10 +422,12 @@ func TestAbsentTracking(t *testing.T) { if ver >= checkingBegins { // version sanity check require.True(t, dl.generator.GenesisProto().EnableAbsenteeTracking()) + require.Equal(t, proposer, vb.Block().BlockHeader.Proposer) require.NotZero(t, prp.LastProposed) require.Zero(t, prp.LastHeartbeat) // genesis participants have never hb } else { require.False(t, dl.generator.GenesisProto().EnableAbsenteeTracking()) + require.Zero(t, vb.Block().BlockHeader.Proposer) require.Zero(t, prp.LastProposed) require.Zero(t, prp.LastHeartbeat) } @@ -433,11 +436,12 @@ func TestAbsentTracking(t *testing.T) { dl.txns(&txntest.Txn{Type: "keyreg", Sender: addrs[2]}) // OFFLINE keyreg regger := lookup(t, dl.validator, addrs[2]) + // total were unchanged by an offline keyreg from an offline account newtotals, err = dl.generator.Totals(dl.generator.Latest()) require.NoError(t, err) require.Equal(t, totals.Online.Money.Raw, newtotals.Online.Money.Raw) - // offline transaction records nothing + // an an offline keyreg transaction records no activity require.Zero(t, regger.LastProposed) require.Zero(t, regger.LastHeartbeat) @@ -448,6 +452,7 @@ func TestAbsentTracking(t *testing.T) { VotePK: [32]byte{1}, SelectionPK: [32]byte{1}, }) + // online totals have grown newtotals, err = dl.generator.Totals(dl.generator.Latest()) require.NoError(t, err) require.Greater(t, newtotals.Online.Money.Raw, totals.Online.Money.Raw) @@ -480,10 +485,10 @@ func TestAbsentTracking(t *testing.T) { // addrs 0-2 all have about 1/3 of stake, so become eligible for // suspension after 30 rounds. We're at about 35. But, since blocks are - // empty, nobody's susspendible account is noticed. - require.True(t, lookup(t, dl.generator, addrs[0]).Status == basics.Online) - require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) - require.True(t, lookup(t, dl.generator, addrs[2]).Status == basics.Online) + // empty, nobody's suspendible account is noticed. + require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[0]).Status) + require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[1]).Status) + require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[2]).Status) // when 2 pays 0, they both get noticed and get suspended dl.txns(&txntest.Txn{ @@ -492,10 +497,17 @@ func TestAbsentTracking(t *testing.T) { Receiver: addrs[0], Amount: 0, }) - require.Equal(t, ver < checkingBegins, lookup(t, dl.generator, addrs[0]).Status == basics.Online) - require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) - require.Equal(t, ver < checkingBegins, lookup(t, dl.generator, addrs[2]).Status == basics.Online) + require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[0]).Status == basics.Offline) + require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[1]).Status) + require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).Status == basics.Offline) + // now, addrs[2] proposes, so it gets back online, but not immediately, + // because processing happens after the proposing block + dl.proposer = addrs[2] + dl.fullBlock() + require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).Status == basics.Offline) + dl.fullBlock() + require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[2]).Status) }) } diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 227421c9c5..4fb81cca96 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -151,6 +151,11 @@ func (u *AccountData) Suspend() { u.Status = basics.Offline } +// Suspended returns true if the account is suspended (offline with keys) +func (u *AccountData) Suspended() bool { + return u.Status == basics.Offline && !u.VoteID.IsEmpty() +} + // MinBalance computes the minimum balance requirements for an account based on // some consensus parameters. MinBalance should correspond roughly to how much // storage the account is allowed to store on disk. diff --git a/test/e2e-go/features/suspension/suspension_test.go b/test/e2e-go/features/suspension/suspension_test.go index 2afae04251..32f41761ae 100644 --- a/test/e2e-go/features/suspension/suspension_test.go +++ b/test/e2e-go/features/suspension/suspension_test.go @@ -78,6 +78,7 @@ func TestBasicSuspension(t *testing.T) { account, err := fixture.LibGoalClient.AccountData(address) a.NoError(err) a.Equal(basics.Online, account.Status) + voteID := account.VoteID // Proceed to round 70 err = fixture.WaitForRound(70, 10*roundTime) @@ -110,9 +111,10 @@ func TestBasicSuspension(t *testing.T) { // Proceed to round 90 err = fixture.WaitForRound(90, 20*roundTime) a.NoError(err) - // n15's account is back online, but has voting key material (suspended) + // n15's account is back online, with same voting material account, err = fixture.LibGoalClient.AccountData(address) a.NoError(err) a.Equal(basics.Online, account.Status) a.NotZero(account.VoteID) + a.Equal(voteID, account.VoteID) } From be105aa90a057ff582e586191f4b8d2112905c5c Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 18 Jan 2024 14:16:24 -0500 Subject: [PATCH 013/117] Suspensions turns off IncentiveEligible --- ledger/eval_simple_test.go | 40 ++++++++++++++++--- ledger/ledgercore/accountdata.go | 2 + .../features/suspension/suspension_test.go | 4 ++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index de05c857f7..d5b7443785 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -244,6 +244,9 @@ func TestMiningFees(t *testing.T) { for _, proposer := range []basics.Address{tooBig, tooSmall, smallest, biggest} { t.Log(proposer) + prp := lookup(dl.t, dl.generator, proposer) + require.False(t, prp.IncentiveEligible) + dl.txn(&txntest.Txn{ Type: "keyreg", Sender: proposer, @@ -254,6 +257,9 @@ func TestMiningFees(t *testing.T) { VoteFirst: 1, VoteLast: 1000, }) + prp = lookup(dl.t, dl.generator, proposer) + require.Equal(t, ver >= miningBegins, prp.IncentiveEligible) + dl.fullBlock() // start with an empty block, so no mining fees are paid at start of next one presink := micros(dl.t, dl.generator, genBalances.FeeSink) @@ -390,10 +396,14 @@ func TestAbsentTracking(t *testing.T) { require.NoError(t, err) require.NotZero(t, totals.Online.Money.Raw) - // as configured above, the first two accounts should be online + // as configured above, only the first two accounts should be online require.True(t, lookup(t, dl.generator, addrs[0]).Status == basics.Online) require.True(t, lookup(t, dl.generator, addrs[1]).Status == basics.Online) require.False(t, lookup(t, dl.generator, addrs[2]).Status == basics.Online) + // genesis accounts don't begin IncentiveEligible, even if online + require.False(t, lookup(t, dl.generator, addrs[0]).IncentiveEligible) + require.False(t, lookup(t, dl.generator, addrs[1]).IncentiveEligible) + require.False(t, lookup(t, dl.generator, addrs[2]).IncentiveEligible) dl.fullBlock() @@ -408,6 +418,12 @@ func TestAbsentTracking(t *testing.T) { }) vb := dl.endBlock(proposer) + // no changes until the next block + prp := lookup(t, dl.validator, proposer) + require.Zero(t, prp.LastProposed) + require.Zero(t, prp.LastHeartbeat) + require.False(t, prp.IncentiveEligible) + // addr[1] is spent to an offline account, so Online totals decrease newtotals, err := dl.generator.Totals(dl.generator.Latest()) require.NoError(t, err) @@ -417,16 +433,15 @@ func TestAbsentTracking(t *testing.T) { dl.fullBlock() - prp := lookup(t, dl.validator, proposer) + prp = lookup(t, dl.validator, proposer) + require.False(t, prp.IncentiveEligible) if ver >= checkingBegins { // version sanity check - require.True(t, dl.generator.GenesisProto().EnableAbsenteeTracking()) require.Equal(t, proposer, vb.Block().BlockHeader.Proposer) require.NotZero(t, prp.LastProposed) require.Zero(t, prp.LastHeartbeat) // genesis participants have never hb } else { - require.False(t, dl.generator.GenesisProto().EnableAbsenteeTracking()) require.Zero(t, vb.Block().BlockHeader.Proposer) require.Zero(t, prp.LastProposed) require.Zero(t, prp.LastHeartbeat) @@ -445,7 +460,7 @@ func TestAbsentTracking(t *testing.T) { require.Zero(t, regger.LastProposed) require.Zero(t, regger.LastHeartbeat) - // ONLINE keyreg + // ONLINE keyreg without extra fee dl.txns(&txntest.Txn{ Type: "keyreg", Sender: addrs[2], @@ -466,6 +481,19 @@ func TestAbsentTracking(t *testing.T) { } else { require.Zero(t, regger.LastHeartbeat) } + require.False(t, regger.IncentiveEligible) + + // ONLINE keyreg with extra fee + dl.txns(&txntest.Txn{ + Type: "keyreg", + Fee: 2_000_000, + Sender: addrs[2], + VotePK: [32]byte{1}, + SelectionPK: [32]byte{1}, + }) + + regger = lookup(t, dl.validator, addrs[2]) + require.Equal(t, ver >= checkingBegins, regger.IncentiveEligible) for i := 0; i < 5; i++ { dl.fullBlock() @@ -489,6 +517,7 @@ func TestAbsentTracking(t *testing.T) { require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[0]).Status) require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[1]).Status) require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[2]).Status) + require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).IncentiveEligible) // when 2 pays 0, they both get noticed and get suspended dl.txns(&txntest.Txn{ @@ -500,6 +529,7 @@ func TestAbsentTracking(t *testing.T) { require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[0]).Status == basics.Offline) require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[1]).Status) require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).Status == basics.Offline) + require.False(t, lookup(t, dl.generator, addrs[2]).IncentiveEligible) // now, addrs[2] proposes, so it gets back online, but not immediately, // because processing happens after the proposing block diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 4fb81cca96..501b4d4eb6 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -149,6 +149,8 @@ func (u *AccountData) ClearOnlineState() { // that a heartbeat can bring the account back Online func (u *AccountData) Suspend() { u.Status = basics.Offline + // To regain eligibility, the account will have to `keyreg` with the extra fee. + u.IncentiveEligible = false } // Suspended returns true if the account is suspended (offline with keys) diff --git a/test/e2e-go/features/suspension/suspension_test.go b/test/e2e-go/features/suspension/suspension_test.go index 32f41761ae..de4a68ca08 100644 --- a/test/e2e-go/features/suspension/suspension_test.go +++ b/test/e2e-go/features/suspension/suspension_test.go @@ -96,6 +96,7 @@ func TestBasicSuspension(t *testing.T) { a.NoError(err) a.Equal(basics.Offline, account.Status) a.NotZero(account.VoteID) + a.False(account.IncentiveEligible) // Use the fixture to start the node again. Since we're only a bit past the // suspension round, it will still be voting. It should get a chance to @@ -117,4 +118,7 @@ func TestBasicSuspension(t *testing.T) { a.Equal(basics.Online, account.Status) a.NotZero(account.VoteID) a.Equal(voteID, account.VoteID) + // coming back online by proposal does not make you incentive eligible (you + // didn't "pay the fine") + a.False(account.IncentiveEligible) } From b6578aa15992d5514ddbf4d0d12c1c33978dc288 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 18 Jan 2024 14:38:24 -0500 Subject: [PATCH 014/117] Don't consider pre-existing accounts absent --- ledger/eval/eval.go | 6 ++++++ ledger/eval_simple_test.go | 34 +++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index fa62521c61..6cb4ec4ce4 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1528,6 +1528,12 @@ func max(a, b basics.Round) basics.Round { } func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, lastSeen basics.Round, current basics.Round) bool { + // Don't consider accounts that were online when mining went into effect as + // absent. They get noticed the next time they propose or keyreg, which + // ought to be soon, if they want to earn incentives. + if lastSeen == 0 { + return false + } // See if the account has exceeded 10x their expected observation interval. allowableLag := basics.Round(10 * totalOnlineStake.Raw / acctStake.Raw) return lastSeen+allowableLag < current diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index d5b7443785..1a279c8c72 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -392,7 +392,15 @@ func TestAbsentTracking(t *testing.T) { dl := NewDoubleLedger(t, genBalances, cv, cfg) defer dl.Close() - totals, err := dl.generator.Totals(0) + // have addrs[1] go online, which makes it eligible for suspension + dl.txn(&txntest.Txn{ + Type: "keyreg", + Sender: addrs[1], + VotePK: [32]byte{1}, + SelectionPK: [32]byte{1}, + }) + + totals, err := dl.generator.Totals(1) require.NoError(t, err) require.NotZero(t, totals.Online.Money.Raw) @@ -511,9 +519,10 @@ func TestAbsentTracking(t *testing.T) { dl.fullBlock() } - // addrs 0-2 all have about 1/3 of stake, so become eligible for - // suspension after 30 rounds. We're at about 35. But, since blocks are - // empty, nobody's suspendible account is noticed. + // addrs 0-2 all have about 1/3 of stake, so seemingly (see next block + // of checks) become eligible for suspension after 30 rounds. We're at + // about 35. But, since blocks are empty, nobody's suspendible account + // is noticed. require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[0]).Status) require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[1]).Status) require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[2]).Status) @@ -526,11 +535,26 @@ func TestAbsentTracking(t *testing.T) { Receiver: addrs[0], Amount: 0, }) - require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[0]).Status == basics.Offline) + // addr[0] has never proposed or heartbeat so it is not considered absent + require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[0]).Status) + // addr[1] still hasn't been "noticed" require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[1]).Status) require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).Status == basics.Offline) require.False(t, lookup(t, dl.generator, addrs[2]).IncentiveEligible) + // now, when 2 pays 1, 1 gets suspended (unlike 0, we had 1 keyreg early on, so LastHeartbeat>0) + dl.txns(&txntest.Txn{ + Type: "pay", + Sender: addrs[2], + Receiver: addrs[1], + Amount: 0, + }) + require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[0]).Status) + require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[1]).Status == basics.Offline) + require.False(t, lookup(t, dl.generator, addrs[1]).IncentiveEligible) + require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).Status == basics.Offline) + require.False(t, lookup(t, dl.generator, addrs[2]).IncentiveEligible) + // now, addrs[2] proposes, so it gets back online, but not immediately, // because processing happens after the proposing block dl.proposer = addrs[2] From 09f72dbbafcdef7ea32ee7522d1a1cfd08f8f7ee Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 24 Jan 2024 15:47:36 -0500 Subject: [PATCH 015/117] Perform bonus payments, with exponential decay --- agreement/msgp_gen.go | 481 +++++++++++++---------- config/consensus.go | 3 + data/basics/overflow.go | 8 + data/basics/units.go | 6 +- data/bookkeeping/block.go | 68 ++++ data/bookkeeping/block_test.go | 40 +- data/bookkeeping/msgp_gen.go | 290 ++++++++------ data/transactions/logic/eval.go | 2 + data/transactions/logic/fields.go | 3 + data/transactions/logic/fields_string.go | 7 +- ledger/eval/eval.go | 30 +- ledger/ledgercore/accountdata.go | 9 + 12 files changed, 601 insertions(+), 346 deletions(-) diff --git a/agreement/msgp_gen.go b/agreement/msgp_gen.go index 2906e73048..965adb2e28 100644 --- a/agreement/msgp_gen.go +++ b/agreement/msgp_gen.go @@ -4493,200 +4493,209 @@ func PlayerMaxSize() (s int) { func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(32) - var zb0005Mask uint64 /* 41 bits */ - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { + zb0005Len := uint32(33) + var zb0005Mask uint64 /* 42 bits */ + if (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40 } - if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { zb0005Len-- zb0005Mask |= 0x80 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200 } - if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { zb0005Len-- zb0005Mask |= 0x400 } - if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { zb0005Len-- zb0005Mask |= 0x800 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x4000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x8000 } - if (*z).unauthenticatedProposal.OriginalPeriod == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { zb0005Len-- zb0005Mask |= 0x10000 } - if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { + if (*z).unauthenticatedProposal.OriginalPeriod == 0 { zb0005Len-- zb0005Mask |= 0x20000 } - if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000 } - if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x80000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { + if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x100000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + zb0005Len-- + zb0005Mask |= 0x1000000 + } if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x2000000 + zb0005Mask |= 0x4000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x4000000 + zb0005Mask |= 0x8000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x8000000 + zb0005Mask |= 0x10000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x10000000 + zb0005Mask |= 0x20000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x20000000 + zb0005Mask |= 0x40000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { zb0005Len-- - zb0005Mask |= 0x40000000 + zb0005Mask |= 0x80000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { zb0005Len-- - zb0005Mask |= 0x80000000 + zb0005Mask |= 0x100000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { zb0005Len-- - zb0005Mask |= 0x100000000 + zb0005Mask |= 0x200000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x200000000 + zb0005Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x400000000 + zb0005Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x800000000 + zb0005Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x1000000000 + zb0005Mask |= 0x2000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x2000000000 + zb0005Mask |= 0x4000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { zb0005Len-- - zb0005Mask |= 0x4000000000 + zb0005Mask |= 0x8000000000 } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) if zb0005Len != 0 { if (zb0005Mask & 0x40) == 0 { // if not empty + // string "bi" + o = append(o, 0xa2, 0x62, 0x69) + o = (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MarshalMsg(o) + } + if (zb0005Mask & 0x80) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel) } - if (zb0005Mask & 0x80) == 0 { // if not empty + if (zb0005Mask & 0x100) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MarshalMsg(o) } - if (zb0005Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0005Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue) } - if (zb0005Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) } - if (zb0005Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0005Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0005Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0005Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0005Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "oper" o = append(o, 0xa4, 0x6f, 0x70, 0x65, 0x72) o = msgp.AppendUint64(o, uint64((*z).unauthenticatedProposal.OriginalPeriod)) } - if (zb0005Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty // string "oprop" o = append(o, 0xa5, 0x6f, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.OriginalProposer.MarshalMsg(o) } - if (zb0005Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "partupdabs" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts == nil { @@ -4698,7 +4707,7 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) } } - if (zb0005Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -4710,52 +4719,52 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0005Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0005Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0005Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0005Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { @@ -4775,42 +4784,42 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter) } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp) } - if (zb0005Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x800000000) == 0 { // if not empty + if (zb0005Mask & 0x1000000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o) } - if (zb0005Mask & 0x1000000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x2000000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x4000000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -4932,6 +4941,14 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Bonus") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -5275,6 +5292,12 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o err = msgp.WrapError(err, "FeesCollected") return } + case "bi": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Bonus") + return + } case "fees": bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -5504,7 +5527,7 @@ func (_ *proposal) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *proposal) Msgsize() (s int) { - s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking { _ = zb0001 @@ -5526,12 +5549,12 @@ func (z *proposal) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *proposal) MsgIsZero() bool { - return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) + return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func ProposalMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.unauthenticatedProposal.Block.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) @@ -8981,204 +9004,213 @@ func ThresholdEventMaxSize() (s int) { func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(33) - var zb0005Mask uint64 /* 41 bits */ - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { + zb0005Len := uint32(34) + var zb0005Mask uint64 /* 42 bits */ + if (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80 } - if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { zb0005Len-- zb0005Mask |= 0x100 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400 } - if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { zb0005Len-- zb0005Mask |= 0x800 } - if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { zb0005Len-- zb0005Mask |= 0x1000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x4000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x8000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x10000 } - if (*z).unauthenticatedProposal.OriginalPeriod == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { zb0005Len-- zb0005Mask |= 0x20000 } - if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { + if (*z).unauthenticatedProposal.OriginalPeriod == 0 { zb0005Len-- zb0005Mask |= 0x40000 } - if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80000 } - if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x100000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { + if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } - if (*z).PriorVote.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).PriorVote.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000000 } + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + zb0005Len-- + zb0005Mask |= 0x4000000 + } if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x8000000 + zb0005Mask |= 0x10000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x10000000 + zb0005Mask |= 0x20000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x20000000 + zb0005Mask |= 0x40000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x40000000 + zb0005Mask |= 0x80000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x80000000 + zb0005Mask |= 0x100000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { zb0005Len-- - zb0005Mask |= 0x100000000 + zb0005Mask |= 0x200000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { zb0005Len-- - zb0005Mask |= 0x200000000 + zb0005Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { zb0005Len-- - zb0005Mask |= 0x400000000 + zb0005Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x800000000 + zb0005Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x1000000000 + zb0005Mask |= 0x2000000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x2000000000 + zb0005Mask |= 0x4000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x4000000000 + zb0005Mask |= 0x8000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x8000000000 + zb0005Mask |= 0x10000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { zb0005Len-- - zb0005Mask |= 0x10000000000 + zb0005Mask |= 0x20000000000 } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) if zb0005Len != 0 { if (zb0005Mask & 0x80) == 0 { // if not empty + // string "bi" + o = append(o, 0xa2, 0x62, 0x69) + o = (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MarshalMsg(o) + } + if (zb0005Mask & 0x100) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel) } - if (zb0005Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MarshalMsg(o) } - if (zb0005Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0005Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue) } - if (zb0005Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) } - if (zb0005Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0005Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0005Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0005Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0005Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty // string "oper" o = append(o, 0xa4, 0x6f, 0x70, 0x65, 0x72) o = msgp.AppendUint64(o, uint64((*z).unauthenticatedProposal.OriginalPeriod)) } - if (zb0005Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "oprop" o = append(o, 0xa5, 0x6f, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.OriginalProposer.MarshalMsg(o) } - if (zb0005Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "partupdabs" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts == nil { @@ -9190,7 +9222,7 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) } } - if (zb0005Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -9202,57 +9234,57 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = (*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0005Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0005Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "pv" o = append(o, 0xa2, 0x70, 0x76) o = (*z).PriorVote.MarshalMsg(o) } - if (zb0005Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0005Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o) } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { @@ -9272,42 +9304,42 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter) } - if (zb0005Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp) } - if (zb0005Mask & 0x800000000) == 0 { // if not empty + if (zb0005Mask & 0x1000000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x1000000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x2000000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o) } - if (zb0005Mask & 0x4000000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x8000000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x10000000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -9429,6 +9461,14 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Bonus") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -9780,6 +9820,12 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal err = msgp.WrapError(err, "FeesCollected") return } + case "bi": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Bonus") + return + } case "fees": bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -10015,7 +10061,7 @@ func (_ *transmittedPayload) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *transmittedPayload) Msgsize() (s int) { - s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking { _ = zb0001 @@ -10037,12 +10083,12 @@ func (z *transmittedPayload) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *transmittedPayload) MsgIsZero() bool { - return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) && ((*z).PriorVote.MsgIsZero()) + return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) && ((*z).PriorVote.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func TransmittedPayloadMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.unauthenticatedProposal.Block.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) @@ -10766,200 +10812,209 @@ func UnauthenticatedEquivocationVoteMaxSize() (s int) { func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(32) - var zb0005Mask uint64 /* 39 bits */ - if (*z).Block.BlockHeader.RewardsState.RewardsLevel == 0 { + zb0005Len := uint32(33) + var zb0005Mask uint64 /* 40 bits */ + if (*z).Block.BlockHeader.Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40 } - if (*z).Block.BlockHeader.FeesCollected.MsgIsZero() { + if (*z).Block.BlockHeader.RewardsState.RewardsLevel == 0 { zb0005Len-- zb0005Mask |= 0x80 } - if (*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { + if (*z).Block.BlockHeader.FeesCollected.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100 } - if (*z).Block.BlockHeader.RewardsState.RewardsResidue == 0 { + if (*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200 } - if (*z).Block.BlockHeader.GenesisID == "" { + if (*z).Block.BlockHeader.RewardsState.RewardsResidue == 0 { zb0005Len-- zb0005Mask |= 0x400 } - if (*z).Block.BlockHeader.GenesisHash.MsgIsZero() { + if (*z).Block.BlockHeader.GenesisID == "" { zb0005Len-- zb0005Mask |= 0x800 } - if (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).Block.BlockHeader.GenesisHash.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000 } - if (*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { + if (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000 } - if (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x4000 } - if (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { + if (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x8000 } - if (*z).OriginalPeriod == 0 { + if (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { zb0005Len-- zb0005Mask |= 0x10000 } - if (*z).OriginalProposer.MsgIsZero() { + if (*z).OriginalPeriod == 0 { zb0005Len-- zb0005Mask |= 0x20000 } - if len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + if (*z).OriginalProposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000 } - if len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x80000 } - if (*z).Block.BlockHeader.Branch.MsgIsZero() { + if len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x100000 } - if (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).Block.BlockHeader.Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).Block.BlockHeader.Proposer.MsgIsZero() { + if (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).Block.BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).Block.BlockHeader.Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } + if (*z).Block.BlockHeader.RewardsState.RewardsRate == 0 { + zb0005Len-- + zb0005Mask |= 0x1000000 + } if (*z).Block.BlockHeader.Round.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x2000000 + zb0005Mask |= 0x4000000 } if (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x4000000 + zb0005Mask |= 0x8000000 } if (*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x8000000 + zb0005Mask |= 0x10000000 } if (*z).SeedProof.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x10000000 + zb0005Mask |= 0x20000000 } if (*z).Block.BlockHeader.Seed.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x20000000 + zb0005Mask |= 0x40000000 } if len((*z).Block.BlockHeader.StateProofTracking) == 0 { zb0005Len-- - zb0005Mask |= 0x40000000 + zb0005Mask |= 0x80000000 } if (*z).Block.BlockHeader.TxnCounter == 0 { zb0005Len-- - zb0005Mask |= 0x80000000 + zb0005Mask |= 0x100000000 } if (*z).Block.BlockHeader.TimeStamp == 0 { zb0005Len-- - zb0005Mask |= 0x100000000 + zb0005Mask |= 0x200000000 } if (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x200000000 + zb0005Mask |= 0x400000000 } if (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x400000000 + zb0005Mask |= 0x800000000 } if (*z).Block.Payset.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x800000000 + zb0005Mask |= 0x1000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x1000000000 + zb0005Mask |= 0x2000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x2000000000 + zb0005Mask |= 0x4000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false { zb0005Len-- - zb0005Mask |= 0x4000000000 + zb0005Mask |= 0x8000000000 } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) if zb0005Len != 0 { if (zb0005Mask & 0x40) == 0 { // if not empty + // string "bi" + o = append(o, 0xa2, 0x62, 0x69) + o = (*z).Block.BlockHeader.Bonus.MarshalMsg(o) + } + if (zb0005Mask & 0x80) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsLevel) } - if (zb0005Mask & 0x80) == 0 { // if not empty + if (zb0005Mask & 0x100) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).Block.BlockHeader.FeesCollected.MarshalMsg(o) } - if (zb0005Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).Block.BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0005Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsResidue) } - if (zb0005Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).Block.BlockHeader.GenesisID) } - if (zb0005Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).Block.BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0005Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0005Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).Block.BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0005Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0005Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "oper" o = append(o, 0xa4, 0x6f, 0x70, 0x65, 0x72) o = msgp.AppendUint64(o, uint64((*z).OriginalPeriod)) } - if (zb0005Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty // string "oprop" o = append(o, 0xa5, 0x6f, 0x70, 0x72, 0x6f, 0x70) o = (*z).OriginalProposer.MarshalMsg(o) } - if (zb0005Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "partupdabs" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) if (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts == nil { @@ -10971,7 +11026,7 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = (*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) } } - if (zb0005Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -10983,52 +11038,52 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = (*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0005Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0005Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).Block.BlockHeader.Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0005Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0005Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).SeedProof.MarshalMsg(o) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).Block.BlockHeader.StateProofTracking == nil { @@ -11048,42 +11103,42 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.TxnCounter) } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).Block.BlockHeader.TimeStamp) } - if (zb0005Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x800000000) == 0 { // if not empty + if (zb0005Mask & 0x1000000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).Block.Payset.MarshalMsg(o) } - if (zb0005Mask & 0x1000000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x2000000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x4000000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -11205,6 +11260,14 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).Block.BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Bonus") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -11548,6 +11611,12 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma err = msgp.WrapError(err, "FeesCollected") return } + case "bi": + bts, err = (*z).Block.BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Bonus") + return + } case "fees": bts, err = (*z).Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -11777,7 +11846,7 @@ func (_ *unauthenticatedProposal) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *unauthenticatedProposal) Msgsize() (s int) { - s = 3 + 4 + (*z).Block.BlockHeader.Round.Msgsize() + 5 + (*z).Block.BlockHeader.Branch.Msgsize() + 5 + (*z).Block.BlockHeader.Seed.Msgsize() + 4 + (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).Block.BlockHeader.GenesisID) + 3 + (*z).Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).Block.BlockHeader.FeesCollected.Msgsize() + 5 + (*z).Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).Block.BlockHeader.Round.Msgsize() + 5 + (*z).Block.BlockHeader.Branch.Msgsize() + 5 + (*z).Block.BlockHeader.Seed.Msgsize() + 4 + (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).Block.BlockHeader.GenesisID) + 3 + (*z).Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).Block.BlockHeader.FeesCollected.Msgsize() + 3 + (*z).Block.BlockHeader.Bonus.Msgsize() + 5 + (*z).Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).Block.BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).Block.BlockHeader.StateProofTracking { _ = zb0001 @@ -11799,12 +11868,12 @@ func (z *unauthenticatedProposal) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *unauthenticatedProposal) MsgIsZero() bool { - return ((*z).Block.BlockHeader.Round.MsgIsZero()) && ((*z).Block.BlockHeader.Branch.MsgIsZero()) && ((*z).Block.BlockHeader.Seed.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TimeStamp == 0) && ((*z).Block.BlockHeader.GenesisID == "") && ((*z).Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).Block.BlockHeader.TxnCounter == 0) && (len((*z).Block.BlockHeader.StateProofTracking) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Block.Payset.MsgIsZero()) && ((*z).SeedProof.MsgIsZero()) && ((*z).OriginalPeriod == 0) && ((*z).OriginalProposer.MsgIsZero()) + return ((*z).Block.BlockHeader.Round.MsgIsZero()) && ((*z).Block.BlockHeader.Branch.MsgIsZero()) && ((*z).Block.BlockHeader.Seed.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TimeStamp == 0) && ((*z).Block.BlockHeader.GenesisID == "") && ((*z).Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).Block.BlockHeader.Bonus.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).Block.BlockHeader.TxnCounter == 0) && (len((*z).Block.BlockHeader.StateProofTracking) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Block.Payset.MsgIsZero()) && ((*z).SeedProof.MsgIsZero()) && ((*z).OriginalPeriod == 0) && ((*z).OriginalProposer.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func UnauthenticatedProposalMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.Block.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) diff --git a/config/consensus.go b/config/consensus.go index 191196b3af..2f2dbe4e9c 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -534,6 +534,9 @@ type ConsensusParams struct { // dynamic filter, it will be calculated and logged (but not used). DynamicFilterTimeout bool + // BonusPlan is the "version" of the block bonus plan. 0 indicates no block bonuses. + BonusPlan uint8 + // EnableMining means that the proposer should be included in the BlockHeader. EnableMining bool diff --git a/data/basics/overflow.go b/data/basics/overflow.go index 9f90577ec7..4c8ded599c 100644 --- a/data/basics/overflow.go +++ b/data/basics/overflow.go @@ -142,6 +142,14 @@ func (t *OverflowTracker) ScalarMulA(a MicroAlgos, b uint64) MicroAlgos { return MicroAlgos{Raw: t.Mul(a.Raw, b)} } +// MinA returns the smaller of 2 MicroAlgos values +func MinA(a, b MicroAlgos) MicroAlgos { + if a.Raw < b.Raw { + return a + } + return b +} + // Muldiv computes a*b/c. The overflow flag indicates that // the result was 2^64 or greater. func Muldiv(a uint64, b uint64, c uint64) (res uint64, overflow bool) { diff --git a/data/basics/units.go b/data/basics/units.go index 09bee7b8a8..466be0ff7a 100644 --- a/data/basics/units.go +++ b/data/basics/units.go @@ -130,9 +130,9 @@ func MicroAlgosMaxSize() (s int) { } // Algos is a convenience function so that whole Algos can be written easily. It -// panics on overflow because it should only be used constants - things that are -// best human-readable in source code - not used on arbitrary values from, say, -// transactions. +// panics on overflow because it should only be used for constants - things that +// are best human-readable in source code - not used on arbitrary values from, +// say, transactions. func Algos(algos uint64) MicroAlgos { if algos > math.MaxUint64/1_000_000 { panic(algos) diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 4d3cf8a5bc..2ce2b9eb0e 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -68,6 +68,10 @@ type ( // block. Populated if proto.EnableMining. FeesCollected basics.MicroAlgos `codec:"fc"` + // Bonus is the bonus incentive to be paid for proposing this block. It + // begins as a consensus parameter value, and decays periodically. + Bonus basics.MicroAlgos `codec:"bi"` + // Rewards. // // When a block is applied, some amount of rewards are accrued to @@ -485,6 +489,61 @@ func ProcessUpgradeParams(prev BlockHeader) (uv UpgradeVote, us UpgradeState, er return upgradeVote, upgradeState, err } +type bonusPlan struct { + baseRound basics.Round + baseAmount basics.MicroAlgos + decayInterval basics.Round +} + +var bonusPlans = []bonusPlan{ + 0: {}, + 1: { + baseRound: 0, // goes into effect with upgrade + baseAmount: basics.Algos(2), + // 2.9 sec rounds gives about 10.8M rounds per year. + decayInterval: 500_000, // .9^(10.8/.5) = 10% decay per year + }, + // If we need to change the decay rate (only), we would create a new plan like: + // { decayInterval: XXX} by using an old baseRound, the amount is not adjusted + // For a bigger change, we'd use a plan like: + // { baseRound: , baseAmount: , decayInterval: } + // the new decay rate would go into effect at upgrade time, and the new + // amount would be set explicitly at baseRound. So care must be taken to + // make it _higher_ than the round of the upgrade. +} + +func nextBonus(prev BlockHeader, params *config.ConsensusParams) basics.MicroAlgos { + // We always set if we are in the baseRound so that a new bonus plan can + // reset the amount. + current := prev.Round + 1 + plan := bonusPlans[params.BonusPlan] + if current == plan.baseRound { + return plan.baseAmount + } + + bonus := prev.Bonus + switch { + case bonus.IsZero() && current > plan.baseRound: + prevParams, _ := config.Consensus[prev.CurrentProtocol] // presence ensured by ProcessUpgradeParams + if prevParams.BonusPlan == 0 { + // We must have have just upgraded, with a passed baseRound, so install the bonus + bonus = plan.baseAmount + } + // else bonus is zero because it has decayed to zero. that's fine. + case bonus.IsZero() && current < plan.baseRound: + /* do nothing, wait for baseRound */ + default: + if plan.decayInterval != 0 && current%plan.decayInterval == 0 { + var o bool + bonus.Raw, o = basics.Muldiv(bonus.Raw, 99, 100) + if o { + logging.Base().Panicf("MakeBlock: error decaying bonus: %d", prev.Bonus) + } + } + } + return bonus +} + // MakeBlock constructs a new valid block with an empty payset and an unset Seed. func MakeBlock(prev BlockHeader) Block { upgradeVote, upgradeState, err := ProcessUpgradeParams(prev) @@ -506,6 +565,8 @@ func MakeBlock(prev BlockHeader) Block { } } + bonus := nextBonus(prev, ¶ms) + // the merkle root of TXs will update when fillpayset is called blk := Block{ BlockHeader: BlockHeader{ @@ -516,6 +577,7 @@ func MakeBlock(prev BlockHeader) Block { TimeStamp: timestamp, GenesisID: prev.GenesisID, GenesisHash: prev.GenesisHash, + Bonus: bonus, }, } blk.TxnCommitments, err = blk.PaysetCommit() @@ -631,6 +693,12 @@ func (bh BlockHeader) PreCheck(prev BlockHeader) error { } } + // check bonus + expectedBonus := nextBonus(prev, ¶ms) + if bh.Bonus != expectedBonus { + return fmt.Errorf("bad bonus: %d != %d ", bh.Bonus, expectedBonus) + } + // Check genesis ID value against previous block, if set if bh.GenesisID == "" { return fmt.Errorf("genesis ID missing") diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index 73cce6d7d4..412c6fb858 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -50,10 +50,12 @@ func init() { } params1.MinUpgradeWaitRounds = 0 params1.MaxUpgradeWaitRounds = 0 + params1.BonusPlan = 0 config.Consensus[proto1] = params1 params2 := config.Consensus[protocol.ConsensusCurrentVersion] params2.ApprovedUpgrades = map[protocol.ConsensusVersion]uint64{} + params2.BonusPlan = 1 config.Consensus[proto2] = params2 paramsDelay := config.Consensus[protocol.ConsensusCurrentVersion] @@ -218,7 +220,7 @@ func TestBlockUnsupported(t *testing.T) { //nolint:paralleltest // Not parallel delete(config.Consensus, protoUnsupported) err := b1.PreCheck(b.BlockHeader) - require.Error(t, err) + require.ErrorContains(t, err, "protocol TestUnsupported not supported") } func TestTime(t *testing.T) { @@ -243,11 +245,43 @@ func TestTime(t *testing.T) { require.NoError(t, b.PreCheck(prev.BlockHeader)) b.TimeStamp = prev.TimeStamp - 1 - require.Error(t, b.PreCheck(prev.BlockHeader)) + require.ErrorContains(t, b.PreCheck(prev.BlockHeader), "bad timestamp") b.TimeStamp = prev.TimeStamp + proto.MaxTimestampIncrement require.NoError(t, b.PreCheck(prev.BlockHeader)) b.TimeStamp = prev.TimeStamp + proto.MaxTimestampIncrement + 1 - require.Error(t, b.PreCheck(prev.BlockHeader)) + require.ErrorContains(t, b.PreCheck(prev.BlockHeader), "bad timestamp") +} + +func TestBonus(t *testing.T) { + partitiontest.PartitionTest(t) + + var prev Block + prev.CurrentProtocol = proto1 + prev.BlockHeader.GenesisID = t.Name() + crypto.RandBytes(prev.BlockHeader.GenesisHash[:]) + + b := MakeBlock(prev.BlockHeader) + require.NoError(t, b.PreCheck(prev.BlockHeader)) + + // proto1 has no bonuses + b.Bonus.Raw++ + require.ErrorContains(t, b.PreCheck(prev.BlockHeader), "bad bonus: {1} != {0}") + + prev.CurrentProtocol = proto2 + prev.Bonus = basics.Algos(5) + b = MakeBlock(prev.BlockHeader) + require.NoError(t, b.PreCheck(prev.BlockHeader)) + + b.Bonus.Raw++ + require.ErrorContains(t, b.PreCheck(prev.BlockHeader), "bad bonus: {5000001} != {5000000}") + + prev.BlockHeader.Round = 10_000_000 - 1 + b = MakeBlock(prev.BlockHeader) + require.NoError(t, b.PreCheck(prev.BlockHeader)) + + // since current block is 0 mod decayInterval, bonus goes down to 4,950,000 + b.Bonus.Raw++ + require.ErrorContains(t, b.PreCheck(prev.BlockHeader), "bad bonus: {4950001} != {4950000}") } func TestRewardsLevel(t *testing.T) { diff --git a/data/bookkeeping/msgp_gen.go b/data/bookkeeping/msgp_gen.go index bebbcff38b..3a81f30e8b 100644 --- a/data/bookkeeping/msgp_gen.go +++ b/data/bookkeeping/msgp_gen.go @@ -143,178 +143,187 @@ import ( func (z *Block) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(29) - var zb0005Mask uint64 /* 34 bits */ - if (*z).BlockHeader.RewardsState.RewardsLevel == 0 { + zb0005Len := uint32(30) + var zb0005Mask uint64 /* 35 bits */ + if (*z).BlockHeader.Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x20 } - if (*z).BlockHeader.FeesCollected.MsgIsZero() { + if (*z).BlockHeader.RewardsState.RewardsLevel == 0 { zb0005Len-- zb0005Mask |= 0x40 } - if (*z).BlockHeader.RewardsState.FeeSink.MsgIsZero() { + if (*z).BlockHeader.FeesCollected.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80 } - if (*z).BlockHeader.RewardsState.RewardsResidue == 0 { + if (*z).BlockHeader.RewardsState.FeeSink.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100 } - if (*z).BlockHeader.GenesisID == "" { + if (*z).BlockHeader.RewardsState.RewardsResidue == 0 { zb0005Len-- zb0005Mask |= 0x200 } - if (*z).BlockHeader.GenesisHash.MsgIsZero() { + if (*z).BlockHeader.GenesisID == "" { zb0005Len-- zb0005Mask |= 0x400 } - if (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).BlockHeader.GenesisHash.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800 } - if (*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { + if (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000 } - if (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000 } - if (*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0 { + if (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x4000 } - if len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + if (*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0 { zb0005Len-- zb0005Mask |= 0x8000 } - if len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x10000 } - if (*z).BlockHeader.Branch.MsgIsZero() { + if len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x20000 } - if (*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).BlockHeader.Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000 } - if (*z).BlockHeader.Proposer.MsgIsZero() { + if (*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80000 } - if (*z).BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).BlockHeader.Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100000 } - if (*z).BlockHeader.Round.MsgIsZero() { + if (*z).BlockHeader.RewardsState.RewardsRate == 0 { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { + if (*z).BlockHeader.Round.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero() { + if (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } - if (*z).BlockHeader.Seed.MsgIsZero() { + if (*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000000 } - if len((*z).BlockHeader.StateProofTracking) == 0 { + if (*z).BlockHeader.Seed.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000000 } - if (*z).BlockHeader.TxnCounter == 0 { + if len((*z).BlockHeader.StateProofTracking) == 0 { zb0005Len-- zb0005Mask |= 0x4000000 } - if (*z).BlockHeader.TimeStamp == 0 { + if (*z).BlockHeader.TxnCounter == 0 { zb0005Len-- zb0005Mask |= 0x8000000 } - if (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { + if (*z).BlockHeader.TimeStamp == 0 { zb0005Len-- zb0005Mask |= 0x10000000 } - if (*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { + if (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x20000000 } - if (*z).Payset.MsgIsZero() { + if (*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000000 } - if (*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { + if (*z).Payset.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80000000 } - if (*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { + if (*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100000000 } - if (*z).BlockHeader.UpgradeVote.UpgradeApprove == false { + if (*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200000000 } + if (*z).BlockHeader.UpgradeVote.UpgradeApprove == false { + zb0005Len-- + zb0005Mask |= 0x400000000 + } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) if zb0005Len != 0 { if (zb0005Mask & 0x20) == 0 { // if not empty + // string "bi" + o = append(o, 0xa2, 0x62, 0x69) + o = (*z).BlockHeader.Bonus.MarshalMsg(o) + } + if (zb0005Mask & 0x40) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsLevel) } - if (zb0005Mask & 0x40) == 0 { // if not empty + if (zb0005Mask & 0x80) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).BlockHeader.FeesCollected.MarshalMsg(o) } - if (zb0005Mask & 0x80) == 0 { // if not empty + if (zb0005Mask & 0x100) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).BlockHeader.RewardsState.FeeSink.MarshalMsg(o) } - if (zb0005Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsResidue) } - if (zb0005Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).BlockHeader.GenesisID) } - if (zb0005Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).BlockHeader.GenesisHash.MarshalMsg(o) } - if (zb0005Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0005Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).BlockHeader.UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0005Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).BlockHeader.UpgradeState.NextProtocolApprovals) } - if (zb0005Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "partupdabs" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) if (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts == nil { @@ -326,7 +335,7 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = (*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) } } - if (zb0005Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -338,47 +347,47 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0005Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).BlockHeader.Branch.MarshalMsg(o) } - if (zb0005Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).BlockHeader.Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsRate) } - if (zb0005Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).BlockHeader.Round.MarshalMsg(o) } - if (zb0005Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).BlockHeader.Seed.MarshalMsg(o) } - if (zb0005Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).BlockHeader.StateProofTracking == nil { @@ -398,42 +407,42 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).BlockHeader.TxnCounter) } - if (zb0005Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).BlockHeader.TimeStamp) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).Payset.MarshalMsg(o) } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).BlockHeader.UpgradeVote.UpgradeApprove) @@ -555,6 +564,14 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Bonus") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -870,6 +887,12 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b err = msgp.WrapError(err, "FeesCollected") return } + case "bi": + bts, err = (*z).BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Bonus") + return + } case "fees": bts, err = (*z).BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -1077,7 +1100,7 @@ func (_ *Block) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *Block) Msgsize() (s int) { - s = 3 + 4 + (*z).BlockHeader.Round.Msgsize() + 5 + (*z).BlockHeader.Branch.Msgsize() + 5 + (*z).BlockHeader.Seed.Msgsize() + 4 + (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).BlockHeader.GenesisID) + 3 + (*z).BlockHeader.GenesisHash.Msgsize() + 4 + (*z).BlockHeader.Proposer.Msgsize() + 3 + (*z).BlockHeader.FeesCollected.Msgsize() + 5 + (*z).BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).BlockHeader.Round.Msgsize() + 5 + (*z).BlockHeader.Branch.Msgsize() + 5 + (*z).BlockHeader.Seed.Msgsize() + 4 + (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).BlockHeader.GenesisID) + 3 + (*z).BlockHeader.GenesisHash.Msgsize() + 4 + (*z).BlockHeader.Proposer.Msgsize() + 3 + (*z).BlockHeader.FeesCollected.Msgsize() + 3 + (*z).BlockHeader.Bonus.Msgsize() + 5 + (*z).BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).BlockHeader.StateProofTracking { _ = zb0001 @@ -1099,12 +1122,12 @@ func (z *Block) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *Block) MsgIsZero() bool { - return ((*z).BlockHeader.Round.MsgIsZero()) && ((*z).BlockHeader.Branch.MsgIsZero()) && ((*z).BlockHeader.Seed.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).BlockHeader.TimeStamp == 0) && ((*z).BlockHeader.GenesisID == "") && ((*z).BlockHeader.GenesisHash.MsgIsZero()) && ((*z).BlockHeader.Proposer.MsgIsZero()) && ((*z).BlockHeader.FeesCollected.MsgIsZero()) && ((*z).BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).BlockHeader.RewardsState.RewardsRate == 0) && ((*z).BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).BlockHeader.TxnCounter == 0) && (len((*z).BlockHeader.StateProofTracking) == 0) && (len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Payset.MsgIsZero()) + return ((*z).BlockHeader.Round.MsgIsZero()) && ((*z).BlockHeader.Branch.MsgIsZero()) && ((*z).BlockHeader.Seed.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).BlockHeader.TimeStamp == 0) && ((*z).BlockHeader.GenesisID == "") && ((*z).BlockHeader.GenesisHash.MsgIsZero()) && ((*z).BlockHeader.Proposer.MsgIsZero()) && ((*z).BlockHeader.FeesCollected.MsgIsZero()) && ((*z).BlockHeader.Bonus.MsgIsZero()) && ((*z).BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).BlockHeader.RewardsState.RewardsRate == 0) && ((*z).BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).BlockHeader.TxnCounter == 0) && (len((*z).BlockHeader.StateProofTracking) == 0) && (len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Payset.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func BlockMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) @@ -1162,174 +1185,183 @@ func BlockHashMaxSize() int { func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(28) - var zb0005Mask uint64 /* 33 bits */ - if (*z).RewardsState.RewardsLevel == 0 { + zb0005Len := uint32(29) + var zb0005Mask uint64 /* 34 bits */ + if (*z).Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x20 } - if (*z).FeesCollected.MsgIsZero() { + if (*z).RewardsState.RewardsLevel == 0 { zb0005Len-- zb0005Mask |= 0x40 } - if (*z).RewardsState.FeeSink.MsgIsZero() { + if (*z).FeesCollected.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80 } - if (*z).RewardsState.RewardsResidue == 0 { + if (*z).RewardsState.FeeSink.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100 } - if (*z).GenesisID == "" { + if (*z).RewardsState.RewardsResidue == 0 { zb0005Len-- zb0005Mask |= 0x200 } - if (*z).GenesisHash.MsgIsZero() { + if (*z).GenesisID == "" { zb0005Len-- zb0005Mask |= 0x400 } - if (*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero() { + if (*z).GenesisHash.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800 } - if (*z).UpgradeState.NextProtocol.MsgIsZero() { + if (*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000 } - if (*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero() { + if (*z).UpgradeState.NextProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000 } - if (*z).UpgradeState.NextProtocolApprovals == 0 { + if (*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x4000 } - if len((*z).ParticipationUpdates.AbsentParticipationAccounts) == 0 { + if (*z).UpgradeState.NextProtocolApprovals == 0 { zb0005Len-- zb0005Mask |= 0x8000 } - if len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0 { + if len((*z).ParticipationUpdates.AbsentParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x10000 } - if (*z).Branch.MsgIsZero() { + if len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0 { zb0005Len-- zb0005Mask |= 0x20000 } - if (*z).UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000 } - if (*z).Proposer.MsgIsZero() { + if (*z).UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80000 } - if (*z).RewardsState.RewardsRate == 0 { + if (*z).Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100000 } - if (*z).Round.MsgIsZero() { + if (*z).RewardsState.RewardsRate == 0 { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).RewardsState.RewardsRecalculationRound.MsgIsZero() { + if (*z).Round.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).RewardsState.RewardsPool.MsgIsZero() { + if (*z).RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } - if (*z).Seed.MsgIsZero() { + if (*z).RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000000 } - if len((*z).StateProofTracking) == 0 { + if (*z).Seed.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000000 } - if (*z).TxnCounter == 0 { + if len((*z).StateProofTracking) == 0 { zb0005Len-- zb0005Mask |= 0x4000000 } - if (*z).TimeStamp == 0 { + if (*z).TxnCounter == 0 { zb0005Len-- zb0005Mask |= 0x8000000 } - if (*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { + if (*z).TimeStamp == 0 { zb0005Len-- zb0005Mask |= 0x10000000 } - if (*z).TxnCommitments.Sha256Commitment.MsgIsZero() { + if (*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x20000000 } - if (*z).UpgradeVote.UpgradeDelay.MsgIsZero() { + if (*z).TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000000 } - if (*z).UpgradeVote.UpgradePropose.MsgIsZero() { + if (*z).UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80000000 } - if (*z).UpgradeVote.UpgradeApprove == false { + if (*z).UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100000000 } + if (*z).UpgradeVote.UpgradeApprove == false { + zb0005Len-- + zb0005Mask |= 0x200000000 + } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) if zb0005Len != 0 { if (zb0005Mask & 0x20) == 0 { // if not empty + // string "bi" + o = append(o, 0xa2, 0x62, 0x69) + o = (*z).Bonus.MarshalMsg(o) + } + if (zb0005Mask & 0x40) == 0 { // if not empty // string "earn" o = append(o, 0xa4, 0x65, 0x61, 0x72, 0x6e) o = msgp.AppendUint64(o, (*z).RewardsState.RewardsLevel) } - if (zb0005Mask & 0x40) == 0 { // if not empty + if (zb0005Mask & 0x80) == 0 { // if not empty // string "fc" o = append(o, 0xa2, 0x66, 0x63) o = (*z).FeesCollected.MarshalMsg(o) } - if (zb0005Mask & 0x80) == 0 { // if not empty + if (zb0005Mask & 0x100) == 0 { // if not empty // string "fees" o = append(o, 0xa4, 0x66, 0x65, 0x65, 0x73) o = (*z).RewardsState.FeeSink.MarshalMsg(o) } - if (zb0005Mask & 0x100) == 0 { // if not empty + if (zb0005Mask & 0x200) == 0 { // if not empty // string "frac" o = append(o, 0xa4, 0x66, 0x72, 0x61, 0x63) o = msgp.AppendUint64(o, (*z).RewardsState.RewardsResidue) } - if (zb0005Mask & 0x200) == 0 { // if not empty + if (zb0005Mask & 0x400) == 0 { // if not empty // string "gen" o = append(o, 0xa3, 0x67, 0x65, 0x6e) o = msgp.AppendString(o, (*z).GenesisID) } - if (zb0005Mask & 0x400) == 0 { // if not empty + if (zb0005Mask & 0x800) == 0 { // if not empty // string "gh" o = append(o, 0xa2, 0x67, 0x68) o = (*z).GenesisHash.MarshalMsg(o) } - if (zb0005Mask & 0x800) == 0 { // if not empty + if (zb0005Mask & 0x1000) == 0 { // if not empty // string "nextbefore" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65) o = (*z).UpgradeState.NextProtocolVoteBefore.MarshalMsg(o) } - if (zb0005Mask & 0x1000) == 0 { // if not empty + if (zb0005Mask & 0x2000) == 0 { // if not empty // string "nextproto" o = append(o, 0xa9, 0x6e, 0x65, 0x78, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).UpgradeState.NextProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x2000) == 0 { // if not empty + if (zb0005Mask & 0x4000) == 0 { // if not empty // string "nextswitch" o = append(o, 0xaa, 0x6e, 0x65, 0x78, 0x74, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68) o = (*z).UpgradeState.NextProtocolSwitchOn.MarshalMsg(o) } - if (zb0005Mask & 0x4000) == 0 { // if not empty + if (zb0005Mask & 0x8000) == 0 { // if not empty // string "nextyes" o = append(o, 0xa7, 0x6e, 0x65, 0x78, 0x74, 0x79, 0x65, 0x73) o = msgp.AppendUint64(o, (*z).UpgradeState.NextProtocolApprovals) } - if (zb0005Mask & 0x8000) == 0 { // if not empty + if (zb0005Mask & 0x10000) == 0 { // if not empty // string "partupdabs" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x61, 0x62, 0x73) if (*z).ParticipationUpdates.AbsentParticipationAccounts == nil { @@ -1341,7 +1373,7 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = (*z).ParticipationUpdates.AbsentParticipationAccounts[zb0004].MarshalMsg(o) } } - if (zb0005Mask & 0x10000) == 0 { // if not empty + if (zb0005Mask & 0x20000) == 0 { // if not empty // string "partupdrmv" o = append(o, 0xaa, 0x70, 0x61, 0x72, 0x74, 0x75, 0x70, 0x64, 0x72, 0x6d, 0x76) if (*z).ParticipationUpdates.ExpiredParticipationAccounts == nil { @@ -1353,47 +1385,47 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = (*z).ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0005Mask & 0x20000) == 0 { // if not empty + if (zb0005Mask & 0x40000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).Branch.MarshalMsg(o) } - if (zb0005Mask & 0x40000) == 0 { // if not empty + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).RewardsState.RewardsRate) } - if (zb0005Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).Round.MarshalMsg(o) } - if (zb0005Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).Seed.MarshalMsg(o) } - if (zb0005Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).StateProofTracking == nil { @@ -1413,37 +1445,37 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).TxnCounter) } - if (zb0005Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).TimeStamp) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).UpgradeVote.UpgradeApprove) @@ -1565,6 +1597,14 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "Bonus") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -1872,6 +1912,12 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "FeesCollected") return } + case "bi": + bts, err = (*z).Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "Bonus") + return + } case "fees": bts, err = (*z).RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -2073,7 +2119,7 @@ func (_ *BlockHeader) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *BlockHeader) Msgsize() (s int) { - s = 3 + 4 + (*z).Round.Msgsize() + 5 + (*z).Branch.Msgsize() + 5 + (*z).Seed.Msgsize() + 4 + (*z).TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).GenesisID) + 3 + (*z).GenesisHash.Msgsize() + 4 + (*z).Proposer.Msgsize() + 3 + (*z).FeesCollected.Msgsize() + 5 + (*z).RewardsState.FeeSink.Msgsize() + 4 + (*z).RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).Round.Msgsize() + 5 + (*z).Branch.Msgsize() + 5 + (*z).Seed.Msgsize() + 4 + (*z).TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).GenesisID) + 3 + (*z).GenesisHash.Msgsize() + 4 + (*z).Proposer.Msgsize() + 3 + (*z).FeesCollected.Msgsize() + 3 + (*z).Bonus.Msgsize() + 5 + (*z).RewardsState.FeeSink.Msgsize() + 4 + (*z).RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).StateProofTracking != nil { for zb0001, zb0002 := range (*z).StateProofTracking { _ = zb0001 @@ -2094,12 +2140,12 @@ func (z *BlockHeader) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *BlockHeader) MsgIsZero() bool { - return ((*z).Round.MsgIsZero()) && ((*z).Branch.MsgIsZero()) && ((*z).Seed.MsgIsZero()) && ((*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).TimeStamp == 0) && ((*z).GenesisID == "") && ((*z).GenesisHash.MsgIsZero()) && ((*z).Proposer.MsgIsZero()) && ((*z).FeesCollected.MsgIsZero()) && ((*z).RewardsState.FeeSink.MsgIsZero()) && ((*z).RewardsState.RewardsPool.MsgIsZero()) && ((*z).RewardsState.RewardsLevel == 0) && ((*z).RewardsState.RewardsRate == 0) && ((*z).RewardsState.RewardsResidue == 0) && ((*z).RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocolApprovals == 0) && ((*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).UpgradeVote.UpgradeApprove == false) && ((*z).TxnCounter == 0) && (len((*z).StateProofTracking) == 0) && (len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).ParticipationUpdates.AbsentParticipationAccounts) == 0) + return ((*z).Round.MsgIsZero()) && ((*z).Branch.MsgIsZero()) && ((*z).Seed.MsgIsZero()) && ((*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).TimeStamp == 0) && ((*z).GenesisID == "") && ((*z).GenesisHash.MsgIsZero()) && ((*z).Proposer.MsgIsZero()) && ((*z).FeesCollected.MsgIsZero()) && ((*z).Bonus.MsgIsZero()) && ((*z).RewardsState.FeeSink.MsgIsZero()) && ((*z).RewardsState.RewardsPool.MsgIsZero()) && ((*z).RewardsState.RewardsLevel == 0) && ((*z).RewardsState.RewardsRate == 0) && ((*z).RewardsState.RewardsResidue == 0) && ((*z).RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocolApprovals == 0) && ((*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).UpgradeVote.UpgradeApprove == false) && ((*z).TxnCounter == 0) && (len((*z).StateProofTracking) == 0) && (len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).ParticipationUpdates.AbsentParticipationAccounts) == 0) } // MaxSize returns a maximum valid message size for this message type func BlockHeaderMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 8d85a15772..d8757d1917 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -5710,6 +5710,8 @@ func opBlock(cx *EvalContext) error { cx.Stack[last].Bytes = hdr.Proposer[:] case BlkFeesCollected: cx.Stack[last].Uint = hdr.FeesCollected.Raw + case BlkBonus: + cx.Stack[last].Uint = hdr.Bonus.Raw default: return fmt.Errorf("invalid block field %s", fs.field) } diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index 7f962125b5..aa01e540af 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -969,6 +969,8 @@ const ( BlkProposer // BlkFeesCollected is the sum of fees for the block, or 0, pre EnableMining BlkFeesCollected + // BlkBonus is the extra amount to be paid for the given block (from FreeSink) + BlkBonus invalidBlockField // compile-time constant for number of fields ) @@ -986,6 +988,7 @@ var blockFieldSpecs = [...]blockFieldSpec{ {BlkTimestamp, StackUint64, randomnessVersion}, {BlkProposer, StackAddress, incentiveVersion}, {BlkFeesCollected, StackUint64, incentiveVersion}, + {BlkBonus, StackUint64, incentiveVersion}, } func blockFieldSpecByField(r BlockField) (blockFieldSpec, bool) { diff --git a/data/transactions/logic/fields_string.go b/data/transactions/logic/fields_string.go index 70f74fe11e..5b92357909 100644 --- a/data/transactions/logic/fields_string.go +++ b/data/transactions/logic/fields_string.go @@ -354,12 +354,13 @@ func _() { _ = x[BlkTimestamp-1] _ = x[BlkProposer-2] _ = x[BlkFeesCollected-3] - _ = x[invalidBlockField-4] + _ = x[BlkBonus-4] + _ = x[invalidBlockField-5] } -const _BlockField_name = "BlkSeedBlkTimestampBlkProposerBlkFeesCollectedinvalidBlockField" +const _BlockField_name = "BlkSeedBlkTimestampBlkProposerBlkFeesCollectedBlkBonusinvalidBlockField" -var _BlockField_index = [...]uint8{0, 7, 19, 30, 46, 63} +var _BlockField_index = [...]uint8{0, 7, 19, 30, 46, 54, 71} func (i BlockField) String() string { if i < 0 || i >= BlockField(len(_BlockField_index)-1) { diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 6cb4ec4ce4..8a4c0ed411 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -716,19 +716,19 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts poolAddr := eval.prevHeader.RewardsPool // get the reward pool account data without any rewards - incentivePoolData, _, err := l.LookupWithoutRewards(eval.prevHeader.Round, poolAddr) + rewardsPoolData, _, err := l.LookupWithoutRewards(eval.prevHeader.Round, poolAddr) if err != nil { return nil, err } // this is expected to be a no-op, but update the rewards on the rewards pool if it was configured to receive rewards ( unlike mainnet ). - incentivePoolData = incentivePoolData.WithUpdatedRewards(prevProto, eval.prevHeader.RewardsLevel) + rewardsPoolData = rewardsPoolData.WithUpdatedRewards(prevProto, eval.prevHeader.RewardsLevel) if evalOpts.Generate { if eval.proto.SupportGenesisHash { eval.block.BlockHeader.GenesisHash = eval.genesisHash } - eval.block.BlockHeader.RewardsState = eval.prevHeader.NextRewardsState(hdr.Round, proto, incentivePoolData.MicroAlgos, prevTotals.RewardUnits(), logging.Base()) + eval.block.BlockHeader.RewardsState = eval.prevHeader.NextRewardsState(hdr.Round, proto, rewardsPoolData.MicroAlgos, prevTotals.RewardUnits(), logging.Base()) } // set the eval state with the current header eval.state = makeRoundCowState(base, eval.block.BlockHeader, proto, eval.prevHeader.TimeStamp, prevTotals, evalOpts.PaysetHint) @@ -740,7 +740,7 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts } // Check that the rewards rate, level and residue match expected values - expectedRewardsState := eval.prevHeader.NextRewardsState(hdr.Round, proto, incentivePoolData.MicroAlgos, prevTotals.RewardUnits(), logging.Base()) + expectedRewardsState := eval.prevHeader.NextRewardsState(hdr.Round, proto, rewardsPoolData.MicroAlgos, prevTotals.RewardUnits(), logging.Base()) if eval.block.RewardsState != expectedRewardsState { return nil, fmt.Errorf("bad rewards state: %+v != %+v", eval.block.RewardsState, expectedRewardsState) } @@ -751,7 +751,7 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts } } - // Withdraw rewards from the incentive pool + // Withdraw rewards from the pool var ot basics.OverflowTracker rewardsPerUnit := ot.Sub(eval.block.BlockHeader.RewardsLevel, eval.prevHeader.RewardsLevel) if ot.Overflowed { @@ -789,11 +789,23 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts } if eval.eligibleForIncentives(prevHeader.Proposer) { - miningIncentive, _ := basics.NewPercent(proto.MiningPercent).DivvyAlgos(prevHeader.FeesCollected) - err = eval.state.Move(prevHeader.FeeSink, prevHeader.Proposer, miningIncentive, nil, nil) + incentive, _ := basics.NewPercent(proto.MiningPercent).DivvyAlgos(prevHeader.FeesCollected) + total, o := basics.OAddA(incentive, prevHeader.Bonus) + if o { + return nil, fmt.Errorf("overflowed adding bonus incentive %d %d", incentive, prevHeader.Bonus) + } + + sink, err := eval.state.lookup(prevHeader.FeeSink) + if err != nil { + return nil, err + } + available := sink.AvailableBalance(&proto) + total = basics.MinA(total, available) + + err = eval.state.Move(prevHeader.FeeSink, prevHeader.Proposer, total, nil, nil) if err != nil { - // This should be impossible. The fees were just collected, and the FeeSink cannot be emptied. - return nil, fmt.Errorf("unable to Move mining incentive") + // Should be impossible, we used AvailableBalance + return nil, fmt.Errorf("unable to pay block incentive: %v", err) } } diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 501b4d4eb6..3b479bf237 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -172,6 +172,15 @@ func (u AccountData) MinBalance(proto *config.ConsensusParams) (res basics.Micro ) } +// AvailableBalance returns the amount of MicroAlgos that are available for +// spending without fully closing the account. +func (u AccountData) AvailableBalance(proto *config.ConsensusParams) (res basics.MicroAlgos) { + if left, o := basics.OSubA(u.MicroAlgos, u.MinBalance(proto)); !o { + return left + } + return basics.MicroAlgos{} +} + // IsZero checks if an AccountData value is the same as its zero value. func (u AccountData) IsZero() bool { return u == AccountData{} From cce943294d398b1e84a9a7e8cca0c8e188c7c3b0 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 26 Jan 2024 15:17:50 -0500 Subject: [PATCH 016/117] Show node can re-reg to become incentive eligible again --- .../features/suspension/suspension_test.go | 65 ++++++++++++++++--- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/test/e2e-go/features/suspension/suspension_test.go b/test/e2e-go/features/suspension/suspension_test.go index de4a68ca08..3f5230b430 100644 --- a/test/e2e-go/features/suspension/suspension_test.go +++ b/test/e2e-go/features/suspension/suspension_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -44,8 +45,9 @@ func TestBasicSuspension(t *testing.T) { // Overview of this test: // Start a three-node network (84,15,1) - // Stop the 15% node - // Let it run for less than 10*100/15 + // Wait for 15% node to propose (we never suspend accounts with lastProposed=lastHeartbeat=0) + // Stop it + // Let it run for less than 10*100/15 = 66.6 // check not suspended, send a tx, still not suspended // Let it run two more, during which the node can't propose, so it is ready for suspension // check not suspended, send a tx, NOW suspended @@ -65,23 +67,37 @@ func TestBasicSuspension(t *testing.T) { a.Equal(accounts[0].Status, basics.Online.String()) address := accounts[0].Address + // wait for n15 to have LastProposed != 0 + account, err := fixture.LibGoalClient.AccountData(address) + for account.LastProposed == 0 { + a.NoError(err) + a.Equal(basics.Online, account.Status) + account, err = fixture.LibGoalClient.AccountData(address) + time.Sleep(roundTime) + } + a.NoError(err) + // turn off Node15 n15, err := fixture.GetNodeController("Node15") a.NoError(err) a.NoError(n15.FullStop()) - // Proceed 60 rounds - err = fixture.WaitForRound(60, 60*roundTime) + afterStop, err := fixture.AlgodClient.Status() + a.NoError(err) + + // Advance 60 rounds + err = fixture.WaitForRound(afterStop.LastRound+60, 60*roundTime) a.NoError(err) // n15account is still online (the node is off, but the account is marked online) - account, err := fixture.LibGoalClient.AccountData(address) + account, err = fixture.LibGoalClient.AccountData(address) a.NoError(err) a.Equal(basics.Online, account.Status) voteID := account.VoteID + a.NotZero(voteID) - // Proceed to round 70 - err = fixture.WaitForRound(70, 10*roundTime) + // Advance 10 more, n15 has been "absent" for 70 rounds now + err = fixture.WaitForRound(afterStop.LastRound+70, 15*roundTime) a.NoError(err) // n15's account is still online, but only because it has gone "unnoticed" @@ -90,6 +106,8 @@ func TestBasicSuspension(t *testing.T) { a.Equal(basics.Online, account.Status) fixture.SendMoneyAndWait(70, 1000, 1000, richAccount.Address, address, "") + // pay n15, so it gets noticed + fixture.SendMoneyAndWait(afterStop.LastRound+70, 5, 1000, richAccount.Address, address, "") // n15's account is now offline, but has voting key material (suspended) account, err = fixture.LibGoalClient.AccountData(address) @@ -116,9 +134,40 @@ func TestBasicSuspension(t *testing.T) { account, err = fixture.LibGoalClient.AccountData(address) a.NoError(err) a.Equal(basics.Online, account.Status) - a.NotZero(account.VoteID) a.Equal(voteID, account.VoteID) // coming back online by proposal does not make you incentive eligible (you // didn't "pay the fine") a.False(account.IncentiveEligible) + + // but n15 wants incentives, so it keyregs again, paying the extra fee. + // We're going to re-reg the exact same key material, so that the running + // node can keep voting. + + // we make an _offline_ tx here, because we want to populate the key + // material ourself, but copying the accounts existing state. That makes it + // an _online_ keyreg. + reReg, err := n15c.MakeUnsignedGoOfflineTx(address, 0, 0, 5_000_000, [32]byte{}) + require.NoError(t, err, "should be able to make tx") + + reReg.KeyregTxnFields = transactions.KeyregTxnFields{ + VotePK: account.VoteID, + SelectionPK: account.SelectionID, + StateProofPK: account.StateProofID, + VoteFirst: account.VoteFirstValid, + VoteLast: account.VoteLastValid, + VoteKeyDilution: account.VoteKeyDilution, + } + + wh, err := n15c.GetUnencryptedWalletHandle() + require.NoError(t, err, "should be able to get unencrypted wallet handle") + onlineTxID, err := n15c.SignAndBroadcastTransaction(wh, nil, reReg) + require.NoError(t, err, "should be no errors when going online") + + fixture.WaitForConfirmedTxn(uint64(reReg.LastValid), address, onlineTxID) + account, err = fixture.LibGoalClient.AccountData(address) + + a.NoError(err) + a.Equal(basics.Online, account.Status) + a.True(account.IncentiveEligible) // eligible! + } From 7e0d2834e15522188c2d15e08ceb8217ba1f6c39 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 26 Jan 2024 21:43:19 -0500 Subject: [PATCH 017/117] Typo level fixes --- data/bookkeeping/block.go | 2 +- data/transactions/logic/fields.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 2ce2b9eb0e..d906cdb818 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -524,7 +524,7 @@ func nextBonus(prev BlockHeader, params *config.ConsensusParams) basics.MicroAlg bonus := prev.Bonus switch { case bonus.IsZero() && current > plan.baseRound: - prevParams, _ := config.Consensus[prev.CurrentProtocol] // presence ensured by ProcessUpgradeParams + prevParams := config.Consensus[prev.CurrentProtocol] // presence ensured by ProcessUpgradeParams if prevParams.BonusPlan == 0 { // We must have have just upgraded, with a passed baseRound, so install the bonus bonus = plan.baseAmount diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index aa01e540af..2b35305607 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -969,7 +969,7 @@ const ( BlkProposer // BlkFeesCollected is the sum of fees for the block, or 0, pre EnableMining BlkFeesCollected - // BlkBonus is the extra amount to be paid for the given block (from FreeSink) + // BlkBonus is the extra amount to be paid for the given block (from FeeSink) BlkBonus invalidBlockField // compile-time constant for number of fields From 8b7946fc719af1c3df527904255783dd7357b905 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 29 Jan 2024 13:22:49 -0500 Subject: [PATCH 018/117] Make test (almost) unflakable --- .../features/suspension/suspension_test.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/test/e2e-go/features/suspension/suspension_test.go b/test/e2e-go/features/suspension/suspension_test.go index 3f5230b430..695673926c 100644 --- a/test/e2e-go/features/suspension/suspension_test.go +++ b/test/e2e-go/features/suspension/suspension_test.go @@ -105,7 +105,6 @@ func TestBasicSuspension(t *testing.T) { a.NoError(err) a.Equal(basics.Online, account.Status) - fixture.SendMoneyAndWait(70, 1000, 1000, richAccount.Address, address, "") // pay n15, so it gets noticed fixture.SendMoneyAndWait(afterStop.LastRound+70, 5, 1000, richAccount.Address, address, "") @@ -124,12 +123,22 @@ func TestBasicSuspension(t *testing.T) { // Wait for newly restarted node to start. Presumably it'll catchup in // seconds, and propose by round 90 - _, err = lg.Status() + stat, err := lg.Status() a.NoError(err) - // Proceed to round 90 - err = fixture.WaitForRound(90, 20*roundTime) - a.NoError(err) + // Proceed until a round is proposed by n15. (Stop at 50 rounds, that's more likely a bug than luck) + for r := stat.LastRound; r < stat.LastRound+50; r++ { + err = fixture.WaitForRound(r, roundTime) + a.NoError(err) + + // Once n15 proposes, break out early + if fixture.VerifyBlockProposed(address, 1) { + // wait one extra round, because changes are processed in block n+1. + err = fixture.WaitForRound(r+1, roundTime) + a.NoError(err) + break + } + } // n15's account is back online, with same voting material account, err = fixture.LibGoalClient.AccountData(address) a.NoError(err) From 1996bce1ea60a015896c4d729823cc14e352fe9a Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 30 Jan 2024 12:13:20 -0500 Subject: [PATCH 019/117] Some CR updates --- data/basics/fraction_test.go | 33 ++++++++++++++++++-------------- data/bookkeeping/block.go | 2 +- ledger/testing/randomAccounts.go | 3 +-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/data/basics/fraction_test.go b/data/basics/fraction_test.go index bc6a639c29..2056717d24 100644 --- a/data/basics/fraction_test.go +++ b/data/basics/fraction_test.go @@ -30,12 +30,12 @@ func TestFraction(t *testing.T) { third := Fraction{1, 3} a, b := third.Divvy(6) - require.EqualValues(t, a, 2) - require.EqualValues(t, b, 4) + require.EqualValues(t, 2, a) + require.EqualValues(t, 4, b) a, b = third.Divvy(10) - require.EqualValues(t, a, 3) - require.EqualValues(t, b, 7) + require.EqualValues(t, 3, a) + require.EqualValues(t, 7, b) } func TestFractionAvoidsOverflow(t *testing.T) { @@ -46,22 +46,22 @@ func TestFractionAvoidsOverflow(t *testing.T) { half := Fraction{biggestEven / 2, biggestEven} // should operate as 1/2 even on large numbers a, b := half.Divvy(6) - require.EqualValues(t, a, 3) - require.EqualValues(t, b, 3) + require.EqualValues(t, 3, a) + require.EqualValues(t, 3, b) a, b = half.Divvy(biggestEven) - require.EqualValues(t, a, biggestEven/2) - require.EqualValues(t, b, biggestEven/2) + require.EqualValues(t, biggestEven/2, a) + require.EqualValues(t, biggestEven/2, b) // ensure that overflow is avoided even if reduction isn't possible uhalf := Fraction{biggestEven / 2, math.MaxUint64} // should be just under half a, b = uhalf.Divvy(6) - require.EqualValues(t, a, 2) - require.EqualValues(t, b, 4) + require.EqualValues(t, 2, a) + require.EqualValues(t, 4, b) a, b = uhalf.Divvy(biggestEven) - require.EqualValues(t, a, biggestEven/2-1) - require.EqualValues(t, b, biggestEven/2+1) + require.EqualValues(t, biggestEven/2-1, a) + require.EqualValues(t, biggestEven/2+1, b) // and just to be super careful, ensure that there's also no reduction // between q and the denominator by using a q that is relatively prime to @@ -71,6 +71,11 @@ func TestFractionAvoidsOverflow(t *testing.T) { require.Positive(t, math.MaxUint64%23) a, b = uhalf.Divvy(23) - require.EqualValues(t, a, 11) - require.EqualValues(t, b, 12) + require.EqualValues(t, 11, a) + require.EqualValues(t, 12, b) + + one := Fraction{math.MaxUint64, math.MaxUint64} + a, b = one.Divvy(math.MaxUint64) + require.EqualValues(t, uint64(math.MaxUint64), a) + require.EqualValues(t, 0, b) } diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index d906cdb818..121a800305 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -501,7 +501,7 @@ var bonusPlans = []bonusPlan{ baseRound: 0, // goes into effect with upgrade baseAmount: basics.Algos(2), // 2.9 sec rounds gives about 10.8M rounds per year. - decayInterval: 500_000, // .9^(10.8/.5) = 10% decay per year + decayInterval: 1_000_000, // .99^(10.8/1) ~ .897 ~ 10% decay per year }, // If we need to change the decay rate (only), we would create a new plan like: // { decayInterval: XXX} by using an old baseRound, the amount is not adjusted diff --git a/ledger/testing/randomAccounts.go b/ledger/testing/randomAccounts.go index 7b99453e2c..d97eee20d4 100644 --- a/ledger/testing/randomAccounts.go +++ b/ledger/testing/randomAccounts.go @@ -18,7 +18,6 @@ package testing import ( "fmt" - "math" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" @@ -84,7 +83,7 @@ func RandomAccountData(rewardsBase uint64) basics.AccountData { crypto.RandBytes(data.SelectionID[:]) crypto.RandBytes(data.StateProofID[:]) data.VoteFirstValid = basics.Round(crypto.RandUint64()) - data.VoteLastValid = basics.Round(crypto.RandUint64() % uint64(math.MaxInt64)) // int64 is the max sqlite can store + data.VoteLastValid = basics.Round(crypto.RandUint63()) // int64 is the max sqlite can store data.VoteKeyDilution = crypto.RandUint64() } From 2c5a66a04c546e71ba989f5fd0cdc2b7ae08caab Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 30 Jan 2024 13:17:39 -0500 Subject: [PATCH 020/117] Remove `WithSeed` from non-validated Block It was confusing --- data/bookkeeping/block.go | 11 ----------- data/datatest/impls.go | 4 +++- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 121a800305..1fc13ff4a8 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -292,17 +292,6 @@ func (block Block) GenesisHash() crypto.Digest { return block.BlockHeader.GenesisHash } -// WithSeed returns a copy of the Block with the seed set to s. -func (block Block) WithSeed(s committee.Seed, proposer basics.Address) Block { - c := block - c.BlockHeader.Seed = s - if !c.BlockHeader.Proposer.IsZero() { - panic("Attempt to re-set the proposer.") - } - c.BlockHeader.Proposer = proposer - return c -} - // Seed returns the Block's random seed. func (block *Block) Seed() committee.Seed { return block.BlockHeader.Seed diff --git a/data/datatest/impls.go b/data/datatest/impls.go index 67bdefbb8c..1281f5cf9d 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -66,7 +66,9 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated // WithSeed implements the agreement.ValidatedBlock interface. func (ve validatedBlock) WithSeed(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { - newblock := ve.blk.WithSeed(s, proposer) + newblock := *ve.blk + newblock.BlockHeader.Seed = s + newblock.BlockHeader.Proposer = proposer return validatedBlock{blk: &newblock} } From ba42ba8766860bde79b183de38deda2a40582ba0 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 31 Jan 2024 11:45:03 -0500 Subject: [PATCH 021/117] Implement "challenges" --- agreement/proposal.go | 26 ++++-- ledger/eval/eval.go | 118 ++++++++++++++++++++------ ledger/eval/eval_test.go | 15 ++++ ledger/eval_simple_test.go | 123 ++++++++++++++++++++++++++-- ledger/ledgercore/validatedBlock.go | 5 +- ledger/simple_test.go | 5 +- 6 files changed, 252 insertions(+), 40 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index 852006952c..8e7e3eda98 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -21,6 +21,7 @@ import ( "fmt" "time" + "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" @@ -189,18 +190,27 @@ func deriveNewSeed(address basics.Address, vrf *crypto.VRFSecrets, rnd round, pe return } +// verifyNewSeed checks the things in the header that can only be confirmed by +// looking into the unauthenticatedProposal, namely the BlockSeed and the +// Proposer. func verifyNewSeed(p unauthenticatedProposal, ledger LedgerReader) error { value := p.value() rnd := p.Round() - cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) - if err != nil { - return fmt.Errorf("failed to obtain consensus parameters in round %d: %v", ParamsRound(rnd), err) - } - if cparams.EnableMining { + curParams := config.Consensus[p.BlockHeader.CurrentProtocol] + if curParams.EnableMining { if p.BlockHeader.Proposer != value.OriginalProposer { return fmt.Errorf("payload has wrong proposer (%v != %v)", p.Proposer, value.OriginalProposer) } + } else { + if !p.BlockHeader.Proposer.IsZero() { + return fmt.Errorf("payload has a proposer %v when Mining disabled", p.Proposer) + } + } + + cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) + if err != nil { + return fmt.Errorf("failed to obtain consensus parameters in round %d: %v", ParamsRound(rnd), err) } balanceRound := balanceRound(rnd, cparams) @@ -257,7 +267,11 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve Validat return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could not derive new seed: %v", err) } - ve = ve.WithSeed(newSeed, address) + var hdrProp basics.Address // The proposer as recorded in BlockHeader + if ve.Block().ConsensusProtocol().EnableMining { + hdrProp = address + } + ve = ve.WithSeed(newSeed, hdrProp) proposal := makeProposal(ve, seedProof, period, address) value := proposalValue{ OriginalPeriod: period, diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 8a4c0ed411..05b83a8425 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" + "github.com/algorand/go-algorand/data/committee" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/data/transactions/verify" @@ -1476,6 +1477,21 @@ func (eval *BlockEvaluator) endOfBlock() error { return nil } +const ( + // Challenges occur once every challengeInterval rounds + challengeInterval = basics.Round(1000) + // Suspensions can between 1 and 2 grace periods after a challenge + challengeGracePeriod = basics.Round(200) + // An account is challenged if the first challengeBits match the start of the account address. + challengeBits = 5 +) + +type challenge struct { + round basics.Round + seed committee.Seed + bits int +} + // generateKnockOfflineAccountsList creates the lists of expired or absent // participation accounts by traversing over the modified accounts in the state // deltas and testing if any of them needs to be reset/suspended. Expiration @@ -1485,18 +1501,15 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { if !eval.generate { return } - // We are going to find the list of modified accounts and the - // current round that is being evaluated. - // Then we are going to go through each modified account and - // see if it meets the criteria for adding it to the expired - // participation accounts list. - currentRound := eval.Round() + current := eval.Round() - expectedMaxNumberOfExpiredAccounts := eval.proto.MaxProposedExpiredOnlineAccounts - expectedMaxNumberOfAbsentAccounts := eval.proto.MaxProposedAbsentOnlineAccounts + maxExpirations := eval.proto.MaxProposedExpiredOnlineAccounts + maxSuspensions := eval.proto.MaxProposedAbsentOnlineAccounts updates := &eval.block.ParticipationUpdates + ch := eval.activeChallenge() + for _, accountAddr := range eval.state.modifiedAccounts() { acctDelta, found := eval.state.mods.Accts.GetData(accountAddr) if !found { @@ -1507,9 +1520,9 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { // account can be expired to remove it. This means an offline account // can be expired (because it was already suspended). if !acctDelta.VoteID.IsEmpty() { - expiresBeforeCurrent := acctDelta.VoteLastValid < currentRound + expiresBeforeCurrent := acctDelta.VoteLastValid < current if expiresBeforeCurrent && - len(updates.ExpiredParticipationAccounts) < expectedMaxNumberOfExpiredAccounts { + len(updates.ExpiredParticipationAccounts) < maxExpirations { updates.ExpiredParticipationAccounts = append( updates.ExpiredParticipationAccounts, accountAddr, @@ -1518,10 +1531,14 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { } } + if len(updates.AbsentParticipationAccounts) >= maxSuspensions { + continue // no more room (don't break the loop, since we may have more expiries) + } + if acctDelta.Status == basics.Online { lastSeen := max(acctDelta.LastProposed, acctDelta.LastHeartbeat) - if isAbsent(eval.state.prevTotals.Online.Money, acctDelta.MicroAlgos, lastSeen, currentRound) && - len(updates.AbsentParticipationAccounts) < expectedMaxNumberOfAbsentAccounts { + if isAbsent(eval.state.prevTotals.Online.Money, acctDelta.MicroAlgos, lastSeen, current) || + failsChallenge(ch, accountAddr, lastSeen) { updates.AbsentParticipationAccounts = append( updates.AbsentParticipationAccounts, accountAddr, @@ -1539,6 +1556,34 @@ func max(a, b basics.Round) basics.Round { return b } +// bitsMatch checks if the first n bits of two byte slices match. Written to +// work on arbitrary slices, but expected that n is small. No attempt to +// optimize for byte by byte comparisons. (Only user today calls with n=5) +func bitsMatch(a, b []byte, n int) bool { + // Ensure n is a valid number of bits to compare + if n < 0 || n > len(a)*8 || n > len(b)*8 { + return false + } + + // Compare each bit up to the specified n bits + for i := 0; i < n; i++ { + // Calculate the byte and bit positions + bytePos := i / 8 + bitPos := i % 8 + + // Extract the bits from each slice + bit1 := (a[bytePos] >> (7 - bitPos)) & 1 + bit2 := (b[bytePos] >> (7 - bitPos)) & 1 + + // Compare the bits + if bit1 != bit2 { + return false + } + } + + return true +} + func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, lastSeen basics.Round, current basics.Round) bool { // Don't consider accounts that were online when mining went into effect as // absent. They get noticed the next time they propose or keyreg, which @@ -1551,6 +1596,28 @@ func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, l return lastSeen+allowableLag < current } +func (eval *BlockEvaluator) activeChallenge() challenge { + current := eval.Round() + var round basics.Round + if current > challengeInterval { + lastChallenge := current - (current % challengeInterval) + // challengeRound is in effect if we're after one grace period, but before the 2nd ends. + if current > lastChallenge+challengeGracePeriod && current < lastChallenge+2*challengeGracePeriod { + round = lastChallenge + challengeHdr, err := eval.state.BlockHdr(round) + if err != nil { + panic(err) + } + return challenge{round, challengeHdr.Seed, challengeBits} + } + } + return challenge{} +} + +func failsChallenge(ch challenge, address basics.Address, lastSeen basics.Round) bool { + return ch.round != 0 && bitsMatch(ch.seed[:], address[:], ch.bits) && lastSeen < ch.round +} + // validateExpiredOnlineAccounts tests the expired online accounts specified in ExpiredParticipationAccounts, and verify // that they have all expired and need to be reset. func (eval *BlockEvaluator) validateExpiredOnlineAccounts() error { @@ -1600,28 +1667,27 @@ func (eval *BlockEvaluator) validateExpiredOnlineAccounts() error { } // validateAbsentOnlineAccounts tests the accounts specified in -// AbsentParticipationAccounts, and verifies that they need to be reset. +// AbsentParticipationAccounts, and verifies that they need to be suspended func (eval *BlockEvaluator) validateAbsentOnlineAccounts() error { if !eval.validate { return nil } - expectedMaxNumberOfAbsentAccounts := eval.proto.MaxProposedAbsentOnlineAccounts - lengthOfAbsentParticipationAccounts := len(eval.block.ParticipationUpdates.AbsentParticipationAccounts) + maxSuspensions := eval.proto.MaxProposedAbsentOnlineAccounts + suspensionCount := len(eval.block.ParticipationUpdates.AbsentParticipationAccounts) // If the length of the array is strictly greater than our max then we have an error. // This works when the expected number of accounts is zero (i.e. it is disabled) as well - if lengthOfAbsentParticipationAccounts > expectedMaxNumberOfAbsentAccounts { + if suspensionCount > maxSuspensions { return fmt.Errorf("length of absent accounts (%d) was greater than expected (%d)", - lengthOfAbsentParticipationAccounts, expectedMaxNumberOfAbsentAccounts) + suspensionCount, maxSuspensions) } // For consistency with expired account handling, we preclude duplicates - addressSet := make(map[basics.Address]bool, lengthOfAbsentParticipationAccounts) + addressSet := make(map[basics.Address]bool, suspensionCount) - // Validate that all accounts have been absent - currentRound := eval.Round() - for _, accountAddr := range eval.block.ParticipationUpdates.AbsentParticipationAccounts { + ch := eval.activeChallenge() + for _, accountAddr := range eval.block.ParticipationUpdates.AbsentParticipationAccounts { if _, exists := addressSet[accountAddr]; exists { return fmt.Errorf("duplicate address found: %v", accountAddr) } @@ -1637,10 +1703,14 @@ func (eval *BlockEvaluator) validateAbsentOnlineAccounts() error { } lastSeen := max(acctData.LastProposed, acctData.LastHeartbeat) - if !isAbsent(eval.state.prevTotals.Online.Money, acctData.MicroAlgos, lastSeen, currentRound) { - return fmt.Errorf("proposed absent account %v is not absent in %d, %d", - accountAddr, acctData.LastProposed, acctData.LastHeartbeat) + if isAbsent(eval.state.prevTotals.Online.Money, acctData.MicroAlgos, lastSeen, eval.Round()) { + continue // ok. it's "normal absent" + } + if failsChallenge(ch, accountAddr, lastSeen) { + continue // ok. it's "challenge absent" } + return fmt.Errorf("proposed absent account %v is not absent in %d, %d", + accountAddr, acctData.LastProposed, acctData.LastHeartbeat) } return nil } diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 1bca8b1705..76cb3776cd 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -1469,3 +1469,18 @@ func TestExpiredAccountGeneration(t *testing.T) { require.Equal(t, crypto.VRFVerifier{}, recvAcct.SelectionID) require.Equal(t, merklesignature.Verifier{}.Commitment, recvAcct.StateProofID) } + +func TestBitsMatch(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + for b := 0; b <= 6; b++ { + require.True(t, bitsMatch([]byte{0x1}, []byte{0x2}, b), "%d", b) + } + require.False(t, bitsMatch([]byte{0x1}, []byte{0x2}, 7)) + + for b := 0; b <= 12; b++ { + require.True(t, bitsMatch([]byte{0x1, 0xff, 0xaa}, []byte{0x1, 0xf0}, b), "%d", b) + } + require.False(t, bitsMatch([]byte{0x1, 0xff, 0xaa}, []byte{0x1, 0xf0}, 13)) +} diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 1a279c8c72..ce5f204c86 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -280,13 +280,11 @@ func TestMiningFees(t *testing.T) { require.True(t, dl.generator.GenesisProto().EnableMining) // version sanity check require.NotZero(t, dl.generator.GenesisProto().MiningPercent) // version sanity check // new fields are in the header - require.EqualValues(t, proposer, vb.Block().BlockHeader.Proposer) require.EqualValues(t, 2000, vb.Block().BlockHeader.FeesCollected.Raw) } else { require.False(t, dl.generator.GenesisProto().EnableMining) require.Zero(t, dl.generator.GenesisProto().MiningPercent) // version sanity check // new fields are not in the header - require.Zero(t, vb.Block().BlockHeader.Proposer) require.Zero(t, vb.Block().BlockHeader.FeesCollected) } @@ -424,7 +422,7 @@ func TestAbsentTracking(t *testing.T) { Receiver: addrs[2], Amount: 100_000, }) - vb := dl.endBlock(proposer) + dl.endBlock(proposer) // no changes until the next block prp := lookup(t, dl.validator, proposer) @@ -446,11 +444,9 @@ func TestAbsentTracking(t *testing.T) { if ver >= checkingBegins { // version sanity check - require.Equal(t, proposer, vb.Block().BlockHeader.Proposer) require.NotZero(t, prp.LastProposed) require.Zero(t, prp.LastHeartbeat) // genesis participants have never hb } else { - require.Zero(t, vb.Block().BlockHeader.Proposer) require.Zero(t, prp.LastProposed) require.Zero(t, prp.LastHeartbeat) } @@ -565,6 +561,123 @@ func TestAbsentTracking(t *testing.T) { }) } +// TestAbsenteeChallenges ensures that online accounts that don't (do) respond +// to challenges end up off (on) line. +func TestAbsenteeChallenges(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + genBalances, addrs, _ := ledgertesting.NewTestGenesis(func(cfg *ledgertesting.GenesisCfg) { + cfg.OnlineCount = 5 // Make online stake big, so these accounts won't be expected to propose + }) + checkingBegins := 40 + + ledgertesting.TestConsensusRange(t, checkingBegins-1, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { + dl := NewDoubleLedger(t, genBalances, cv, cfg) + defer dl.Close() + + // We'll generate a challenge for accounts that start with 0xaa. + propguy := basics.Address{0xaa, 0xaa, 0xaa} // Will propose during the challenge window + regguy := basics.Address{0xaa, 0xbb, 0xbb} // Will re-reg during the challenge window + badguy := basics.Address{0xaa, 0x11, 0x11} // Will ignore the challenge + + // Fund them all and have them go online. That makes them eligible to be challenged + for i, guy := range []basics.Address{propguy, regguy, badguy} { + dl.txns(&txntest.Txn{ + Type: "pay", + Sender: addrs[0], + Receiver: guy, + Amount: 10_000_000, + }, &txntest.Txn{ + Type: "keyreg", + Fee: 5_000_000, // enough to be incentive eligible + Sender: guy, + VotePK: [32]byte{byte(i + 1)}, + SelectionPK: [32]byte{byte(i + 1)}, + }) + acct := lookup(t, dl.generator, guy) + require.Equal(t, basics.Online, acct.Status) + require.Equal(t, ver >= checkingBegins, acct.IncentiveEligible, guy) + } + + for vb := dl.fullBlock(); vb.Block().BlockHeader.Round < 999; vb = dl.fullBlock() { + // we just advancing to one before the challenge round + } + // All still online, same eligibility + for _, guy := range []basics.Address{propguy, regguy, badguy} { + acct := lookup(t, dl.generator, guy) + require.Equal(t, basics.Online, acct.Status) + require.Equal(t, ver >= checkingBegins, acct.IncentiveEligible, guy) + } + // make the BlockSeed start with 0xa in the challenge round + dl.beginBlock() + dl.endBlock(basics.Address{0xaa}) // This becomes the seed, which is used for the challenge + + for vb := dl.fullBlock(); vb.Block().BlockHeader.Round < 1200; vb = dl.fullBlock() { + // advance through first grace period + } + dl.beginBlock() + dl.endBlock(propguy) // propose, which is a fine (though less likely) way to respond + + // All still online, unchanged eligibility + for _, guy := range []basics.Address{propguy, regguy, badguy} { + acct := lookup(t, dl.generator, guy) + require.Equal(t, basics.Online, acct.Status) + require.Equal(t, ver >= checkingBegins, acct.IncentiveEligible, guy) + } + + for vb := dl.fullBlock(); vb.Block().BlockHeader.Round < 1220; vb = dl.fullBlock() { + // advance into knockoff period. but no transactions means + // unresponsive accounts go unnoticed. + } + // All still online, same eligibility + for _, guy := range []basics.Address{propguy, regguy, badguy} { + acct := lookup(t, dl.generator, guy) + require.Equal(t, basics.Online, acct.Status) + require.Equal(t, ver >= checkingBegins, acct.IncentiveEligible, guy) + } + + // badguy never responded, he gets knocked off when paid + dl.txns(&txntest.Txn{ + Type: "pay", + Sender: addrs[0], + Receiver: badguy, + }) + acct := lookup(t, dl.generator, badguy) + require.Equal(t, ver >= checkingBegins, basics.Offline == acct.Status) // if checking, badguy fails + require.False(t, acct.IncentiveEligible) + + // propguy proposed during the grace period, he stays on even when paid + dl.txns(&txntest.Txn{ + Type: "pay", + Sender: addrs[0], + Receiver: propguy, + }) + acct = lookup(t, dl.generator, propguy) + require.Equal(t, basics.Online, acct.Status) + require.Equal(t, ver >= checkingBegins, acct.IncentiveEligible) + + // regguy keyregs before he's caught, which is a heartbeat, he stays on as well + dl.txns(&txntest.Txn{ + Type: "keyreg", // Does not pay extra fee, since he's still eligible + Sender: regguy, + VotePK: [32]byte{1}, + SelectionPK: [32]byte{1}, + }) + acct = lookup(t, dl.generator, regguy) + require.Equal(t, basics.Online, acct.Status) + require.Equal(t, ver >= checkingBegins, acct.IncentiveEligible) + dl.txns(&txntest.Txn{ + Type: "pay", + Sender: addrs[0], + Receiver: regguy, + }) + acct = lookup(t, dl.generator, regguy) + require.Equal(t, basics.Online, acct.Status) + require.Equal(t, ver >= checkingBegins, acct.IncentiveEligible) + }) +} + // TestHoldingGet tests some of the corner cases for the asset_holding_get // opcode: the asset doesn't exist, the account doesn't exist, account not opted // in, vs it has none of the asset. This is tested here, even though it should diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 3bfef32649..9ee15e5e1b 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -44,10 +44,7 @@ func (vb ValidatedBlock) Delta() StateDelta { func (vb ValidatedBlock) WithSeed(s committee.Seed, proposer basics.Address) ValidatedBlock { newblock := vb.blk newblock.BlockHeader.Seed = s - - if vb.blk.ConsensusProtocol().EnableMining { - newblock.BlockHeader.Proposer = proposer - } + newblock.BlockHeader.Proposer = proposer return ValidatedBlock{ blk: newblock, diff --git a/ledger/simple_test.go b/ledger/simple_test.go index d3ad9f4ed2..a5ab3d8da1 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -151,7 +151,10 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer if len(proposer) > 0 { prp = proposer[0] } - *vb = vb.WithSeed(committee.Seed{}, prp) + + // We have this backdoor way to install a proposer or seed into the header + // for tests. Doesn't matter that it makes them both the same. + *vb = vb.WithSeed(committee.Seed(prp), prp) err = ledger.AddValidatedBlock(*vb, agreement.Certificate{}) require.NoError(t, err) From 06c10c112acf955882ea19392e11abed65185ec9 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 31 Jan 2024 13:03:45 -0500 Subject: [PATCH 022/117] CR fixes --- data/basics/userBalance.go | 7 +++---- data/transactions/transaction_test.go | 4 +++- ledger/apptxn_test.go | 2 -- ledger/ledgercore/accountdata.go | 4 ++-- ledger/testing/testGenesis.go | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/data/basics/userBalance.go b/data/basics/userBalance.go index f8c06a816e..c00ed5be22 100644 --- a/data/basics/userBalance.go +++ b/data/basics/userBalance.go @@ -482,7 +482,7 @@ func (u AccountData) WithUpdatedRewards(proto config.ConsensusParams, rewardsLev // MinBalance computes the minimum balance requirements for an account based on // some consensus parameters. MinBalance should correspond roughly to how much // storage the account is allowed to store on disk. -func (u AccountData) MinBalance(proto *config.ConsensusParams) (res MicroAlgos) { +func (u AccountData) MinBalance(proto *config.ConsensusParams) MicroAlgos { return MinBalance( proto, uint64(len(u.Assets)), @@ -503,7 +503,7 @@ func MinBalance( totalAppParams uint64, totalAppLocalStates uint64, totalExtraAppPages uint64, totalBoxes uint64, totalBoxBytes uint64, -) (res MicroAlgos) { +) MicroAlgos { var min uint64 // First, base MinBalance @@ -538,8 +538,7 @@ func MinBalance( boxByteCost := MulSaturate(proto.BoxByteMinBalance, totalBoxBytes) min = AddSaturate(min, boxByteCost) - res.Raw = min - return res + return MicroAlgos{min} } // OnlineAccountData returns subset of AccountData as OnlineAccountData data structure. diff --git a/data/transactions/transaction_test.go b/data/transactions/transaction_test.go index dfec67c558..08dd145a8c 100644 --- a/data/transactions/transaction_test.go +++ b/data/transactions/transaction_test.go @@ -102,12 +102,14 @@ func TestGoOnlineGoNonparticipatingContradiction(t *testing.T) { VotePK: v.OneTimeSignatureVerifier, SelectionPK: vrf.PK, VoteKeyDilution: 1, + VoteFirst: 1, + VoteLast: 100, Nonparticipation: true, } // this tx tries to both register keys to go online, and mark an account as non-participating. // it is not well-formed. err = tx.WellFormed(SpecialAddresses{}, config.Consensus[protocol.ConsensusCurrentVersion]) - require.ErrorContains(t, err, "go online, but vote last is set to zero") + require.ErrorContains(t, err, "tries to register keys to go online, but nonparticipatory flag is set") } func TestGoNonparticipatingWellFormed(t *testing.T) { diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index 0e193e3eb2..37b5764552 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -91,11 +91,9 @@ func TestPayAction(t *testing.T) { // First MiningPct > 0 if ver >= 40 { require.True(t, dl.generator.GenesisProto().EnableMining) - require.EqualValues(t, proposer, vb.Block().BlockHeader.Proposer) require.EqualValues(t, 2000, vb.Block().BlockHeader.FeesCollected.Raw) } else { require.False(t, dl.generator.GenesisProto().EnableMining) - require.Zero(t, vb.Block().BlockHeader.Proposer) require.Zero(t, vb.Block().BlockHeader.FeesCollected) } diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 3b479bf237..0e37f6464f 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -161,7 +161,7 @@ func (u *AccountData) Suspended() bool { // MinBalance computes the minimum balance requirements for an account based on // some consensus parameters. MinBalance should correspond roughly to how much // storage the account is allowed to store on disk. -func (u AccountData) MinBalance(proto *config.ConsensusParams) (res basics.MicroAlgos) { +func (u AccountData) MinBalance(proto *config.ConsensusParams) basics.MicroAlgos { return basics.MinBalance( proto, uint64(u.TotalAssets), @@ -174,7 +174,7 @@ func (u AccountData) MinBalance(proto *config.ConsensusParams) (res basics.Micro // AvailableBalance returns the amount of MicroAlgos that are available for // spending without fully closing the account. -func (u AccountData) AvailableBalance(proto *config.ConsensusParams) (res basics.MicroAlgos) { +func (u AccountData) AvailableBalance(proto *config.ConsensusParams) basics.MicroAlgos { if left, o := basics.OSubA(u.MicroAlgos, u.MinBalance(proto)); !o { return left } diff --git a/ledger/testing/testGenesis.go b/ledger/testing/testGenesis.go index 71004373f3..81be9e9118 100644 --- a/ledger/testing/testGenesis.go +++ b/ledger/testing/testGenesis.go @@ -34,7 +34,7 @@ type GenesisCfg struct { // TestGenesisOption provides functional options for testGenesisCfg. type TestGenesisOption func(*GenesisCfg) -// TurnOffRewards turns off the rewards pool for tests that are sensistive to +// TurnOffRewards turns off the rewards pool for tests that are sensitive to // "surprise" balance changes. var TurnOffRewards = func(cfg *GenesisCfg) { cfg.rewardsPoolAmount = basics.MicroAlgos{Raw: 100_000} } From 794dcea7bd0f781a5bfe876e14fa2533b322455d Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 31 Jan 2024 13:46:28 -0500 Subject: [PATCH 023/117] Add BlkBonus assembly test --- data/transactions/logic/assembler_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 0d2ae828ce..ec5537a701 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -1711,6 +1711,8 @@ pushint 1 block BlkProposer pushint 1 block BlkFeesCollected +pushint 1 +block BlkBonus `, AssemblerMaxVersion) for _, names := range [][]string{GlobalFieldNames[:], TxnFieldNames[:], blockFieldNames[:]} { for _, f := range names { From ee9a623287350e58650be8f0f25a246fa1e22478 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 31 Jan 2024 14:18:51 -0500 Subject: [PATCH 024/117] Account for new fields in header --- protocol/tags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/tags.go b/protocol/tags.go index 6a51626f70..3b59b3db86 100644 --- a/protocol/tags.go +++ b/protocol/tags.go @@ -73,7 +73,7 @@ const PingReplyTagMaxSize = 8 // ProposalPayloadTagMaxSize is the maximum size of a ProposalPayloadTag message // This value is dominated by the MaxTxnBytesPerBlock -const ProposalPayloadTagMaxSize = 5250289 +const ProposalPayloadTagMaxSize = 5250301 // StateProofSigTagMaxSize is the maximum size of a StateProofSigTag message const StateProofSigTagMaxSize = 6378 From 59b4f9857af8baa6ea692b974773f95bea418adb Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 31 Jan 2024 15:06:25 -0500 Subject: [PATCH 025/117] Simplify decay code, show first year behaviour --- data/bookkeeping/block.go | 49 +++++++++++++++++----------------- data/bookkeeping/block_test.go | 28 +++++++++++++++++++ 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 1fc13ff4a8..45f9e605b3 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -479,8 +479,18 @@ func ProcessUpgradeParams(prev BlockHeader) (uv UpgradeVote, us UpgradeState, er } type bonusPlan struct { - baseRound basics.Round - baseAmount basics.MicroAlgos + // baseRound is the earliest round this plan can apply. Of course, the + // consensus update must also have happened. So using a low value makes it + // go into effect immediately upon upgrade. + baseRound basics.Round + // baseAmount is the bonus to be paid when this plan first applies (see + // baseRound). If it is zero, then no explicit change is made to the bonus + // (useful for only changing the decay rate). + baseAmount basics.MicroAlgos + // decayInterval is the time in rounds between 1% decays. For simplicity, + // decay occurs based on round % decayInterval, so a decay can happen right + // after going into effect. The decayInterval goes into effect at upgrade + // time, regardless of `baseRound`. decayInterval basics.Round } @@ -488,7 +498,7 @@ var bonusPlans = []bonusPlan{ 0: {}, 1: { baseRound: 0, // goes into effect with upgrade - baseAmount: basics.Algos(2), + baseAmount: basics.MicroAlgos{Raw: 5_000_000}, // 2.9 sec rounds gives about 10.8M rounds per year. decayInterval: 1_000_000, // .99^(10.8/1) ~ .897 ~ 10% decay per year }, @@ -502,35 +512,24 @@ var bonusPlans = []bonusPlan{ } func nextBonus(prev BlockHeader, params *config.ConsensusParams) basics.MicroAlgos { - // We always set if we are in the baseRound so that a new bonus plan can - // reset the amount. + prevParams := config.Consensus[prev.CurrentProtocol] // presence ensured by ProcessUpgradeParams + upgrading := prevParams.BonusPlan != params.BonusPlan + current := prev.Round + 1 plan := bonusPlans[params.BonusPlan] - if current == plan.baseRound { + // Reset the amount if it's non-zero and we are upgrading, or the time has come. + if (upgrading || current == plan.baseRound) && !plan.baseAmount.IsZero() { return plan.baseAmount } - bonus := prev.Bonus - switch { - case bonus.IsZero() && current > plan.baseRound: - prevParams := config.Consensus[prev.CurrentProtocol] // presence ensured by ProcessUpgradeParams - if prevParams.BonusPlan == 0 { - // We must have have just upgraded, with a passed baseRound, so install the bonus - bonus = plan.baseAmount - } - // else bonus is zero because it has decayed to zero. that's fine. - case bonus.IsZero() && current < plan.baseRound: - /* do nothing, wait for baseRound */ - default: - if plan.decayInterval != 0 && current%plan.decayInterval == 0 { - var o bool - bonus.Raw, o = basics.Muldiv(bonus.Raw, 99, 100) - if o { - logging.Base().Panicf("MakeBlock: error decaying bonus: %d", prev.Bonus) - } + // decay + if plan.decayInterval != 0 && current%plan.decayInterval == 0 { + if newBonus, o := basics.Muldiv(prev.Bonus.Raw, 99, 100); !o { + return basics.MicroAlgos{Raw: newBonus} } + logging.Base().Panicf("MakeBlock: error decaying bonus: %d", prev.Bonus) } - return bonus + return prev.Bonus } // MakeBlock constructs a new valid block with an empty payset and an unset Seed. diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index 412c6fb858..a46178bb09 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -19,6 +19,7 @@ package bookkeeping import ( "bytes" "encoding/hex" + "fmt" "math" "testing" "time" @@ -908,3 +909,30 @@ func TestBlockHeader_Serialization(t *testing.T) { a.Equal(crypto.Digest{}, blkHdr.TxnCommitments.Sha256Commitment) a.NotEqual(crypto.Digest{}, blkHdr.TxnCommitments.NativeSha512_256Commitment) } + +// TestFirstYearBonus shows what about a year's worth of block bonuses would pay out. +func TestFirstYearBonus(t *testing.T) { + yearSeconds := 365 * 24 * 60 * 60 + yearRounds := int(float64(yearSeconds) / 2.9) + + sum := uint64(0) + bonus := bonusPlans[1].baseAmount.Raw + interval := int(bonusPlans[1].decayInterval) + for i := 0; i < yearRounds; i++ { + sum += bonus + if i%interval == 0 { + bonus, _ = basics.Muldiv(bonus, 99, 100) + } + } + sum /= 1_000_000 // micro to Algos + + fmt.Printf("paid %d algos\n", sum) + fmt.Printf("bonus start: %d end: %d\n", bonusPlans[1].baseAmount.Raw, bonus) + + // pays about 51M algos + require.InDelta(t, 51_000_000, sum, 500_000) + + // decline about 10% + require.InDelta(t, 0.90, float64(bonus)/float64(bonusPlans[1].baseAmount.Raw), 0.01) + +} From 1f3e48501f38f72e02943896ddacf531006c5d73 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 31 Jan 2024 16:04:17 -0500 Subject: [PATCH 026/117] forgotten partitiontest --- data/bookkeeping/block_test.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index a46178bb09..89be53c75d 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -70,6 +70,7 @@ func init() { func TestUpgradeVote(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() s := UpgradeState{ CurrentProtocol: proto1, @@ -133,6 +134,7 @@ func TestUpgradeVote(t *testing.T) { func TestUpgradeVariableDelay(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() s := UpgradeState{ CurrentProtocol: protoDelay, @@ -159,6 +161,7 @@ func TestUpgradeVariableDelay(t *testing.T) { func TestMakeBlockUpgrades(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var b Block b.BlockHeader.GenesisID = t.Name() @@ -211,6 +214,7 @@ func TestMakeBlockUpgrades(t *testing.T) { func TestBlockUnsupported(t *testing.T) { //nolint:paralleltest // Not parallel because it modifies config.Consensus partitiontest.PartitionTest(t) + t.Parallel() var b Block b.CurrentProtocol = protoUnsupported @@ -226,6 +230,7 @@ func TestBlockUnsupported(t *testing.T) { //nolint:paralleltest // Not parallel func TestTime(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var prev Block prev.BlockHeader.GenesisID = t.Name() @@ -255,6 +260,7 @@ func TestTime(t *testing.T) { func TestBonus(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var prev Block prev.CurrentProtocol = proto1 @@ -287,6 +293,7 @@ func TestBonus(t *testing.T) { func TestRewardsLevel(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var buf bytes.Buffer log := logging.NewLogger() @@ -307,6 +314,7 @@ func TestRewardsLevel(t *testing.T) { func TestRewardsLevelWithResidue(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var buf bytes.Buffer log := logging.NewLogger() @@ -329,6 +337,7 @@ func TestRewardsLevelWithResidue(t *testing.T) { func TestRewardsLevelNoUnits(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var buf bytes.Buffer log := logging.NewLogger() @@ -350,6 +359,7 @@ func TestRewardsLevelNoUnits(t *testing.T) { func TestTinyLevel(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var buf bytes.Buffer log := logging.NewLogger() @@ -370,6 +380,7 @@ func TestTinyLevel(t *testing.T) { func TestRewardsRate(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var buf bytes.Buffer log := logging.NewLogger() @@ -395,6 +406,7 @@ func TestRewardsRate(t *testing.T) { func TestRewardsRateRefresh(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var buf bytes.Buffer log := logging.NewLogger() @@ -420,6 +432,7 @@ func TestRewardsRateRefresh(t *testing.T) { func TestEncodeDecodeSignedTxn(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var b Block b.BlockHeader.GenesisID = "foo" @@ -440,6 +453,7 @@ func TestEncodeDecodeSignedTxn(t *testing.T) { func TestEncodeMalformedSignedTxn(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var b Block b.BlockHeader.GenesisID = "foo" @@ -465,6 +479,7 @@ func TestEncodeMalformedSignedTxn(t *testing.T) { func TestDecodeMalformedSignedTxn(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() var b Block b.BlockHeader.GenesisID = "foo" @@ -486,6 +501,7 @@ func TestDecodeMalformedSignedTxn(t *testing.T) { // running the rounds in the same way eval() is executing them over RewardsRateRefreshInterval rounds. func TestInitialRewardsRateCalculation(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() consensusParams := config.Consensus[protocol.ConsensusCurrentVersion] consensusParams.RewardsCalculationFix = false @@ -588,6 +604,7 @@ func performRewardsRateCalculation( func TestNextRewardsRateWithFix(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() proto, ok := config.Consensus[protocol.ConsensusCurrentVersion] require.True(t, ok) @@ -633,6 +650,7 @@ func TestNextRewardsRateWithFix(t *testing.T) { func TestNextRewardsRateFailsWithoutFix(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() proto, ok := config.Consensus[protocol.ConsensusCurrentVersion] require.True(t, ok) @@ -652,6 +670,7 @@ func TestNextRewardsRateFailsWithoutFix(t *testing.T) { func TestNextRewardsRateWithFixUsesNewRate(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() proto, ok := config.Consensus[protocol.ConsensusCurrentVersion] require.True(t, ok) @@ -686,6 +705,7 @@ func TestNextRewardsRateWithFixUsesNewRate(t *testing.T) { func TestNextRewardsRateWithFixPoolBalanceInsufficient(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() proto, ok := config.Consensus[protocol.ConsensusCurrentVersion] require.True(t, ok) @@ -720,6 +740,7 @@ func TestNextRewardsRateWithFixPoolBalanceInsufficient(t *testing.T) { func TestNextRewardsRateWithFixMaxSpentOverOverflow(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() proto, ok := config.Consensus[protocol.ConsensusCurrentVersion] require.True(t, ok) @@ -756,6 +777,7 @@ func TestNextRewardsRateWithFixMaxSpentOverOverflow(t *testing.T) { func TestNextRewardsRateWithFixRewardsWithResidueOverflow(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() proto, ok := config.Consensus[protocol.ConsensusCurrentVersion] require.True(t, ok) @@ -782,6 +804,7 @@ func TestNextRewardsRateWithFixRewardsWithResidueOverflow(t *testing.T) { func TestNextRewardsRateWithFixNextRewardLevelOverflow(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() proto, ok := config.Consensus[protocol.ConsensusCurrentVersion] require.True(t, ok) @@ -808,6 +831,7 @@ func TestNextRewardsRateWithFixNextRewardLevelOverflow(t *testing.T) { func TestBlock_ContentsMatchHeader(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() a := require.New(t) // Create a block without SHA256 TxnCommitments @@ -895,6 +919,7 @@ func TestBlock_ContentsMatchHeader(t *testing.T) { func TestBlockHeader_Serialization(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() a := require.New(t) // This serialized block header was generated from V32 e2e test, using the old BlockHeader struct which contains only TxnCommitments SHA512_256 value @@ -912,6 +937,10 @@ func TestBlockHeader_Serialization(t *testing.T) { // TestFirstYearBonus shows what about a year's worth of block bonuses would pay out. func TestFirstYearBonus(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + a := require.New(t) + yearSeconds := 365 * 24 * 60 * 60 yearRounds := int(float64(yearSeconds) / 2.9) @@ -930,9 +959,9 @@ func TestFirstYearBonus(t *testing.T) { fmt.Printf("bonus start: %d end: %d\n", bonusPlans[1].baseAmount.Raw, bonus) // pays about 51M algos - require.InDelta(t, 51_000_000, sum, 500_000) + a.InDelta(51_000_000, sum, 500_000) // decline about 10% - require.InDelta(t, 0.90, float64(bonus)/float64(bonusPlans[1].baseAmount.Raw), 0.01) + a.InDelta(0.90, float64(bonus)/float64(bonusPlans[1].baseAmount.Raw), 0.01) } From eb923da627fae6ad36d9d2126d7711ec7a9123a3 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 31 Jan 2024 16:10:45 -0500 Subject: [PATCH 027/117] Completed a comment --- config/consensus.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/consensus.go b/config/consensus.go index 2f2dbe4e9c..9ad989842d 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -452,7 +452,8 @@ type ConsensusParams struct { MaxProposedExpiredOnlineAccounts int // MaxProposedAbsentOnlineAccounts is the maximum number of online accounts, - // that a proposer can suspend for not proposing "lately" (TBD) + // that a proposer can suspend for not proposing "lately" (In 10x expected + // interval, or within a grace period from being challenged) MaxProposedAbsentOnlineAccounts int // EnableAccountDataResourceSeparation enables the support for extended application and asset storage From b2f7691c204f0311b2d6039f0f1a0f5bf5ecdd11 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 1 Feb 2024 11:47:14 -0500 Subject: [PATCH 028/117] Tidying and testing --- ledger/apptxn_test.go | 4 ++-- ledger/eval/eval.go | 14 ++++++++++---- ledger/eval_simple_test.go | 27 ++++++++++++++++++--------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index 37b5764552..05d2a8b033 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -91,10 +91,10 @@ func TestPayAction(t *testing.T) { // First MiningPct > 0 if ver >= 40 { require.True(t, dl.generator.GenesisProto().EnableMining) - require.EqualValues(t, 2000, vb.Block().BlockHeader.FeesCollected.Raw) + require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) } else { require.False(t, dl.generator.GenesisProto().EnableMining) - require.Zero(t, vb.Block().BlockHeader.FeesCollected) + require.Zero(t, vb.Block().FeesCollected) } postsink := micros(dl.t, dl.generator, genBalances.FeeSink) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 05b83a8425..cfe69db021 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1557,16 +1557,22 @@ func max(a, b basics.Round) basics.Round { } // bitsMatch checks if the first n bits of two byte slices match. Written to -// work on arbitrary slices, but expected that n is small. No attempt to -// optimize for byte by byte comparisons. (Only user today calls with n=5) +// work on arbitrary slices, but we expect that n is small. Only user today +// calls with n=5. func bitsMatch(a, b []byte, n int) bool { // Ensure n is a valid number of bits to compare if n < 0 || n > len(a)*8 || n > len(b)*8 { return false } - // Compare each bit up to the specified n bits - for i := 0; i < n; i++ { + // Compare entire bytes when n is bigger than 8 + for i := 0; i < n/8; i++ { + if a[i] != b[i] { + return false + } + } + // Compare bits in the last byte + for i := n / 8 * 8; i < n; i++ { // Calculate the byte and bit positions bytePos := i / 8 bitPos := i % 8 diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index ce5f204c86..d02bdddc4c 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -280,12 +280,12 @@ func TestMiningFees(t *testing.T) { require.True(t, dl.generator.GenesisProto().EnableMining) // version sanity check require.NotZero(t, dl.generator.GenesisProto().MiningPercent) // version sanity check // new fields are in the header - require.EqualValues(t, 2000, vb.Block().BlockHeader.FeesCollected.Raw) + require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) } else { require.False(t, dl.generator.GenesisProto().EnableMining) require.Zero(t, dl.generator.GenesisProto().MiningPercent) // version sanity check // new fields are not in the header - require.Zero(t, vb.Block().BlockHeader.FeesCollected) + require.Zero(t, vb.Block().FeesCollected) } postsink := micros(dl.t, dl.generator, genBalances.FeeSink) @@ -524,13 +524,16 @@ func TestAbsentTracking(t *testing.T) { require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[2]).Status) require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).IncentiveEligible) - // when 2 pays 0, they both get noticed and get suspended - dl.txns(&txntest.Txn{ + // when 2 pays 0, they both get noticed but addr[0] is not considered absent + vb := dl.fullBlock(&txntest.Txn{ Type: "pay", Sender: addrs[2], Receiver: addrs[0], Amount: 0, }) + if ver >= checkingBegins { + require.Equal(t, vb.Block().AbsentParticipationAccounts, []basics.Address{addrs[2]}) + } // addr[0] has never proposed or heartbeat so it is not considered absent require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[0]).Status) // addr[1] still hasn't been "noticed" @@ -539,12 +542,15 @@ func TestAbsentTracking(t *testing.T) { require.False(t, lookup(t, dl.generator, addrs[2]).IncentiveEligible) // now, when 2 pays 1, 1 gets suspended (unlike 0, we had 1 keyreg early on, so LastHeartbeat>0) - dl.txns(&txntest.Txn{ + vb = dl.fullBlock(&txntest.Txn{ Type: "pay", Sender: addrs[2], Receiver: addrs[1], Amount: 0, }) + if ver >= checkingBegins { + require.Equal(t, vb.Block().AbsentParticipationAccounts, []basics.Address{addrs[1]}) + } require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[0]).Status) require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[1]).Status == basics.Offline) require.False(t, lookup(t, dl.generator, addrs[1]).IncentiveEligible) @@ -600,7 +606,7 @@ func TestAbsenteeChallenges(t *testing.T) { require.Equal(t, ver >= checkingBegins, acct.IncentiveEligible, guy) } - for vb := dl.fullBlock(); vb.Block().BlockHeader.Round < 999; vb = dl.fullBlock() { + for vb := dl.fullBlock(); vb.Block().Round() < 999; vb = dl.fullBlock() { // we just advancing to one before the challenge round } // All still online, same eligibility @@ -613,7 +619,7 @@ func TestAbsenteeChallenges(t *testing.T) { dl.beginBlock() dl.endBlock(basics.Address{0xaa}) // This becomes the seed, which is used for the challenge - for vb := dl.fullBlock(); vb.Block().BlockHeader.Round < 1200; vb = dl.fullBlock() { + for vb := dl.fullBlock(); vb.Block().Round() < 1200; vb = dl.fullBlock() { // advance through first grace period } dl.beginBlock() @@ -626,7 +632,7 @@ func TestAbsenteeChallenges(t *testing.T) { require.Equal(t, ver >= checkingBegins, acct.IncentiveEligible, guy) } - for vb := dl.fullBlock(); vb.Block().BlockHeader.Round < 1220; vb = dl.fullBlock() { + for vb := dl.fullBlock(); vb.Block().Round() < 1220; vb = dl.fullBlock() { // advance into knockoff period. but no transactions means // unresponsive accounts go unnoticed. } @@ -638,11 +644,14 @@ func TestAbsenteeChallenges(t *testing.T) { } // badguy never responded, he gets knocked off when paid - dl.txns(&txntest.Txn{ + vb := dl.fullBlock(&txntest.Txn{ Type: "pay", Sender: addrs[0], Receiver: badguy, }) + if ver >= checkingBegins { + require.Equal(t, vb.Block().AbsentParticipationAccounts, []basics.Address{badguy}) + } acct := lookup(t, dl.generator, badguy) require.Equal(t, ver >= checkingBegins, basics.Offline == acct.Status) // if checking, badguy fails require.False(t, acct.IncentiveEligible) From 80410fe377fc4cabd4abe9233d7de5ed425639c6 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 1 Feb 2024 14:09:08 -0500 Subject: [PATCH 029/117] bonus rate change tests --- data/bookkeeping/block.go | 38 ++++++++++++++---------- data/bookkeeping/block_test.go | 54 +++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 45f9e605b3..8a80429f4e 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -503,33 +503,41 @@ var bonusPlans = []bonusPlan{ decayInterval: 1_000_000, // .99^(10.8/1) ~ .897 ~ 10% decay per year }, // If we need to change the decay rate (only), we would create a new plan like: - // { decayInterval: XXX} by using an old baseRound, the amount is not adjusted + // { decayInterval: XXX} by using a zero baseAmount, the amount not affected. // For a bigger change, we'd use a plan like: - // { baseRound: , baseAmount: , decayInterval: } + // { baseRound: , baseAmount: , decayInterval: } + // or just + // { baseAmount: , decayInterval: } // the new decay rate would go into effect at upgrade time, and the new - // amount would be set explicitly at baseRound. So care must be taken to - // make it _higher_ than the round of the upgrade. + // amount would be set at baseRound or at upgrade time. } +// nextBonus determines the bonus that should be paid out for proposing the next block. func nextBonus(prev BlockHeader, params *config.ConsensusParams) basics.MicroAlgos { + current := prev.Round + 1 prevParams := config.Consensus[prev.CurrentProtocol] // presence ensured by ProcessUpgradeParams - upgrading := prevParams.BonusPlan != params.BonusPlan + return computeBonus(current, prev.Bonus, bonusPlans[params.BonusPlan], bonusPlans[prevParams.BonusPlan]) +} - current := prev.Round + 1 - plan := bonusPlans[params.BonusPlan] - // Reset the amount if it's non-zero and we are upgrading, or the time has come. - if (upgrading || current == plan.baseRound) && !plan.baseAmount.IsZero() { - return plan.baseAmount +// computeBonus is the guts of nextBonus that can be unit tested more effectively. +func computeBonus(current basics.Round, prevBonus basics.MicroAlgos, curPlan bonusPlan, prevPlan bonusPlan) basics.MicroAlgos { + // Set the amount if it's non-zero... + if !curPlan.baseAmount.IsZero() { + upgrading := curPlan != prevPlan + // and the time has come. When the baseRound arrives, or at upgrade time is already passed. + if current == curPlan.baseRound || (upgrading && current > curPlan.baseRound) { + return curPlan.baseAmount + } } - // decay - if plan.decayInterval != 0 && current%plan.decayInterval == 0 { - if newBonus, o := basics.Muldiv(prev.Bonus.Raw, 99, 100); !o { + if curPlan.decayInterval != 0 && current%curPlan.decayInterval == 0 { + // decay + if newBonus, o := basics.Muldiv(prevBonus.Raw, 99, 100); !o { return basics.MicroAlgos{Raw: newBonus} } - logging.Base().Panicf("MakeBlock: error decaying bonus: %d", prev.Bonus) + logging.Base().Panicf("MakeBlock: error decaying bonus: %d", prevBonus) } - return prev.Bonus + return prevBonus } // MakeBlock constructs a new valid block with an empty payset and an unset Seed. diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index 89be53c75d..c01ebbe5e5 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -214,7 +214,7 @@ func TestMakeBlockUpgrades(t *testing.T) { func TestBlockUnsupported(t *testing.T) { //nolint:paralleltest // Not parallel because it modifies config.Consensus partitiontest.PartitionTest(t) - t.Parallel() + // t.Parallel() not parallel because it modifies config.Consensus var b Block b.CurrentProtocol = protoUnsupported @@ -935,6 +935,58 @@ func TestBlockHeader_Serialization(t *testing.T) { a.NotEqual(crypto.Digest{}, blkHdr.TxnCommitments.NativeSha512_256Commitment) } +func TestBonusUpgrades(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + a := require.New(t) + + ma0 := basics.MicroAlgos{Raw: 0} + ma99 := basics.MicroAlgos{Raw: 99} + ma100 := basics.MicroAlgos{Raw: 100} + ma198 := basics.MicroAlgos{Raw: 198} + ma200 := basics.MicroAlgos{Raw: 200} + + old := bonusPlan{} + plan := bonusPlan{} + + // Nothing happens with empty plans + a.Equal(ma0, computeBonus(1, ma0, plan, old)) + a.Equal(ma100, computeBonus(1, ma100, plan, old)) + + // When plan doesn't change, just expect decay on the intervals + plan.decayInterval = 100 + a.Equal(ma100, computeBonus(1, ma100, plan, plan)) + a.Equal(ma100, computeBonus(99, ma100, plan, plan)) + a.Equal(ma99, computeBonus(100, ma100, plan, plan)) + a.Equal(ma100, computeBonus(101, ma100, plan, plan)) + a.Equal(ma99, computeBonus(10000, ma100, plan, plan)) + + // When plan changes, the new decay is in effect + d90 := bonusPlan{decayInterval: 90} + a.Equal(ma100, computeBonus(100, ma100, d90, plan)) // no decay + a.Equal(ma99, computeBonus(180, ma100, d90, plan)) // decay + + // When plan changes and amount is present, it is installed + d90.baseAmount = ma200 + a.Equal(ma200, computeBonus(100, ma100, d90, plan)) // no decay (wrong round and upgrade anyway) + a.Equal(ma200, computeBonus(180, ma100, d90, plan)) // no decay (upgrade) + a.Equal(ma198, computeBonus(180, ma200, d90, d90)) // decay + a.Equal(ma99, computeBonus(180, ma100, d90, d90)) // decay (no install) + + // If there's a baseRound, the amount is installed accordingly + d90.baseRound = 150 + a.Equal(ma99, computeBonus(90, ma100, d90, plan)) // decay because baseRound delays install + a.Equal(ma100, computeBonus(149, ma100, d90, plan)) // no decay (interval) but also not installed yet + a.Equal(ma200, computeBonus(150, ma100, d90, plan)) // no decay (upgrade and immediate change) + a.Equal(ma200, computeBonus(151, ma100, d90, plan)) // no decay (upgrade and immediate change) + + // same tests, but not the upgrade round. only the "immediate installs" changes + a.Equal(ma99, computeBonus(90, ma100, d90, d90)) // decay + a.Equal(ma100, computeBonus(149, ma100, d90, d90)) // no decay (interval) but also not installed yet + a.Equal(ma200, computeBonus(150, ma100, d90, d90)) // not upgrade, but baseRound means install time + a.Equal(ma100, computeBonus(151, ma100, d90, d90)) // no decay (interval) +} + // TestFirstYearBonus shows what about a year's worth of block bonuses would pay out. func TestFirstYearBonus(t *testing.T) { partitiontest.PartitionTest(t) From b69c021d2fa2cc2efeeaa1475c0e3e0b0728637a Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 1 Feb 2024 14:14:26 -0500 Subject: [PATCH 030/117] Turn on block bonuses in vFuture --- config/consensus.go | 1 + test/e2e-go/features/suspension/suspension_test.go | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/config/consensus.go b/config/consensus.go index 9ad989842d..d1fd8c1069 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -1442,6 +1442,7 @@ func initConsensusProtocols() { vFuture.MiningPercent = 75 vFuture.MaxProposedAbsentOnlineAccounts = 32 + vFuture.BonusPlan = 1 Consensus[protocol.ConsensusFuture] = vFuture diff --git a/test/e2e-go/features/suspension/suspension_test.go b/test/e2e-go/features/suspension/suspension_test.go index 695673926c..54769bb8ad 100644 --- a/test/e2e-go/features/suspension/suspension_test.go +++ b/test/e2e-go/features/suspension/suspension_test.go @@ -153,8 +153,9 @@ func TestBasicSuspension(t *testing.T) { // node can keep voting. // we make an _offline_ tx here, because we want to populate the key - // material ourself, but copying the accounts existing state. That makes it - // an _online_ keyreg. + // material ourself, by copying the account's existing state. That makes it + // an _online_ keyreg. That allows the running node to chug along without + // new part keys. reReg, err := n15c.MakeUnsignedGoOfflineTx(address, 0, 0, 5_000_000, [32]byte{}) require.NoError(t, err, "should be able to make tx") From fd4ccaf8d63f65c522eda4f7260c7ecaf3d8c862 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 1 Feb 2024 14:50:28 -0500 Subject: [PATCH 031/117] Relocate suspension_test.go to prep for adding similar incentives --- .../features/{suspension => incentives}/suspension_test.go | 0 test/scripts/e2e_subs/absentee.py | 4 ++++ 2 files changed, 4 insertions(+) rename test/e2e-go/features/{suspension => incentives}/suspension_test.go (100%) diff --git a/test/e2e-go/features/suspension/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go similarity index 100% rename from test/e2e-go/features/suspension/suspension_test.go rename to test/e2e-go/features/incentives/suspension_test.go diff --git a/test/scripts/e2e_subs/absentee.py b/test/scripts/e2e_subs/absentee.py index f889a4ff35..3a1419d547 100755 --- a/test/scripts/e2e_subs/absentee.py +++ b/test/scripts/e2e_subs/absentee.py @@ -31,5 +31,9 @@ assert prp_info["last-proposed"] > 0 assert "last-heartbeat" not in prp_info, prp_info # was a genesis account +# This test really only examines the fields needed for absenteeism +# tracking. For actually seeing accounts being taken offline, see +# `suspension_test.go` + stamp = datetime.now().strftime("%Y%m%d_%H%M%S") print(f"{os.path.basename(sys.argv[0])} OK {stamp}") From 3959e1dadad0861827820e3b4966e426ed8be912 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 2 Feb 2024 16:18:32 -0500 Subject: [PATCH 032/117] Add e2e test for mining and bonuses --- data/bookkeeping/block.go | 2 +- test/e2e-go/cli/goal/clerk_test.go | 4 +- .../catchup/catchpointCatchup_test.go | 12 +- .../e2e-go/features/incentives/mining_test.go | 185 ++++++++++++++++++ .../features/incentives/suspension_test.go | 4 +- .../features/transactions/accountv2_test.go | 4 +- .../features/transactions/app_pages_test.go | 10 +- .../features/transactions/asset_test.go | 2 +- .../transactions/close_account_test.go | 4 +- .../features/transactions/proof_test.go | 4 +- test/e2e-go/perf/basic_test.go | 2 +- .../upgrades/send_receive_upgrade_test.go | 4 +- test/framework/fixtures/restClientFixture.go | 10 +- 13 files changed, 215 insertions(+), 32 deletions(-) create mode 100644 test/e2e-go/features/incentives/mining_test.go diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 8a80429f4e..9fbb087e9c 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -523,7 +523,7 @@ func nextBonus(prev BlockHeader, params *config.ConsensusParams) basics.MicroAlg func computeBonus(current basics.Round, prevBonus basics.MicroAlgos, curPlan bonusPlan, prevPlan bonusPlan) basics.MicroAlgos { // Set the amount if it's non-zero... if !curPlan.baseAmount.IsZero() { - upgrading := curPlan != prevPlan + upgrading := curPlan != prevPlan || current == 1 // and the time has come. When the baseRound arrives, or at upgrade time is already passed. if current == curPlan.baseRound || (upgrading && current > curPlan.baseRound) { return curPlan.baseAmount diff --git a/test/e2e-go/cli/goal/clerk_test.go b/test/e2e-go/cli/goal/clerk_test.go index ff02474210..05c1495d37 100644 --- a/test/e2e-go/cli/goal/clerk_test.go +++ b/test/e2e-go/cli/goal/clerk_test.go @@ -68,14 +68,14 @@ func TestClerkSendNoteEncoding(t *testing.T) { for i := uint64(0); i < maxRetry && (!foundTx1 || !foundTx2); i++ { if !foundTx1 { - tx1, err := fixture.WaitForConfirmedTxn(status.LastRound+i, account, txID) + tx1, err := fixture.WaitForConfirmedTxn(status.LastRound+i, txID) if err == nil { foundTx1 = true a.Equal(noteText, string(tx1.Txn.Txn.Note)) } } if !foundTx2 { - tx2, err := fixture.WaitForConfirmedTxn(status.LastRound+i, account, txID2) + tx2, err := fixture.WaitForConfirmedTxn(status.LastRound+i, txID2) if err == nil { foundTx2 = true // If the note matches our original text, then goal is still expecting strings encoded diff --git a/test/e2e-go/features/catchup/catchpointCatchup_test.go b/test/e2e-go/features/catchup/catchpointCatchup_test.go index 7d731e9e94..3a1eefedc4 100644 --- a/test/e2e-go/features/catchup/catchpointCatchup_test.go +++ b/test/e2e-go/features/catchup/catchpointCatchup_test.go @@ -537,7 +537,7 @@ func TestNodeTxHandlerRestart(t *testing.T) { a.NoError(err) status, err := client1.Status() a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(status.LastRound+100, addrs1[0], tx.ID().String()) + _, err = fixture.WaitForConfirmedTxn(status.LastRound+100, tx.ID().String()) a.NoError(err) targetCatchpointRound := status.LastRound @@ -563,7 +563,7 @@ func TestNodeTxHandlerRestart(t *testing.T) { status, err = client2.Status() a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(status.LastRound+50, addrs2[0], tx.ID().String()) + _, err = fixture.WaitForConfirmedTxn(status.LastRound+50, tx.ID().String()) a.NoError(err) } @@ -645,7 +645,7 @@ func TestReadyEndpoint(t *testing.T) { a.NoError(err) status, err := client1.Status() a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(status.LastRound+100, addrs1[0], tx.ID().String()) + _, err = fixture.WaitForConfirmedTxn(status.LastRound+100, tx.ID().String()) a.NoError(err) targetCatchpointRound := status.LastRound @@ -784,7 +784,7 @@ func TestNodeTxSyncRestart(t *testing.T) { a.NoError(err) status, err := client1.Status() a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(status.LastRound+100, addrs1[0], tx.ID().String()) + _, err = fixture.WaitForConfirmedTxn(status.LastRound+100, tx.ID().String()) a.NoError(err) targetCatchpointRound := status.LastRound @@ -806,7 +806,7 @@ func TestNodeTxSyncRestart(t *testing.T) { a.NoError(err) // the transaction should not be confirmed yet - _, err = fixture.WaitForConfirmedTxn(0, addrs2[0], tx.ID().String()) + _, err = fixture.WaitForConfirmedTxn(0, tx.ID().String()) a.Error(err) // Wait for the catchup @@ -826,6 +826,6 @@ func TestNodeTxSyncRestart(t *testing.T) { status, err = client2.Status() a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(status.LastRound+50, addrs2[0], tx.ID().String()) + _, err = fixture.WaitForConfirmedTxn(status.LastRound+50, tx.ID().String()) a.NoError(err) } diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go new file mode 100644 index 0000000000..ab47cf790b --- /dev/null +++ b/test/e2e-go/features/incentives/mining_test.go @@ -0,0 +1,185 @@ +// Copyright (C) 2019-2024 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// go-algorand is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with go-algorand. If not, see . + +package suspension + +import ( + "fmt" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/data/transactions" + "github.com/algorand/go-algorand/test/framework/fixtures" + "github.com/algorand/go-algorand/test/partitiontest" +) + +const roundTime = 4 * time.Second + +// TestBasicMining shows proposers getting paid +func TestBasicMining(t *testing.T) { + partitiontest.PartitionTest(t) + defer fixtures.ShutdownSynchronizedTest(t) + + if testing.Short() { + t.Skip() + } + t.Parallel() + a := require.New(fixtures.SynchronizedTest(t)) + + // Overview of this test: + // Start a single network + // Show that a genesis account does not get incentives + // rereg to become eligible + // show incentives are paid (mining and bonuses) + + var fixture fixtures.RestClientFixture + fixture.Setup(t, filepath.Join("nettemplates", "TwoNodes50EachFuture.json")) + defer fixture.Shutdown() + client := fixture.LibGoalClient + + richAccount, err := fixture.GetRichestAccount() + a.NoError(err) + + // wait for richAccount to have LastProposed != 0 + account, err := client.AccountData(richAccount.Address) + fmt.Printf(" rich balance %v %d\n", richAccount.Address, account.MicroAlgos) + for account.LastProposed == 0 { + a.NoError(err) + a.Equal(basics.Online, account.Status) + account, err = client.AccountData(richAccount.Address) + time.Sleep(roundTime) + } + a.NoError(err) + + // we make an _offline_ tx here, because we want to populate the key + // material ourself, by copying the account's existing state. That makes it + // an _online_ keyreg. That allows the running node to chug along without + // new part keys. We overpay the fee, just to get some funds into FeeSink + // because we will watch it drain toward bottom of test. + reReg, err := client.MakeUnsignedGoOfflineTx(richAccount.Address, 0, 0, 12_000_000, [32]byte{}) + require.NoError(t, err, "should be able to make tx") + + reReg.KeyregTxnFields = transactions.KeyregTxnFields{ + VotePK: account.VoteID, + SelectionPK: account.SelectionID, + StateProofPK: account.StateProofID, + VoteFirst: account.VoteFirstValid, + VoteLast: account.VoteLastValid, + VoteKeyDilution: account.VoteKeyDilution, + } + + wh, err := client.GetUnencryptedWalletHandle() + require.NoError(t, err, "should be able to get unencrypted wallet handle") + onlineTxID, err := client.SignAndBroadcastTransaction(wh, nil, reReg) + require.NoError(t, err, "should be no errors when going online") + fixture.WaitForConfirmedTxn(uint64(reReg.LastValid), onlineTxID) + + account, err = client.AccountData(richAccount.Address) + a.NoError(err) + a.True(account.IncentiveEligible) + // wait for richAccount to propose again, earns nothing (too much balance) + proposed := account.LastProposed + priorBalance := account.MicroAlgos + for account.LastProposed == proposed { + a.NoError(err) + account, err = client.AccountData(richAccount.Address) + a.NoError(err) + time.Sleep(roundTime) + } + + // incentives would pay out in the next round (they won't here, but makes the test realistic) + fixture.WaitForRound(uint64(account.LastProposed+1), roundTime) + account, err = client.AccountData(richAccount.Address) + a.NoError(err) + a.Equal(priorBalance, account.MicroAlgos) + + // Unload some algos, so we become incentive eligible + burn := "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ" + target := basics.Algos(1_000_000) // Assumes 1M algos is eligible + diff, _ := basics.OSubA(account.MicroAlgos, target) + fmt.Printf(" rich pays out %d + fee\n", diff.Raw) + tx, err := client.SendPaymentFromUnencryptedWallet(richAccount.Address, burn, 0, diff.Raw, nil) + a.NoError(err) + status, err := client.Status() + a.NoError(err) + info, err := fixture.WaitForConfirmedTxn(status.LastRound+10, tx.ID().String()) + a.NoError(err) + + // Figure out whether rich account was proposer of its own payment + blockProposer := func(r uint64) string { + block, err := client.Block(r) + a.NoError(err) + return block.Block["prp"].(string) + } + + // allow the incentive payment to happen + err = fixture.WaitForRound(*info.ConfirmedRound+1, roundTime) + a.NoError(err) + // check that rich account got paid (or didn't) based on whether it proposed + if blockProposer(*info.ConfirmedRound) == richAccount.Address { + // should earn the block bonus + target, _ = basics.OAddA(target, basics.Algos(5)) + // and only spent 25% of fee, since we earned 75% back + target, _ = basics.OAddA(target, basics.MicroAlgos{Raw: 750}) + } + // undershot 1M because of fee + target, _ = basics.OSubA(target, basics.MicroAlgos{Raw: 1000}) + account, err = client.AccountData(richAccount.Address) + a.NoError(err) + a.Equal(target, account.MicroAlgos) + + proposed = account.LastProposed + priorBalance = account.MicroAlgos + // wait for richAccount to propose following its payment to become eligible + r := *info.ConfirmedRound + 1 + for blockProposer(r) != richAccount.Address { + r++ + err = fixture.WaitForRound(r, roundTime) + a.NoError(err) + a.Less(r, *info.ConfirmedRound+20) // avoid infinite loop if bug + } + account, err = client.AccountData(richAccount.Address) + a.NoError(err) + fmt.Printf(" rich balance after eligible proposal %d\n", account.MicroAlgos) + // incentives pay out in the next round + err = fixture.WaitForRound(r+1, roundTime) + a.NoError(err) + account, err = client.AccountData(richAccount.Address) + a.NoError(err) + fmt.Printf(" rich balance after eligible payout %d\n", account.MicroAlgos) + // Should have earned 5A + target, _ = basics.OAddA(target, basics.Algos(5)) + a.Equal(target, account.MicroAlgos) + + // Now that we've proven incentives get paid, let's drain the FeeSink and + // ensure it happens gracefully. + block, err := client.Block(r + 1) + a.NoError(err) + feesink := block.Block["fees"].(string) + status, err = client.Status() + a.NoError(err) + for i := uint64(0); i < 10; i++ { + err = fixture.WaitForRound(status.LastRound+i, roundTime) + a.NoError(err) + account, err = client.AccountData(feesink) + a.NoError(err) + } + a.EqualValues(100000, account.MicroAlgos.Raw) // won't go below minfee +} diff --git a/test/e2e-go/features/incentives/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go index 54769bb8ad..ba3fb3ce9f 100644 --- a/test/e2e-go/features/incentives/suspension_test.go +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -29,8 +29,6 @@ import ( "github.com/algorand/go-algorand/test/partitiontest" ) -const roundTime = 4 * time.Second - // TestBasicSuspension confirms that accounts that don't propose get suspended // (when a tx naming them occurs) func TestBasicSuspension(t *testing.T) { @@ -173,7 +171,7 @@ func TestBasicSuspension(t *testing.T) { onlineTxID, err := n15c.SignAndBroadcastTransaction(wh, nil, reReg) require.NoError(t, err, "should be no errors when going online") - fixture.WaitForConfirmedTxn(uint64(reReg.LastValid), address, onlineTxID) + fixture.WaitForConfirmedTxn(uint64(reReg.LastValid), onlineTxID) account, err = fixture.LibGoalClient.AccountData(address) a.NoError(err) diff --git a/test/e2e-go/features/transactions/accountv2_test.go b/test/e2e-go/features/transactions/accountv2_test.go index fb255fa9c2..97eb1b4bd8 100644 --- a/test/e2e-go/features/transactions/accountv2_test.go +++ b/test/e2e-go/features/transactions/accountv2_test.go @@ -114,7 +114,7 @@ func TestAccountInformationV2(t *testing.T) { round, err := client.CurrentRound() a.NoError(err) - fixture.WaitForConfirmedTxn(round+4, creator, txn.ID().String()) + fixture.WaitForConfirmedTxn(round+4, txn.ID().String()) // There should be no apps to start with ad, err := client.AccountData(creator) @@ -419,7 +419,7 @@ func accountInformationCheckWithOffendingFields(t *testing.T, round, err := client.CurrentRound() a.NoError(err) - fixture.WaitForConfirmedTxn(round+4, creator, txn.ID().String()) + fixture.WaitForConfirmedTxn(round+4, txn.ID().String()) // There should be no apps to start with ad, err := client.AccountData(creator) diff --git a/test/e2e-go/features/transactions/app_pages_test.go b/test/e2e-go/features/transactions/app_pages_test.go index 710c00fc7f..5058fb59b8 100644 --- a/test/e2e-go/features/transactions/app_pages_test.go +++ b/test/e2e-go/features/transactions/app_pages_test.go @@ -95,7 +95,7 @@ return a.NoError(err) txid, err := client.SignAndBroadcastTransaction(walletHandle, nil, tx) a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(status.LastRound+5, baseAcct, txid) + _, err = fixture.WaitForConfirmedTxn(status.LastRound+5, txid) a.NoError(err) app1CreateTxn, err := client.PendingTransactionInformation(txid) @@ -116,7 +116,7 @@ return a.NoError(err) txid, err = client.SignAndBroadcastTransaction(walletHandle, nil, tx) a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(*app1CreateTxn.ConfirmedRound+5, baseAcct, txid) + _, err = fixture.WaitForConfirmedTxn(*app1CreateTxn.ConfirmedRound+5, txid) a.NoError(err) app1UpdateTxn, err := client.PendingTransactionInformation(txid) @@ -136,7 +136,7 @@ return a.NoError(err) txid, err = client.SignAndBroadcastTransaction(walletHandle, nil, tx) a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(*app1UpdateTxn.ConfirmedRound+5, baseAcct, txid) + _, err = fixture.WaitForConfirmedTxn(*app1UpdateTxn.ConfirmedRound+5, txid) a.NoError(err) app2CreateTxn, err := client.PendingTransactionInformation(txid) @@ -157,7 +157,7 @@ return a.NoError(err) txid, err = client.SignAndBroadcastTransaction(walletHandle, nil, tx) a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(*app2CreateTxn.ConfirmedRound+5, baseAcct, txid) + _, err = fixture.WaitForConfirmedTxn(*app2CreateTxn.ConfirmedRound+5, txid) a.NoError(err) app1DeleteTxn, err := client.PendingTransactionInformation(txid) @@ -176,7 +176,7 @@ return a.NoError(err) txid, err = client.SignAndBroadcastTransaction(walletHandle, nil, tx) a.NoError(err) - _, err = fixture.WaitForConfirmedTxn(*app1DeleteTxn.ConfirmedRound+5, baseAcct, txid) + _, err = fixture.WaitForConfirmedTxn(*app1DeleteTxn.ConfirmedRound+5, txid) a.NoError(err) accountInfo, err = client.AccountInformation(baseAcct, false) diff --git a/test/e2e-go/features/transactions/asset_test.go b/test/e2e-go/features/transactions/asset_test.go index 82e949d0bb..11baa9b7e4 100644 --- a/test/e2e-go/features/transactions/asset_test.go +++ b/test/e2e-go/features/transactions/asset_test.go @@ -774,7 +774,7 @@ func TestAssetSend(t *testing.T) { tx, err = client.SendPaymentFromUnencryptedWallet(account0, extra, 0, 10000000000, nil) a.NoError(err) _, curRound = fixture.GetBalanceAndRound(account0) - fixture.WaitForConfirmedTxn(curRound+20, account0, tx.ID().String()) + fixture.WaitForConfirmedTxn(curRound+20, tx.ID().String()) // Sending assets to account that hasn't opted in should fail, but // after opting in, should succeed for non-frozen asset. diff --git a/test/e2e-go/features/transactions/close_account_test.go b/test/e2e-go/features/transactions/close_account_test.go index a0f42a40d5..c15a39288d 100644 --- a/test/e2e-go/features/transactions/close_account_test.go +++ b/test/e2e-go/features/transactions/close_account_test.go @@ -64,11 +64,11 @@ func TestAccountsCanClose(t *testing.T) { // Transfer some money to acct0 and wait. tx, err := client.SendPaymentFromUnencryptedWallet(baseAcct, acct0, 1000, 10000000, nil) a.NoError(err) - fixture.WaitForConfirmedTxn(status.LastRound+10, baseAcct, tx.ID().String()) + fixture.WaitForConfirmedTxn(status.LastRound+10, tx.ID().String()) tx, err = client.SendPaymentFromWallet(walletHandle, nil, acct0, acct1, 1000, 1000000, nil, acct2, 0, 0) a.NoError(err) - fixture.WaitForConfirmedTxn(status.LastRound+10, acct0, tx.ID().String()) + fixture.WaitForConfirmedTxn(status.LastRound+10, tx.ID().String()) bal0, err := client.GetBalance(acct0) a.NoError(err) diff --git a/test/e2e-go/features/transactions/proof_test.go b/test/e2e-go/features/transactions/proof_test.go index 6588174f36..4f6226f139 100644 --- a/test/e2e-go/features/transactions/proof_test.go +++ b/test/e2e-go/features/transactions/proof_test.go @@ -92,7 +92,7 @@ func TestTxnMerkleProof(t *testing.T) { txid := tx.ID() txidSHA256 := tx.IDSha256() // only used for verification - confirmedTx, err := fixture.WaitForConfirmedTxn(status.LastRound+10, baseAcct, txid.String()) + confirmedTx, err := fixture.WaitForConfirmedTxn(status.LastRound+10, txid.String()) a.NoError(err) a.NotNil(confirmedTx.ConfirmedRound) @@ -175,7 +175,7 @@ func TestTxnMerkleProofSHA256(t *testing.T) { a.NoError(err) txid := tx.ID() - confirmedTx, err := fixture.WaitForConfirmedTxn(status.LastRound+10, baseAcct, txid.String()) + confirmedTx, err := fixture.WaitForConfirmedTxn(status.LastRound+10, txid.String()) a.NoError(err) a.NotNil(confirmedTx.ConfirmedRound) diff --git a/test/e2e-go/perf/basic_test.go b/test/e2e-go/perf/basic_test.go index 8509c40006..bdb8067c40 100644 --- a/test/e2e-go/perf/basic_test.go +++ b/test/e2e-go/perf/basic_test.go @@ -231,7 +231,7 @@ func doBenchTemplate(b *testing.B, template string, moneynode string) { time.Sleep(5 * time.Second) } - _, err = fixture.WaitForConfirmedTxn(status.LastRound+100, addr, tx.ID().String()) + _, err = fixture.WaitForConfirmedTxn(status.LastRound+100, tx.ID().String()) fmt.Printf("Waiting for confirmation transaction to commit..\n") a.NoError(err) } diff --git a/test/e2e-go/upgrades/send_receive_upgrade_test.go b/test/e2e-go/upgrades/send_receive_upgrade_test.go index f9e0746cbc..86127aba8b 100644 --- a/test/e2e-go/upgrades/send_receive_upgrade_test.go +++ b/test/e2e-go/upgrades/send_receive_upgrade_test.go @@ -278,12 +278,12 @@ func runUntilProtocolUpgrades(a *require.Assertions, fixture *fixtures.RestClien // wait for all transactions to confirm for _, txid := range pingTxids { - _, err = fixture.WaitForConfirmedTxn(curStatus.LastRound+5, pingAccount, txid) + _, err = fixture.WaitForConfirmedTxn(curStatus.LastRound+5, txid) a.NoError(err, "waiting for txn") } for _, txid := range pongTxids { - _, err = fixture.WaitForConfirmedTxn(curStatus.LastRound+5, pongAccount, txid) + _, err = fixture.WaitForConfirmedTxn(curStatus.LastRound+5, txid) a.NoError(err, "waiting for txn") } diff --git a/test/framework/fixtures/restClientFixture.go b/test/framework/fixtures/restClientFixture.go index be8ac2c252..38e89c35e5 100644 --- a/test/framework/fixtures/restClientFixture.go +++ b/test/framework/fixtures/restClientFixture.go @@ -224,15 +224,15 @@ func (f *RestClientFixture) GetNodeWalletsSortedByBalance(client libgoal.Client) // WaitForTxnConfirmation waits until either the passed txid is confirmed // or until the passed roundTimeout passes // or until waiting for a round to pass times out -func (f *RestClientFixture) WaitForTxnConfirmation(roundTimeout uint64, accountAddress, txid string) bool { - _, err := f.WaitForConfirmedTxn(roundTimeout, accountAddress, txid) +func (f *RestClientFixture) WaitForTxnConfirmation(roundTimeout uint64, txid string) bool { + _, err := f.WaitForConfirmedTxn(roundTimeout, txid) return err == nil } // WaitForConfirmedTxn waits until either the passed txid is confirmed // or until the passed roundTimeout passes // or until waiting for a round to pass times out -func (f *RestClientFixture) WaitForConfirmedTxn(roundTimeout uint64, accountAddress, txid string) (txn v2.PreEncodedTxInfo, err error) { +func (f *RestClientFixture) WaitForConfirmedTxn(roundTimeout uint64, txid string) (txn v2.PreEncodedTxInfo, err error) { client := f.AlgodClient for { // Get current round information @@ -270,7 +270,7 @@ func (f *RestClientFixture) WaitForAllTxnsToConfirm(roundTimeout uint64, txidsAn return true } for txid, addr := range txidsAndAddresses { - _, err := f.WaitForConfirmedTxn(roundTimeout, addr, txid) + _, err := f.WaitForConfirmedTxn(roundTimeout, txid) if err != nil { f.t.Logf("txn failed to confirm: addr=%s, txid=%s", addr, txid) pendingTxns, err := f.LibGoalClient.GetParsedPendingTransactions(0) @@ -359,7 +359,7 @@ func (f *RestClientFixture) SendMoneyAndWaitFromWallet(walletHandle, walletPassw require.NoError(f.t, err, "client should be able to send money from rich to poor account") require.NotEmpty(f.t, fundingTx.ID().String(), "transaction ID should not be empty") waitingDeadline := curRound + uint64(5) - txn, err = f.WaitForConfirmedTxn(waitingDeadline, fromAccount, fundingTx.ID().String()) + txn, err = f.WaitForConfirmedTxn(waitingDeadline, fundingTx.ID().String()) require.NoError(f.t, err) return } From bb401cf6551251b3c847aa04b0b76abd3029fc58 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 5 Feb 2024 09:44:21 -0500 Subject: [PATCH 033/117] pass up the error --- ledger/eval/eval.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index cfe69db021..97ddbe2e06 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -789,7 +789,12 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return nil, fmt.Errorf("overflowed subtracting rewards for block %v", hdr.Round) } - if eval.eligibleForIncentives(prevHeader.Proposer) { + eligible, err := eval.eligibleForIncentives(prevHeader.Proposer) + if err != nil { + return nil, err + } + + if eligible { incentive, _ := basics.NewPercent(proto.MiningPercent).DivvyAlgos(prevHeader.FeesCollected) total, o := basics.OAddA(incentive, prevHeader.Bonus) if o { @@ -858,18 +863,18 @@ var ( incentiveMaxBalance = basics.Algos(100_000_000) ) -func (eval *BlockEvaluator) eligibleForIncentives(proposer basics.Address) bool { +func (eval *BlockEvaluator) eligibleForIncentives(proposer basics.Address) (bool, error) { proposerState, err := eval.state.Get(proposer, true) if err != nil { - return false + return false, err } if proposerState.MicroAlgos.LessThan(incentiveMinBalance) { - return false + return false, nil } if proposerState.MicroAlgos.GreaterThan(incentiveMaxBalance) { - return false + return false, nil } - return proposerState.IncentiveEligible + return proposerState.IncentiveEligible, nil } // hotfix for testnet stall 08/26/2019; move some algos from testnet bank to rewards pool to give it enough time until protocol upgrade occur. From 316083f330c1ceeb809f35d9f26edf7b84768722 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 5 Feb 2024 13:44:54 -0500 Subject: [PATCH 034/117] WithSeed -> WithProposal --- agreement/abstractions.go | 8 +++++--- agreement/agreementtest/simulate_test.go | 2 +- agreement/common_test.go | 2 +- agreement/fuzzer/ledger_test.go | 2 +- agreement/proposal.go | 2 +- data/datatest/impls.go | 2 +- ledger/ledgercore/validatedBlock.go | 2 +- ledger/simple_test.go | 2 +- node/node.go | 4 ++-- 9 files changed, 14 insertions(+), 12 deletions(-) diff --git a/agreement/abstractions.go b/agreement/abstractions.go index ca640260c7..8cb5cf2831 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -54,12 +54,14 @@ type BlockValidator interface { // and can now be recorded in the ledger. This is an optimized version of // calling EnsureBlock() on the Ledger. type ValidatedBlock interface { - // WithSeed creates a copy of this ValidatedBlock with its - // cryptographically random seed set to the given value. + // WithProposal creates a copy of this ValidatedBlock with its + // cryptographically random seed and proposer set. Abstractly, it is how the + // agreement code "finishes" a block and makes it a proposal for a specific + // account. // // Calls to Seed() or to Digest() on the copy's Block must // reflect the value of the new seed. - WithSeed(committee.Seed, basics.Address) ValidatedBlock + WithProposal(committee.Seed, basics.Address) ValidatedBlock // Block returns the underlying block that has been validated. Block() bookkeeping.Block diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index fa1e4c1c5c..3c24c2df74 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -79,7 +79,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithSeed(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer return b diff --git a/agreement/common_test.go b/agreement/common_test.go index 11d6f5f90e..40c031384a 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -165,7 +165,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithSeed(s committee.Seed, proposer basics.Address) ValidatedBlock { +func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address) ValidatedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer return b diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go index 91d261c6e7..53c0f8defe 100644 --- a/agreement/fuzzer/ledger_test.go +++ b/agreement/fuzzer/ledger_test.go @@ -93,7 +93,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithSeed(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer return b diff --git a/agreement/proposal.go b/agreement/proposal.go index 8e7e3eda98..0c9cb91318 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -271,7 +271,7 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve Validat if ve.Block().ConsensusProtocol().EnableMining { hdrProp = address } - ve = ve.WithSeed(newSeed, hdrProp) + ve = ve.WithProposal(newSeed, hdrProp) proposal := makeProposal(ve, seedProof, period, address) value := proposalValue{ OriginalPeriod: period, diff --git a/data/datatest/impls.go b/data/datatest/impls.go index 1281f5cf9d..6812c73ae8 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -65,7 +65,7 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated } // WithSeed implements the agreement.ValidatedBlock interface. -func (ve validatedBlock) WithSeed(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { +func (ve validatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { newblock := *ve.blk newblock.BlockHeader.Seed = s newblock.BlockHeader.Proposer = proposer diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 9ee15e5e1b..aba447d092 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -41,7 +41,7 @@ func (vb ValidatedBlock) Delta() StateDelta { } // WithSeed returns a copy of the ValidatedBlock with a modified seed and associated proposer -func (vb ValidatedBlock) WithSeed(s committee.Seed, proposer basics.Address) ValidatedBlock { +func (vb ValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address) ValidatedBlock { newblock := vb.blk newblock.BlockHeader.Seed = s newblock.BlockHeader.Proposer = proposer diff --git a/ledger/simple_test.go b/ledger/simple_test.go index a5ab3d8da1..2c4453222f 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -154,7 +154,7 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer // We have this backdoor way to install a proposer or seed into the header // for tests. Doesn't matter that it makes them both the same. - *vb = vb.WithSeed(committee.Seed(prp), prp) + *vb = vb.WithProposal(committee.Seed(prp), prp) err = ledger.AddValidatedBlock(*vb, agreement.Certificate{}) require.NoError(t, err) diff --git a/node/node.go b/node/node.go index 2c1e675e1b..a83f292589 100644 --- a/node/node.go +++ b/node/node.go @@ -1291,8 +1291,8 @@ type validatedBlock struct { } // WithSeed satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) WithSeed(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { - lvb := vb.vb.WithSeed(s, proposer) +func (vb validatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { + lvb := vb.vb.WithProposal(s, proposer) return validatedBlock{vb: &lvb} } From 46470f782ff54d9797189d1ec1f484270f4864b6 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 7 Feb 2024 11:13:27 -0500 Subject: [PATCH 035/117] Many CR changes, most notably using consensus params --- agreement/msgp_gen.go | 30 +++++----- agreement/proposal.go | 4 +- config/consensus.go | 85 ++++++++++++++++++++--------- data/basics/fraction.go | 2 +- data/basics/units.go | 5 -- data/bookkeeping/block.go | 24 ++++---- data/bookkeeping/block_test.go | 4 +- data/bookkeeping/msgp_gen.go | 30 +++++----- data/datatest/impls.go | 2 +- data/transactions/payment.go | 2 +- ledger/apply/keyreg.go | 8 +-- ledger/apptxn_test.go | 4 +- ledger/eval/eval.go | 51 ++++++----------- ledger/eval_simple_test.go | 8 +-- ledger/ledger_test.go | 28 +++++----- ledger/ledgercore/validatedBlock.go | 2 +- node/node.go | 2 +- 17 files changed, 149 insertions(+), 142 deletions(-) diff --git a/agreement/msgp_gen.go b/agreement/msgp_gen.go index 965adb2e28..4c32195211 100644 --- a/agreement/msgp_gen.go +++ b/agreement/msgp_gen.go @@ -5143,8 +5143,8 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } - if zb0012 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0012 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } @@ -5457,8 +5457,8 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o err = msgp.WrapError(err, "AbsentParticipationAccounts") return } - if zb0020 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0020 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "AbsentParticipationAccounts") return } @@ -5565,7 +5565,7 @@ func ProposalMaxSize() (s int) { s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) s += 11 // Calculating size of slice: z.unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts - s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) + s += msgp.ArrayHeaderSize + ((config.MaxMarkAbsent) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.unauthenticatedProposal.Block.Payset s += config.MaxTxnBytesPerBlock @@ -9663,8 +9663,8 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } - if zb0012 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0012 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } @@ -9985,8 +9985,8 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal err = msgp.WrapError(err, "AbsentParticipationAccounts") return } - if zb0020 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0020 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "AbsentParticipationAccounts") return } @@ -10099,7 +10099,7 @@ func TransmittedPayloadMaxSize() (s int) { s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) s += 11 // Calculating size of slice: z.unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts - s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) + s += msgp.ArrayHeaderSize + ((config.MaxMarkAbsent) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.unauthenticatedProposal.Block.Payset s += config.MaxTxnBytesPerBlock @@ -11462,8 +11462,8 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } - if zb0012 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0012 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } @@ -11776,8 +11776,8 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma err = msgp.WrapError(err, "AbsentParticipationAccounts") return } - if zb0020 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0020 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "AbsentParticipationAccounts") return } @@ -11884,7 +11884,7 @@ func UnauthenticatedProposalMaxSize() (s int) { s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) s += 11 // Calculating size of slice: z.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts - s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) + s += msgp.ArrayHeaderSize + ((config.MaxMarkAbsent) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.Block.Payset s += config.MaxTxnBytesPerBlock diff --git a/agreement/proposal.go b/agreement/proposal.go index 0c9cb91318..7c87f094ed 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -198,7 +198,7 @@ func verifyNewSeed(p unauthenticatedProposal, ledger LedgerReader) error { rnd := p.Round() curParams := config.Consensus[p.BlockHeader.CurrentProtocol] - if curParams.EnableMining { + if curParams.Mining().Enabled { if p.BlockHeader.Proposer != value.OriginalProposer { return fmt.Errorf("payload has wrong proposer (%v != %v)", p.Proposer, value.OriginalProposer) } @@ -268,7 +268,7 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve Validat } var hdrProp basics.Address // The proposer as recorded in BlockHeader - if ve.Block().ConsensusProtocol().EnableMining { + if ve.Block().ConsensusProtocol().Mining().Enabled { hdrProp = address } ve = ve.WithProposal(newSeed, hdrProp) diff --git a/config/consensus.go b/config/consensus.go index d1fd8c1069..3f8245527a 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -451,11 +451,6 @@ type ConsensusParams struct { // that a proposer can take offline for having expired voting keys. MaxProposedExpiredOnlineAccounts int - // MaxProposedAbsentOnlineAccounts is the maximum number of online accounts, - // that a proposer can suspend for not proposing "lately" (In 10x expected - // interval, or within a grace period from being challenged) - MaxProposedAbsentOnlineAccounts int - // EnableAccountDataResourceSeparation enables the support for extended application and asset storage // in a separate table. EnableAccountDataResourceSeparation bool @@ -535,21 +530,64 @@ type ConsensusParams struct { // dynamic filter, it will be calculated and logged (but not used). DynamicFilterTimeout bool - // BonusPlan is the "version" of the block bonus plan. 0 indicates no block bonuses. - BonusPlan uint8 + // MiningRules is the version of mining related rules. It excludes anything + // related to block "bonuses" - extra payments made beyond what fees could + // provide. 0 disables mining and related tracking + MiningRulesVer uint8 + + // BonusPlanVer is the version of the block bonus plan. 0 indicates no block bonuses. + BonusPlanVer uint8 +} - // EnableMining means that the proposer should be included in the BlockHeader. - EnableMining bool +type MiningRules struct { + // Enabled turns on several things needed for paying block incentives, + // including tracking of the proposer and fees collected. + Enabled bool + + // GoOnlineFee imparts a small cost on moving from offline to online. This + // will impose a cost to running unreliable nodes that get suspended and + // then come back online. + GoOnlineFee uint64 + + // Percent specifies the percent of fees paid in a block that go to the + // proposer instead of the FeeSink. + Percent uint64 + + // MinBalance is the minimum balance an account must have to be eligible for + // incentives. It ensures that smaller accounts continue to operate for the + // same motivations they had before block incentives were + // introduced. Without that assurance, it is difficult to model their + // behaviour - might many participants join for the hope of easy financial + // rewards, but without caring enough to run a high-quality node? + MinBalance uint64 - // MiningPercent specifies the percent of fees paid in a block that go to - // the proposer instead of the FeeSink. - MiningPercent uint64 + // MaxBalance is the maximum balance an account can have to be eligible for + // incentives. It encourages large accounts to split their stake to add + // resilience to consensus in the case of outages. Nothing in protocol can + // prevent such accounts from running nodes that share fate (same machine, + // same data center, etc), but this serves as a gentle reminder. + MaxBalance uint64 + + // MaxMarkAbsent is the maximum number of online accounts, that a proposer + // can suspend for not proposing "lately" (In 10x expected interval, or + // within a grace period from being challenged) + MaxMarkAbsent int } -// EnableAbsenteeTracking returns true if the suspension mechanism of absentee -// accounts is enabled. -func (cp ConsensusParams) EnableAbsenteeTracking() bool { - return cp.MaxProposedAbsentOnlineAccounts > 0 +var miningRules = [...]MiningRules{ + {Enabled: false}, + { + Enabled: true, + Percent: 75, + GoOnlineFee: 2_000_000, // 2 algos + MinBalance: 100_000_000_000, // 100,000 algos + MaxBalance: 100_000_000_000_000, // 100M algos + MaxMarkAbsent: 32, + }, +} + +func (cp ConsensusParams) Mining() MiningRules { + return miningRules[cp.MiningRulesVer] } // PaysetCommitType enumerates possible ways for the block header to commit to @@ -628,9 +666,9 @@ var MaxAvailableAppProgramLen int // that a proposer can take offline for having expired voting keys. var MaxProposedExpiredOnlineAccounts int -// MaxProposedAbsentOnlineAccounts is the maximum number of online accounts, -// that a proposer can suspend for not proposing "lately" (TBD) -var MaxProposedAbsentOnlineAccounts int +// MaxMarkAbsent is the maximum number of online accounts that a proposer can +// suspend for not proposing "lately" +var MaxMarkAbsent int // MaxAppTotalArgLen is the maximum number of bytes across all arguments of an application // max sum([len(arg) for arg in txn.ApplicationArgs]) @@ -705,7 +743,7 @@ func checkSetAllocBounds(p ConsensusParams) { checkSetMax(p.MaxAppProgramLen, &MaxLogCalls) checkSetMax(p.MaxInnerTransactions*p.MaxTxGroupSize, &MaxInnerTransactionsPerDelta) checkSetMax(p.MaxProposedExpiredOnlineAccounts, &MaxProposedExpiredOnlineAccounts) - checkSetMax(p.MaxProposedAbsentOnlineAccounts, &MaxProposedAbsentOnlineAccounts) + checkSetMax(p.Mining().MaxMarkAbsent, &MaxMarkAbsent) // These bounds are exported to make them available to the msgp generator for calculating // maximum valid message size for each message going across the wire. @@ -1438,11 +1476,8 @@ func initConsensusProtocols() { vFuture.LogicSigVersion = 11 // When moving this to a release, put a new higher LogicSigVersion here - vFuture.EnableMining = true - vFuture.MiningPercent = 75 - - vFuture.MaxProposedAbsentOnlineAccounts = 32 - vFuture.BonusPlan = 1 + vFuture.MiningRulesVer = 1 + vFuture.BonusPlanVer = 1 Consensus[protocol.ConsensusFuture] = vFuture diff --git a/data/basics/fraction.go b/data/basics/fraction.go index 8a643f2520..925d342380 100644 --- a/data/basics/fraction.go +++ b/data/basics/fraction.go @@ -29,7 +29,7 @@ type Fraction struct { } // NewFraction creates the obvious Fraction, and checks that is not improper, -// nor dives by zero. +// nor divides by zero. func NewFraction(numerator uint64, denominator uint64) Fraction { if denominator == 0 { panic("/0") diff --git a/data/basics/units.go b/data/basics/units.go index 466be0ff7a..8e6ef0946c 100644 --- a/data/basics/units.go +++ b/data/basics/units.go @@ -45,11 +45,6 @@ func (a MicroAlgos) GreaterThan(b MicroAlgos) bool { return a.Raw > b.Raw } -// GTE implements arithmetic comparison for MicroAlgos -func (a MicroAlgos) GTE(b MicroAlgos) bool { - return a.Raw >= b.Raw -} - // IsZero implements arithmetic comparison for MicroAlgos func (a MicroAlgos) IsZero() bool { return a.Raw == 0 diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 9fbb087e9c..792f1b9384 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -162,7 +162,7 @@ type ( // AbsentParticipationAccounts contains a list of online accounts that // needs to be converted to offline since they are not proposing. - AbsentParticipationAccounts []basics.Address `codec:"partupdabs,allocbound=config.MaxProposedAbsentOnlineAccounts"` + AbsentParticipationAccounts []basics.Address `codec:"partupdabs,allocbound=config.MaxMarkAbsent"` } // RewardsState represents the global parameters controlling the rate @@ -512,14 +512,14 @@ var bonusPlans = []bonusPlan{ // amount would be set at baseRound or at upgrade time. } -// nextBonus determines the bonus that should be paid out for proposing the next block. -func nextBonus(prev BlockHeader, params *config.ConsensusParams) basics.MicroAlgos { +// NextBonus determines the bonus that should be paid out for proposing the next block. +func NextBonus(prev BlockHeader, params *config.ConsensusParams) basics.MicroAlgos { current := prev.Round + 1 prevParams := config.Consensus[prev.CurrentProtocol] // presence ensured by ProcessUpgradeParams - return computeBonus(current, prev.Bonus, bonusPlans[params.BonusPlan], bonusPlans[prevParams.BonusPlan]) + return computeBonus(current, prev.Bonus, bonusPlans[params.BonusPlanVer], bonusPlans[prevParams.BonusPlanVer]) } -// computeBonus is the guts of nextBonus that can be unit tested more effectively. +// computeBonus is the guts of NextBonus that can be unit tested more effectively. func computeBonus(current basics.Round, prevBonus basics.MicroAlgos, curPlan bonusPlan, prevPlan bonusPlan) basics.MicroAlgos { // Set the amount if it's non-zero... if !curPlan.baseAmount.IsZero() { @@ -532,10 +532,8 @@ func computeBonus(current basics.Round, prevBonus basics.MicroAlgos, curPlan bon if curPlan.decayInterval != 0 && current%curPlan.decayInterval == 0 { // decay - if newBonus, o := basics.Muldiv(prevBonus.Raw, 99, 100); !o { - return basics.MicroAlgos{Raw: newBonus} - } - logging.Base().Panicf("MakeBlock: error decaying bonus: %d", prevBonus) + keep, _ := basics.NewPercent(99).DivvyAlgos(prevBonus) + return keep } return prevBonus } @@ -561,18 +559,18 @@ func MakeBlock(prev BlockHeader) Block { } } - bonus := nextBonus(prev, ¶ms) + bonus := NextBonus(prev, ¶ms) // the merkle root of TXs will update when fillpayset is called blk := Block{ BlockHeader: BlockHeader{ Round: prev.Round + 1, Branch: prev.Hash(), - UpgradeVote: upgradeVote, - UpgradeState: upgradeState, TimeStamp: timestamp, GenesisID: prev.GenesisID, GenesisHash: prev.GenesisHash, + UpgradeVote: upgradeVote, + UpgradeState: upgradeState, Bonus: bonus, }, } @@ -690,7 +688,7 @@ func (bh BlockHeader) PreCheck(prev BlockHeader) error { } // check bonus - expectedBonus := nextBonus(prev, ¶ms) + expectedBonus := NextBonus(prev, ¶ms) if bh.Bonus != expectedBonus { return fmt.Errorf("bad bonus: %d != %d ", bh.Bonus, expectedBonus) } diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index c01ebbe5e5..aa6f6f0463 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -51,12 +51,12 @@ func init() { } params1.MinUpgradeWaitRounds = 0 params1.MaxUpgradeWaitRounds = 0 - params1.BonusPlan = 0 + params1.BonusPlanVer = 0 config.Consensus[proto1] = params1 params2 := config.Consensus[protocol.ConsensusCurrentVersion] params2.ApprovedUpgrades = map[protocol.ConsensusVersion]uint64{} - params2.BonusPlan = 1 + params2.BonusPlanVer = 1 config.Consensus[proto2] = params2 paramsDelay := config.Consensus[protocol.ConsensusCurrentVersion] diff --git a/data/bookkeeping/msgp_gen.go b/data/bookkeeping/msgp_gen.go index 3a81f30e8b..ec6d772bd9 100644 --- a/data/bookkeeping/msgp_gen.go +++ b/data/bookkeeping/msgp_gen.go @@ -766,8 +766,8 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } - if zb0012 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0012 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } @@ -1052,8 +1052,8 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b err = msgp.WrapError(err, "AbsentParticipationAccounts") return } - if zb0019 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0019), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0019 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0019), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "AbsentParticipationAccounts") return } @@ -1138,7 +1138,7 @@ func BlockMaxSize() (s int) { s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) s += 11 // Calculating size of slice: z.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts - s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) + s += msgp.ArrayHeaderSize + ((config.MaxMarkAbsent) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.Payset s += config.MaxTxnBytesPerBlock @@ -1799,8 +1799,8 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } - if zb0012 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0012 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } @@ -2077,8 +2077,8 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "AbsentParticipationAccounts") return } - if zb0019 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0019), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0019 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0019), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "AbsentParticipationAccounts") return } @@ -2156,7 +2156,7 @@ func BlockHeaderMaxSize() (s int) { s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) s += 11 // Calculating size of slice: z.ParticipationUpdates.AbsentParticipationAccounts - s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) + s += msgp.ArrayHeaderSize + ((config.MaxMarkAbsent) * (basics.AddressMaxSize())) return } @@ -3273,8 +3273,8 @@ func (z *ParticipationUpdates) UnmarshalMsgWithState(bts []byte, st msgp.Unmarsh err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } - if zb0007 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0007), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0007 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0007), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "struct-from-array", "AbsentParticipationAccounts") return } @@ -3351,8 +3351,8 @@ func (z *ParticipationUpdates) UnmarshalMsgWithState(bts []byte, st msgp.Unmarsh err = msgp.WrapError(err, "AbsentParticipationAccounts") return } - if zb0011 > config.MaxProposedAbsentOnlineAccounts { - err = msgp.ErrOverflow(uint64(zb0011), uint64(config.MaxProposedAbsentOnlineAccounts)) + if zb0011 > config.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0011), uint64(config.MaxMarkAbsent)) err = msgp.WrapError(err, "AbsentParticipationAccounts") return } @@ -3416,7 +3416,7 @@ func ParticipationUpdatesMaxSize() (s int) { s += msgp.ArrayHeaderSize + ((config.MaxProposedExpiredOnlineAccounts) * (basics.AddressMaxSize())) s += 11 // Calculating size of slice: z.AbsentParticipationAccounts - s += msgp.ArrayHeaderSize + ((config.MaxProposedAbsentOnlineAccounts) * (basics.AddressMaxSize())) + s += msgp.ArrayHeaderSize + ((config.MaxMarkAbsent) * (basics.AddressMaxSize())) return } diff --git a/data/datatest/impls.go b/data/datatest/impls.go index 6812c73ae8..06495e007e 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -64,7 +64,7 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated return validatedBlock{blk: &b}, nil } -// WithSeed implements the agreement.ValidatedBlock interface. +// WithProposal implements the agreement.ValidatedBlock interface. func (ve validatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { newblock := *ve.blk newblock.BlockHeader.Seed = s diff --git a/data/transactions/payment.go b/data/transactions/payment.go index d3c1432e7c..464028be35 100644 --- a/data/transactions/payment.go +++ b/data/transactions/payment.go @@ -44,7 +44,7 @@ func (payment PaymentTxnFields) checkSpender(header Header, spec SpecialAddresse // the FeeSink account may only spend to the IncentivePool (not at all, if EnableMining) if header.Sender == spec.FeeSink { - if proto.EnableMining { + if proto.Mining().Enabled { return fmt.Errorf("cannot spend from fee sink address %v", header.Sender) } if payment.Receiver != spec.RewardsPool { diff --git a/ledger/apply/keyreg.go b/ledger/apply/keyreg.go index f54d146ba4..d892c40bc8 100644 --- a/ledger/apply/keyreg.go +++ b/ledger/apply/keyreg.go @@ -83,7 +83,7 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal record.VoteFirstValid = keyreg.VoteFirst record.VoteLastValid = keyreg.VoteLast record.VoteKeyDilution = keyreg.VoteKeyDilution - if header.Fee.GTE(incentiveFeeForEligibility) && params.EnableMining { + if header.Fee.Raw >= params.Mining().GoOnlineFee && params.Mining().Enabled { record.IncentiveEligible = true } } @@ -96,9 +96,3 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal return nil } - -// incentiveFeeForEligibility imparts a small cost on moving from offline to -// online. This will impose a cost to running unreliable nodes that get -// suspended and then come back online. Becomes a consensus param if ever -// changed. -var incentiveFeeForEligibility = basics.Algos(2) diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index 05d2a8b033..c4d2a94812 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -90,10 +90,10 @@ func TestPayAction(t *testing.T) { vb := dl.endBlock(proposer) // First MiningPct > 0 if ver >= 40 { - require.True(t, dl.generator.GenesisProto().EnableMining) + require.True(t, dl.generator.GenesisProto().Mining().Enabled) require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) } else { - require.False(t, dl.generator.GenesisProto().EnableMining) + require.False(t, dl.generator.GenesisProto().Mining().Enabled) require.Zero(t, vb.Block().FeesCollected) } diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 97ddbe2e06..9f379729da 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -20,6 +20,7 @@ import ( "context" "errors" "fmt" + "math" "sync" "github.com/algorand/go-algorand/config" @@ -795,7 +796,7 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts } if eligible { - incentive, _ := basics.NewPercent(proto.MiningPercent).DivvyAlgos(prevHeader.FeesCollected) + incentive, _ := basics.NewPercent(proto.Mining().Percent).DivvyAlgos(prevHeader.FeesCollected) total, o := basics.OAddA(incentive, prevHeader.Bonus) if o { return nil, fmt.Errorf("overflowed adding bonus incentive %d %d", incentive, prevHeader.Bonus) @@ -816,7 +817,7 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts } // Note their proposal if MiningEnabled. - if proto.EnableMining { + if proto.Mining().Enabled { prp, err := eval.state.Get(prevHeader.Proposer, false) if err != nil { return nil, err @@ -839,39 +840,16 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return eval, nil } -var ( - // these would become ConsensusParameters if we ever wanted to change them - - // incentiveMinBalance is the minimum balance an account must have to be - // eligible for incentives. It serves a couple purposes. First, it sets a - // manageable upper bound on the total number of incentivized participating - // accounts. This means it is possible to track these accounts in memory, - // which is required to ensure absenteeism checking time is bounded. Second, - // it ensures that smaller accounts continue to operate for the same - // motivations they had before block incentives were introduced. Without - // that assurance, it is difficult to model their behaviour - might many - // participants join for the hope of easy financial rewards, but without - // caring enough to run a high-quality node? - incentiveMinBalance = basics.Algos(100_000) - - // incentiveMaxBalance is the maximum balance an account might have to be - // eligible for incentives. It encourages large accounts to split their - // stake to add resilience to consensus in the case of outages. Of course, - // nothing in protocol can prevent such accounts from running nodes that - // share fate (same machine, same data center, etc), but this serves as a - // gentle reminder. - incentiveMaxBalance = basics.Algos(100_000_000) -) - func (eval *BlockEvaluator) eligibleForIncentives(proposer basics.Address) (bool, error) { proposerState, err := eval.state.Get(proposer, true) if err != nil { return false, err } - if proposerState.MicroAlgos.LessThan(incentiveMinBalance) { + params := eval.state.ConsensusParams() + if proposerState.MicroAlgos.Raw < params.Mining().MinBalance { return false, nil } - if proposerState.MicroAlgos.GreaterThan(incentiveMaxBalance) { + if proposerState.MicroAlgos.Raw > params.Mining().MaxBalance { return false, nil } return proposerState.IncentiveEligible, nil @@ -1369,7 +1347,7 @@ func (eval *BlockEvaluator) endOfBlock() error { eval.block.TxnCounter = 0 } - if eval.proto.EnableMining { + if eval.proto.Mining().Enabled { eval.block.FeesCollected = eval.state.feesCollected } @@ -1422,7 +1400,7 @@ func (eval *BlockEvaluator) endOfBlock() error { } var expectedFeesCollected basics.MicroAlgos - if eval.proto.EnableMining { + if eval.proto.Mining().Enabled { expectedFeesCollected = eval.state.feesCollected } if eval.block.FeesCollected != expectedFeesCollected { @@ -1509,7 +1487,7 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { current := eval.Round() maxExpirations := eval.proto.MaxProposedExpiredOnlineAccounts - maxSuspensions := eval.proto.MaxProposedAbsentOnlineAccounts + maxSuspensions := eval.proto.Mining().MaxMarkAbsent updates := &eval.block.ParticipationUpdates @@ -1603,8 +1581,13 @@ func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, l return false } // See if the account has exceeded 10x their expected observation interval. - allowableLag := basics.Round(10 * totalOnlineStake.Raw / acctStake.Raw) - return lastSeen+allowableLag < current + allowableLag, o := basics.Muldiv(10, totalOnlineStake.Raw, acctStake.Raw) + if o { + // This can't happen with 10B total possible stake, but if we imagine + // another algorand network with huge possible stake, this seems reasonable. + allowableLag = math.MaxInt64 / acctStake.Raw + } + return lastSeen+basics.Round(allowableLag) < current } func (eval *BlockEvaluator) activeChallenge() challenge { @@ -1683,7 +1666,7 @@ func (eval *BlockEvaluator) validateAbsentOnlineAccounts() error { if !eval.validate { return nil } - maxSuspensions := eval.proto.MaxProposedAbsentOnlineAccounts + maxSuspensions := eval.proto.Mining().MaxMarkAbsent suspensionCount := len(eval.block.ParticipationUpdates.AbsentParticipationAccounts) // If the length of the array is strictly greater than our max then we have an error. diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index d02bdddc4c..1073964006 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -277,13 +277,13 @@ func TestMiningFees(t *testing.T) { vb := dl.endBlock(proposer) if ver >= miningBegins { - require.True(t, dl.generator.GenesisProto().EnableMining) // version sanity check - require.NotZero(t, dl.generator.GenesisProto().MiningPercent) // version sanity check + require.True(t, dl.generator.GenesisProto().Mining().Enabled) // version sanity check + require.NotZero(t, dl.generator.GenesisProto().Mining().Percent) // version sanity check // new fields are in the header require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) } else { - require.False(t, dl.generator.GenesisProto().EnableMining) - require.Zero(t, dl.generator.GenesisProto().MiningPercent) // version sanity check + require.False(t, dl.generator.GenesisProto().Mining().Enabled) + require.Zero(t, dl.generator.GenesisProto().Mining().Percent) // version sanity check // new fields are not in the header require.Zero(t, vb.Block().FeesCollected) } diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go index 0305ccc9c0..f1bbfab5d3 100644 --- a/ledger/ledger_test.go +++ b/ledger/ledger_test.go @@ -126,13 +126,14 @@ func makeNewEmptyBlock(t *testing.T, l *Ledger, GenesisID string, initAccounts m a.NoError(err, "could not get incentive pool balance") blk.BlockHeader = bookkeeping.BlockHeader{ - GenesisID: GenesisID, - Round: l.Latest() + 1, - Branch: lastBlock.Hash(), + Round: l.Latest() + 1, + Branch: lastBlock.Hash(), + // Seed: does not matter, TimeStamp: 0, + GenesisID: GenesisID, + Bonus: bookkeeping.NextBonus(lastBlock.BlockHeader, &proto), RewardsState: lastBlock.NextRewardsState(l.Latest()+1, proto, poolBal.MicroAlgos, totalRewardUnits, logging.Base()), UpgradeState: lastBlock.UpgradeState, - // Seed: does not matter, // UpgradeVote: empty, } @@ -173,7 +174,7 @@ func (l *Ledger) appendUnvalidatedSignedTx(t *testing.T, initAccounts map[basics if proto.TxnCounter { blk.TxnCounter = blk.TxnCounter + 1 } - if proto.EnableMining { + if proto.Mining().Enabled { blk.FeesCollected = stx.Txn.Fee } blk.Payset = append(blk.Payset, txib) @@ -244,13 +245,13 @@ func TestLedgerBlockHeaders(t *testing.T) { a.NoError(err, "could not get incentive pool balance") correctHeader := bookkeeping.BlockHeader{ - GenesisID: t.Name(), - Round: l.Latest() + 1, - Branch: lastBlock.Hash(), + Round: l.Latest() + 1, + Branch: lastBlock.Hash(), + // Seed: does not matter, TimeStamp: 0, + GenesisID: t.Name(), RewardsState: lastBlock.NextRewardsState(l.Latest()+1, proto, poolBal.MicroAlgos, totalRewardUnits, logging.Base()), UpgradeState: lastBlock.UpgradeState, - // Seed: does not matter, // UpgradeVote: empty, } @@ -1251,13 +1252,14 @@ func testLedgerSingleTxApplyData(t *testing.T, version protocol.ConsensusVersion a.NoError(err, "could not get last block") correctHeader := bookkeeping.BlockHeader{ - GenesisID: t.Name(), - Round: l.Latest() + 1, - Branch: lastBlock.Hash(), + Round: l.Latest() + 1, + Branch: lastBlock.Hash(), + // Seed: does not matter, TimeStamp: 0, + GenesisID: t.Name(), + Bonus: bookkeeping.NextBonus(lastBlock.BlockHeader, &proto), RewardsState: lastBlock.NextRewardsState(l.Latest()+1, proto, poolBal.MicroAlgos, totalRewardUnits, logging.Base()), UpgradeState: lastBlock.UpgradeState, - // Seed: does not matter, // UpgradeVote: empty, } correctHeader.RewardsPool = testPoolAddr diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index aba447d092..ce34e3d44b 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -40,7 +40,7 @@ func (vb ValidatedBlock) Delta() StateDelta { return vb.delta } -// WithSeed returns a copy of the ValidatedBlock with a modified seed and associated proposer +// WithProposal returns a copy of the ValidatedBlock with a modified seed and associated proposer func (vb ValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address) ValidatedBlock { newblock := vb.blk newblock.BlockHeader.Seed = s diff --git a/node/node.go b/node/node.go index a83f292589..32500077fb 100644 --- a/node/node.go +++ b/node/node.go @@ -1290,7 +1290,7 @@ type validatedBlock struct { vb *ledgercore.ValidatedBlock } -// WithSeed satisfies the agreement.ValidatedBlock interface. +// WithProposal satisfies the agreement.ValidatedBlock interface. func (vb validatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { lvb := vb.vb.WithProposal(s, proposer) return validatedBlock{vb: &lvb} From 762a77acdb63ed33f84934cd9843df57c3aaaf6a Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 7 Feb 2024 11:46:59 -0500 Subject: [PATCH 036/117] Missed a change --- ledger/apply/keyreg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ledger/apply/keyreg.go b/ledger/apply/keyreg.go index d892c40bc8..b5cb1380bd 100644 --- a/ledger/apply/keyreg.go +++ b/ledger/apply/keyreg.go @@ -77,7 +77,7 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal } } record.Status = basics.Online - if params.EnableAbsenteeTracking() { + if params.Mining().Enabled { record.LastHeartbeat = header.FirstValid } record.VoteFirstValid = keyreg.VoteFirst From a05e317dfbc1a705d7d2a805516d7b5e53c8ab59 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 7 Feb 2024 11:51:06 -0500 Subject: [PATCH 037/117] Better comment about overflow --- ledger/eval/cow.go | 2 +- ledger/eval/eval.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ledger/eval/cow.go b/ledger/eval/cow.go index 1e8f3c1a50..23c415bbdf 100644 --- a/ledger/eval/cow.go +++ b/ledger/eval/cow.go @@ -301,7 +301,7 @@ func (cb *roundCowState) commitToParent() { cb.commitParent.mods.Txids[txid] = ledgercore.IncludedTransactions{LastValid: incTxn.LastValid, Intra: commitParentBaseIdx + incTxn.Intra} } cb.commitParent.txnCount += cb.txnCount - // no overflow because of 10B algo cap + // no overflow because max supply is uint64, can't exceed that in fees paid cb.commitParent.feesCollected, _ = basics.OAddA(cb.commitParent.feesCollected, cb.feesCollected) for txl, expires := range cb.mods.Txleases { diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 9f379729da..833c350da3 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1235,7 +1235,7 @@ func (cs *roundCowState) takeFee(tx *transactions.Transaction, senderRewards *ba if tx.Sender == ep.Specials.FeeSink { return nil } - // overflow impossible, since these sum the fees actually paid. 10B algo limit + // overflow impossible, since these sum the fees actually paid and max supply is uint64 cs.feesCollected, _ = basics.OAddA(cs.feesCollected, tx.Fee) return nil From c0cfdefaaa8df580e7e35a60c4797b8b2dda7fc2 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 7 Feb 2024 12:17:42 -0500 Subject: [PATCH 038/117] Lint related comments --- config/consensus.go | 9 ++++++++- data/bookkeeping/block.go | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/config/consensus.go b/config/consensus.go index 3f8245527a..124cc40712 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -530,7 +530,7 @@ type ConsensusParams struct { // dynamic filter, it will be calculated and logged (but not used). DynamicFilterTimeout bool - // MiningRules is the version of mining related rules. It excludes anything + // MiningRulesVer is the version of mining related rules. It excludes anything // related to block "bonuses" - extra payments made beyond what fees could // provide. 0 disables mining and related tracking MiningRulesVer uint8 @@ -539,6 +539,8 @@ type ConsensusParams struct { BonusPlanVer uint8 } +// MiningRules puts several related consensus parameters in one place. The same +// care for backward compatibility with old blocks must be taken. type MiningRules struct { // Enabled turns on several things needed for paying block incentives, // including tracking of the proposer and fees collected. @@ -574,6 +576,8 @@ type MiningRules struct { MaxMarkAbsent int } +// miningRules should be extended, never changed, since old blocks must retain +// their behavior. var miningRules = [...]MiningRules{ {Enabled: false}, { @@ -586,6 +590,9 @@ var miningRules = [...]MiningRules{ }, } +// Mining() returns the MiningRules of the ConsensusParams. These are the +// consensus params related to tracking proposers and paying a portion of fees +// to eligible recipients. func (cp ConsensusParams) Mining() MiningRules { return miningRules[cp.MiningRulesVer] } diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 792f1b9384..dafed90d5c 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -494,6 +494,8 @@ type bonusPlan struct { decayInterval basics.Round } +// bonusPlans should be extended, never changed, since old blocks must retain +// their behavior. var bonusPlans = []bonusPlan{ 0: {}, 1: { From c26ca5be0867d003fe4a3503ba8eefc1c8d716c3 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 8 Feb 2024 13:46:34 -0500 Subject: [PATCH 039/117] ok reviewdog, you win --- config/consensus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/consensus.go b/config/consensus.go index 124cc40712..cc3a15dbda 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -590,7 +590,7 @@ var miningRules = [...]MiningRules{ }, } -// Mining() returns the MiningRules of the ConsensusParams. These are the +// Mining returns the MiningRules of the ConsensusParams. These are the // consensus params related to tracking proposers and paying a portion of fees // to eligible recipients. func (cp ConsensusParams) Mining() MiningRules { From 0ed15ec8809036554fa099f165ec74c69644e66f Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 9 Feb 2024 14:07:53 -0500 Subject: [PATCH 040/117] CR changes Moved challenge constant into mining rules. Don't let challenges cross upgrades. Better unit testing of small eval.go functions. --- config/config_test.go | 12 +++ config/consensus.go | 29 ++++-- data/bookkeeping/block_test.go | 10 +++ ledger/eval/eval.go | 62 +++++++------ ledger/eval/eval_test.go | 88 +++++++++++++++++++ .../upgrades/application_support_test.go | 2 +- 6 files changed, 167 insertions(+), 36 deletions(-) diff --git a/config/config_test.go b/config/config_test.go index 432c0f9281..3e5ae33f8e 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -996,3 +996,15 @@ func TestTracksCatchpointsWithoutStoring(t *testing.T) { require.Equal(t, true, cfg.TracksCatchpoints()) require.Equal(t, false, cfg.StoresCatchpoints()) } + +func TestAllProtocolMiningVersions(t *testing.T) { + // in reality, this test will never even get a chance to run if it would + // fail, since checkSetMax will panic. + partitiontest.PartitionTest(t) + t.Parallel() + a := assert.New(t) + + for _, p := range Consensus { + a.Less(int(p.MiningRulesVer), len(miningRules)) + } +} diff --git a/config/consensus.go b/config/consensus.go index cc3a15dbda..6d72e9185d 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -574,6 +574,19 @@ type MiningRules struct { // can suspend for not proposing "lately" (In 10x expected interval, or // within a grace period from being challenged) MaxMarkAbsent int + + // Challenges occur once every challengeInterval rounds. + ChallengeInterval uint64 + // Suspensions happen between 1 and 2 grace periods after a challenge. Must + // be less than half MaxTxnLife to ensure the Block header will be cached + // and less than half ChallengeInterval to overlapping challenges. A larger + // grace period means larger stake nodes will probably propose before they + // need to consider an active heartbeat. + ChallengeGracePeriod uint64 + // An account is challenged if the first challengeBits match the start of + // the account address. An online account will be challenged about once + // every interval*2^bits rounds. + ChallengeBits int } // miningRules should be extended, never changed, since old blocks must retain @@ -581,12 +594,16 @@ type MiningRules struct { var miningRules = [...]MiningRules{ {Enabled: false}, { - Enabled: true, - Percent: 75, - GoOnlineFee: 2_000_000, // 2 algos - MinBalance: 100_000_000_000, // 100,000 algos - MaxBalance: 100_000_000_000_000, // 100M algos - MaxMarkAbsent: 32, + Enabled: true, + Percent: 75, + GoOnlineFee: 2_000_000, // 2 algos + MinBalance: 10_000_000_000, // 10,000 algos + MaxBalance: 50_000_000_000_000, // 50M algos + MaxMarkAbsent: 32, + ChallengeInterval: 1000, + ChallengeGracePeriod: 200, + ChallengeBits: 5, + // With about 31k rounds per day, we expect about (31k/interval)/2^bits ~= 1 challenge / day / account) }, } diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index aa6f6f0463..e9e1ec0247 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -1017,3 +1017,13 @@ func TestFirstYearBonus(t *testing.T) { a.InDelta(0.90, float64(bonus)/float64(bonusPlans[1].baseAmount.Raw), 0.01) } + +func TestAllProtocolBonusPlans(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + a := assert.New(t) + + for _, p := range config.Consensus { + a.Less(int(p.BonusPlanVer), len(bonusPlans)) + } +} diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 833c350da3..949ad87083 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1460,19 +1460,12 @@ func (eval *BlockEvaluator) endOfBlock() error { return nil } -const ( - // Challenges occur once every challengeInterval rounds - challengeInterval = basics.Round(1000) - // Suspensions can between 1 and 2 grace periods after a challenge - challengeGracePeriod = basics.Round(200) - // An account is challenged if the first challengeBits match the start of the account address. - challengeBits = 5 -) - type challenge struct { + // round is the round the challenge occured in. 0 indicates this is not a challenge. round basics.Round - seed committee.Seed - bits int + // accounts that match the first `bits` of `seed` must propose or heartbeat to stay online + seed committee.Seed + bits int } // generateKnockOfflineAccountsList creates the lists of expired or absent @@ -1491,7 +1484,7 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { updates := &eval.block.ParticipationUpdates - ch := eval.activeChallenge() + ch := activeChallenge(&eval.proto, uint64(eval.Round()), eval.state) for _, accountAddr := range eval.state.modifiedAccounts() { acctDelta, found := eval.state.mods.Accts.GetData(accountAddr) @@ -1576,7 +1569,7 @@ func bitsMatch(a, b []byte, n int) bool { func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, lastSeen basics.Round, current basics.Round) bool { // Don't consider accounts that were online when mining went into effect as // absent. They get noticed the next time they propose or keyreg, which - // ought to be soon, if they want to earn incentives. + // ought to be soon, if they are high stake or want to earn incentives. if lastSeen == 0 { return false } @@ -1590,22 +1583,33 @@ func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, l return lastSeen+basics.Round(allowableLag) < current } -func (eval *BlockEvaluator) activeChallenge() challenge { - current := eval.Round() - var round basics.Round - if current > challengeInterval { - lastChallenge := current - (current % challengeInterval) - // challengeRound is in effect if we're after one grace period, but before the 2nd ends. - if current > lastChallenge+challengeGracePeriod && current < lastChallenge+2*challengeGracePeriod { - round = lastChallenge - challengeHdr, err := eval.state.BlockHdr(round) - if err != nil { - panic(err) - } - return challenge{round, challengeHdr.Seed, challengeBits} - } +type headerSource interface { + BlockHdr(round basics.Round) (bookkeeping.BlockHeader, error) +} + +func activeChallenge(proto *config.ConsensusParams, current uint64, headers headerSource) challenge { + rules := proto.Mining() + // are challenges active? + if rules.ChallengeInterval == 0 || current < rules.ChallengeInterval { + return challenge{} + } + lastChallenge := current - (current % rules.ChallengeInterval) + // challenge is in effect if we're after one grace period, but before the 2nd ends. + if current <= lastChallenge+rules.ChallengeGracePeriod || + current > lastChallenge+2*rules.ChallengeGracePeriod { + return challenge{} + } + round := basics.Round(lastChallenge) + challengeHdr, err := headers.BlockHdr(round) + if err != nil { + panic(err) + } + challengeProto := config.Consensus[challengeHdr.CurrentProtocol] + // challenge is not considered if rules have changed since that round + if challengeProto.Mining() != rules { + return challenge{} } - return challenge{} + return challenge{round, challengeHdr.Seed, rules.ChallengeBits} } func failsChallenge(ch challenge, address basics.Address, lastSeen basics.Round) bool { @@ -1679,7 +1683,7 @@ func (eval *BlockEvaluator) validateAbsentOnlineAccounts() error { // For consistency with expired account handling, we preclude duplicates addressSet := make(map[basics.Address]bool, suspensionCount) - ch := eval.activeChallenge() + ch := activeChallenge(&eval.proto, uint64(eval.Round()), eval.state) for _, accountAddr := range eval.block.ParticipationUpdates.AbsentParticipationAccounts { if _, exists := addressSet[accountAddr]; exists { diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 76cb3776cd..8a4d56bfdb 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -1484,3 +1484,91 @@ func TestBitsMatch(t *testing.T) { } require.False(t, bitsMatch([]byte{0x1, 0xff, 0xaa}, []byte{0x1, 0xf0}, 13)) } + +func TestIsAbsent(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + a := assert.New(t) + + var absent = func(total uint64, acct uint64, last uint64, current uint64) bool { + return isAbsent(basics.Algos(total), basics.Algos(acct), basics.Round(last), basics.Round(current)) + } + // 1% of stake, absent for 1000 rounds + a.False(absent(1000, 10, 5000, 6000)) + a.True(absent(1000, 10, 5000, 6001)) // longer + a.True(absent(1000, 11, 5000, 6001)) // more acct stake + a.False(absent(1000, 9, 5000, 6001)) // less acct stake + a.False(absent(1001, 10, 5000, 6001)) // more online stake + // not absent if never seen + a.False(absent(1000, 10, 0, 6000)) + a.False(absent(1000, 10, 0, 6001)) +} + +func TestFailsChallenge(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + a := assert.New(t) + + // a valid challenge, with 4 matching bits, and an old last seen + a.True(failsChallenge(challenge{round: 11, seed: [32]byte{0xb0, 0xb4}, bits: 4}, basics.Address{0xbf, 0x34}, 10)) + + // challenge isn't "on" + a.False(failsChallenge(challenge{round: 0, seed: [32]byte{0xb0, 0xb4}, bits: 4}, basics.Address{0xbf, 0x34}, 10)) + // node has appeared more recently + a.False(failsChallenge(challenge{round: 11, seed: [32]byte{0xb0, 0xb4}, bits: 4}, basics.Address{0xbf, 0x34}, 12)) + // bits don't match + a.False(failsChallenge(challenge{round: 11, seed: [32]byte{0xb0, 0xb4}, bits: 4}, basics.Address{0xcf, 0x34}, 10)) + // no enough bits match + a.False(failsChallenge(challenge{round: 11, seed: [32]byte{0xb0, 0xb4}, bits: 5}, basics.Address{0xbf, 0x34}, 10)) +} + +type singleSource bookkeeping.BlockHeader + +func (ss singleSource) BlockHdr(r basics.Round) (bookkeeping.BlockHeader, error) { + return bookkeeping.BlockHeader(ss), nil +} + +func TestActiveChallenge(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + a := assert.New(t) + + nowHeader := bookkeeping.BlockHeader{ + UpgradeState: bookkeeping.UpgradeState{ + // Here the rules are on, so they certainly differ from rules in oldHeader's params + CurrentProtocol: protocol.ConsensusFuture, + }, + } + now := config.Consensus[nowHeader.CurrentProtocol] + + // simplest test. when interval=X and grace=G, X+G+1 is a challenge + inChallenge := now.Mining().ChallengeInterval + now.Mining().ChallengeGracePeriod + 1 + ch := activeChallenge(&now, inChallenge, singleSource(nowHeader)) + a.NotZero(ch.round) + + // all rounds before that have no challenge + for r := uint64(1); r < inChallenge; r++ { + ch := activeChallenge(&now, r, singleSource(nowHeader)) + a.Zero(ch.round, r) + } + + // ChallengeGracePeriod rounds allow challenges starting with inChallenge + for r := inChallenge; r < inChallenge+now.Mining().ChallengeGracePeriod; r++ { + ch := activeChallenge(&now, r, singleSource(nowHeader)) + a.EqualValues(ch.round, now.Mining().ChallengeInterval) + } + + // And the next round is again challenge-less + ch = activeChallenge(&now, inChallenge+now.Mining().ChallengeGracePeriod, singleSource(nowHeader)) + a.Zero(ch.round) + + // ignore challenge if upgrade happened + oldHeader := bookkeeping.BlockHeader{ + UpgradeState: bookkeeping.UpgradeState{ + // We need a version from before mining rules got turned on + CurrentProtocol: protocol.ConsensusV39, + }, + } + ch = activeChallenge(&now, inChallenge, singleSource(oldHeader)) + a.Zero(ch.round) +} diff --git a/test/e2e-go/upgrades/application_support_test.go b/test/e2e-go/upgrades/application_support_test.go index bd5372244b..549a82c5ab 100644 --- a/test/e2e-go/upgrades/application_support_test.go +++ b/test/e2e-go/upgrades/application_support_test.go @@ -454,7 +454,7 @@ int 1 // Try polling 10 rounds to ensure txn is committed. round, err = client.CurrentRound() a.NoError(err) - isCommitted := fixture.WaitForTxnConfirmation(round+10, creator, txid) + isCommitted := fixture.WaitForTxnConfirmation(round+10, txid) a.True(isCommitted) // check creator's balance record for the app entry and the state changes From 23b12cf9ace9aac253ced5e614ed821f227a066e Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 9 Feb 2024 14:56:31 -0500 Subject: [PATCH 041/117] Some missed function parameter changes --- test/e2e-go/features/multisig/multisig_test.go | 2 +- .../features/participation/onlineOfflineParticipation_test.go | 4 ++-- .../features/participation/participationExpiration_test.go | 4 ++-- .../features/participation/participationRewards_test.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/e2e-go/features/multisig/multisig_test.go b/test/e2e-go/features/multisig/multisig_test.go index 6d7b1c616d..6264b70161 100644 --- a/test/e2e-go/features/multisig/multisig_test.go +++ b/test/e2e-go/features/multisig/multisig_test.go @@ -91,7 +91,7 @@ func TestBasicMultisig(t *testing.T) { txid, err := client.BroadcastTransaction(signedTransactionWithTwo) r.NoError(err, "Trying to broadcast 2-of-3 multisig with 2 sig should not cause error") curStatus, _ = client.Status() - r.True(fixture.WaitForTxnConfirmation(curStatus.LastRound+uint64(5), multisigAddr, txid)) + r.True(fixture.WaitForTxnConfirmation(curStatus.LastRound+uint64(5), txid)) // Need a new txid to avoid dup detection unsignedTransaction, err = client.ConstructPayment(multisigAddr, addrs[0], minTxnFee, amountToSend, []byte("foobar"), "", [32]byte{}, 0, 0) diff --git a/test/e2e-go/features/participation/onlineOfflineParticipation_test.go b/test/e2e-go/features/participation/onlineOfflineParticipation_test.go index 38f2e4bc35..f7cf67f77f 100644 --- a/test/e2e-go/features/participation/onlineOfflineParticipation_test.go +++ b/test/e2e-go/features/participation/onlineOfflineParticipation_test.go @@ -196,7 +196,7 @@ func TestNewAccountCanGoOnlineAndParticipate(t *testing.T) { fixture.AssertValidTxid(onlineTxID) maxRoundsToWaitForTxnConfirm := uint64(5) - fixture.WaitForTxnConfirmation(seededRound+maxRoundsToWaitForTxnConfirm, newAccount, onlineTxID) + fixture.WaitForTxnConfirmation(seededRound+maxRoundsToWaitForTxnConfirm, onlineTxID) nodeStatus, _ = client.Status() onlineRound := nodeStatus.LastRound newAccountStatus, err := client.AccountInformation(newAccount, false) @@ -311,7 +311,7 @@ func TestAccountGoesOnlineForShortPeriod(t *testing.T) { nodeStatus, err := client.Status() a.NoError(err) seededRound := nodeStatus.LastRound - fixture.WaitForTxnConfirmation(seededRound+maxRoundsToWaitForTxnConfirm, newAccount, onlineTxID) + fixture.WaitForTxnConfirmation(seededRound+maxRoundsToWaitForTxnConfirm, onlineTxID) nodeStatus, _ = client.Status() accountStatus, err := client.AccountInformation(newAccount, false) diff --git a/test/e2e-go/features/participation/participationExpiration_test.go b/test/e2e-go/features/participation/participationExpiration_test.go index 5cb4f941dc..e0569cf040 100644 --- a/test/e2e-go/features/participation/participationExpiration_test.go +++ b/test/e2e-go/features/participation/participationExpiration_test.go @@ -111,7 +111,7 @@ func testExpirationAccounts(t *testing.T, fixture *fixtures.RestClientFixture, f a.NoError(err) seededRound := sNodeStatus.LastRound - txnConfirmed := fixture.WaitForTxnConfirmation(seededRound+maxRoundsToWaitForTxnConfirm, sAccount, onlineTxID) + txnConfirmed := fixture.WaitForTxnConfirmation(seededRound+maxRoundsToWaitForTxnConfirm, onlineTxID) a.True(txnConfirmed) newAccountStatus, err = pClient.AccountInformation(sAccount, false) @@ -156,7 +156,7 @@ func testExpirationAccounts(t *testing.T, fixture *fixtures.RestClientFixture, f sendMoneyTxn := fixture.SendMoneyAndWait(latestRound, amountToSendInitial, transactionFee, richAccount, sAccount, "") - txnConfirmed = fixture.WaitForTxnConfirmation(latestRound+maxRoundsToWaitForTxnConfirm, sAccount, sendMoneyTxn.Txn.ID().String()) + txnConfirmed = fixture.WaitForTxnConfirmation(latestRound+maxRoundsToWaitForTxnConfirm, sendMoneyTxn.Txn.ID().String()) a.True(txnConfirmed) newAccountStatus, err = pClient.AccountInformation(sAccount, false) diff --git a/test/e2e-go/features/participation/participationRewards_test.go b/test/e2e-go/features/participation/participationRewards_test.go index 8c0fb64a8d..043f58f3c7 100644 --- a/test/e2e-go/features/participation/participationRewards_test.go +++ b/test/e2e-go/features/participation/participationRewards_test.go @@ -181,7 +181,7 @@ func TestPartkeyOnlyRewards(t *testing.T) { // do a balance poke by moving funds b/w accounts. this will cause balances to reflect received rewards tx, err := fixture.LibGoalClient.SendPaymentFromUnencryptedWallet(richAccount.Address, account.String(), minFee, minBalance, nil) r.NoError(err) - fixture.WaitForTxnConfirmation(arbitraryPostGenesisRound+uint64(10), tx.ID().String(), richAccount.Address) + fixture.WaitForTxnConfirmation(arbitraryPostGenesisRound+uint64(10), tx.ID().String()) finalBalance, err := client.GetBalance(account.String()) r.NoError(err) delta := finalBalance - initialBalance From 5743edf0eb49c89a7088a925783539dbc8d5b9a5 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 9 Feb 2024 15:03:56 -0500 Subject: [PATCH 042/117] spelling --- ledger/eval/eval.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 949ad87083..1728904d44 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1461,7 +1461,7 @@ func (eval *BlockEvaluator) endOfBlock() error { } type challenge struct { - // round is the round the challenge occured in. 0 indicates this is not a challenge. + // round is when the challenge occurred. 0 means this is not a challenge. round basics.Round // accounts that match the first `bits` of `seed` must propose or heartbeat to stay online seed committee.Seed From 69c982edeaf958de7ea7d2d22c18237ffacc0c67 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 9 Feb 2024 15:11:36 -0500 Subject: [PATCH 043/117] missing word Co-authored-by: Jason Paulos --- config/consensus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/consensus.go b/config/consensus.go index 6d72e9185d..c49414e0cb 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -579,7 +579,7 @@ type MiningRules struct { ChallengeInterval uint64 // Suspensions happen between 1 and 2 grace periods after a challenge. Must // be less than half MaxTxnLife to ensure the Block header will be cached - // and less than half ChallengeInterval to overlapping challenges. A larger + // and less than half ChallengeInterval to avoid overlapping challenges. A larger // grace period means larger stake nodes will probably propose before they // need to consider an active heartbeat. ChallengeGracePeriod uint64 From 8aa85e9cd5537cf13a10b205b22f6f1e65e4bb83 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 12 Feb 2024 12:25:49 -0500 Subject: [PATCH 044/117] Adjust test for bonus payout --- ledger/apptxn_test.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index c4d2a94812..ab1600e87e 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -22,6 +22,7 @@ import ( "strconv" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/config" @@ -85,11 +86,12 @@ func TestPayAction(t *testing.T) { presink := micros(dl.t, dl.generator, genBalances.FeeSink) preprop := micros(dl.t, dl.generator, proposer) + dl.t.Log("presink", presink, "preprop", preprop) dl.beginBlock() dl.txns(&payout1) vb := dl.endBlock(proposer) - // First MiningPct > 0 - if ver >= 40 { + const miningVer = 40 + if ver >= miningVer { require.True(t, dl.generator.GenesisProto().Mining().Enabled) require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) } else { @@ -107,10 +109,11 @@ func TestPayAction(t *testing.T) { dl.fullBlock() postsink = micros(dl.t, dl.generator, genBalances.FeeSink) postprop = micros(dl.t, dl.generator, proposer) - // First MiningPct > 0 - if ver >= 40 { - require.EqualValues(t, 500, postsink-presink) // based on 75% in config/consensus.go - require.EqualValues(t, 1500, postprop-preprop) + dl.t.Log("postsink", postsink, "postprop", postprop) + if ver >= miningVer { + bonus := 5_000_000 // block.go + assert.EqualValues(t, bonus-500, presink-postsink) // based on 75% in config/consensus.go + require.EqualValues(t, bonus+1500, postprop-preprop) } else { require.EqualValues(t, 2000, postsink-presink) // no mining yet } From 64b2103d95ec7406cf5d5aaabdbef09e846a4823 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 4 Mar 2024 13:27:35 -0500 Subject: [PATCH 045/117] Record the block payout explicitly Add ProposerPayout to the block header to make it very clear how much will be moved from the feesink to the proposer. It is tentatively recorded during block evaluation, but agreement can zero out the payment because it is only in the agreement code that the actual proposer is known - so it can be checked for eligibility. --- agreement/abstractions.go | 5 +- agreement/agreementtest/simulate_test.go | 5 +- agreement/common_test.go | 5 +- agreement/fuzzer/ledger_test.go | 5 +- agreement/msgp_gen.go | 313 +++++++++++------- agreement/proposal.go | 45 ++- agreement/vote.go | 2 +- data/basics/userBalance.go | 2 + data/bookkeeping/block.go | 6 + data/bookkeeping/msgp_gen.go | 194 ++++++----- data/datatest/impls.go | 5 +- ledger/eval/eval.go | 97 +++--- ledger/eval/eval_test.go | 8 + .../prefetcher/prefetcher_alignment_test.go | 38 +-- ledger/eval/prefetcher/prefetcher_test.go | 5 +- ledger/ledgercore/validatedBlock.go | 5 +- ledger/simple_test.go | 2 +- node/node.go | 4 +- 18 files changed, 454 insertions(+), 292 deletions(-) diff --git a/agreement/abstractions.go b/agreement/abstractions.go index 8cb5cf2831..9f3341aaed 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -55,13 +55,14 @@ type BlockValidator interface { // calling EnsureBlock() on the Ledger. type ValidatedBlock interface { // WithProposal creates a copy of this ValidatedBlock with its - // cryptographically random seed and proposer set. Abstractly, it is how the + // cryptographically random seed and proposer set. The block's + // ProposerPayout is zero'd if !eligible. Abstractly, it is how the // agreement code "finishes" a block and makes it a proposal for a specific // account. // // Calls to Seed() or to Digest() on the copy's Block must // reflect the value of the new seed. - WithProposal(committee.Seed, basics.Address) ValidatedBlock + WithProposal(seed committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock // Block returns the underlying block that has been validated. Block() bookkeeping.Block diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index 3c24c2df74..df346098a1 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -79,9 +79,12 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer + if !eligible { + b.Inside.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } return b } diff --git a/agreement/common_test.go b/agreement/common_test.go index 40c031384a..c7e39f132a 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -165,9 +165,12 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address) ValidatedBlock { +func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer + if !eligible { + b.Inside.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } return b } diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go index 53c0f8defe..c184623120 100644 --- a/agreement/fuzzer/ledger_test.go +++ b/agreement/fuzzer/ledger_test.go @@ -93,9 +93,12 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer + if !eligible { + b.Inside.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } return b } diff --git a/agreement/msgp_gen.go b/agreement/msgp_gen.go index 4c32195211..b012f66da2 100644 --- a/agreement/msgp_gen.go +++ b/agreement/msgp_gen.go @@ -4493,8 +4493,8 @@ func PlayerMaxSize() (s int) { func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(33) - var zb0005Mask uint64 /* 42 bits */ + zb0005Len := uint32(34) + var zb0005Mask uint64 /* 43 bits */ if (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40 @@ -4555,77 +4555,81 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { zb0005Len-- zb0005Mask |= 0x100000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000000 } + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + zb0005Len-- + zb0005Mask |= 0x2000000 + } if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x4000000 + zb0005Mask |= 0x8000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x8000000 + zb0005Mask |= 0x10000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x10000000 + zb0005Mask |= 0x20000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x20000000 + zb0005Mask |= 0x40000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x40000000 + zb0005Mask |= 0x80000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { zb0005Len-- - zb0005Mask |= 0x80000000 + zb0005Mask |= 0x100000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { zb0005Len-- - zb0005Mask |= 0x100000000 + zb0005Mask |= 0x200000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { zb0005Len-- - zb0005Mask |= 0x200000000 + zb0005Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x400000000 + zb0005Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x800000000 + zb0005Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x1000000000 + zb0005Mask |= 0x2000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x2000000000 + zb0005Mask |= 0x4000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x4000000000 + zb0005Mask |= 0x8000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { zb0005Len-- - zb0005Mask |= 0x8000000000 + zb0005Mask |= 0x10000000000 } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) @@ -4720,51 +4724,56 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { } } if (zb0005Mask & 0x200000) == 0 { // if not empty + // string "pp" + o = append(o, 0xa2, 0x70, 0x70) + o = (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.MarshalMsg(o) + } + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0005Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0005Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0005Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { @@ -4784,42 +4793,42 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter) } - if (zb0005Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp) } - if (zb0005Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x800000000) == 0 { // if not empty + if (zb0005Mask & 0x1000000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x1000000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o) } - if (zb0005Mask & 0x2000000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x4000000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x8000000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -4949,6 +4958,14 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "ProposerPayout") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -5298,6 +5315,12 @@ func (z *proposal) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o err = msgp.WrapError(err, "Bonus") return } + case "pp": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "ProposerPayout") + return + } case "fees": bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -5527,7 +5550,7 @@ func (_ *proposal) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *proposal) Msgsize() (s int) { - s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking { _ = zb0001 @@ -5549,12 +5572,12 @@ func (z *proposal) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *proposal) MsgIsZero() bool { - return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) + return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func ProposalMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.unauthenticatedProposal.Block.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) @@ -9004,8 +9027,8 @@ func ThresholdEventMaxSize() (s int) { func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(34) - var zb0005Mask uint64 /* 42 bits */ + zb0005Len := uint32(35) + var zb0005Mask uint64 /* 43 bits */ if (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80 @@ -9066,81 +9089,85 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000000 } - if (*z).PriorVote.MsgIsZero() { + if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000000 } - if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).PriorVote.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x4000000 } + if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { + zb0005Len-- + zb0005Mask |= 0x8000000 + } if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x10000000 + zb0005Mask |= 0x20000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x20000000 + zb0005Mask |= 0x40000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x40000000 + zb0005Mask |= 0x80000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x80000000 + zb0005Mask |= 0x100000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x100000000 + zb0005Mask |= 0x200000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { zb0005Len-- - zb0005Mask |= 0x200000000 + zb0005Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { zb0005Len-- - zb0005Mask |= 0x400000000 + zb0005Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { zb0005Len-- - zb0005Mask |= 0x800000000 + zb0005Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x1000000000 + zb0005Mask |= 0x2000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x2000000000 + zb0005Mask |= 0x4000000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x4000000000 + zb0005Mask |= 0x8000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x8000000000 + zb0005Mask |= 0x10000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x10000000000 + zb0005Mask |= 0x20000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { zb0005Len-- - zb0005Mask |= 0x20000000000 + zb0005Mask |= 0x40000000000 } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) @@ -9235,56 +9262,61 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { } } if (zb0005Mask & 0x400000) == 0 { // if not empty + // string "pp" + o = append(o, 0xa2, 0x70, 0x70) + o = (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.MarshalMsg(o) + } + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "pv" o = append(o, 0xa2, 0x70, 0x76) o = (*z).PriorVote.MarshalMsg(o) } - if (zb0005Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).unauthenticatedProposal.SeedProof.MarshalMsg(o) } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0005Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking == nil { @@ -9304,42 +9336,42 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter) } - if (zb0005Mask & 0x800000000) == 0 { // if not empty + if (zb0005Mask & 0x1000000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp) } - if (zb0005Mask & 0x1000000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x2000000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x4000000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).unauthenticatedProposal.Block.Payset.MarshalMsg(o) } - if (zb0005Mask & 0x8000000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x10000000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x20000000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -9469,6 +9501,14 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "ProposerPayout") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -9826,6 +9866,12 @@ func (z *transmittedPayload) UnmarshalMsgWithState(bts []byte, st msgp.Unmarshal err = msgp.WrapError(err, "Bonus") return } + case "pp": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "ProposerPayout") + return + } case "fees": bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -10061,7 +10107,7 @@ func (_ *transmittedPayload) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *transmittedPayload) Msgsize() (s int) { - s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Round.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Branch.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.Seed.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID) + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.Msgsize() + 3 + (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.Msgsize() + 5 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking { _ = zb0001 @@ -10083,12 +10129,12 @@ func (z *transmittedPayload) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *transmittedPayload) MsgIsZero() bool { - return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) && ((*z).PriorVote.MsgIsZero()) + return ((*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "") && ((*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).unauthenticatedProposal.Block.Payset.MsgIsZero()) && ((*z).unauthenticatedProposal.SeedProof.MsgIsZero()) && ((*z).unauthenticatedProposal.OriginalPeriod == 0) && ((*z).unauthenticatedProposal.OriginalProposer.MsgIsZero()) && ((*z).PriorVote.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func TransmittedPayloadMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.unauthenticatedProposal.Block.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) @@ -10812,8 +10858,8 @@ func UnauthenticatedEquivocationVoteMaxSize() (s int) { func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(33) - var zb0005Mask uint64 /* 40 bits */ + zb0005Len := uint32(34) + var zb0005Mask uint64 /* 41 bits */ if (*z).Block.BlockHeader.Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40 @@ -10874,77 +10920,81 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { zb0005Len-- zb0005Mask |= 0x100000 } - if (*z).Block.BlockHeader.Branch.MsgIsZero() { + if (*z).Block.BlockHeader.ProposerPayout.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).Block.BlockHeader.Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).Block.BlockHeader.Proposer.MsgIsZero() { + if (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } - if (*z).Block.BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).Block.BlockHeader.Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000000 } + if (*z).Block.BlockHeader.RewardsState.RewardsRate == 0 { + zb0005Len-- + zb0005Mask |= 0x2000000 + } if (*z).Block.BlockHeader.Round.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x4000000 + zb0005Mask |= 0x8000000 } if (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x8000000 + zb0005Mask |= 0x10000000 } if (*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x10000000 + zb0005Mask |= 0x20000000 } if (*z).SeedProof.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x20000000 + zb0005Mask |= 0x40000000 } if (*z).Block.BlockHeader.Seed.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x40000000 + zb0005Mask |= 0x80000000 } if len((*z).Block.BlockHeader.StateProofTracking) == 0 { zb0005Len-- - zb0005Mask |= 0x80000000 + zb0005Mask |= 0x100000000 } if (*z).Block.BlockHeader.TxnCounter == 0 { zb0005Len-- - zb0005Mask |= 0x100000000 + zb0005Mask |= 0x200000000 } if (*z).Block.BlockHeader.TimeStamp == 0 { zb0005Len-- - zb0005Mask |= 0x200000000 + zb0005Mask |= 0x400000000 } if (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x400000000 + zb0005Mask |= 0x800000000 } if (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x800000000 + zb0005Mask |= 0x1000000000 } if (*z).Block.Payset.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x1000000000 + zb0005Mask |= 0x2000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x2000000000 + zb0005Mask |= 0x4000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- - zb0005Mask |= 0x4000000000 + zb0005Mask |= 0x8000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false { zb0005Len-- - zb0005Mask |= 0x8000000000 + zb0005Mask |= 0x10000000000 } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) @@ -11039,51 +11089,56 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { } } if (zb0005Mask & 0x200000) == 0 { // if not empty + // string "pp" + o = append(o, 0xa2, 0x70, 0x70) + o = (*z).Block.BlockHeader.ProposerPayout.MarshalMsg(o) + } + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).Block.BlockHeader.Branch.MarshalMsg(o) } - if (zb0005Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).Block.BlockHeader.Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.RewardsState.RewardsRate) } - if (zb0005Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).Block.BlockHeader.Round.MarshalMsg(o) } - if (zb0005Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).Block.BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "sdpf" o = append(o, 0xa4, 0x73, 0x64, 0x70, 0x66) o = (*z).SeedProof.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).Block.BlockHeader.Seed.MarshalMsg(o) } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).Block.BlockHeader.StateProofTracking == nil { @@ -11103,42 +11158,42 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).Block.BlockHeader.TxnCounter) } - if (zb0005Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).Block.BlockHeader.TimeStamp) } - if (zb0005Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x800000000) == 0 { // if not empty + if (zb0005Mask & 0x1000000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x1000000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).Block.Payset.MarshalMsg(o) } - if (zb0005Mask & 0x2000000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x4000000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x8000000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove) @@ -11268,6 +11323,14 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).Block.BlockHeader.ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "ProposerPayout") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -11617,6 +11680,12 @@ func (z *unauthenticatedProposal) UnmarshalMsgWithState(bts []byte, st msgp.Unma err = msgp.WrapError(err, "Bonus") return } + case "pp": + bts, err = (*z).Block.BlockHeader.ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "ProposerPayout") + return + } case "fees": bts, err = (*z).Block.BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -11846,7 +11915,7 @@ func (_ *unauthenticatedProposal) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *unauthenticatedProposal) Msgsize() (s int) { - s = 3 + 4 + (*z).Block.BlockHeader.Round.Msgsize() + 5 + (*z).Block.BlockHeader.Branch.Msgsize() + 5 + (*z).Block.BlockHeader.Seed.Msgsize() + 4 + (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).Block.BlockHeader.GenesisID) + 3 + (*z).Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).Block.BlockHeader.FeesCollected.Msgsize() + 3 + (*z).Block.BlockHeader.Bonus.Msgsize() + 5 + (*z).Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).Block.BlockHeader.Round.Msgsize() + 5 + (*z).Block.BlockHeader.Branch.Msgsize() + 5 + (*z).Block.BlockHeader.Seed.Msgsize() + 4 + (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).Block.BlockHeader.GenesisID) + 3 + (*z).Block.BlockHeader.GenesisHash.Msgsize() + 4 + (*z).Block.BlockHeader.Proposer.Msgsize() + 3 + (*z).Block.BlockHeader.FeesCollected.Msgsize() + 3 + (*z).Block.BlockHeader.Bonus.Msgsize() + 3 + (*z).Block.BlockHeader.ProposerPayout.Msgsize() + 5 + (*z).Block.BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).Block.BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).Block.BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).Block.BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).Block.BlockHeader.StateProofTracking { _ = zb0001 @@ -11868,12 +11937,12 @@ func (z *unauthenticatedProposal) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *unauthenticatedProposal) MsgIsZero() bool { - return ((*z).Block.BlockHeader.Round.MsgIsZero()) && ((*z).Block.BlockHeader.Branch.MsgIsZero()) && ((*z).Block.BlockHeader.Seed.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TimeStamp == 0) && ((*z).Block.BlockHeader.GenesisID == "") && ((*z).Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).Block.BlockHeader.Bonus.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).Block.BlockHeader.TxnCounter == 0) && (len((*z).Block.BlockHeader.StateProofTracking) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Block.Payset.MsgIsZero()) && ((*z).SeedProof.MsgIsZero()) && ((*z).OriginalPeriod == 0) && ((*z).OriginalProposer.MsgIsZero()) + return ((*z).Block.BlockHeader.Round.MsgIsZero()) && ((*z).Block.BlockHeader.Branch.MsgIsZero()) && ((*z).Block.BlockHeader.Seed.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).Block.BlockHeader.TimeStamp == 0) && ((*z).Block.BlockHeader.GenesisID == "") && ((*z).Block.BlockHeader.GenesisHash.MsgIsZero()) && ((*z).Block.BlockHeader.Proposer.MsgIsZero()) && ((*z).Block.BlockHeader.FeesCollected.MsgIsZero()) && ((*z).Block.BlockHeader.Bonus.MsgIsZero()) && ((*z).Block.BlockHeader.ProposerPayout.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).Block.BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRate == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).Block.BlockHeader.TxnCounter == 0) && (len((*z).Block.BlockHeader.StateProofTracking) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Block.Payset.MsgIsZero()) && ((*z).SeedProof.MsgIsZero()) && ((*z).OriginalPeriod == 0) && ((*z).OriginalProposer.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func UnauthenticatedProposalMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + bookkeeping.BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.Block.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) diff --git a/agreement/proposal.go b/agreement/proposal.go index 7c87f094ed..47e10871f5 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -260,18 +260,49 @@ func verifyNewSeed(p unauthenticatedProposal, ledger LedgerReader) error { return nil } +// payoutForBlock determines whether the proposer ought to be recorded, and +// whether that proposer ought to be paid for proposing. +func payoutForBlock(blk bookkeeping.Block, proposer basics.Address, ledger LedgerReader) (recorded basics.Address, eligible bool, err error) { + rnd := blk.Round() + proto, err := ledger.ConsensusParams(rnd) + if err != nil || !proto.Mining().Enabled { + return recorded, eligible, err // err may or may not be nil, either way, others are zero'd + } + + // We want to check eligibility and the online balance in the round that + // mattered to select this proposer. We use the ParamsRound() to find the + // round in question, not current proto. + agreementParams, err := ledger.ConsensusParams(ParamsRound(rnd)) + if err != nil { + return recorded, false, err + } + + // Check the balance from the agreement round + balanceRound := balanceRound(rnd, agreementParams) + balanceRecord, err := ledger.LookupAgreement(balanceRound, proposer) + if err != nil { + return recorded, false, err + } + + eligible = balanceRecord.IncentiveEligible && + balanceRecord.MicroAlgosWithRewards.Raw >= proto.Mining().MinBalance && + balanceRecord.MicroAlgosWithRewards.Raw <= proto.Mining().MaxBalance + return proposer, eligible, nil +} + func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve ValidatedBlock, period period, ledger LedgerReader) (proposal, proposalValue, error) { - rnd := ve.Block().Round() - newSeed, seedProof, err := deriveNewSeed(address, vrf, rnd, period, ledger) + blk := ve.Block() + newSeed, seedProof, err := deriveNewSeed(address, vrf, blk.Round(), period, ledger) if err != nil { - return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could not derive new seed: %v", err) + return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could not derive new seed: %w", err) } - var hdrProp basics.Address // The proposer as recorded in BlockHeader - if ve.Block().ConsensusProtocol().Mining().Enabled { - hdrProp = address + hdrProp, eligible, err := payoutForBlock(blk, address, ledger) + if err != nil { + return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could determine payout: %w", err) } - ve = ve.WithProposal(newSeed, hdrProp) + + ve = ve.WithProposal(newSeed, hdrProp, eligible) proposal := makeProposal(ve, seedProof, period, address) value := proposalValue{ OriginalPeriod: period, diff --git a/agreement/vote.go b/agreement/vote.go index 5dad87c85c..0d0c5da27f 100644 --- a/agreement/vote.go +++ b/agreement/vote.go @@ -148,7 +148,7 @@ func (uv unauthenticatedVote) verify(l LedgerReader) (vote, error) { // makeVote creates a new unauthenticated vote from its constituent components. // -// makeVote returns an error it it fails. +// makeVote returns an error if it fails. func makeVote(rv rawVote, voting crypto.OneTimeSigner, selection *crypto.VRFSecrets, l Ledger) (unauthenticatedVote, error) { m, err := membership(l, rv.Sender, rv.Round, rv.Period, rv.Step) if err != nil { diff --git a/data/basics/userBalance.go b/data/basics/userBalance.go index c00ed5be22..d8f86aea54 100644 --- a/data/basics/userBalance.go +++ b/data/basics/userBalance.go @@ -111,6 +111,7 @@ type VotingData struct { type OnlineAccountData struct { MicroAlgosWithRewards MicroAlgos VotingData + IncentiveEligible bool } // AccountData contains the data associated with a given address. @@ -559,6 +560,7 @@ func (u AccountData) OnlineAccountData() OnlineAccountData { VoteLastValid: u.VoteLastValid, VoteKeyDilution: u.VoteKeyDilution, }, + IncentiveEligible: u.IncentiveEligible, } } diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index dafed90d5c..13ba40d170 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -72,6 +72,12 @@ type ( // begins as a consensus parameter value, and decays periodically. Bonus basics.MicroAlgos `codec:"bi"` + // ProposerPayout is the amount that should be moved from the FeeSink to + // the Proposer at the start of the next block. It is basically the + // bonus + the mining fraction of FeesCollected, but may be zero'd by + // proposer ineligibility. + ProposerPayout basics.MicroAlgos `codec:"pp"` + // Rewards. // // When a block is applied, some amount of rewards are accrued to diff --git a/data/bookkeeping/msgp_gen.go b/data/bookkeeping/msgp_gen.go index ec6d772bd9..fef26fd1b9 100644 --- a/data/bookkeeping/msgp_gen.go +++ b/data/bookkeeping/msgp_gen.go @@ -143,8 +143,8 @@ import ( func (z *Block) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(30) - var zb0005Mask uint64 /* 35 bits */ + zb0005Len := uint32(31) + var zb0005Mask uint64 /* 36 bits */ if (*z).BlockHeader.Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x20 @@ -197,74 +197,78 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { zb0005Len-- zb0005Mask |= 0x20000 } - if (*z).BlockHeader.Branch.MsgIsZero() { + if (*z).BlockHeader.ProposerPayout.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000 } - if (*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).BlockHeader.Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80000 } - if (*z).BlockHeader.Proposer.MsgIsZero() { + if (*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100000 } - if (*z).BlockHeader.RewardsState.RewardsRate == 0 { + if (*z).BlockHeader.Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).BlockHeader.Round.MsgIsZero() { + if (*z).BlockHeader.RewardsState.RewardsRate == 0 { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { + if (*z).BlockHeader.Round.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } - if (*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero() { + if (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000000 } - if (*z).BlockHeader.Seed.MsgIsZero() { + if (*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000000 } - if len((*z).BlockHeader.StateProofTracking) == 0 { + if (*z).BlockHeader.Seed.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x4000000 } - if (*z).BlockHeader.TxnCounter == 0 { + if len((*z).BlockHeader.StateProofTracking) == 0 { zb0005Len-- zb0005Mask |= 0x8000000 } - if (*z).BlockHeader.TimeStamp == 0 { + if (*z).BlockHeader.TxnCounter == 0 { zb0005Len-- zb0005Mask |= 0x10000000 } - if (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { + if (*z).BlockHeader.TimeStamp == 0 { zb0005Len-- zb0005Mask |= 0x20000000 } - if (*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { + if (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000000 } - if (*z).Payset.MsgIsZero() { + if (*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80000000 } - if (*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { + if (*z).Payset.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100000000 } - if (*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { + if (*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200000000 } - if (*z).BlockHeader.UpgradeVote.UpgradeApprove == false { + if (*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x400000000 } + if (*z).BlockHeader.UpgradeVote.UpgradeApprove == false { + zb0005Len-- + zb0005Mask |= 0x800000000 + } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) if zb0005Len != 0 { @@ -348,46 +352,51 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { } } if (zb0005Mask & 0x40000) == 0 { // if not empty + // string "pp" + o = append(o, 0xa2, 0x70, 0x70) + o = (*z).BlockHeader.ProposerPayout.MarshalMsg(o) + } + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).BlockHeader.Branch.MarshalMsg(o) } - if (zb0005Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).BlockHeader.UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).BlockHeader.Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).BlockHeader.RewardsState.RewardsRate) } - if (zb0005Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).BlockHeader.Round.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).BlockHeader.RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).BlockHeader.Seed.MarshalMsg(o) } - if (zb0005Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).BlockHeader.StateProofTracking == nil { @@ -407,42 +416,42 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).BlockHeader.TxnCounter) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).BlockHeader.TimeStamp) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).BlockHeader.TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "txns" o = append(o, 0xa4, 0x74, 0x78, 0x6e, 0x73) o = (*z).Payset.MarshalMsg(o) } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).BlockHeader.UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).BlockHeader.UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x400000000) == 0 { // if not empty + if (zb0005Mask & 0x800000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).BlockHeader.UpgradeVote.UpgradeApprove) @@ -572,6 +581,14 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).BlockHeader.ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "ProposerPayout") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -893,6 +910,12 @@ func (z *Block) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) (o []b err = msgp.WrapError(err, "Bonus") return } + case "pp": + bts, err = (*z).BlockHeader.ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "ProposerPayout") + return + } case "fees": bts, err = (*z).BlockHeader.RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -1100,7 +1123,7 @@ func (_ *Block) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *Block) Msgsize() (s int) { - s = 3 + 4 + (*z).BlockHeader.Round.Msgsize() + 5 + (*z).BlockHeader.Branch.Msgsize() + 5 + (*z).BlockHeader.Seed.Msgsize() + 4 + (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).BlockHeader.GenesisID) + 3 + (*z).BlockHeader.GenesisHash.Msgsize() + 4 + (*z).BlockHeader.Proposer.Msgsize() + 3 + (*z).BlockHeader.FeesCollected.Msgsize() + 3 + (*z).BlockHeader.Bonus.Msgsize() + 5 + (*z).BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).BlockHeader.Round.Msgsize() + 5 + (*z).BlockHeader.Branch.Msgsize() + 5 + (*z).BlockHeader.Seed.Msgsize() + 4 + (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).BlockHeader.TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).BlockHeader.GenesisID) + 3 + (*z).BlockHeader.GenesisHash.Msgsize() + 4 + (*z).BlockHeader.Proposer.Msgsize() + 3 + (*z).BlockHeader.FeesCollected.Msgsize() + 3 + (*z).BlockHeader.Bonus.Msgsize() + 3 + (*z).BlockHeader.ProposerPayout.Msgsize() + 5 + (*z).BlockHeader.RewardsState.FeeSink.Msgsize() + 4 + (*z).BlockHeader.RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).BlockHeader.RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).BlockHeader.UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).BlockHeader.UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).BlockHeader.UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).BlockHeader.UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).BlockHeader.StateProofTracking != nil { for zb0001, zb0002 := range (*z).BlockHeader.StateProofTracking { _ = zb0001 @@ -1122,12 +1145,12 @@ func (z *Block) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *Block) MsgIsZero() bool { - return ((*z).BlockHeader.Round.MsgIsZero()) && ((*z).BlockHeader.Branch.MsgIsZero()) && ((*z).BlockHeader.Seed.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).BlockHeader.TimeStamp == 0) && ((*z).BlockHeader.GenesisID == "") && ((*z).BlockHeader.GenesisHash.MsgIsZero()) && ((*z).BlockHeader.Proposer.MsgIsZero()) && ((*z).BlockHeader.FeesCollected.MsgIsZero()) && ((*z).BlockHeader.Bonus.MsgIsZero()) && ((*z).BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).BlockHeader.RewardsState.RewardsRate == 0) && ((*z).BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).BlockHeader.TxnCounter == 0) && (len((*z).BlockHeader.StateProofTracking) == 0) && (len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Payset.MsgIsZero()) + return ((*z).BlockHeader.Round.MsgIsZero()) && ((*z).BlockHeader.Branch.MsgIsZero()) && ((*z).BlockHeader.Seed.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).BlockHeader.TimeStamp == 0) && ((*z).BlockHeader.GenesisID == "") && ((*z).BlockHeader.GenesisHash.MsgIsZero()) && ((*z).BlockHeader.Proposer.MsgIsZero()) && ((*z).BlockHeader.FeesCollected.MsgIsZero()) && ((*z).BlockHeader.Bonus.MsgIsZero()) && ((*z).BlockHeader.ProposerPayout.MsgIsZero()) && ((*z).BlockHeader.RewardsState.FeeSink.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero()) && ((*z).BlockHeader.RewardsState.RewardsLevel == 0) && ((*z).BlockHeader.RewardsState.RewardsRate == 0) && ((*z).BlockHeader.RewardsState.RewardsResidue == 0) && ((*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0) && ((*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).BlockHeader.UpgradeVote.UpgradeApprove == false) && ((*z).BlockHeader.TxnCounter == 0) && (len((*z).BlockHeader.StateProofTracking) == 0) && (len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0) && ((*z).Payset.MsgIsZero()) } // MaxSize returns a maximum valid message size for this message type func BlockMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.BlockHeader.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) @@ -1185,8 +1208,8 @@ func BlockHashMaxSize() int { func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0005Len := uint32(29) - var zb0005Mask uint64 /* 34 bits */ + zb0005Len := uint32(30) + var zb0005Mask uint64 /* 35 bits */ if (*z).Bonus.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x20 @@ -1239,70 +1262,74 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { zb0005Len-- zb0005Mask |= 0x20000 } - if (*z).Branch.MsgIsZero() { + if (*z).ProposerPayout.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000 } - if (*z).UpgradeState.CurrentProtocol.MsgIsZero() { + if (*z).Branch.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80000 } - if (*z).Proposer.MsgIsZero() { + if (*z).UpgradeState.CurrentProtocol.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100000 } - if (*z).RewardsState.RewardsRate == 0 { + if (*z).Proposer.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200000 } - if (*z).Round.MsgIsZero() { + if (*z).RewardsState.RewardsRate == 0 { zb0005Len-- zb0005Mask |= 0x400000 } - if (*z).RewardsState.RewardsRecalculationRound.MsgIsZero() { + if (*z).Round.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x800000 } - if (*z).RewardsState.RewardsPool.MsgIsZero() { + if (*z).RewardsState.RewardsRecalculationRound.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x1000000 } - if (*z).Seed.MsgIsZero() { + if (*z).RewardsState.RewardsPool.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x2000000 } - if len((*z).StateProofTracking) == 0 { + if (*z).Seed.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x4000000 } - if (*z).TxnCounter == 0 { + if len((*z).StateProofTracking) == 0 { zb0005Len-- zb0005Mask |= 0x8000000 } - if (*z).TimeStamp == 0 { + if (*z).TxnCounter == 0 { zb0005Len-- zb0005Mask |= 0x10000000 } - if (*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { + if (*z).TimeStamp == 0 { zb0005Len-- zb0005Mask |= 0x20000000 } - if (*z).TxnCommitments.Sha256Commitment.MsgIsZero() { + if (*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x40000000 } - if (*z).UpgradeVote.UpgradeDelay.MsgIsZero() { + if (*z).TxnCommitments.Sha256Commitment.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x80000000 } - if (*z).UpgradeVote.UpgradePropose.MsgIsZero() { + if (*z).UpgradeVote.UpgradeDelay.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x100000000 } - if (*z).UpgradeVote.UpgradeApprove == false { + if (*z).UpgradeVote.UpgradePropose.MsgIsZero() { zb0005Len-- zb0005Mask |= 0x200000000 } + if (*z).UpgradeVote.UpgradeApprove == false { + zb0005Len-- + zb0005Mask |= 0x400000000 + } // variable map header, size zb0005Len o = msgp.AppendMapHeader(o, zb0005Len) if zb0005Len != 0 { @@ -1386,46 +1413,51 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { } } if (zb0005Mask & 0x40000) == 0 { // if not empty + // string "pp" + o = append(o, 0xa2, 0x70, 0x70) + o = (*z).ProposerPayout.MarshalMsg(o) + } + if (zb0005Mask & 0x80000) == 0 { // if not empty // string "prev" o = append(o, 0xa4, 0x70, 0x72, 0x65, 0x76) o = (*z).Branch.MarshalMsg(o) } - if (zb0005Mask & 0x80000) == 0 { // if not empty + if (zb0005Mask & 0x100000) == 0 { // if not empty // string "proto" o = append(o, 0xa5, 0x70, 0x72, 0x6f, 0x74, 0x6f) o = (*z).UpgradeState.CurrentProtocol.MarshalMsg(o) } - if (zb0005Mask & 0x100000) == 0 { // if not empty + if (zb0005Mask & 0x200000) == 0 { // if not empty // string "prp" o = append(o, 0xa3, 0x70, 0x72, 0x70) o = (*z).Proposer.MarshalMsg(o) } - if (zb0005Mask & 0x200000) == 0 { // if not empty + if (zb0005Mask & 0x400000) == 0 { // if not empty // string "rate" o = append(o, 0xa4, 0x72, 0x61, 0x74, 0x65) o = msgp.AppendUint64(o, (*z).RewardsState.RewardsRate) } - if (zb0005Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x800000) == 0 { // if not empty // string "rnd" o = append(o, 0xa3, 0x72, 0x6e, 0x64) o = (*z).Round.MarshalMsg(o) } - if (zb0005Mask & 0x800000) == 0 { // if not empty + if (zb0005Mask & 0x1000000) == 0 { // if not empty // string "rwcalr" o = append(o, 0xa6, 0x72, 0x77, 0x63, 0x61, 0x6c, 0x72) o = (*z).RewardsState.RewardsRecalculationRound.MarshalMsg(o) } - if (zb0005Mask & 0x1000000) == 0 { // if not empty + if (zb0005Mask & 0x2000000) == 0 { // if not empty // string "rwd" o = append(o, 0xa3, 0x72, 0x77, 0x64) o = (*z).RewardsState.RewardsPool.MarshalMsg(o) } - if (zb0005Mask & 0x2000000) == 0 { // if not empty + if (zb0005Mask & 0x4000000) == 0 { // if not empty // string "seed" o = append(o, 0xa4, 0x73, 0x65, 0x65, 0x64) o = (*z).Seed.MarshalMsg(o) } - if (zb0005Mask & 0x4000000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).StateProofTracking == nil { @@ -1445,37 +1477,37 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0005Mask & 0x8000000) == 0 { // if not empty + if (zb0005Mask & 0x10000000) == 0 { // if not empty // string "tc" o = append(o, 0xa2, 0x74, 0x63) o = msgp.AppendUint64(o, (*z).TxnCounter) } - if (zb0005Mask & 0x10000000) == 0 { // if not empty + if (zb0005Mask & 0x20000000) == 0 { // if not empty // string "ts" o = append(o, 0xa2, 0x74, 0x73) o = msgp.AppendInt64(o, (*z).TimeStamp) } - if (zb0005Mask & 0x20000000) == 0 { // if not empty + if (zb0005Mask & 0x40000000) == 0 { // if not empty // string "txn" o = append(o, 0xa3, 0x74, 0x78, 0x6e) o = (*z).TxnCommitments.NativeSha512_256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x40000000) == 0 { // if not empty + if (zb0005Mask & 0x80000000) == 0 { // if not empty // string "txn256" o = append(o, 0xa6, 0x74, 0x78, 0x6e, 0x32, 0x35, 0x36) o = (*z).TxnCommitments.Sha256Commitment.MarshalMsg(o) } - if (zb0005Mask & 0x80000000) == 0 { // if not empty + if (zb0005Mask & 0x100000000) == 0 { // if not empty // string "upgradedelay" o = append(o, 0xac, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x65, 0x6c, 0x61, 0x79) o = (*z).UpgradeVote.UpgradeDelay.MarshalMsg(o) } - if (zb0005Mask & 0x100000000) == 0 { // if not empty + if (zb0005Mask & 0x200000000) == 0 { // if not empty // string "upgradeprop" o = append(o, 0xab, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x70, 0x72, 0x6f, 0x70) o = (*z).UpgradeVote.UpgradePropose.MarshalMsg(o) } - if (zb0005Mask & 0x200000000) == 0 { // if not empty + if (zb0005Mask & 0x400000000) == 0 { // if not empty // string "upgradeyes" o = append(o, 0xaa, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x79, 0x65, 0x73) o = msgp.AppendBool(o, (*z).UpgradeVote.UpgradeApprove) @@ -1605,6 +1637,14 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) return } } + if zb0005 > 0 { + zb0005-- + bts, err = (*z).ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "ProposerPayout") + return + } + } if zb0005 > 0 { zb0005-- bts, err = (*z).RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) @@ -1918,6 +1958,12 @@ func (z *BlockHeader) UnmarshalMsgWithState(bts []byte, st msgp.UnmarshalState) err = msgp.WrapError(err, "Bonus") return } + case "pp": + bts, err = (*z).ProposerPayout.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "ProposerPayout") + return + } case "fees": bts, err = (*z).RewardsState.FeeSink.UnmarshalMsgWithState(bts, st) if err != nil { @@ -2119,7 +2165,7 @@ func (_ *BlockHeader) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *BlockHeader) Msgsize() (s int) { - s = 3 + 4 + (*z).Round.Msgsize() + 5 + (*z).Branch.Msgsize() + 5 + (*z).Seed.Msgsize() + 4 + (*z).TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).GenesisID) + 3 + (*z).GenesisHash.Msgsize() + 4 + (*z).Proposer.Msgsize() + 3 + (*z).FeesCollected.Msgsize() + 3 + (*z).Bonus.Msgsize() + 5 + (*z).RewardsState.FeeSink.Msgsize() + 4 + (*z).RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize + s = 3 + 4 + (*z).Round.Msgsize() + 5 + (*z).Branch.Msgsize() + 5 + (*z).Seed.Msgsize() + 4 + (*z).TxnCommitments.NativeSha512_256Commitment.Msgsize() + 7 + (*z).TxnCommitments.Sha256Commitment.Msgsize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + len((*z).GenesisID) + 3 + (*z).GenesisHash.Msgsize() + 4 + (*z).Proposer.Msgsize() + 3 + (*z).FeesCollected.Msgsize() + 3 + (*z).Bonus.Msgsize() + 3 + (*z).ProposerPayout.Msgsize() + 5 + (*z).RewardsState.FeeSink.Msgsize() + 4 + (*z).RewardsState.RewardsPool.Msgsize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + (*z).RewardsState.RewardsRecalculationRound.Msgsize() + 6 + (*z).UpgradeState.CurrentProtocol.Msgsize() + 10 + (*z).UpgradeState.NextProtocol.Msgsize() + 8 + msgp.Uint64Size + 11 + (*z).UpgradeState.NextProtocolVoteBefore.Msgsize() + 11 + (*z).UpgradeState.NextProtocolSwitchOn.Msgsize() + 12 + (*z).UpgradeVote.UpgradePropose.Msgsize() + 13 + (*z).UpgradeVote.UpgradeDelay.Msgsize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + msgp.MapHeaderSize if (*z).StateProofTracking != nil { for zb0001, zb0002 := range (*z).StateProofTracking { _ = zb0001 @@ -2140,12 +2186,12 @@ func (z *BlockHeader) Msgsize() (s int) { // MsgIsZero returns whether this is a zero value func (z *BlockHeader) MsgIsZero() bool { - return ((*z).Round.MsgIsZero()) && ((*z).Branch.MsgIsZero()) && ((*z).Seed.MsgIsZero()) && ((*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).TimeStamp == 0) && ((*z).GenesisID == "") && ((*z).GenesisHash.MsgIsZero()) && ((*z).Proposer.MsgIsZero()) && ((*z).FeesCollected.MsgIsZero()) && ((*z).Bonus.MsgIsZero()) && ((*z).RewardsState.FeeSink.MsgIsZero()) && ((*z).RewardsState.RewardsPool.MsgIsZero()) && ((*z).RewardsState.RewardsLevel == 0) && ((*z).RewardsState.RewardsRate == 0) && ((*z).RewardsState.RewardsResidue == 0) && ((*z).RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocolApprovals == 0) && ((*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).UpgradeVote.UpgradeApprove == false) && ((*z).TxnCounter == 0) && (len((*z).StateProofTracking) == 0) && (len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).ParticipationUpdates.AbsentParticipationAccounts) == 0) + return ((*z).Round.MsgIsZero()) && ((*z).Branch.MsgIsZero()) && ((*z).Seed.MsgIsZero()) && ((*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero()) && ((*z).TxnCommitments.Sha256Commitment.MsgIsZero()) && ((*z).TimeStamp == 0) && ((*z).GenesisID == "") && ((*z).GenesisHash.MsgIsZero()) && ((*z).Proposer.MsgIsZero()) && ((*z).FeesCollected.MsgIsZero()) && ((*z).Bonus.MsgIsZero()) && ((*z).ProposerPayout.MsgIsZero()) && ((*z).RewardsState.FeeSink.MsgIsZero()) && ((*z).RewardsState.RewardsPool.MsgIsZero()) && ((*z).RewardsState.RewardsLevel == 0) && ((*z).RewardsState.RewardsRate == 0) && ((*z).RewardsState.RewardsResidue == 0) && ((*z).RewardsState.RewardsRecalculationRound.MsgIsZero()) && ((*z).UpgradeState.CurrentProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocol.MsgIsZero()) && ((*z).UpgradeState.NextProtocolApprovals == 0) && ((*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero()) && ((*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero()) && ((*z).UpgradeVote.UpgradePropose.MsgIsZero()) && ((*z).UpgradeVote.UpgradeDelay.MsgIsZero()) && ((*z).UpgradeVote.UpgradeApprove == false) && ((*z).TxnCounter == 0) && (len((*z).StateProofTracking) == 0) && (len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0) && (len((*z).ParticipationUpdates.AbsentParticipationAccounts) == 0) } // MaxSize returns a maximum valid message size for this message type func BlockHeaderMaxSize() (s int) { - s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 + s = 3 + 4 + basics.RoundMaxSize() + 5 + BlockHashMaxSize() + 5 + committee.SeedMaxSize() + 4 + crypto.DigestMaxSize() + 7 + crypto.DigestMaxSize() + 3 + msgp.Int64Size + 4 + msgp.StringPrefixSize + config.MaxGenesisIDLen + 3 + crypto.DigestMaxSize() + 4 + basics.AddressMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 3 + basics.MicroAlgosMaxSize() + 5 + basics.AddressMaxSize() + 4 + basics.AddressMaxSize() + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 5 + msgp.Uint64Size + 7 + basics.RoundMaxSize() + 6 + protocol.ConsensusVersionMaxSize() + 10 + protocol.ConsensusVersionMaxSize() + 8 + msgp.Uint64Size + 11 + basics.RoundMaxSize() + 11 + basics.RoundMaxSize() + 12 + protocol.ConsensusVersionMaxSize() + 13 + basics.RoundMaxSize() + 11 + msgp.BoolSize + 3 + msgp.Uint64Size + 4 s += msgp.MapHeaderSize // Adding size of map keys for z.StateProofTracking s += protocol.NumStateProofTypes * (protocol.StateProofTypeMaxSize()) diff --git a/data/datatest/impls.go b/data/datatest/impls.go index 06495e007e..c4c251c726 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -65,10 +65,13 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated } // WithProposal implements the agreement.ValidatedBlock interface. -func (ve validatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { +func (ve validatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { newblock := *ve.blk newblock.BlockHeader.Seed = s newblock.BlockHeader.Proposer = proposer + if !eligible { + newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } return validatedBlock{blk: &newblock} } diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 1728904d44..73f3869fb7 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "math" + "math/bits" "sync" "github.com/algorand/go-algorand/config" @@ -783,6 +784,8 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return nil, err } + // Move last block's proposer payout to the proposer + // ensure that we have at least MinBalance after withdrawing rewards ot.SubA(poolNew.MicroAlgos, basics.MicroAlgos{Raw: proto.MinBalance}) if ot.Overflowed { @@ -790,40 +793,29 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return nil, fmt.Errorf("overflowed subtracting rewards for block %v", hdr.Round) } - eligible, err := eval.eligibleForIncentives(prevHeader.Proposer) - if err != nil { - return nil, err - } - - if eligible { - incentive, _ := basics.NewPercent(proto.Mining().Percent).DivvyAlgos(prevHeader.FeesCollected) - total, o := basics.OAddA(incentive, prevHeader.Bonus) - if o { - return nil, fmt.Errorf("overflowed adding bonus incentive %d %d", incentive, prevHeader.Bonus) - } - - sink, err := eval.state.lookup(prevHeader.FeeSink) - if err != nil { - return nil, err - } - available := sink.AvailableBalance(&proto) - total = basics.MinA(total, available) - - err = eval.state.Move(prevHeader.FeeSink, prevHeader.Proposer, total, nil, nil) - if err != nil { - // Should be impossible, we used AvailableBalance - return nil, fmt.Errorf("unable to pay block incentive: %v", err) + if !prevHeader.Proposer.IsZero() { + // Payout the previous proposer. + if !prevHeader.ProposerPayout.IsZero() { + // Use the FeeSink from that header, since that's the checked + // balance. (Though we have no expectation it will ever change.) + err = eval.state.Move(prevHeader.FeeSink, prevHeader.Proposer, prevHeader.ProposerPayout, nil, nil) + if err != nil { + // Should be impossible, it was checked when put into the block + return nil, fmt.Errorf("unable to payout block incentive: %v", err) + } } - } - // Note their proposal if MiningEnabled. - if proto.Mining().Enabled { + // Increment LastProposed on the proposer account prp, err := eval.state.Get(prevHeader.Proposer, false) if err != nil { return nil, err } prp.LastProposed = hdr.Round - 1 - // An account could propose, even while suspended, because of the 320 round lookback. + + // An account could propose, even while suspended, because of the 320 + // round lookback. Doing so is evidence the account is + // back. Unsuspend. But the account will remain not IncentiveElgible + // until they repay the initial fee. if prp.Suspended() { prp.Status = basics.Online } @@ -840,21 +832,6 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return eval, nil } -func (eval *BlockEvaluator) eligibleForIncentives(proposer basics.Address) (bool, error) { - proposerState, err := eval.state.Get(proposer, true) - if err != nil { - return false, err - } - params := eval.state.ConsensusParams() - if proposerState.MicroAlgos.Raw < params.Mining().MinBalance { - return false, nil - } - if proposerState.MicroAlgos.Raw > params.Mining().MaxBalance { - return false, nil - } - return proposerState.IncentiveEligible, nil -} - // hotfix for testnet stall 08/26/2019; move some algos from testnet bank to rewards pool to give it enough time until protocol upgrade occur. // hotfix for testnet stall 11/07/2019; do the same thing func (eval *BlockEvaluator) workaroundOverspentRewards(rewardPoolBalance ledgercore.AccountData, headerRound basics.Round) (poolOld ledgercore.AccountData, err error) { @@ -1348,7 +1325,23 @@ func (eval *BlockEvaluator) endOfBlock() error { } if eval.proto.Mining().Enabled { + // Determine how much the proposer should be paid. Agreement code + // can cancel this payment by zero'ing the ProposerPayout if the + // proposer is found to be ineligible. See WithProposal(). eval.block.FeesCollected = eval.state.feesCollected + + incentive, _ := basics.NewPercent(eval.proto.Mining().Percent).DivvyAlgos(eval.block.FeesCollected) + total, o := basics.OAddA(incentive, eval.block.Bonus) + if o { + return fmt.Errorf("overflowed adding bonus incentive %d %d", incentive, eval.block.Bonus) + } + + sink, lerr := eval.state.lookup(eval.block.FeeSink) + if lerr != nil { + return lerr + } + available := sink.AvailableBalance(&eval.proto) + eval.block.ProposerPayout = basics.MinA(total, available) } eval.generateKnockOfflineAccountsList() @@ -1547,23 +1540,11 @@ func bitsMatch(a, b []byte, n int) bool { return false } } - // Compare bits in the last byte - for i := n / 8 * 8; i < n; i++ { - // Calculate the byte and bit positions - bytePos := i / 8 - bitPos := i % 8 - - // Extract the bits from each slice - bit1 := (a[bytePos] >> (7 - bitPos)) & 1 - bit2 := (b[bytePos] >> (7 - bitPos)) & 1 - - // Compare the bits - if bit1 != bit2 { - return false - } + remaining := n % 8 + if remaining == 0 { + return true } - - return true + return bits.LeadingZeros8(a[n/8]^b[n/8]) >= remaining } func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, lastSeen basics.Round, current basics.Round) bool { diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 8a4d56bfdb..f935288d38 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -1478,11 +1478,19 @@ func TestBitsMatch(t *testing.T) { require.True(t, bitsMatch([]byte{0x1}, []byte{0x2}, b), "%d", b) } require.False(t, bitsMatch([]byte{0x1}, []byte{0x2}, 7)) + require.False(t, bitsMatch([]byte{0x1}, []byte{0x2}, 8)) + require.False(t, bitsMatch([]byte{0x1}, []byte{0x2}, 9)) for b := 0; b <= 12; b++ { require.True(t, bitsMatch([]byte{0x1, 0xff, 0xaa}, []byte{0x1, 0xf0}, b), "%d", b) } require.False(t, bitsMatch([]byte{0x1, 0xff, 0xaa}, []byte{0x1, 0xf0}, 13)) + + // on a byte boundary + require.True(t, bitsMatch([]byte{0x1}, []byte{0x1}, 8)) + require.False(t, bitsMatch([]byte{0x1}, []byte{0x1}, 9)) + require.True(t, bitsMatch([]byte{0x1, 0xff}, []byte{0x1, 0x00}, 8)) + require.False(t, bitsMatch([]byte{0x1, 0xff}, []byte{0x1, 00}, 9)) } func TestIsAbsent(t *testing.T) { diff --git a/ledger/eval/prefetcher/prefetcher_alignment_test.go b/ledger/eval/prefetcher/prefetcher_alignment_test.go index 2f79cd45d4..b6e85a21a2 100644 --- a/ledger/eval/prefetcher/prefetcher_alignment_test.go +++ b/ledger/eval/prefetcher/prefetcher_alignment_test.go @@ -377,7 +377,7 @@ func TestEvaluatorPrefetcherAlignmentPayment(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -409,7 +409,7 @@ func TestEvaluatorPrefetcherAlignmentCreateAsset(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) // Only one (non-existing) asset is requested. Ignore it. require.Len(t, requested.Assets, 1) require.Len(t, requested.Assets[makeAddress(1)], 1) @@ -465,7 +465,7 @@ func TestEvaluatorPrefetcherAlignmentReconfigAsset(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -520,7 +520,7 @@ func TestEvaluatorPrefetcherAlignmentAssetOptIn(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -586,7 +586,7 @@ func TestEvaluatorPrefetcherAlignmentAssetOptInCloseTo(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -657,7 +657,7 @@ func TestEvaluatorPrefetcherAlignmentAssetTransfer(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) // zero transfer of any asset @@ -676,7 +676,7 @@ func TestEvaluatorPrefetcherAlignmentAssetTransfer(t *testing.T) { requested, prefetched = run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -757,7 +757,7 @@ func TestEvaluatorPrefetcherAlignmentAssetClawback(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -827,7 +827,7 @@ func TestEvaluatorPrefetcherAlignmentAssetFreeze(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -874,7 +874,7 @@ func TestEvaluatorPrefetcherAlignmentKeyreg(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -911,7 +911,7 @@ func TestEvaluatorPrefetcherAlignmentCreateApplication(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) // Only one (non-existing) asset is requested. Ignore it. require.Len(t, requested.Apps, 1) require.Len(t, requested.Apps[makeAddress(1)], 1) @@ -969,7 +969,7 @@ func TestEvaluatorPrefetcherAlignmentDeleteApplication(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -1027,7 +1027,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationOptIn(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -1091,7 +1091,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCloseOut(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -1155,7 +1155,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationClearState(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -1219,7 +1219,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCallAccountsDeclaration(t *testi requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) // Foreign accounts are not loaded, ensure they are not prefetched require.NotContains(t, prefetched.Accounts, makeAddress(5)) require.NotContains(t, prefetched.Accounts, makeAddress(3)) @@ -1287,7 +1287,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCallForeignAppsDeclaration(t *te requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) // Foreign apps are not loaded, ensure they are not prefetched require.NotContains(t, prefetched.Creators, creatable{cindex: 6, ctype: basics.AppCreatable}) require.NotContains(t, prefetched.Creators, creatable{cindex: 8, ctype: basics.AppCreatable}) @@ -1354,7 +1354,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCallForeignAssetsDeclaration(t * requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) // Foreign apps are not loaded, ensure they are not prefetched require.NotContains(t, prefetched.Creators, creatable{cindex: 6, ctype: basics.AssetCreatable}) require.NotContains(t, prefetched.Creators, creatable{cindex: 8, ctype: basics.AssetCreatable}) @@ -1401,6 +1401,6 @@ func TestEvaluatorPrefetcherAlignmentStateProof(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.pretend(rewardsPool(), proposer()) + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } diff --git a/ledger/eval/prefetcher/prefetcher_test.go b/ledger/eval/prefetcher/prefetcher_test.go index 1ec96ad3f7..f1b6ab9eda 100644 --- a/ledger/eval/prefetcher/prefetcher_test.go +++ b/ledger/eval/prefetcher/prefetcher_test.go @@ -48,7 +48,10 @@ func makeAddress(addressSeed int) (o basics.Address) { return } -const proto = protocol.ConsensusCurrentVersion +// It would be nice to test current and future, but until that change is made, +// it's better to test future, as that's likely to catch mistakes made while +// developing something new (and likely to catch changes that affect current) +const proto = protocol.ConsensusFuture type lookupError struct{} diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index ce34e3d44b..16ebd38b37 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -41,10 +41,13 @@ func (vb ValidatedBlock) Delta() StateDelta { } // WithProposal returns a copy of the ValidatedBlock with a modified seed and associated proposer -func (vb ValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address) ValidatedBlock { +func (vb ValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { newblock := vb.blk newblock.BlockHeader.Seed = s newblock.BlockHeader.Proposer = proposer + if !eligible { + newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } return ValidatedBlock{ blk: newblock, diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 2c4453222f..a8922b7d45 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -154,7 +154,7 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer // We have this backdoor way to install a proposer or seed into the header // for tests. Doesn't matter that it makes them both the same. - *vb = vb.WithProposal(committee.Seed(prp), prp) + *vb = vb.WithProposal(committee.Seed(prp), prp, true) err = ledger.AddValidatedBlock(*vb, agreement.Certificate{}) require.NoError(t, err) diff --git a/node/node.go b/node/node.go index 32500077fb..1224f55147 100644 --- a/node/node.go +++ b/node/node.go @@ -1291,8 +1291,8 @@ type validatedBlock struct { } // WithProposal satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) WithProposal(s committee.Seed, proposer basics.Address) agreement.ValidatedBlock { - lvb := vb.vb.WithProposal(s, proposer) +func (vb validatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { + lvb := vb.vb.WithProposal(s, proposer, eligible) return validatedBlock{vb: &lvb} } From 34f1ede42084a70548355fd8d4c41a715b69eefd Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 5 Mar 2024 16:02:48 -0500 Subject: [PATCH 046/117] Fixups for moving some functionality to agreemnet --- agreement/service_test.go | 432 +++++++++++++++---------------------- config/consensus.go | 2 +- ledger/double_test.go | 5 +- ledger/eval_simple_test.go | 140 ++++++------ ledger/simple_test.go | 12 +- 5 files changed, 256 insertions(+), 335 deletions(-) diff --git a/agreement/service_test.go b/agreement/service_test.go index 5cc05901ed..3426b4df3b 100644 --- a/agreement/service_test.go +++ b/agreement/service_test.go @@ -650,14 +650,10 @@ func createTestAccountsAndBalances(t *testing.T, numNodes int, rootSeed []byte) // add new account rootAddress to db { rootAccess, err := db.MakeAccessor(t.Name()+"root"+strconv.Itoa(i+off), false, true) - if err != nil { - panic(err) - } + require.NoError(t, err) seed = sha256.Sum256(seed[:]) // rehash every node to get different root addresses root, err := account.ImportRoot(rootAccess, seed) - if err != nil { - panic(err) - } + require.NoError(t, err) rootAddress = root.Address() } @@ -742,9 +738,7 @@ func setupAgreementWithValidator(t *testing.T, numNodes int, traceLevel traceLev for i := 0; i < numNodes; i++ { accessor, err := db.MakeAccessor(t.Name()+"_"+strconv.Itoa(i)+"_crash.db", false, true) - if err != nil { - panic(err) - } + require.NoError(t, err) dbAccessors[i] = accessor m := baseNetwork.monitors[nodeID(i)] @@ -826,23 +820,17 @@ func (m *coserviceMonitor) clearClock() { } } -func expectNewPeriod(clocks []timers.Clock[TimeoutType], zeroes uint) (newzeroes uint) { +func expectNewPeriod(t *testing.T, clocks []timers.Clock[TimeoutType], zeroes uint) (newzeroes uint) { zeroes++ for i := range clocks { - if clocks[i].(*testingClock).zeroes != zeroes { - errstr := fmt.Sprintf("unexpected number of zeroes: %v != %v", clocks[i].(*testingClock).zeroes, zeroes) - panic(errstr) - } + require.Equal(t, zeroes, clocks[i].(*testingClock).zeroes, "unexpected number of zeroes") } return zeroes } -func expectNoNewPeriod(clocks []timers.Clock[TimeoutType], zeroes uint) (newzeroes uint) { +func expectNoNewPeriod(t *testing.T, clocks []timers.Clock[TimeoutType], zeroes uint) (newzeroes uint) { for i := range clocks { - if clocks[i].(*testingClock).zeroes != zeroes { - errstr := fmt.Sprintf("unexpected number of zeroes: %v != %v", clocks[i].(*testingClock).zeroes, zeroes) - panic(errstr) - } + require.Equal(t, zeroes, clocks[i].(*testingClock).zeroes, "unexpected number of zeroes") } return zeroes } @@ -869,13 +857,13 @@ func triggerGlobalTimeoutType(timeoutType TimeoutType, clocks []timers.Clock[Tim activityMonitor.waitForQuiet() } -func runRound(clocks []timers.Clock[TimeoutType], activityMonitor *activityMonitor, zeroes uint, filterTimeout time.Duration) (newzeroes uint) { +func runRound(t *testing.T, clocks []timers.Clock[TimeoutType], activityMonitor *activityMonitor, zeroes uint, filterTimeout time.Duration) (newzeroes uint) { triggerGlobalTimeout(filterTimeout, TimeoutFilter, clocks, activityMonitor) - return expectNewPeriod(clocks, zeroes) + return expectNewPeriod(t, clocks, zeroes) } -func runRoundTriggerFilter(clocks []timers.Clock[TimeoutType], activityMonitor *activityMonitor, zeroes uint) (newzeroes uint) { +func runRoundTriggerFilter(t *testing.T, clocks []timers.Clock[TimeoutType], activityMonitor *activityMonitor, zeroes uint) (newzeroes uint) { triggerGlobalTimeoutType(TimeoutFilter, clocks, activityMonitor) - return expectNewPeriod(clocks, zeroes) + return expectNewPeriod(t, clocks, zeroes) } func sanityCheck(startRound round, numRounds round, ledgers []Ledger) { @@ -916,19 +904,19 @@ func simulateAgreementWithLedgerFactory(t *testing.T, numNodes int, numRounds in } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) filterTimeouts := make([][]time.Duration, numNodes, numNodes) // run round with round-specific consensus version first (since fix in #1896) - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) for j := 1; j < numRounds; j++ { for srvIdx, clock := range clocks { delta, err := clock.(*testingClock).when(TimeoutFilter) require.NoError(t, err) filterTimeouts[srvIdx] = append(filterTimeouts[srvIdx], delta) } - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) } for i := 0; i < numNodes; i++ { @@ -1127,21 +1115,21 @@ func TestDynamicFilterTimeoutResets(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) filterTimeouts := make([][]time.Duration, numNodes, numNodes) baseHistoryRounds := dynamicFilterCredentialArrivalHistory + int(credentialRoundLag) // run round with round-specific consensus version first (since fix in #1896) - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) for j := 1; j < baseHistoryRounds+2; j++ { for srvIdx, clock := range clocks { delta, err := clock.(*testingClock).when(TimeoutFilter) require.NoError(t, err) filterTimeouts[srvIdx] = append(filterTimeouts[srvIdx], delta) } - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) } for i := range clocks { @@ -1158,36 +1146,36 @@ func TestDynamicFilterTimeoutResets(t *testing.T) { baseNetwork.dropAllSlowNextVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeoutType(TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(firstFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 1 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } filterTimeoutsPostRecovery := make([][]time.Duration, numNodes, numNodes) // run round with round-specific consensus version first (since fix in #1896) - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) for j := 1; j < baseHistoryRounds+1; j++ { for srvIdx, clock := range clocks { delta, err := clock.(*testingClock).when(TimeoutFilter) require.NoError(t, err) filterTimeoutsPostRecovery[srvIdx] = append(filterTimeoutsPostRecovery[srvIdx], delta) } - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) } for i := range clocks { @@ -1262,11 +1250,11 @@ func TestAgreementFastRecoveryDownEarly(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force fast partition recovery into bottom @@ -1275,28 +1263,28 @@ func TestAgreementFastRecoveryDownEarly(t *testing.T) { baseNetwork.dropAllSlowNextVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeoutType(TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(firstFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 1 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1320,11 +1308,11 @@ func TestAgreementFastRecoveryDownMiss(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force fast partition recovery into bottom @@ -1332,13 +1320,13 @@ func TestAgreementFastRecoveryDownMiss(t *testing.T) { // fail all steps baseNetwork.dropAllVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) firstClocks := clocks[:4] restClocks := clocks[4:] @@ -1351,7 +1339,7 @@ func TestAgreementFastRecoveryDownMiss(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() for i := range restClocks { @@ -1362,22 +1350,22 @@ func TestAgreementFastRecoveryDownMiss(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(secondFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 1 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1401,11 +1389,11 @@ func TestAgreementFastRecoveryLate(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force fast partition recovery into value @@ -1415,31 +1403,26 @@ func TestAgreementFastRecoveryLate(t *testing.T) { closeFn := baseNetwork.pocketAllCertVotes(pocket) baseNetwork.dropAllSlowNextVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } } triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.dropAllVotes() firstClocks := clocks[:4] @@ -1453,7 +1436,7 @@ func TestAgreementFastRecoveryLate(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() for i := range restClocks { @@ -1464,33 +1447,28 @@ func TestAgreementFastRecoveryLate(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(secondFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 1 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } for _, l := range ledgers { lastHash, err := l.LookupDigest(l.NextRound() - 1) - if err != nil { - panic(err) - } - if lastHash != expected.BlockDigest { - errstr := fmt.Sprintf("converged on wrong block: %v != %v", lastHash, expected.BlockDigest) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected.BlockDigest, lastHash, "converged on wrong block") } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1514,11 +1492,11 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force fast partition recovery into value @@ -1528,31 +1506,26 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { closeFn := baseNetwork.pocketAllCertVotes(pocket) baseNetwork.dropAllSlowNextVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } } triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.dropAllVotes() firstClocks := clocks[:4] @@ -1566,7 +1539,7 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() for i := range restClocks { @@ -1577,23 +1550,23 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(secondFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // fail period 1 with value again { baseNetwork.dropAllVotes() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.dropAllVotes() firstClocks := clocks[:4] @@ -1607,7 +1580,7 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() for i := range restClocks { @@ -1618,33 +1591,28 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(secondFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 2 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } for _, l := range ledgers { lastHash, err := l.LookupDigest(l.NextRound() - 1) - if err != nil { - panic(err) - } - if lastHash != expected.BlockDigest { - errstr := fmt.Sprintf("converged on wrong block: %v != %v", lastHash, expected.BlockDigest) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected.BlockDigest, lastHash, "converged on wrong block") } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1668,42 +1636,42 @@ func TestAgreementBlockReplayBug_b29ea57(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // fail period 0 { baseNetwork.dropAllSoftVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // fail period 1 on bottom with block { triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 2 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1727,11 +1695,11 @@ func TestAgreementLateCertBug(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // delay minority cert votes to force period 1 @@ -1739,12 +1707,12 @@ func TestAgreementLateCertBug(t *testing.T) { { closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() baseNetwork.repairAll() triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 0 in period 1 @@ -1756,12 +1724,12 @@ func TestAgreementLateCertBug(t *testing.T) { baseNetwork.finishAllMulticast() activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1785,11 +1753,11 @@ func TestAgreementRecoverGlobalStartingValue(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force partition recovery into value @@ -1799,28 +1767,23 @@ func TestAgreementRecoverGlobalStartingValue(t *testing.T) { closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } } triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 4, int(zeroes)) } @@ -1830,24 +1793,18 @@ func TestAgreementRecoverGlobalStartingValue(t *testing.T) { closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } - - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 5, int(zeroes)) } @@ -1856,13 +1813,13 @@ func TestAgreementRecoverGlobalStartingValue(t *testing.T) { { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 6, int(zeroes)) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -1885,11 +1842,11 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force partition recovery into value. @@ -1898,23 +1855,18 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { pocket := make(chan multicastParams, 100) closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } } // intercept all proposals for the next period; replace with unexpected @@ -1925,7 +1877,7 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { return params }) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 4, int(zeroes)) } @@ -1935,23 +1887,17 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { pocket := make(chan multicastParams, 100) closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } - - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } @@ -1959,13 +1905,13 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 6, int(zeroes)) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -1988,11 +1934,11 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force partition recovery into both bottom and value. one node enters bottom, the rest enter value @@ -2001,7 +1947,7 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { pocket := make(chan multicastParams, 100) closeFn := baseNetwork.pocketAllSoftVotes(pocket) triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() pocketedSoft := make([]multicastParams, len(pocket)) i := 0 @@ -2009,16 +1955,11 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { r := bytes.NewBuffer(params.data) var uv unauthenticatedVote err := protocol.DecodeStream(r, &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected soft vote: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected soft vote") } pocketedSoft[i] = params i++ @@ -2026,11 +1967,9 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { // generate a bottom quorum; let only one node see it. baseNetwork.crown(0) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - if clocks[0].(*testingClock).zeroes != zeroes+1 { - errstr := fmt.Sprintf("node 0 did not enter new period from bot quorum") - panic(errstr) - } - zeroes = expectNoNewPeriod(clocks[1:], zeroes) + require.Equal(t, zeroes+1, clocks[0].(*testingClock).zeroes, + "node 0 did not enter new period from bot quorum") + zeroes = expectNoNewPeriod(t, clocks[1:], zeroes) // enable creation of a value quorum; let everyone else see it baseNetwork.repairAll() @@ -2045,12 +1984,12 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { // actually create the value quorum _, upper := (next).nextVoteRanges(DeadlineTimeout(0, version)) triggerGlobalTimeout(upper, TimeoutDeadline, clocks[1:], activityMonitor) // activates next timers - zeroes = expectNoNewPeriod(clocks[1:], zeroes) + zeroes = expectNoNewPeriod(t, clocks[1:], zeroes) lower, upper := (next + 1).nextVoteRanges(DeadlineTimeout(0, version)) delta := time.Duration(testingRand{}.Uint64() % uint64(upper-lower)) triggerGlobalTimeout(lower+delta, TimeoutDeadline, clocks[1:], activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 4, int(zeroes)) } @@ -2060,37 +1999,31 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { pocket := make(chan multicastParams, 100) closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } - - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected, uv.R.Proposal, "got unexpected proposal") } triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // Finish in period 2 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 6, int(zeroes)) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2113,11 +2046,11 @@ func TestAgreementSlowPayloadsPreDeadline(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // run round and then start pocketing payloads @@ -2125,13 +2058,13 @@ func TestAgreementSlowPayloadsPreDeadline(t *testing.T) { closeFn := baseNetwork.pocketAllCompound(pocket) // (takes effect next round) { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run round with late payload { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) // release payloads; expect new round closeFn() @@ -2143,12 +2076,12 @@ func TestAgreementSlowPayloadsPreDeadline(t *testing.T) { baseNetwork.finishAllMulticast() activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2171,11 +2104,11 @@ func TestAgreementSlowPayloadsPostDeadline(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // run round and then start pocketing payloads @@ -2183,15 +2116,15 @@ func TestAgreementSlowPayloadsPostDeadline(t *testing.T) { closeFn := baseNetwork.pocketAllCompound(pocket) // (takes effect next round) { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // force network into period 1 by delaying proposals { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // recover in period 1 @@ -2205,15 +2138,15 @@ func TestAgreementSlowPayloadsPostDeadline(t *testing.T) { baseNetwork.finishAllMulticast() activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2236,11 +2169,11 @@ func TestAgreementLargePeriods(t *testing.T) { activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // partition the network, run until period 60 @@ -2248,11 +2181,11 @@ func TestAgreementLargePeriods(t *testing.T) { { baseNetwork.partition(0, 1, 2) triggerGlobalTimeout(FilterTimeout(period(p), version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() triggerGlobalTimeout(DeadlineTimeout(period(p), version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 4+p, int(zeroes)) } } @@ -2260,12 +2193,12 @@ func TestAgreementLargePeriods(t *testing.T) { // terminate { triggerGlobalTimeout(FilterTimeout(60, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2273,9 +2206,8 @@ func TestAgreementLargePeriods(t *testing.T) { const expectNumRounds = 5 for i := 0; i < numNodes; i++ { - if ledgers[i].NextRound() != startRound+round(expectNumRounds) { - panic("did not progress 5 rounds") - } + require.Equal(t, startRound+round(expectNumRounds), ledgers[i].NextRound(), + "did not progress 5 rounds") } for j := 0; j < expectNumRounds; j++ { @@ -2283,9 +2215,7 @@ func TestAgreementLargePeriods(t *testing.T) { reference := ledger.entries[startRound+round(j)].Digest() for i := 0; i < numNodes; i++ { ledger := ledgers[i].(*testLedger) - if ledger.entries[startRound+round(j)].Digest() != reference { - panic("wrong block confirmed") - } + require.Equal(t, reference, ledger.entries[startRound+round(j)].Digest(), "wrong block confirmed") } } } @@ -2334,11 +2264,11 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // run round and then start pocketing payloads, suspending validation @@ -2347,13 +2277,13 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 closeFn := baseNetwork.pocketAllCompound(pocket0) // (takes effect next round) { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // force network into period 1 by failing period 0, entering with bottom and no soft threshold (to prevent proposal value pinning) baseNetwork.dropAllSoftVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) // resume delivery of payloads in following period baseNetwork.repairAll() @@ -2440,15 +2370,15 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 } baseNetwork.finishAllMulticast() - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) activityMonitor.waitForQuiet() // run two more rounds //for j := 0; j < 2; j++ { - // zeroes = runRound(clocks, activityMonitor, zeroes, period(1-j)) + // zeroes = runRound(t, clocks, activityMonitor, zeroes, period(1-j)) //} - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(1, version)) - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(1, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2456,9 +2386,8 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 const expectNumRounds = 5 for i := 0; i < numNodes; i++ { - if ledgers[i].NextRound() != startRound+round(expectNumRounds) { - panic("did not progress 5 rounds") - } + require.Equal(t, startRound+round(expectNumRounds), ledgers[i].NextRound(), + "did not progress 5 rounds") } for j := 0; j < expectNumRounds; j++ { @@ -2466,9 +2395,7 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 reference := ledger.entries[startRound+round(j)].Digest() for i := 0; i < numNodes; i++ { ledger := ledgers[i].(*testLedger) - if ledger.entries[startRound+round(j)].Digest() != reference { - panic("wrong block confirmed") - } + require.Equal(t, reference, ledger.entries[startRound+round(j)].Digest(), "wrong block confirmed") } } } @@ -2491,17 +2418,15 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) // make sure relay does not see block proposal for round 3 baseNetwork.intercept(func(params multicastParams) multicastParams { if params.tag == protocol.ProposalPayloadTag { var tp transmittedPayload err := protocol.DecodeStream(bytes.NewBuffer(params.data), &tp) - if err != nil { - panic(err) - } + require.NoError(t, err) if tp.Round() == basics.Round(startRound+2) { params.exclude = relayID } @@ -2512,9 +2437,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { if params.tag == protocol.AgreementVoteTag { var uv unauthenticatedVote err := protocol.DecodeStream(r, &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if uv.R.Step != propose { return params } @@ -2524,7 +2447,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { return params }) - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) // Round 3: // First partition the relay to prevent it from seeing certificate or block @@ -2537,9 +2460,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { r := bytes.NewBuffer(params.data) var uv unauthenticatedVote err := protocol.DecodeStream(r, &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if uv.R.Step == cert { pocketCert <- params } @@ -2548,7 +2469,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { }) // And with some hypothetical second relay the network achieves consensus on a certificate and block. triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks[1:], zeroes) + zeroes = expectNewPeriod(t, clocks[1:], zeroes) require.Equal(t, uint(3), clocks[0].(*testingClock).zeroes) close(pocketCert) @@ -2567,7 +2488,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { // this relay must still relay initial messages. Note that payloads were already relayed with // the previous global timeout. triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks[1:], activityMonitor) - zeroes = expectNewPeriod(clocks[1:], zeroes) + zeroes = expectNewPeriod(t, clocks[1:], zeroes) require.Equal(t, uint(3), clocks[0].(*testingClock).zeroes) for i := 0; i < numNodes; i++ { @@ -2575,18 +2496,15 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { } const expectNumRounds = 4 for i := 1; i < numNodes; i++ { - if ledgers[i].NextRound() != startRound+round(expectNumRounds) { - panic("did not progress 4 rounds") - } + require.Equal(t, startRound+round(expectNumRounds), ledgers[i].NextRound(), + "did not progress 4 rounds") } for j := 0; j < expectNumRounds; j++ { ledger := ledgers[1].(*testLedger) reference := ledger.entries[startRound+round(j)].Digest() for i := 1; i < numNodes; i++ { ledger := ledgers[i].(*testLedger) - if ledger.entries[startRound+round(j)].Digest() != reference { - panic("wrong block confirmed") - } + require.Equal(t, reference, ledger.entries[startRound+round(j)].Digest(), "wrong block confirmed") } } } diff --git a/config/consensus.go b/config/consensus.go index c49414e0cb..b8ba4f48b3 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -597,7 +597,7 @@ var miningRules = [...]MiningRules{ Enabled: true, Percent: 75, GoOnlineFee: 2_000_000, // 2 algos - MinBalance: 10_000_000_000, // 10,000 algos + MinBalance: 30_000_000_000, // 30,000 algos MaxBalance: 50_000_000_000_000, // 50M algos MaxMarkAbsent: 32, ChallengeInterval: 1000, diff --git a/ledger/double_test.go b/ledger/double_test.go index 00ae863399..5dd3a31868 100644 --- a/ledger/double_test.go +++ b/ledger/double_test.go @@ -202,7 +202,10 @@ func checkBlock(t testing.TB, checkLedger *Ledger, vb *ledgercore.ValidatedBlock require.NoError(t, err, "%+v", reconstituted.Payset) } check.SetGenerateForTesting(true) - cb := endBlock(t, checkLedger, check, vb.Block().Proposer) + // We use the same value for seed and proposer. But the proposer is + // sometimes zero'd to account for mining being disabled. So we provide we + // get the blocks to match by providing the Seed as the proposer. + cb := endBlock(t, checkLedger, check, basics.Address(vb.Block().BlockHeader.Seed)) check.SetGenerateForTesting(false) require.Equal(t, vb.Block(), cb.Block()) diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 1073964006..3f1af17d75 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -224,92 +224,84 @@ func TestMiningFees(t *testing.T) { dl := NewDoubleLedger(t, genBalances, cv, cfg) defer dl.Close() - tooBig := basics.Address{0x01, 0x011} - tooSmall := basics.Address{0x01, 0x022} - smallest := basics.Address{0x01, 0x033} - biggest := basics.Address{0x01, 0x044} - + proposer := basics.Address{0x01, 0x011} const eFee = 3_000_000 - dl.txns( - &txntest.Txn{Type: "pay", Sender: addrs[1], - Receiver: tooBig, Amount: eFee + 100_000_000*1_000_000 + 1}, - &txntest.Txn{Type: "pay", Sender: addrs[1], - Receiver: tooSmall, Amount: eFee + 100_000*1_000_000 - 1}, - &txntest.Txn{Type: "pay", Sender: addrs[1], - Receiver: smallest, Amount: eFee + 100_000*1_000_000}, + dl.txn( &txntest.Txn{Type: "pay", Sender: addrs[1], - Receiver: biggest, Amount: eFee + 100_000_000*1_000_000}, + Receiver: proposer, Amount: eFee + 50_000_000*1_000_000 + 1}, ) - for _, proposer := range []basics.Address{tooBig, tooSmall, smallest, biggest} { - t.Log(proposer) + prp := lookup(dl.t, dl.generator, proposer) + require.False(t, prp.IncentiveEligible) - prp := lookup(dl.t, dl.generator, proposer) - require.False(t, prp.IncentiveEligible) + dl.txn(&txntest.Txn{ + Type: "keyreg", + Sender: proposer, + Fee: eFee, + VotePK: crypto.OneTimeSignatureVerifier{0x01}, + SelectionPK: crypto.VRFVerifier{0x02}, + StateProofPK: merklesignature.Commitment{0x03}, + VoteFirst: 1, VoteLast: 1000, + }) - dl.txn(&txntest.Txn{ - Type: "keyreg", - Sender: proposer, - Fee: eFee, - VotePK: crypto.OneTimeSignatureVerifier{0x01}, - SelectionPK: crypto.VRFVerifier{0x02}, - StateProofPK: merklesignature.Commitment{0x03}, - VoteFirst: 1, VoteLast: 1000, - }) + prp = lookup(dl.t, dl.generator, proposer) + require.Equal(t, ver >= miningBegins, prp.IncentiveEligible) - prp = lookup(dl.t, dl.generator, proposer) - require.Equal(t, ver >= miningBegins, prp.IncentiveEligible) + dl.fullBlock() // start with an empty block, so no mining fees are paid at start of next one - dl.fullBlock() // start with an empty block, so no mining fees are paid at start of next one + presink := micros(dl.t, dl.generator, genBalances.FeeSink) + preprop := micros(dl.t, dl.generator, proposer) + t.Log(" presink", presink) + t.Log(" preprop", preprop) + dl.beginBlock() + pay := txntest.Txn{ + Type: "pay", + Sender: addrs[1], + Receiver: addrs[2], + Amount: 100000, + } + dl.txns(&pay, pay.Args("again")) + vb := dl.endBlock(proposer) + + const bonus1 = 5_000_000 // the first bonus value, set in + if ver >= miningBegins { + require.True(t, dl.generator.GenesisProto().Mining().Enabled) // version sanity check + require.NotZero(t, dl.generator.GenesisProto().Mining().Percent) // version sanity check + // new fields are in the header + require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) + require.EqualValues(t, bonus1, vb.Block().Bonus.Raw) + require.EqualValues(t, bonus1+1_500, vb.Block().ProposerPayout.Raw) + } else { + require.False(t, dl.generator.GenesisProto().Mining().Enabled) + require.Zero(t, dl.generator.GenesisProto().Mining().Percent) // version sanity check + require.Zero(t, vb.Block().FeesCollected) + require.Zero(t, vb.Block().Bonus) + require.Zero(t, vb.Block().ProposerPayout) + } - presink := micros(dl.t, dl.generator, genBalances.FeeSink) - preprop := micros(dl.t, dl.generator, proposer) - t.Log(" presink", presink) - t.Log(" preprop", preprop) - dl.beginBlock() - pay := txntest.Txn{ - Type: "pay", - Sender: addrs[1], - Receiver: addrs[2], - Amount: 100000, - } - dl.txns(&pay, pay.Args("again")) - vb := dl.endBlock(proposer) - - if ver >= miningBegins { - require.True(t, dl.generator.GenesisProto().Mining().Enabled) // version sanity check - require.NotZero(t, dl.generator.GenesisProto().Mining().Percent) // version sanity check - // new fields are in the header - require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) - } else { - require.False(t, dl.generator.GenesisProto().Mining().Enabled) - require.Zero(t, dl.generator.GenesisProto().Mining().Percent) // version sanity check - // new fields are not in the header - require.Zero(t, vb.Block().FeesCollected) - } + postsink := micros(dl.t, dl.generator, genBalances.FeeSink) + postprop := micros(dl.t, dl.generator, proposer) + t.Log(" postsink", postsink) + t.Log(" postprop", postprop) - postsink := micros(dl.t, dl.generator, genBalances.FeeSink) - postprop := micros(dl.t, dl.generator, proposer) - t.Log(" postsink", postsink) - t.Log(" postprop", postprop) + // At the end of the block, all fees are still in the sink. + require.EqualValues(t, 2000, postsink-presink) + require.EqualValues(t, 0, postprop-preprop) - // At the end of the block, all fees are still in the sink. + // Do the next block, which moves part of the fees + bonus to proposer + dl.fullBlock() + postsink = micros(dl.t, dl.generator, genBalances.FeeSink) + postprop = micros(dl.t, dl.generator, proposer) + t.Log(" postsink2", postsink) + t.Log(" postprop2", postprop) + + if ver >= miningBegins { + require.EqualValues(t, bonus1+1500, postprop-preprop) // based on 75% in config/consensus.go + require.EqualValues(t, bonus1-500, presink-postsink) + } else { + // stayed in the feesink + require.EqualValues(t, 0, postprop-preprop, "%v", proposer) require.EqualValues(t, 2000, postsink-presink) - //require.EqualValues(t, 0, postprop-preprop) - - // Do the next block, which moves part of the fees to proposer - dl.fullBlock() - postsink = micros(dl.t, dl.generator, genBalances.FeeSink) - postprop = micros(dl.t, dl.generator, proposer) - - if ver >= miningBegins && (proposer == smallest || proposer == biggest) { - require.EqualValues(t, 500, postsink-presink) // based on 75% in config/consensus.go - require.EqualValues(t, 1500, postprop-preprop) - } else { - // stayed in the feesink - require.EqualValues(t, 0, postprop-preprop, "%v", proposer) - require.EqualValues(t, 2000, postsink-presink) - } } }) } diff --git a/ledger/simple_test.go b/ledger/simple_test.go index a8922b7d45..9db2d90c37 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -153,8 +153,16 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer } // We have this backdoor way to install a proposer or seed into the header - // for tests. Doesn't matter that it makes them both the same. - *vb = vb.WithProposal(committee.Seed(prp), prp, true) + // for tests. Doesn't matter that it makes them both the same. Since this + // can't call the agreement code, the eligibility of the prp is not + // considered. + if ledger.GenesisProto().Mining().Enabled { + *vb = vb.WithProposal(committee.Seed(prp), prp, true) + } else { + // To more closely mimic the agreement code, we don't + // write the proposer when !Mining().Enabled. + *vb = vb.WithProposal(committee.Seed(prp), basics.Address{}, false) + } err = ledger.AddValidatedBlock(*vb, agreement.Certificate{}) require.NoError(t, err) From 693ba6126992eb86e04f44ee43d4429681ea1f72 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 6 Mar 2024 15:50:15 -0500 Subject: [PATCH 047/117] max size change for proposerPayout --- protocol/tags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/tags.go b/protocol/tags.go index 3b59b3db86..6cfcacd714 100644 --- a/protocol/tags.go +++ b/protocol/tags.go @@ -73,7 +73,7 @@ const PingReplyTagMaxSize = 8 // ProposalPayloadTagMaxSize is the maximum size of a ProposalPayloadTag message // This value is dominated by the MaxTxnBytesPerBlock -const ProposalPayloadTagMaxSize = 5250301 +const ProposalPayloadTagMaxSize = 5250313 // StateProofSigTagMaxSize is the maximum size of a StateProofSigTag message const StateProofSigTagMaxSize = 6378 From 6db9d51ffc0091d3611a35df3507c0b409ea836f Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 7 Mar 2024 09:41:43 -0500 Subject: [PATCH 048/117] Correctly learn consensus params for current proposal. --- agreement/proposal.go | 6 ++++-- test/e2e-go/features/incentives/mining_test.go | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index 47e10871f5..ebde097636 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -21,7 +21,6 @@ import ( "fmt" "time" - "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" @@ -197,7 +196,10 @@ func verifyNewSeed(p unauthenticatedProposal, ledger LedgerReader) error { value := p.value() rnd := p.Round() - curParams := config.Consensus[p.BlockHeader.CurrentProtocol] + curParams, err := ledger.ConsensusParams(rnd) + if err != nil { + return fmt.Errorf("failed to obtain consensus parameters in round %d: %v", ParamsRound(rnd), err) + } if curParams.Mining().Enabled { if p.BlockHeader.Proposer != value.OriginalProposer { return fmt.Errorf("payload has wrong proposer (%v != %v)", p.Proposer, value.OriginalProposer) diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index ab47cf790b..2386bbd520 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -59,12 +59,26 @@ func TestBasicMining(t *testing.T) { // wait for richAccount to have LastProposed != 0 account, err := client.AccountData(richAccount.Address) + a.NoError(err) fmt.Printf(" rich balance %v %d\n", richAccount.Address, account.MicroAlgos) + waits := 0 for account.LastProposed == 0 { a.NoError(err) a.Equal(basics.Online, account.Status) account, err = client.AccountData(richAccount.Address) + if account.LastProposed > 0 { + break + } + status, err := client.Status() + a.NoError(err) + block, err := client.BookkeepingBlock(status.LastRound) + a.NoError(err) + fmt.Printf(" block proposed by %v\n", block.Proposer) time.Sleep(roundTime) + waits++ + if waits > 15 { + panic(fmt.Sprintf(" timeout at rnd %d waiting for proposal by %v\n", status.LastRound, account)) + } } a.NoError(err) From 4eac1368c95c3b07f49c905d862377ca6bddc4de Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 8 Mar 2024 12:56:52 -0500 Subject: [PATCH 049/117] Fixups to get the separation between eval and agreement right --- agreement/proposal.go | 87 ++++++++++--------- config/consensus.go | 8 +- data/datatest/impls.go | 12 ++- ledger/eval/eval.go | 62 ++++++++++--- ledger/eval/eval_test.go | 67 +++++++------- .../e2e-go/features/incentives/mining_test.go | 2 +- 6 files changed, 140 insertions(+), 98 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index ebde097636..f8ce9c8af3 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -189,25 +189,28 @@ func deriveNewSeed(address basics.Address, vrf *crypto.VRFSecrets, rnd round, pe return } -// verifyNewSeed checks the things in the header that can only be confirmed by -// looking into the unauthenticatedProposal, namely the BlockSeed and the -// Proposer. -func verifyNewSeed(p unauthenticatedProposal, ledger LedgerReader) error { +// verifyHeader checks the things in the header that can only be confirmed by +// looking into the unauthenticatedProposal or using LookupAgreement. The +// Proposer, ProposerPayout, and Seed. +func verifyHeader(p unauthenticatedProposal, ledger LedgerReader) error { value := p.value() rnd := p.Round() - curParams, err := ledger.ConsensusParams(rnd) - if err != nil { - return fmt.Errorf("failed to obtain consensus parameters in round %d: %v", ParamsRound(rnd), err) + // ledger.ConsensusParams(rnd) is not allowed because rnd isn't committed. + // The BlockHeader isn't trustworthy yet, since we haven't checked the + // upgrade state. So, for now, we confirm that the BlockSeed is *either* + // correct or missing. `eval` package will using Mining().Enabled to confirm + // which it should be. + if !p.BlockHeader.Proposer.IsZero() && p.BlockHeader.Proposer != value.OriginalProposer { + return fmt.Errorf("wrong proposer (%v != %v)", p.Proposer, value.OriginalProposer) } - if curParams.Mining().Enabled { - if p.BlockHeader.Proposer != value.OriginalProposer { - return fmt.Errorf("payload has wrong proposer (%v != %v)", p.Proposer, value.OriginalProposer) - } - } else { - if !p.BlockHeader.Proposer.IsZero() { - return fmt.Errorf("payload has a proposer %v when Mining disabled", p.Proposer) - } + + // Similarly, we only check here that the payout is zero if + // ineligibile. `eval` code must check that it is correct if > 0. + eligible, err := payoutEligible(rnd, p.Proposer, ledger) + if !eligible && p.BlockHeader.ProposerPayout.Raw > 0 { + return fmt.Errorf("proposer payout (%d) for ineligible Proposer %v", + p.BlockHeader.ProposerPayout.Raw, p.Proposer) } cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) @@ -224,14 +227,14 @@ func verifyNewSeed(p unauthenticatedProposal, ledger LedgerReader) error { var alpha crypto.Digest prevSeed, err := ledger.Seed(seedRound(rnd, cparams)) if err != nil { - return fmt.Errorf("failed read seed of round %d: %v", seedRound(rnd, cparams), err) + return fmt.Errorf("failed to read seed of round %d: %v", seedRound(rnd, cparams), err) } if value.OriginalPeriod == 0 { verifier := proposerRecord.SelectionID ok, _ := verifier.Verify(p.SeedProof, prevSeed) // ignoring VrfOutput returned by Verify if !ok { - return fmt.Errorf("payload seed proof malformed (%v, %v)", prevSeed, p.SeedProof) + return fmt.Errorf("seed proof malformed (%v, %v)", prevSeed, p.SeedProof) } // TODO remove the following Hash() call, // redundant with the Verify() call above. @@ -257,39 +260,39 @@ func verifyNewSeed(p unauthenticatedProposal, ledger LedgerReader) error { input.History = oldDigest } if p.Seed() != committee.Seed(crypto.HashObj(input)) { - return fmt.Errorf("payload seed malformed (%v != %v)", committee.Seed(crypto.HashObj(input)), p.Seed()) + return fmt.Errorf("seed malformed (%v != %v)", committee.Seed(crypto.HashObj(input)), p.Seed()) } return nil } -// payoutForBlock determines whether the proposer ought to be recorded, and -// whether that proposer ought to be paid for proposing. -func payoutForBlock(blk bookkeeping.Block, proposer basics.Address, ledger LedgerReader) (recorded basics.Address, eligible bool, err error) { - rnd := blk.Round() - proto, err := ledger.ConsensusParams(rnd) - if err != nil || !proto.Mining().Enabled { - return recorded, eligible, err // err may or may not be nil, either way, others are zero'd - } - - // We want to check eligibility and the online balance in the round that - // mattered to select this proposer. We use the ParamsRound() to find the - // round in question, not current proto. +// payoutEligible determines whether the proposer is eligible for block incentive payout. +func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerReader) (bool, error) { + // We want to check eligibility of the online balance in the round that + // mattered to select this proposer. agreementParams, err := ledger.ConsensusParams(ParamsRound(rnd)) if err != nil { - return recorded, false, err + return false, err } // Check the balance from the agreement round balanceRound := balanceRound(rnd, agreementParams) balanceRecord, err := ledger.LookupAgreement(balanceRound, proposer) if err != nil { - return recorded, false, err + return false, err + } + + balanceParams, err := ledger.ConsensusParams(balanceRound) + if err != nil { + return false, err } - eligible = balanceRecord.IncentiveEligible && - balanceRecord.MicroAlgosWithRewards.Raw >= proto.Mining().MinBalance && - balanceRecord.MicroAlgosWithRewards.Raw <= proto.Mining().MaxBalance - return proposer, eligible, nil + // It's only fair to compare balance's from 320 rounds ago to the mining + // rules that were in effect then. To make this work in a reasonable way + // when mining begins, the miningRules[0] in consensus.go has Min and Max. + eligible := balanceRecord.IncentiveEligible && + balanceRecord.MicroAlgosWithRewards.Raw >= balanceParams.Mining().MinBalance && + balanceRecord.MicroAlgosWithRewards.Raw <= balanceParams.Mining().MaxBalance + return eligible, nil } func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve ValidatedBlock, period period, ledger LedgerReader) (proposal, proposalValue, error) { @@ -299,12 +302,12 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve Validat return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could not derive new seed: %w", err) } - hdrProp, eligible, err := payoutForBlock(blk, address, ledger) + eligible, err := payoutEligible(blk.Round(), address, ledger) if err != nil { - return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could determine payout: %w", err) + return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could determine eligibility: %w", err) } - ve = ve.WithProposal(newSeed, hdrProp, eligible) + ve = ve.WithProposal(newSeed, address, eligible) proposal := makeProposal(ve, seedProof, period, address) value := proposalValue{ OriginalPeriod: period, @@ -325,14 +328,14 @@ func (p unauthenticatedProposal) validate(ctx context.Context, current round, le return invalid, fmt.Errorf("proposed entry from wrong round: entry.Round() != current: %v != %v", entry.Round(), current) } - err := verifyNewSeed(p, ledger) + err := verifyHeader(p, ledger) if err != nil { - return invalid, fmt.Errorf("proposal has bad seed: %v", err) + return invalid, fmt.Errorf("unable to verify header: %w", err) } ve, err := validator.Validate(ctx, entry) if err != nil { - return invalid, fmt.Errorf("EntryValidator rejected entry: %v", err) + return invalid, fmt.Errorf("EntryValidator rejected entry: %w", err) } return makeProposal(ve, p.SeedProof, p.OriginalPeriod, p.OriginalProposer), nil diff --git a/config/consensus.go b/config/consensus.go index b8ba4f48b3..b515c0c9e1 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -592,7 +592,13 @@ type MiningRules struct { // miningRules should be extended, never changed, since old blocks must retain // their behavior. var miningRules = [...]MiningRules{ - {Enabled: false}, + { + Enabled: false, + // Because the eligibility check has a lookback, we need these set even + // here before mining begins. + MinBalance: 30_000_000_000, // 30,000 algos + MaxBalance: 50_000_000_000_000, // 50M algos + }, { Enabled: true, Percent: 75, diff --git a/data/datatest/impls.go b/data/datatest/impls.go index c4c251c726..136ea2e1c2 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -68,10 +68,18 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated func (ve validatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { newblock := *ve.blk newblock.BlockHeader.Seed = s - newblock.BlockHeader.Proposer = proposer - if !eligible { + + // agreement is telling us who the proposer is and if they're eligible, but + // agreement does not consider the current config params, so here we decide + // what really goes into the BlockHeader. + proto := config.Consensus[ve.blk.CurrentProtocol] + if proto.Mining().Enabled { + newblock.BlockHeader.Proposer = proposer + } + if !proto.Mining().Enabled || !eligible { newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} } + return validatedBlock{blk: &newblock} } diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 73f3869fb7..4f48858753 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -784,8 +784,6 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return nil, err } - // Move last block's proposer payout to the proposer - // ensure that we have at least MinBalance after withdrawing rewards ot.SubA(poolNew.MicroAlgos, basics.MicroAlgos{Raw: proto.MinBalance}) if ot.Overflowed { @@ -793,6 +791,7 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return nil, fmt.Errorf("overflowed subtracting rewards for block %v", hdr.Round) } + // Move last block's proposer payout to the proposer if !prevHeader.Proposer.IsZero() { // Payout the previous proposer. if !prevHeader.ProposerPayout.IsZero() { @@ -1329,19 +1328,10 @@ func (eval *BlockEvaluator) endOfBlock() error { // can cancel this payment by zero'ing the ProposerPayout if the // proposer is found to be ineligible. See WithProposal(). eval.block.FeesCollected = eval.state.feesCollected - - incentive, _ := basics.NewPercent(eval.proto.Mining().Percent).DivvyAlgos(eval.block.FeesCollected) - total, o := basics.OAddA(incentive, eval.block.Bonus) - if o { - return fmt.Errorf("overflowed adding bonus incentive %d %d", incentive, eval.block.Bonus) - } - - sink, lerr := eval.state.lookup(eval.block.FeeSink) - if lerr != nil { - return lerr + eval.block.ProposerPayout, err = eval.proposerPayout() + if err != nil { + return err } - available := sink.AvailableBalance(&eval.proto) - eval.block.ProposerPayout = basics.MinA(total, available) } eval.generateKnockOfflineAccountsList() @@ -1400,6 +1390,35 @@ func (eval *BlockEvaluator) endOfBlock() error { return fmt.Errorf("fees collected wrong: %v != %v", eval.block.FeesCollected, expectedFeesCollected) } + // agreement will check that the proposer is correct (we can't because + // we don't see the bundle), but agreement allows the proposer to be set + // even if Mining is not enabled (and unset any time). So make sure + // it's set iff it should be. + if eval.proto.Mining().Enabled { + if eval.block.Proposer.IsZero() && !eval.generate { // if generating, proposer is set later by agreement + return fmt.Errorf("proposer missing when mining enabled") + } + } else { + if !eval.block.Proposer.IsZero() { + return fmt.Errorf("proposer %v present when mining disabled", eval.block.Proposer) + } + } + + // agreement will check that the payout is zero if the proposer is + // ineligible, but we must check that it is correct if non-zero. We allow it + // to be too low. A proposer can be algruistic. + maxPayout := uint64(0) + if eval.proto.Mining().Enabled { + payout, err := eval.proposerPayout() + if err != nil { + return err + } + maxPayout = payout.Raw + } + if eval.block.ProposerPayout.Raw > maxPayout { + return fmt.Errorf("proposal wants %d payout, %d is allowed", eval.block.ProposerPayout.Raw, maxPayout) + } + expectedVoters, expectedVotersWeight, err2 := eval.stateProofVotersAndTotal() if err2 != nil { return err2 @@ -1453,6 +1472,21 @@ func (eval *BlockEvaluator) endOfBlock() error { return nil } +func (eval *BlockEvaluator) proposerPayout() (basics.MicroAlgos, error) { + incentive, _ := basics.NewPercent(eval.proto.Mining().Percent).DivvyAlgos(eval.block.FeesCollected) + total, o := basics.OAddA(incentive, eval.block.Bonus) + if o { + return basics.MicroAlgos{}, fmt.Errorf("payout overflowed adding bonus incentive %d %d", incentive, eval.block.Bonus) + } + + sink, err := eval.state.lookup(eval.block.FeeSink) + if err != nil { + return basics.MicroAlgos{}, err + } + available := sink.AvailableBalance(&eval.proto) + return basics.MinA(total, available), nil +} + type challenge struct { // round is when the challenge occurred. 0 means this is not a challenge. round basics.Round diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index f935288d38..9bcb131fe1 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -34,6 +34,7 @@ import ( "github.com/algorand/go-algorand/data/basics" basics_testing "github.com/algorand/go-algorand/data/basics/testing" "github.com/algorand/go-algorand/data/bookkeeping" + "github.com/algorand/go-algorand/data/committee" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/data/transactions/logic/mocktracer" @@ -182,8 +183,7 @@ func TestEvalAppStateCountsWithTxnGroup(t *testing.T) { partitiontest.PartitionTest(t) _, _, err := testEvalAppGroup(t, basics.StateSchema{NumByteSlice: 1}) - require.Error(t, err) - require.Contains(t, err.Error(), "store bytes count 2 exceeds schema bytes count 1") + require.ErrorContains(t, err, "store bytes count 2 exceeds schema bytes count 1") } // TestEvalAppAllocStateWithTxnGroup ensures roundCowState.deltas and applyStorageDelta @@ -213,7 +213,7 @@ func TestTestTransactionGroup(t *testing.T) { eval.proto = config.Consensus[protocol.ConsensusCurrentVersion] txgroup = make([]transactions.SignedTxn, eval.proto.MaxTxGroupSize+1) err = eval.TestTransactionGroup(txgroup) - require.Error(t, err) // too many + require.ErrorContains(t, err, "group size") } // test BlockEvaluator.transactionGroup() @@ -229,7 +229,7 @@ func TestPrivateTransactionGroup(t *testing.T) { eval.proto = config.Consensus[protocol.ConsensusCurrentVersion] txgroup = make([]transactions.SignedTxnWithAD, eval.proto.MaxTxGroupSize+1) err = eval.TransactionGroup(txgroup) - require.Error(t, err) // too many + require.ErrorContains(t, err, "group size") } func TestTransactionGroupWithTracer(t *testing.T) { @@ -647,7 +647,7 @@ func testnetFixupExecution(t *testing.T, headerRound basics.Round, poolBonus uin // won't work before funding bank if poolBonus > 0 { _, err = eval.workaroundOverspentRewards(rewardPoolBalance, headerRound) - require.Error(t, err) + require.ErrorContains(t, err, "unable to move funds from testnet bank") } bankAddr, _ := basics.UnmarshalChecksumAddress("GD64YIY3TWGDMCNPP553DZPPR6LDUSFQOIJVFDPPXWEG3FVOJCCDBBHU5A") @@ -1206,6 +1206,9 @@ func TestEvalFunctionForExpiredAccounts(t *testing.T) { validatedBlock, err := blkEval.GenerateBlock() require.NoError(t, err) + // fake agreement's setting of header fields so later validates work + *validatedBlock = validatedBlock.WithProposal(committee.Seed{}, testPoolAddr, true) + expired := false for _, acct := range validatedBlock.Block().ExpiredParticipationAccounts { if acct == recvAddr { @@ -1222,51 +1225,40 @@ func TestEvalFunctionForExpiredAccounts(t *testing.T) { require.Zero(t, acctData.StateProofID) require.Zero(t, acctData.SelectionID) require.Zero(t, acctData.VoteID) - badBlock := *validatedBlock + goodBlock := validatedBlock.Block() - // First validate that bad block is fine if we dont touch it... - _, err = Eval(context.Background(), l, badBlock.Block(), true, verify.GetMockedCache(true), nil, l.tracer) + // First validate that it's fine if we dont touch it. + _, err = Eval(context.Background(), l, goodBlock, true, verify.GetMockedCache(true), nil, l.tracer) require.NoError(t, err) - badBlock = *validatedBlock - // Introduce an unknown address to introduce an error - badBlockObj := badBlock.Block() - badBlockObj.ExpiredParticipationAccounts = append(badBlockObj.ExpiredParticipationAccounts, basics.Address{1}) - badBlock = ledgercore.MakeValidatedBlock(badBlockObj, badBlock.Delta()) - - _, err = Eval(context.Background(), l, badBlock.Block(), true, verify.GetMockedCache(true), nil, l.tracer) - require.Error(t, err) - - badBlock = *validatedBlock + badBlock := goodBlock + badBlock.ExpiredParticipationAccounts = append(badBlock.ExpiredParticipationAccounts, basics.Address{1}) - addressToCopy := badBlock.Block().ExpiredParticipationAccounts[0] + _, err = Eval(context.Background(), l, badBlock, true, verify.GetMockedCache(true), nil, l.tracer) + require.ErrorContains(t, err, "expiration candidate") // Add more than the expected number of accounts - badBlockObj = badBlock.Block() + badBlock = goodBlock + addressToCopy := badBlock.ExpiredParticipationAccounts[0] for i := 0; i < blkEval.proto.MaxProposedExpiredOnlineAccounts+1; i++ { - badBlockObj.ExpiredParticipationAccounts = append(badBlockObj.ExpiredParticipationAccounts, addressToCopy) + badBlock.ExpiredParticipationAccounts = append(badBlock.ExpiredParticipationAccounts, addressToCopy) } - badBlock = ledgercore.MakeValidatedBlock(badBlockObj, badBlock.Delta()) - _, err = Eval(context.Background(), l, badBlock.Block(), true, verify.GetMockedCache(true), nil, l.tracer) - require.Error(t, err) - - badBlock = *validatedBlock + _, err = Eval(context.Background(), l, badBlock, true, verify.GetMockedCache(true), nil, l.tracer) + require.ErrorContains(t, err, "length of expired accounts") // Duplicate an address - badBlockObj = badBlock.Block() - badBlockObj.ExpiredParticipationAccounts = append(badBlockObj.ExpiredParticipationAccounts, badBlockObj.ExpiredParticipationAccounts[0]) - badBlock = ledgercore.MakeValidatedBlock(badBlockObj, badBlock.Delta()) + badBlock = goodBlock + badBlock.ExpiredParticipationAccounts = append(badBlock.ExpiredParticipationAccounts, addressToCopy) - _, err = Eval(context.Background(), l, badBlock.Block(), true, verify.GetMockedCache(true), nil, l.tracer) - require.Error(t, err) + _, err = Eval(context.Background(), l, badBlock, true, verify.GetMockedCache(true), nil, l.tracer) + require.ErrorContains(t, err, "duplicate address found") - badBlock = *validatedBlock + badBlock = goodBlock // sanity check that bad block is being actually copied and not just the pointer - _, err = Eval(context.Background(), l, badBlock.Block(), true, verify.GetMockedCache(true), nil, l.tracer) + _, err = Eval(context.Background(), l, badBlock, true, verify.GetMockedCache(true), nil, l.tracer) require.NoError(t, err) - } type failRoundCowParent struct { @@ -1353,17 +1345,16 @@ func TestExpiredAccountGenerationWithDiskFailure(t *testing.T) { eval.block.ExpiredParticipationAccounts = append(eval.block.ExpiredParticipationAccounts, recvAddr) err = eval.endOfBlock() - require.Error(t, err) + require.ErrorContains(t, err, "found expiration candidate") eval.block.ExpiredParticipationAccounts = []basics.Address{{}} eval.state.mods.Accts = ledgercore.AccountDeltas{} eval.state.lookupParent = &failRoundCowParent{} err = eval.endOfBlock() - require.Error(t, err) + require.ErrorContains(t, err, "disk I/O fail (on purpose)") err = eval.resetExpiredOnlineAccountsParticipationKeys() - require.Error(t, err) - + require.ErrorContains(t, err, "disk I/O fail (on purpose)") } // TestExpiredAccountGeneration test that expired accounts are added to a block header and validated diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 2386bbd520..6feb7680f8 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -77,7 +77,7 @@ func TestBasicMining(t *testing.T) { time.Sleep(roundTime) waits++ if waits > 15 { - panic(fmt.Sprintf(" timeout at rnd %d waiting for proposal by %v\n", status.LastRound, account)) + a.Failf("timeout", "rnd %d waiting for proposal by %v\n", status.LastRound, account) } } a.NoError(err) From 44d120f3e4622a9f56f61f4c4f104403507009e5 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 11 Mar 2024 10:37:42 -0400 Subject: [PATCH 050/117] Don't ignore err --- agreement/proposal.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/agreement/proposal.go b/agreement/proposal.go index f8ce9c8af3..e814e4b60e 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -208,6 +208,9 @@ func verifyHeader(p unauthenticatedProposal, ledger LedgerReader) error { // Similarly, we only check here that the payout is zero if // ineligibile. `eval` code must check that it is correct if > 0. eligible, err := payoutEligible(rnd, p.Proposer, ledger) + if err != nil { + return fmt.Errorf("failed to determine incentive eligibility %w", err) + } if !eligible && p.BlockHeader.ProposerPayout.Raw > 0 { return fmt.Errorf("proposer payout (%d) for ineligible Proposer %v", p.BlockHeader.ProposerPayout.Raw, p.Proposer) From 43940e9d45959d74b299c8f1121e257159c1ca24 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 11 Mar 2024 14:40:44 -0400 Subject: [PATCH 051/117] Fixup ledger tests for proposer presence --- ledger/ledger_test.go | 116 ++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go index f1bbfab5d3..a402549dd3 100644 --- a/ledger/ledger_test.go +++ b/ledger/ledger_test.go @@ -71,7 +71,7 @@ func (l *Ledger) appendUnvalidated(blk bookkeeping.Block) error { l.verifiedTxnCache = verify.GetMockedCache(false) vb, err := l.Validate(context.Background(), blk, backlogPool) if err != nil { - return fmt.Errorf("appendUnvalidated error in Validate: %s", err.Error()) + return fmt.Errorf("appendUnvalidated error in Validate: %w", err) } return l.AddValidatedBlock(*vb, agreement.Certificate{}) @@ -100,6 +100,22 @@ func initNextBlockHeader(correctHeader *bookkeeping.BlockHeader, lastBlock bookk } } +// endOfBlock is simplified implementation of BlockEvaluator.endOfBlock so that +// our test blocks can pass validation. +func endOfBlock(blk *bookkeeping.Block) error { + if blk.ConsensusProtocol().Mining().Enabled { + // This won't work for inner fees, and it's not bothering with overflow + for _, txn := range blk.Payset { + blk.FeesCollected.Raw += txn.Txn.Fee.Raw + } + // blk.ProposerPayout is allowed to be zero, so don't reproduce the calc here. + blk.Proposer = basics.Address{0x01} // Must be set to _something_. + } + var err error + blk.TxnCommitments, err = blk.PaysetCommit() + return err +} + func makeNewEmptyBlock(t *testing.T, l *Ledger, GenesisID string, initAccounts map[basics.Address]basics.AccountData) (blk bookkeeping.Block) { a := require.New(t) @@ -171,15 +187,11 @@ func (l *Ledger) appendUnvalidatedSignedTx(t *testing.T, initAccounts map[basics if err != nil { return fmt.Errorf("could not sign txn: %s", err.Error()) } + blk.Payset = append(blk.Payset, txib) if proto.TxnCounter { blk.TxnCounter = blk.TxnCounter + 1 } - if proto.Mining().Enabled { - blk.FeesCollected = stx.Txn.Fee - } - blk.Payset = append(blk.Payset, txib) - blk.TxnCommitments, err = blk.PaysetCommit() - require.NoError(t, err) + require.NoError(t, endOfBlock(&blk)) return l.appendUnvalidated(blk) } @@ -274,55 +286,72 @@ func TestLedgerBlockHeaders(t *testing.T) { badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.Round++ - a.Error(l.appendUnvalidated(badBlock), "added block header with round that was too high") + a.ErrorContains(l.appendUnvalidated(badBlock), "ledger does not have entry") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.Round-- - a.Error(l.appendUnvalidated(badBlock), "added block header with round that was too low") + a.ErrorIs(l.appendUnvalidated(badBlock), eval.ErrRoundZero) badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.Round = 0 - a.Error(l.appendUnvalidated(badBlock), "added block header with round 0") + a.ErrorIs(l.appendUnvalidated(badBlock), eval.ErrRoundZero) badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.GenesisID = "" - a.Error(l.appendUnvalidated(badBlock), "added block header with empty genesis ID") + a.ErrorContains(l.appendUnvalidated(badBlock), "genesis ID missing") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.GenesisID = "incorrect" - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect genesis ID") + a.ErrorContains(l.appendUnvalidated(badBlock), "genesis ID mismatch") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.UpgradePropose = "invalid" - a.Error(l.appendUnvalidated(badBlock), "added block header with invalid upgrade proposal") + a.ErrorContains(l.appendUnvalidated(badBlock), "proposed upgrade wait rounds 0") + + badBlock = bookkeeping.Block{BlockHeader: correctHeader} + badBlock.BlockHeader.UpgradePropose = "invalid" + badBlock.BlockHeader.UpgradeDelay = 20000 + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.UpgradeApprove = true - a.Error(l.appendUnvalidated(badBlock), "added block header with upgrade approve set but no open upgrade") + a.ErrorContains(l.appendUnvalidated(badBlock), "approval without an active proposal") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.CurrentProtocol = "incorrect" - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect current protocol") + a.ErrorContains(l.appendUnvalidated(badBlock), "protocol not supported") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.CurrentProtocol = "" - a.Error(l.appendUnvalidated(badBlock), "added block header with empty current protocol") + a.ErrorContains(l.appendUnvalidated(badBlock), "protocol not supported", "header with empty current protocol") + + badBlock = bookkeeping.Block{BlockHeader: correctHeader} + var wrongVersion protocol.ConsensusVersion + for ver := range config.Consensus { + if ver != correctHeader.CurrentProtocol { + wrongVersion = ver + break + } + } + a.NotEmpty(wrongVersion) + badBlock.BlockHeader.CurrentProtocol = wrongVersion + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.NextProtocol = "incorrect" - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect next protocol") + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch", "added block header with incorrect next protocol") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.NextProtocolApprovals++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect number of upgrade approvals") + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch", "added block header with incorrect number of upgrade approvals") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.NextProtocolVoteBefore++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect next protocol vote deadline") + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch", "added block header with incorrect next protocol vote deadline") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.NextProtocolSwitchOn++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect next protocol switch round") + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch", "added block header with incorrect next protocol switch round") // TODO test upgrade cases with a valid upgrade in progress @@ -330,33 +359,33 @@ func TestLedgerBlockHeaders(t *testing.T) { badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.Branch = bookkeeping.BlockHash{} - a.Error(l.appendUnvalidated(badBlock), "added block header with empty previous-block hash") + a.ErrorContains(l.appendUnvalidated(badBlock), "block branch incorrect") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.Branch[0]++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect previous-block hash") + a.ErrorContains(l.appendUnvalidated(badBlock), "block branch incorrect") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.RewardsLevel++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect rewards level") + a.ErrorContains(l.appendUnvalidated(badBlock), "bad rewards state") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.RewardsRate++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect rewards rate") + a.ErrorContains(l.appendUnvalidated(badBlock), "bad rewards state") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.RewardsResidue++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect rewards residue") + a.ErrorContains(l.appendUnvalidated(badBlock), "bad rewards state") // TODO test rewards cases with changing poolAddr money, with changing round, and with changing total reward units badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.TxnCommitments.NativeSha512_256Commitment = crypto.Hash([]byte{0}) - a.Error(l.appendUnvalidated(badBlock), "added block header with empty transaction root") + a.ErrorContains(l.appendUnvalidated(badBlock), "txn root wrong") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.TxnCommitments.NativeSha512_256Commitment[0]++ - a.Error(l.appendUnvalidated(badBlock), "added block header with invalid transaction root") + a.ErrorContains(l.appendUnvalidated(badBlock), "txn root wrong") correctBlock := bookkeeping.Block{BlockHeader: correctHeader} a.NoError(l.appendUnvalidated(correctBlock), "could not add block with correct header") @@ -659,42 +688,36 @@ func TestLedgerSingleTxV24(t *testing.T) { badTx = correctAssetConfig badTx.ConfigAsset = 2 err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), "asset 2 does not exist or has been deleted") + a.ErrorContains(err, "asset 2 does not exist or has been deleted") badTx = correctAssetConfig badTx.ConfigAsset = assetIdx badTx.AssetFrozen = true err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), "type acfg has non-zero fields for type afrz") + a.ErrorContains(err, "type acfg has non-zero fields for type afrz") badTx = correctAssetConfig badTx.ConfigAsset = assetIdx badTx.Sender = addrList[1] badTx.AssetParams.Freeze = addrList[0] err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), "this transaction should be issued by the manager") + a.ErrorContains(err, "this transaction should be issued by the manager") badTx = correctAssetConfig badTx.AssetParams.UnitName = "very long unit name that exceeds the limit" err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), "transaction asset unit name too big: 42 > 8") + a.ErrorContains(err, "transaction asset unit name too big: 42 > 8") badTx = correctAssetTransfer badTx.XferAsset = assetIdx badTx.AssetAmount = 101 err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), "underflow on subtracting 101 from sender amount 100") + a.ErrorContains(err, "underflow on subtracting 101 from sender amount 100") badTx = correctAssetTransfer badTx.XferAsset = assetIdx err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), fmt.Sprintf("asset %d missing from", assetIdx)) + a.ErrorContains(err, fmt.Sprintf("asset %d missing from", assetIdx)) a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctAppCreate, ad)) appIdx = 2 // the second successful txn @@ -704,24 +727,20 @@ func TestLedgerSingleTxV24(t *testing.T) { program[0] = '\x01' badTx.ApprovalProgram = program err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), "program version must be >= 2") + a.ErrorContains(err, "program version must be >= 2") badTx = correctAppCreate badTx.ApplicationID = appIdx err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), "programs may only be specified during application creation or update") + a.ErrorContains(err, "programs may only be specified during application creation or update") badTx = correctAppCall badTx.ApplicationID = 0 err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), "ApprovalProgram: invalid program (empty)") + a.ErrorContains(err, "ApprovalProgram: invalid program (empty)") badTx.ApprovalProgram = []byte{242} err = l.appendUnvalidatedTx(t, initAccounts, initSecrets, badTx, ad) - a.Error(err) - a.Contains(err.Error(), "ApprovalProgram: invalid version") + a.ErrorContains(err, "ApprovalProgram: invalid version") correctAppCall.ApplicationID = appIdx a.NoError(l.appendUnvalidatedTx(t, initAccounts, initSecrets, correctAppCall, ad)) @@ -1272,8 +1291,7 @@ func testLedgerSingleTxApplyData(t *testing.T, version protocol.ConsensusVersion initNextBlockHeader(&correctHeader, lastBlock, proto) correctBlock := bookkeeping.Block{BlockHeader: correctHeader} - correctBlock.TxnCommitments, err = correctBlock.PaysetCommit() - a.NoError(err) + a.NoError(endOfBlock(&correctBlock)) a.NoError(l.appendUnvalidated(correctBlock), "could not add block with correct header") } From dfe7bb67965a75d94c6a5bae05a64cfc710d53c2 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 11 Mar 2024 15:01:49 -0400 Subject: [PATCH 052/117] comment wording --- ledger/double_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ledger/double_test.go b/ledger/double_test.go index 5dd3a31868..9db07bcf4d 100644 --- a/ledger/double_test.go +++ b/ledger/double_test.go @@ -203,8 +203,8 @@ func checkBlock(t testing.TB, checkLedger *Ledger, vb *ledgercore.ValidatedBlock } check.SetGenerateForTesting(true) // We use the same value for seed and proposer. But the proposer is - // sometimes zero'd to account for mining being disabled. So we provide we - // get the blocks to match by providing the Seed as the proposer. + // sometimes zero'd to account for mining being disabled. So we get the + // blocks to match by providing the Seed as the proposer. cb := endBlock(t, checkLedger, check, basics.Address(vb.Block().BlockHeader.Seed)) check.SetGenerateForTesting(false) require.Equal(t, vb.Block(), cb.Block()) From bee6bddf6f3d0e0e039cadf7ef99659f88096d3e Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 12 Mar 2024 09:07:32 -0400 Subject: [PATCH 053/117] feature flag the header properly --- data/datatest/impls.go | 12 ++---------- ledger/ledgercore/validatedBlock.go | 11 +++++++++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/data/datatest/impls.go b/data/datatest/impls.go index 136ea2e1c2..c4c251c726 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -68,18 +68,10 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated func (ve validatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { newblock := *ve.blk newblock.BlockHeader.Seed = s - - // agreement is telling us who the proposer is and if they're eligible, but - // agreement does not consider the current config params, so here we decide - // what really goes into the BlockHeader. - proto := config.Consensus[ve.blk.CurrentProtocol] - if proto.Mining().Enabled { - newblock.BlockHeader.Proposer = proposer - } - if !proto.Mining().Enabled || !eligible { + newblock.BlockHeader.Proposer = proposer + if !eligible { newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} } - return validatedBlock{blk: &newblock} } diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 16ebd38b37..975973f50c 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -17,6 +17,7 @@ package ledgercore import ( + "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/committee" @@ -44,8 +45,14 @@ func (vb ValidatedBlock) Delta() StateDelta { func (vb ValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { newblock := vb.blk newblock.BlockHeader.Seed = s - newblock.BlockHeader.Proposer = proposer - if !eligible { + // agreement is telling us who the proposer is and if they're eligible, but + // agreement does not consider the current config params, so here we decide + // what really goes into the BlockHeader. + proto := config.Consensus[vb.blk.CurrentProtocol] + if proto.Mining().Enabled { + newblock.BlockHeader.Proposer = proposer + } + if !proto.Mining().Enabled || !eligible { newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} } From aee989c9c81553956755f7b2ff6c5eba72861f89 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 14 Mar 2024 11:05:36 -0400 Subject: [PATCH 054/117] Comment change and pointless code removal --- agreement/proposal.go | 9 ++++----- .../participation/onlineOfflineParticipation_test.go | 4 ---- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index e814e4b60e..07133967ec 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -198,7 +198,7 @@ func verifyHeader(p unauthenticatedProposal, ledger LedgerReader) error { // ledger.ConsensusParams(rnd) is not allowed because rnd isn't committed. // The BlockHeader isn't trustworthy yet, since we haven't checked the - // upgrade state. So, for now, we confirm that the BlockSeed is *either* + // upgrade state. So, for now, we confirm that the Proposer is *either* // correct or missing. `eval` package will using Mining().Enabled to confirm // which it should be. if !p.BlockHeader.Proposer.IsZero() && p.BlockHeader.Proposer != value.OriginalProposer { @@ -284,14 +284,13 @@ func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerRead return false, err } + // It's only fair to compare balances from 320 rounds ago to the mining + // rules that were in effect then. To make this work in a reasonable way + // when mining begins, the miningRules[0] in consensus.go has Min and Max. balanceParams, err := ledger.ConsensusParams(balanceRound) if err != nil { return false, err } - - // It's only fair to compare balance's from 320 rounds ago to the mining - // rules that were in effect then. To make this work in a reasonable way - // when mining begins, the miningRules[0] in consensus.go has Min and Max. eligible := balanceRecord.IncentiveEligible && balanceRecord.MicroAlgosWithRewards.Raw >= balanceParams.Mining().MinBalance && balanceRecord.MicroAlgosWithRewards.Raw <= balanceParams.Mining().MaxBalance diff --git a/test/e2e-go/features/participation/onlineOfflineParticipation_test.go b/test/e2e-go/features/participation/onlineOfflineParticipation_test.go index f7cf67f77f..0b38fe76ff 100644 --- a/test/e2e-go/features/participation/onlineOfflineParticipation_test.go +++ b/test/e2e-go/features/participation/onlineOfflineParticipation_test.go @@ -247,11 +247,7 @@ func TestAccountGoesOnlineForShortPeriod(t *testing.T) { t.Parallel() a := require.New(fixtures.SynchronizedTest(t)) - // Make the seed lookback shorter, otherwise will wait for 320 rounds - consensus := make(config.ConsensusProtocols) - var fixture fixtures.RestClientFixture - fixture.SetConsensus(consensus) fixture.SetupNoStart(t, filepath.Join("nettemplates", "TwoNodes50EachFuture.json")) // update the config file by setting the ParticipationKeysRefreshInterval to 5 second. From a8c1d9b73f4787012077ecc09bb895af4c5db853 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 14 Mar 2024 13:18:12 -0400 Subject: [PATCH 055/117] Fix unit test for eligibility test in blance round --- data/basics/userBalance_test.go | 2 + ledger/acctonline.go | 1 + ledger/ledgercore/accountdata.go | 1 + ledger/lruonlineaccts_test.go | 13 +- ledger/store/trackerdb/data.go | 11 +- ledger/store/trackerdb/data_test.go | 4 +- ledger/store/trackerdb/msgp_gen.go | 39 ++- .../e2e-go/features/incentives/mining_test.go | 275 ++++++++++-------- .../features/incentives/suspension_test.go | 2 + test/testdata/nettemplates/Suspension.json | 12 +- 10 files changed, 217 insertions(+), 143 deletions(-) diff --git a/data/basics/userBalance_test.go b/data/basics/userBalance_test.go index da094329fd..c49afb9abf 100644 --- a/data/basics/userBalance_test.go +++ b/data/basics/userBalance_test.go @@ -128,6 +128,7 @@ func getSampleAccountData() AccountData { AppLocalStates: make(map[AppIndex]AppLocalState), AppParams: make(map[AppIndex]AppParams), AuthAddr: Address(crypto.Hash([]byte{1, 2, 3, 4})), + IncentiveEligible: true, } } @@ -190,4 +191,5 @@ func TestOnlineAccountData(t *testing.T) { require.Equal(t, ad.MicroAlgos, oad.MicroAlgosWithRewards) require.Equal(t, ad.VoteID, oad.VoteID) require.Equal(t, ad.SelectionID, oad.SelectionID) + require.Equal(t, ad.IncentiveEligible, oad.IncentiveEligible) } diff --git a/ledger/acctonline.go b/ledger/acctonline.go index cdccc9a9e1..f204681abe 100644 --- a/ledger/acctonline.go +++ b/ledger/acctonline.go @@ -630,6 +630,7 @@ func (ao *onlineAccounts) LookupOnlineAccountData(rnd basics.Round, addr basics. data.VotingData.VoteFirstValid = oad.VotingData.VoteFirstValid data.VotingData.VoteLastValid = oad.VotingData.VoteLastValid data.VotingData.VoteKeyDilution = oad.VotingData.VoteKeyDilution + data.IncentiveEligible = oad.IncentiveEligible return } diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 0e37f6464f..60c9b9645a 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -69,6 +69,7 @@ type VotingData struct { type OnlineAccountData struct { MicroAlgosWithRewards basics.MicroAlgos VotingData + IncentiveEligible bool } // ToAccountData returns ledgercore.AccountData from basics.AccountData diff --git a/ledger/lruonlineaccts_test.go b/ledger/lruonlineaccts_test.go index 0f3bc81712..fb37867802 100644 --- a/ledger/lruonlineaccts_test.go +++ b/ledger/lruonlineaccts_test.go @@ -39,10 +39,13 @@ func TestLRUOnlineAccountsBasic(t *testing.T) { // write 50 accounts for i := 0; i < accountsNum; i++ { acct := trackerdb.PersistedOnlineAccountData{ - Addr: basics.Address(crypto.Hash([]byte{byte(i)})), - Round: basics.Round(i), - Ref: mockEntryRef{int64(i)}, - AccountData: trackerdb.BaseOnlineAccountData{MicroAlgos: basics.MicroAlgos{Raw: uint64(i)}}, + Addr: basics.Address(crypto.Hash([]byte{byte(i)})), + Round: basics.Round(i), + Ref: mockEntryRef{int64(i)}, + AccountData: trackerdb.BaseOnlineAccountData{ + MicroAlgos: basics.MicroAlgos{Raw: uint64(i)}, + IncentiveEligible: i%2 == 0, + }, } baseOnlineAcct.write(acct) } @@ -55,6 +58,7 @@ func TestLRUOnlineAccountsBasic(t *testing.T) { require.Equal(t, basics.Round(i), acct.Round) require.Equal(t, addr, acct.Addr) require.Equal(t, uint64(i), acct.AccountData.MicroAlgos.Raw) + require.Equal(t, i%2 == 0, acct.AccountData.IncentiveEligible) require.Equal(t, mockEntryRef{int64(i)}, acct.Ref) } @@ -79,6 +83,7 @@ func TestLRUOnlineAccountsBasic(t *testing.T) { require.Equal(t, basics.Round(i), acct.Round) require.Equal(t, addr, acct.Addr) require.Equal(t, uint64(i), acct.AccountData.MicroAlgos.Raw) + require.Equal(t, i%2 == 0, acct.AccountData.IncentiveEligible) require.Equal(t, mockEntryRef{int64(i)}, acct.Ref) } else { require.False(t, has) diff --git a/ledger/store/trackerdb/data.go b/ledger/store/trackerdb/data.go index adb8f4e0e5..c1d5c52d93 100644 --- a/ledger/store/trackerdb/data.go +++ b/ledger/store/trackerdb/data.go @@ -152,8 +152,9 @@ type BaseOnlineAccountData struct { BaseVotingData - MicroAlgos basics.MicroAlgos `codec:"Y"` - RewardsBase uint64 `codec:"Z"` + IncentiveEligible bool `codec:"W"` + MicroAlgos basics.MicroAlgos `codec:"Y"` + RewardsBase uint64 `codec:"Z"` } // PersistedKVData represents the stored entry behind a application boxed key/value. @@ -447,7 +448,7 @@ func (bo *BaseOnlineAccountData) IsVotingEmpty() bool { func (bo *BaseOnlineAccountData) IsEmpty() bool { return bo.IsVotingEmpty() && bo.MicroAlgos.Raw == 0 && - bo.RewardsBase == 0 + bo.RewardsBase == 0 && !bo.IncentiveEligible } // GetOnlineAccount returns ledgercore.OnlineAccount for top online accounts / voters @@ -481,6 +482,7 @@ func (bo *BaseOnlineAccountData) GetOnlineAccountData(proto config.ConsensusPara VoteLastValid: bo.VoteLastValid, VoteKeyDilution: bo.VoteKeyDilution, }, + IncentiveEligible: bo.IncentiveEligible, } } @@ -493,9 +495,10 @@ func (bo *BaseOnlineAccountData) NormalizedOnlineBalance(proto config.ConsensusP func (bo *BaseOnlineAccountData) SetCoreAccountData(ad *ledgercore.AccountData) { bo.BaseVotingData.SetCoreAccountData(ad) - // MicroAlgos/RewardsBase are updated by the evaluator when accounts are touched + // These are updated by the evaluator when accounts are touched bo.MicroAlgos = ad.MicroAlgos bo.RewardsBase = ad.RewardsBase + bo.IncentiveEligible = ad.IncentiveEligible } // MakeResourcesData returns a new empty instance of resourcesData. diff --git a/ledger/store/trackerdb/data_test.go b/ledger/store/trackerdb/data_test.go index f15a43b216..edc0d0dc9e 100644 --- a/ledger/store/trackerdb/data_test.go +++ b/ledger/store/trackerdb/data_test.go @@ -1152,7 +1152,7 @@ func TestBaseOnlineAccountDataIsEmpty(t *testing.T) { structureTesting := func(t *testing.T) { encoding, err := json.Marshal(&empty) zeros32 := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - expectedEncoding := `{"VoteID":[` + zeros32 + `],"SelectionID":[` + zeros32 + `],"VoteFirstValid":0,"VoteLastValid":0,"VoteKeyDilution":0,"StateProofID":[` + zeros32 + `,` + zeros32 + `],"MicroAlgos":{"Raw":0},"RewardsBase":0}` + expectedEncoding := `{"VoteID":[` + zeros32 + `],"SelectionID":[` + zeros32 + `],"VoteFirstValid":0,"VoteLastValid":0,"VoteKeyDilution":0,"StateProofID":[` + zeros32 + `,` + zeros32 + `],"IncentiveEligible":false,"MicroAlgos":{"Raw":0},"RewardsBase":0}` require.NoError(t, err) require.Equal(t, expectedEncoding, string(encoding)) } @@ -1249,7 +1249,7 @@ func TestBaseOnlineAccountDataReflect(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() - require.Equal(t, 4, reflect.TypeOf(BaseOnlineAccountData{}).NumField(), "update all getters and setters for baseOnlineAccountData and change the field count") + require.Equal(t, 5, reflect.TypeOf(BaseOnlineAccountData{}).NumField(), "update all getters and setters for baseOnlineAccountData and change the field count") } func TestBaseVotingDataReflect(t *testing.T) { diff --git a/ledger/store/trackerdb/msgp_gen.go b/ledger/store/trackerdb/msgp_gen.go index 285b0e5b44..21d0b677c9 100644 --- a/ledger/store/trackerdb/msgp_gen.go +++ b/ledger/store/trackerdb/msgp_gen.go @@ -749,8 +749,8 @@ func BaseAccountDataMaxSize() (s int) { func (z *BaseOnlineAccountData) MarshalMsg(b []byte) (o []byte) { o = msgp.Require(b, z.Msgsize()) // omitempty: check for empty values - zb0001Len := uint32(8) - var zb0001Mask uint16 /* 10 bits */ + zb0001Len := uint32(9) + var zb0001Mask uint16 /* 11 bits */ if (*z).BaseVotingData.VoteID.MsgIsZero() { zb0001Len-- zb0001Mask |= 0x1 @@ -775,14 +775,18 @@ func (z *BaseOnlineAccountData) MarshalMsg(b []byte) (o []byte) { zb0001Len-- zb0001Mask |= 0x20 } - if (*z).MicroAlgos.MsgIsZero() { + if (*z).IncentiveEligible == false { zb0001Len-- zb0001Mask |= 0x40 } - if (*z).RewardsBase == 0 { + if (*z).MicroAlgos.MsgIsZero() { zb0001Len-- zb0001Mask |= 0x80 } + if (*z).RewardsBase == 0 { + zb0001Len-- + zb0001Mask |= 0x100 + } // variable map header, size zb0001Len o = append(o, 0x80|uint8(zb0001Len)) if zb0001Len != 0 { @@ -817,11 +821,16 @@ func (z *BaseOnlineAccountData) MarshalMsg(b []byte) (o []byte) { o = (*z).BaseVotingData.StateProofID.MarshalMsg(o) } if (zb0001Mask & 0x40) == 0 { // if not empty + // string "W" + o = append(o, 0xa1, 0x57) + o = msgp.AppendBool(o, (*z).IncentiveEligible) + } + if (zb0001Mask & 0x80) == 0 { // if not empty // string "Y" o = append(o, 0xa1, 0x59) o = (*z).MicroAlgos.MarshalMsg(o) } - if (zb0001Mask & 0x80) == 0 { // if not empty + if (zb0001Mask & 0x100) == 0 { // if not empty // string "Z" o = append(o, 0xa1, 0x5a) o = msgp.AppendUint64(o, (*z).RewardsBase) @@ -901,6 +910,14 @@ func (z *BaseOnlineAccountData) UnmarshalMsgWithState(bts []byte, st msgp.Unmars return } } + if zb0001 > 0 { + zb0001-- + (*z).IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "IncentiveEligible") + return + } + } if zb0001 > 0 { zb0001-- bts, err = (*z).MicroAlgos.UnmarshalMsgWithState(bts, st) @@ -976,6 +993,12 @@ func (z *BaseOnlineAccountData) UnmarshalMsgWithState(bts []byte, st msgp.Unmars err = msgp.WrapError(err, "StateProofID") return } + case "W": + (*z).IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) + if err != nil { + err = msgp.WrapError(err, "IncentiveEligible") + return + } case "Y": bts, err = (*z).MicroAlgos.UnmarshalMsgWithState(bts, st) if err != nil { @@ -1011,18 +1034,18 @@ func (_ *BaseOnlineAccountData) CanUnmarshalMsg(z interface{}) bool { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *BaseOnlineAccountData) Msgsize() (s int) { - s = 1 + 2 + (*z).BaseVotingData.VoteID.Msgsize() + 2 + (*z).BaseVotingData.SelectionID.Msgsize() + 2 + (*z).BaseVotingData.VoteFirstValid.Msgsize() + 2 + (*z).BaseVotingData.VoteLastValid.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).BaseVotingData.StateProofID.Msgsize() + 2 + (*z).MicroAlgos.Msgsize() + 2 + msgp.Uint64Size + s = 1 + 2 + (*z).BaseVotingData.VoteID.Msgsize() + 2 + (*z).BaseVotingData.SelectionID.Msgsize() + 2 + (*z).BaseVotingData.VoteFirstValid.Msgsize() + 2 + (*z).BaseVotingData.VoteLastValid.Msgsize() + 2 + msgp.Uint64Size + 2 + (*z).BaseVotingData.StateProofID.Msgsize() + 2 + msgp.BoolSize + 2 + (*z).MicroAlgos.Msgsize() + 2 + msgp.Uint64Size return } // MsgIsZero returns whether this is a zero value func (z *BaseOnlineAccountData) MsgIsZero() bool { - return ((*z).BaseVotingData.VoteID.MsgIsZero()) && ((*z).BaseVotingData.SelectionID.MsgIsZero()) && ((*z).BaseVotingData.VoteFirstValid.MsgIsZero()) && ((*z).BaseVotingData.VoteLastValid.MsgIsZero()) && ((*z).BaseVotingData.VoteKeyDilution == 0) && ((*z).BaseVotingData.StateProofID.MsgIsZero()) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) + return ((*z).BaseVotingData.VoteID.MsgIsZero()) && ((*z).BaseVotingData.SelectionID.MsgIsZero()) && ((*z).BaseVotingData.VoteFirstValid.MsgIsZero()) && ((*z).BaseVotingData.VoteLastValid.MsgIsZero()) && ((*z).BaseVotingData.VoteKeyDilution == 0) && ((*z).BaseVotingData.StateProofID.MsgIsZero()) && ((*z).IncentiveEligible == false) && ((*z).MicroAlgos.MsgIsZero()) && ((*z).RewardsBase == 0) } // MaxSize returns a maximum valid message size for this message type func BaseOnlineAccountDataMaxSize() (s int) { - s = 1 + 2 + crypto.OneTimeSignatureVerifierMaxSize() + 2 + crypto.VRFVerifierMaxSize() + 2 + basics.RoundMaxSize() + 2 + basics.RoundMaxSize() + 2 + msgp.Uint64Size + 2 + merklesignature.CommitmentMaxSize() + 2 + basics.MicroAlgosMaxSize() + 2 + msgp.Uint64Size + s = 1 + 2 + crypto.OneTimeSignatureVerifierMaxSize() + 2 + crypto.VRFVerifierMaxSize() + 2 + basics.RoundMaxSize() + 2 + basics.RoundMaxSize() + 2 + msgp.Uint64Size + 2 + merklesignature.CommitmentMaxSize() + 2 + msgp.BoolSize + 2 + basics.MicroAlgosMaxSize() + 2 + msgp.Uint64Size return } diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 6feb7680f8..9d418e21cb 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -19,19 +19,22 @@ package suspension import ( "fmt" "path/filepath" + "runtime" "testing" "time" "github.com/stretchr/testify/require" + "github.com/algorand/go-algorand/config" + "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" + "github.com/algorand/go-algorand/libgoal" + "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) -const roundTime = 4 * time.Second - // TestBasicMining shows proposers getting paid func TestBasicMining(t *testing.T) { partitiontest.PartitionTest(t) @@ -43,157 +46,187 @@ func TestBasicMining(t *testing.T) { t.Parallel() a := require.New(fixtures.SynchronizedTest(t)) + // Make the seed lookback shorter, otherwise we need wait 320 rounds to become IncentiveEligible. + var fixture fixtures.RestClientFixture + consensus := make(config.ConsensusProtocols) + fast := config.Consensus[protocol.ConsensusFuture] + fast.SeedRefreshInterval = 8 // so balanceRound ends up 2 * 8 * 2 = 32 + // and speed up the roudns while we're at it + if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" { + fast.AgreementFilterTimeoutPeriod0 = time.Second / 2 + fast.AgreementFilterTimeout = time.Second / 2 + } + consensus[protocol.ConsensusFuture] = fast + fixture.SetConsensus(consensus) + // Overview of this test: // Start a single network // Show that a genesis account does not get incentives // rereg to become eligible // show incentives are paid (mining and bonuses) - var fixture fixtures.RestClientFixture - fixture.Setup(t, filepath.Join("nettemplates", "TwoNodes50EachFuture.json")) + fixture.Setup(t, filepath.Join("nettemplates", "Suspension.json")) defer fixture.Shutdown() - client := fixture.LibGoalClient - - richAccount, err := fixture.GetRichestAccount() - a.NoError(err) - // wait for richAccount to have LastProposed != 0 - account, err := client.AccountData(richAccount.Address) - a.NoError(err) - fmt.Printf(" rich balance %v %d\n", richAccount.Address, account.MicroAlgos) - waits := 0 - for account.LastProposed == 0 { + clientAndAccount := func(name string) (libgoal.Client, model.Account) { + c := fixture.GetLibGoalClientForNamedNode(name) + accounts, err := fixture.GetNodeWalletsSortedByBalance(c) a.NoError(err) - a.Equal(basics.Online, account.Status) - account, err = client.AccountData(richAccount.Address) - if account.LastProposed > 0 { - break - } - status, err := client.Status() - a.NoError(err) - block, err := client.BookkeepingBlock(status.LastRound) - a.NoError(err) - fmt.Printf(" block proposed by %v\n", block.Proposer) - time.Sleep(roundTime) - waits++ - if waits > 15 { - a.Failf("timeout", "rnd %d waiting for proposal by %v\n", status.LastRound, account) - } + a.Len(accounts, 1) + return c, accounts[0] } - a.NoError(err) - // we make an _offline_ tx here, because we want to populate the key - // material ourself, by copying the account's existing state. That makes it - // an _online_ keyreg. That allows the running node to chug along without - // new part keys. We overpay the fee, just to get some funds into FeeSink - // because we will watch it drain toward bottom of test. - reReg, err := client.MakeUnsignedGoOfflineTx(richAccount.Address, 0, 0, 12_000_000, [32]byte{}) - require.NoError(t, err, "should be able to make tx") - - reReg.KeyregTxnFields = transactions.KeyregTxnFields{ - VotePK: account.VoteID, - SelectionPK: account.SelectionID, - StateProofPK: account.StateProofID, - VoteFirst: account.VoteFirstValid, - VoteLast: account.VoteLastValid, - VoteKeyDilution: account.VoteKeyDilution, - } + c15, account15 := clientAndAccount("Node15") + c01, account01 := clientAndAccount("Node01") + + rekeyreg := func(client libgoal.Client, address string) basics.AccountData { + // we start by making an _offline_ tx here, because we want to populate the + // key material ourself with a copy of the account's existing material. That + // makes it an _online_ keyreg. That allows the running node to chug along + // without new part keys. We overpay the fee, which makes us + // IncentiveEligible, and to get some funds into FeeSink because we will + // watch it drain toward bottom of test. + reReg, err := client.MakeUnsignedGoOfflineTx(address, 0, 0, 12_000_000, [32]byte{}) + a.NoError(err) - wh, err := client.GetUnencryptedWalletHandle() - require.NoError(t, err, "should be able to get unencrypted wallet handle") - onlineTxID, err := client.SignAndBroadcastTransaction(wh, nil, reReg) - require.NoError(t, err, "should be no errors when going online") - fixture.WaitForConfirmedTxn(uint64(reReg.LastValid), onlineTxID) + data, err := client.AccountData(address) + a.True(data.LastHeartbeat == 0) + a.NoError(err) + reReg.KeyregTxnFields = transactions.KeyregTxnFields{ + VotePK: data.VoteID, + SelectionPK: data.SelectionID, + StateProofPK: data.StateProofID, + VoteFirst: data.VoteFirstValid, + VoteLast: data.VoteLastValid, + VoteKeyDilution: data.VoteKeyDilution, + } - account, err = client.AccountData(richAccount.Address) - a.NoError(err) - a.True(account.IncentiveEligible) - // wait for richAccount to propose again, earns nothing (too much balance) - proposed := account.LastProposed - priorBalance := account.MicroAlgos - for account.LastProposed == proposed { + wh, err := client.GetUnencryptedWalletHandle() + a.NoError(err) + onlineTxID, err := client.SignAndBroadcastTransaction(wh, nil, reReg) + a.NoError(err) + txn, err := fixture.WaitForConfirmedTxn(uint64(reReg.LastValid), onlineTxID) a.NoError(err) - account, err = client.AccountData(richAccount.Address) + data, err = client.AccountData(address) a.NoError(err) - time.Sleep(roundTime) + a.Equal(basics.Online, data.Status) + a.True(data.IncentiveEligible) + a.True(data.LastHeartbeat > 0) + fmt.Printf(" %v has %v in round %d\n", address, data.MicroAlgos.Raw, *txn.ConfirmedRound) + return data } - // incentives would pay out in the next round (they won't here, but makes the test realistic) - fixture.WaitForRound(uint64(account.LastProposed+1), roundTime) - account, err = client.AccountData(richAccount.Address) + data01 := rekeyreg(c01, account01.Address) + data15 := rekeyreg(c15, account15.Address) + + // have account01 burn some money to get below to eligibility cap + // Starts with 100M, so burn 60M and get under 50M cap. + txn, err := c01.SendPaymentFromUnencryptedWallet(account01.Address, basics.Address{}.String(), + 1000, 60_000_000_000_000, nil) a.NoError(err) - a.Equal(priorBalance, account.MicroAlgos) - - // Unload some algos, so we become incentive eligible - burn := "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ" - target := basics.Algos(1_000_000) // Assumes 1M algos is eligible - diff, _ := basics.OSubA(account.MicroAlgos, target) - fmt.Printf(" rich pays out %d + fee\n", diff.Raw) - tx, err := client.SendPaymentFromUnencryptedWallet(richAccount.Address, burn, 0, diff.Raw, nil) + burn, err := fixture.WaitForConfirmedTxn(uint64(txn.LastValid), txn.ID().String()) a.NoError(err) - status, err := client.Status() + data01, err = c01.AccountData(account01.Address) a.NoError(err) - info, err := fixture.WaitForConfirmedTxn(status.LastRound+10, tx.ID().String()) + + // Go 31 rounds after the burn happened. rounds. During this time, + // incentive eligibility is not in effect yet, so regardless of who + // proposes, they won't earn anything. + + client := fixture.LibGoalClient + status, err := client.Status() a.NoError(err) + for status.LastRound < *burn.ConfirmedRound+31 { + block, err := client.BookkeepingBlock(status.LastRound) + a.NoError(err) - // Figure out whether rich account was proposer of its own payment - blockProposer := func(r uint64) string { - block, err := client.Block(r) + fmt.Printf(" 1 block %d proposed by %v\n", status.LastRound, block.Proposer) + a.Zero(block.ProposerPayout) // nobody is eligible yet (hasn't worked back to balance round) + a.EqualValues(5_000_000, block.Bonus.Raw) + fixture.WaitForRoundWithTimeout(status.LastRound + 1) + + // incentives would pay out in the next round (they won't here, but makes the test realistic) + next, err := client.AccountData(block.Proposer.String()) + a.EqualValues(next.LastProposed, status.LastRound) + // regardless of proposer, nobody gets paid + switch block.Proposer.String() { + case account01.Address: + a.Equal(data01.MicroAlgos, next.MicroAlgos) + data01 = next + case account15.Address: + a.Equal(data15.MicroAlgos, next.MicroAlgos) + data15 = next + default: + a.Fail("bad proposer", "%v proposed", block.Proposer) + } + status, err = client.Status() a.NoError(err) - return block.Block["prp"].(string) } - // allow the incentive payment to happen - err = fixture.WaitForRound(*info.ConfirmedRound+1, roundTime) - a.NoError(err) - // check that rich account got paid (or didn't) based on whether it proposed - if blockProposer(*info.ConfirmedRound) == richAccount.Address { - // should earn the block bonus - target, _ = basics.OAddA(target, basics.Algos(5)) - // and only spent 25% of fee, since we earned 75% back - target, _ = basics.OAddA(target, basics.MicroAlgos{Raw: 750}) - } - // undershot 1M because of fee - target, _ = basics.OSubA(target, basics.MicroAlgos{Raw: 1000}) - account, err = client.AccountData(richAccount.Address) - a.NoError(err) - a.Equal(target, account.MicroAlgos) - - proposed = account.LastProposed - priorBalance = account.MicroAlgos - // wait for richAccount to propose following its payment to become eligible - r := *info.ConfirmedRound + 1 - for blockProposer(r) != richAccount.Address { - r++ - err = fixture.WaitForRound(r, roundTime) + // Wait until each have proposed, so we can see that 01 gets paid and 15 does not (too much balance) + proposed01 := false + proposed15 := false + for i := 0; !proposed01 || !proposed15; i++ { + status, err := client.Status() a.NoError(err) - a.Less(r, *info.ConfirmedRound+20) // avoid infinite loop if bug + block, err := client.BookkeepingBlock(status.LastRound) + a.NoError(err) + + fmt.Printf(" 3 block %d proposed by %v\n", status.LastRound, block.Proposer) + a.EqualValues(5_000_000, block.Bonus.Raw) + + // incentives would pay out in the next round so wait to see them + fixture.WaitForRoundWithTimeout(status.LastRound + 1) + next, err := client.AccountData(block.Proposer.String()) + fmt.Printf(" proposer %v has %d at round %d\n", block.Proposer, next.MicroAlgos.Raw, status.LastRound) + + // 01 would get paid (because under balance cap) 15 would not + switch block.Proposer.String() { + case account01.Address: + a.NotZero(block.ProposerPayout) + a.NotEqual(data01.MicroAlgos, next.MicroAlgos) + proposed01 = true + data01 = next + case account15.Address: + a.Zero(block.ProposerPayout) + a.Equal(data15.MicroAlgos, next.MicroAlgos) + data15 = next + proposed15 = true + default: + a.Fail("bad proposer", "%v proposed", block.Proposer) + } } - account, err = client.AccountData(richAccount.Address) - a.NoError(err) - fmt.Printf(" rich balance after eligible proposal %d\n", account.MicroAlgos) - // incentives pay out in the next round - err = fixture.WaitForRound(r+1, roundTime) - a.NoError(err) - account, err = client.AccountData(richAccount.Address) - a.NoError(err) - fmt.Printf(" rich balance after eligible payout %d\n", account.MicroAlgos) - // Should have earned 5A - target, _ = basics.OAddA(target, basics.Algos(5)) - a.Equal(target, account.MicroAlgos) + a.True(proposed15) + a.True(proposed01) // There's some chance of this triggering flakily // Now that we've proven incentives get paid, let's drain the FeeSink and - // ensure it happens gracefully. - block, err := client.Block(r + 1) + // ensure it happens gracefully. Have account15 go offline so that (after + // 32 rounds) only account01 is proposing. It is eligible and will drain the + // fee sink. + + offline, err := c15.MakeUnsignedGoOfflineTx(account15.Address, 0, 0, 1000, [32]byte{}) a.NoError(err) - feesink := block.Block["fees"].(string) - status, err = client.Status() + wh, err := c15.GetUnencryptedWalletHandle() a.NoError(err) - for i := uint64(0); i < 10; i++ { - err = fixture.WaitForRound(status.LastRound+i, roundTime) + _, err = c15.SignAndBroadcastTransaction(wh, nil, offline) + a.NoError(err) + + for i := 0; i < 100; i++ { + status, err := client.Status() a.NoError(err) - account, err = client.AccountData(feesink) + block, err := client.BookkeepingBlock(status.LastRound) + a.NoError(err) + + feesink := block.BlockHeader.FeeSink + err = fixture.WaitForRoundWithTimeout(status.LastRound + 1) a.NoError(err) + data, err := client.AccountData(feesink.String()) + a.NoError(err) + fmt.Printf(" feesink has %d at round %d\n", data.MicroAlgos.Raw, status.LastRound) + a.LessOrEqual(100000, int(data.MicroAlgos.Raw)) // won't go below minfee + if data.MicroAlgos.Raw == 100000 { + break + } + a.Less(i, 32+20) } - a.EqualValues(100000, account.MicroAlgos.Raw) // won't go below minfee } diff --git a/test/e2e-go/features/incentives/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go index ba3fb3ce9f..42a6c39dfa 100644 --- a/test/e2e-go/features/incentives/suspension_test.go +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -29,6 +29,8 @@ import ( "github.com/algorand/go-algorand/test/partitiontest" ) +const roundTime = 2 * time.Second // with speedup below, what's a good value? + // TestBasicSuspension confirms that accounts that don't propose get suspended // (when a tx naming them occurs) func TestBasicSuspension(t *testing.T) { diff --git a/test/testdata/nettemplates/Suspension.json b/test/testdata/nettemplates/Suspension.json index 7dae62aa3e..586370921e 100644 --- a/test/testdata/nettemplates/Suspension.json +++ b/test/testdata/nettemplates/Suspension.json @@ -4,10 +4,11 @@ "ConsensusProtocol": "future", "LastPartKeyRound": 500, "Wallets": [ - { "Name": "Relay", "Stake": 84, "Online": true }, + { "Name": "Relay", "Stake": 84, "Online": false }, { "Name": "Wallet15", "Stake": 15, "Online": true }, - { "Name": "Wallet1", "Stake": 1, "Online": true } - ] + { "Name": "Wallet01", "Stake": 1, "Online": true } + ], + "RewardsPoolBalance": 0 }, "Nodes": [ { @@ -19,6 +20,9 @@ "Name": "Node15", "Wallets": [{ "Name": "Wallet15", "ParticipationOnly": false }] }, - { "Name": "Node1", "Wallets": [{ "Name": "Wallet1", "ParticipationOnly": false }] } + { + "Name": "Node01", + "Wallets": [{ "Name": "Wallet01", "ParticipationOnly": false }] + } ] } From 7abf6b4e1490fd729a561816883777e695c5f883 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 15 Mar 2024 13:17:35 -0400 Subject: [PATCH 056/117] suspension test updates --- .../e2e-go/features/incentives/mining_test.go | 107 +++++------- .../features/incentives/suspension_test.go | 163 ++++++++---------- test/framework/fixtures/libgoalFixture.go | 19 ++ test/testdata/nettemplates/Mining.json | 28 +++ test/testdata/nettemplates/Suspension.json | 14 +- 5 files changed, 170 insertions(+), 161 deletions(-) create mode 100644 test/testdata/nettemplates/Mining.json diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 9d418e21cb..353b161988 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -19,13 +19,10 @@ package suspension import ( "fmt" "path/filepath" - "runtime" "testing" - "time" "github.com/stretchr/testify/require" - "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" @@ -40,33 +37,19 @@ func TestBasicMining(t *testing.T) { partitiontest.PartitionTest(t) defer fixtures.ShutdownSynchronizedTest(t) - if testing.Short() { - t.Skip() - } t.Parallel() a := require.New(fixtures.SynchronizedTest(t)) - // Make the seed lookback shorter, otherwise we need wait 320 rounds to become IncentiveEligible. var fixture fixtures.RestClientFixture - consensus := make(config.ConsensusProtocols) - fast := config.Consensus[protocol.ConsensusFuture] - fast.SeedRefreshInterval = 8 // so balanceRound ends up 2 * 8 * 2 = 32 - // and speed up the roudns while we're at it - if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" { - fast.AgreementFilterTimeoutPeriod0 = time.Second / 2 - fast.AgreementFilterTimeout = time.Second / 2 - } - consensus[protocol.ConsensusFuture] = fast - fixture.SetConsensus(consensus) + // Make the seed lookback shorter, otherwise we need to wait 320 rounds to become IncentiveEligible. + fixture.FasterConsensus(protocol.ConsensusFuture) + fixture.Setup(t, filepath.Join("nettemplates", "Mining.json")) + defer fixture.Shutdown() // Overview of this test: - // Start a single network - // Show that a genesis account does not get incentives - // rereg to become eligible + // rereg to become eligible (must pay extra fee) // show incentives are paid (mining and bonuses) - - fixture.Setup(t, filepath.Join("nettemplates", "Suspension.json")) - defer fixture.Shutdown() + // deplete feesink to ensure it's graceful clientAndAccount := func(name string) (libgoal.Client, model.Account) { c := fixture.GetLibGoalClientForNamedNode(name) @@ -79,45 +62,8 @@ func TestBasicMining(t *testing.T) { c15, account15 := clientAndAccount("Node15") c01, account01 := clientAndAccount("Node01") - rekeyreg := func(client libgoal.Client, address string) basics.AccountData { - // we start by making an _offline_ tx here, because we want to populate the - // key material ourself with a copy of the account's existing material. That - // makes it an _online_ keyreg. That allows the running node to chug along - // without new part keys. We overpay the fee, which makes us - // IncentiveEligible, and to get some funds into FeeSink because we will - // watch it drain toward bottom of test. - reReg, err := client.MakeUnsignedGoOfflineTx(address, 0, 0, 12_000_000, [32]byte{}) - a.NoError(err) - - data, err := client.AccountData(address) - a.True(data.LastHeartbeat == 0) - a.NoError(err) - reReg.KeyregTxnFields = transactions.KeyregTxnFields{ - VotePK: data.VoteID, - SelectionPK: data.SelectionID, - StateProofPK: data.StateProofID, - VoteFirst: data.VoteFirstValid, - VoteLast: data.VoteLastValid, - VoteKeyDilution: data.VoteKeyDilution, - } - - wh, err := client.GetUnencryptedWalletHandle() - a.NoError(err) - onlineTxID, err := client.SignAndBroadcastTransaction(wh, nil, reReg) - a.NoError(err) - txn, err := fixture.WaitForConfirmedTxn(uint64(reReg.LastValid), onlineTxID) - a.NoError(err) - data, err = client.AccountData(address) - a.NoError(err) - a.Equal(basics.Online, data.Status) - a.True(data.IncentiveEligible) - a.True(data.LastHeartbeat > 0) - fmt.Printf(" %v has %v in round %d\n", address, data.MicroAlgos.Raw, *txn.ConfirmedRound) - return data - } - - data01 := rekeyreg(c01, account01.Address) - data15 := rekeyreg(c15, account15.Address) + data01 := rekeyreg(&fixture, a, c01, account01.Address) + data15 := rekeyreg(&fixture, a, c15, account15.Address) // have account01 burn some money to get below to eligibility cap // Starts with 100M, so burn 60M and get under 50M cap. @@ -230,3 +176,40 @@ func TestBasicMining(t *testing.T) { a.Less(i, 32+20) } } + +func rekeyreg(f *fixtures.RestClientFixture, a *require.Assertions, client libgoal.Client, address string) basics.AccountData { + // we start by making an _offline_ tx here, because we want to populate the + // key material ourself with a copy of the account's existing material. That + // makes it an _online_ keyreg. That allows the running node to chug along + // without new part keys. We overpay the fee, which makes us + // IncentiveEligible, and to get some funds into FeeSink because we will + // watch it drain toward bottom of test. + reReg, err := client.MakeUnsignedGoOfflineTx(address, 0, 0, 12_000_000, [32]byte{}) + a.NoError(err) + + data, err := client.AccountData(address) + a.True(data.LastHeartbeat == 0) + a.NoError(err) + reReg.KeyregTxnFields = transactions.KeyregTxnFields{ + VotePK: data.VoteID, + SelectionPK: data.SelectionID, + StateProofPK: data.StateProofID, + VoteFirst: data.VoteFirstValid, + VoteLast: data.VoteLastValid, + VoteKeyDilution: data.VoteKeyDilution, + } + + wh, err := client.GetUnencryptedWalletHandle() + a.NoError(err) + onlineTxID, err := client.SignAndBroadcastTransaction(wh, nil, reReg) + a.NoError(err) + txn, err := f.WaitForConfirmedTxn(uint64(reReg.LastValid), onlineTxID) + a.NoError(err) + data, err = client.AccountData(address) + a.NoError(err) + a.Equal(basics.Online, data.Status) + a.True(data.IncentiveEligible) + a.True(data.LastHeartbeat > 0) + fmt.Printf(" %v has %v in round %d\n", address, data.MicroAlgos.Raw, *txn.ConfirmedRound) + return data +} diff --git a/test/e2e-go/features/incentives/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go index 42a6c39dfa..a5a0c41415 100644 --- a/test/e2e-go/features/incentives/suspension_test.go +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -17,14 +17,17 @@ package suspension import ( + "fmt" "path/filepath" "testing" "time" "github.com/stretchr/testify/require" + "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/basics" - "github.com/algorand/go-algorand/data/transactions" + "github.com/algorand/go-algorand/libgoal" + "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) @@ -37,88 +40,99 @@ func TestBasicSuspension(t *testing.T) { partitiontest.PartitionTest(t) defer fixtures.ShutdownSynchronizedTest(t) - if testing.Short() { - t.Skip() - } t.Parallel() a := require.New(fixtures.SynchronizedTest(t)) // Overview of this test: - // Start a three-node network (84,15,1) - // Wait for 15% node to propose (we never suspend accounts with lastProposed=lastHeartbeat=0) - // Stop it - // Let it run for less than 10*100/15 = 66.6 - // check not suspended, send a tx, still not suspended - // Let it run two more, during which the node can't propose, so it is ready for suspension - // check not suspended, send a tx, NOW suspended + // Start a three-node network (70,20,10), all online + // Wait for 10 and 20% nodes to propose (we never suspend accounts with lastProposed=lastHeartbeat=0) + // Stop them both + // Run for 55 rounds, which is enough for 20% node to be suspended, but not 10% + // check neither suspended, send a tx from 20% to 10%, only 20% gets suspended + // TODO once we have heartbeats: bring them back up, make sure 20% gets back online var fixture fixtures.RestClientFixture + // Make the seed lookback shorter, so the test runs faster + fixture.FasterConsensus(protocol.ConsensusFuture) fixture.Setup(t, filepath.Join("nettemplates", "Suspension.json")) defer fixture.Shutdown() - richAccount, err := fixture.GetRichestAccount() - a.NoError(err) + clientAndAccount := func(name string) (libgoal.Client, model.Account) { + c := fixture.GetLibGoalClientForNamedNode(name) + accounts, err := fixture.GetNodeWalletsSortedByBalance(c) + a.NoError(err) + a.Len(accounts, 1) + return c, accounts[0] + } - // get Node15's address - n15c := fixture.GetLibGoalClientForNamedNode("Node15") - accounts, err := fixture.GetNodeWalletsSortedByBalance(n15c) - a.NoError(err) - a.Len(accounts, 1) - a.Equal(accounts[0].Status, basics.Online.String()) - address := accounts[0].Address + c10, account10 := clientAndAccount("Node10") + c20, account20 := clientAndAccount("Node20") - // wait for n15 to have LastProposed != 0 - account, err := fixture.LibGoalClient.AccountData(address) - for account.LastProposed == 0 { + rekeyreg(&fixture, a, c10, account10.Address) + rekeyreg(&fixture, a, c20, account20.Address) + + // Wait until each have proposed, so they are suspendable + proposed10 := false + proposed20 := false + for !proposed10 || !proposed20 { + status, err := c10.Status() + a.NoError(err) + block, err := c10.BookkeepingBlock(status.LastRound) a.NoError(err) - a.Equal(basics.Online, account.Status) - account, err = fixture.LibGoalClient.AccountData(address) - time.Sleep(roundTime) + + fmt.Printf(" block %d proposed by %v\n", status.LastRound, block.Proposer) + + fixture.WaitForRoundWithTimeout(status.LastRound + 1) + + switch block.Proposer.String() { + case account10.Address: + proposed10 = true + case account20.Address: + proposed20 = true + } } - a.NoError(err) - // turn off Node15 - n15, err := fixture.GetNodeController("Node15") - a.NoError(err) - a.NoError(n15.FullStop()) + a.NoError(c20.FullStop()) - afterStop, err := fixture.AlgodClient.Status() + afterStop, err := c10.Status() a.NoError(err) - // Advance 60 rounds - err = fixture.WaitForRound(afterStop.LastRound+60, 60*roundTime) + // Advance 55 rounds + err = fixture.WaitForRoundWithTimeout(afterStop.LastRound + 55) a.NoError(err) - // n15account is still online (the node is off, but the account is marked online) - account, err = fixture.LibGoalClient.AccountData(address) + // n20 is still online after 55 rounds of absence (the node is off, but the + // account is marked online) because it has not been "noticed". + account, err := fixture.LibGoalClient.AccountData(account20.Address) a.NoError(err) a.Equal(basics.Online, account.Status) voteID := account.VoteID a.NotZero(voteID) - // Advance 10 more, n15 has been "absent" for 70 rounds now - err = fixture.WaitForRound(afterStop.LastRound+70, 15*roundTime) + // pay n10 & n20, so both could be noticed + richAccount, err := fixture.GetRichestAccount() a.NoError(err) + fixture.SendMoneyAndWait(afterStop.LastRound+55, 5, 1000, richAccount.Address, account10.Address, "") + fixture.SendMoneyAndWait(afterStop.LastRound+55, 5, 1000, richAccount.Address, account20.Address, "") - // n15's account is still online, but only because it has gone "unnoticed" - account, err = fixture.LibGoalClient.AccountData(address) + // n20's account is now offline, but has voting key material (suspended) + account, err = c10.AccountData(account20.Address) a.NoError(err) - a.Equal(basics.Online, account.Status) - - // pay n15, so it gets noticed - fixture.SendMoneyAndWait(afterStop.LastRound+70, 5, 1000, richAccount.Address, address, "") + a.Equal(basics.Offline, account.Status) + a.NotZero(account.VoteID) + a.False(account.IncentiveEligible) // suspension turns off flag - // n15's account is now offline, but has voting key material (suspended) - account, err = fixture.LibGoalClient.AccountData(address) + // n10's account is still online, because it's got less stake, not absent 10 x interval. + account, err = c10.AccountData(account10.Address) a.NoError(err) - a.Equal(basics.Offline, account.Status) + a.Equal(basics.Online, account.Status) a.NotZero(account.VoteID) - a.False(account.IncentiveEligible) + a.True(account.IncentiveEligible) // Use the fixture to start the node again. Since we're only a bit past the // suspension round, it will still be voting. It should get a chance to - // propose soon (15/100 of blocks) which will put it back online. - lg, err := fixture.StartNode(n15.GetDataDir()) + // propose soon (20/100 of blocks) which will put it back online. + lg, err := fixture.StartNode(c20.DataDir()) a.NoError(err) // Wait for newly restarted node to start. Presumably it'll catchup in @@ -126,58 +140,23 @@ func TestBasicSuspension(t *testing.T) { stat, err := lg.Status() a.NoError(err) - // Proceed until a round is proposed by n15. (Stop at 50 rounds, that's more likely a bug than luck) + // Proceed until a round is proposed by n20. (Stop at 50 rounds, that's more likely a bug than luck) for r := stat.LastRound; r < stat.LastRound+50; r++ { - err = fixture.WaitForRound(r, roundTime) + err = fixture.WaitForRoundWithTimeout(r) a.NoError(err) - // Once n15 proposes, break out early - if fixture.VerifyBlockProposed(address, 1) { + // Once n20 proposes, break out early + if fixture.VerifyBlockProposed(account20.Address, 1) { // wait one extra round, because changes are processed in block n+1. - err = fixture.WaitForRound(r+1, roundTime) + err = fixture.WaitForRoundWithTimeout(r + 1) a.NoError(err) break } } - // n15's account is back online, with same voting material - account, err = fixture.LibGoalClient.AccountData(address) + // n20's account is back online, with same voting material + account, err = fixture.LibGoalClient.AccountData(account20.Address) a.NoError(err) a.Equal(basics.Online, account.Status) a.Equal(voteID, account.VoteID) - // coming back online by proposal does not make you incentive eligible (you - // didn't "pay the fine") a.False(account.IncentiveEligible) - - // but n15 wants incentives, so it keyregs again, paying the extra fee. - // We're going to re-reg the exact same key material, so that the running - // node can keep voting. - - // we make an _offline_ tx here, because we want to populate the key - // material ourself, by copying the account's existing state. That makes it - // an _online_ keyreg. That allows the running node to chug along without - // new part keys. - reReg, err := n15c.MakeUnsignedGoOfflineTx(address, 0, 0, 5_000_000, [32]byte{}) - require.NoError(t, err, "should be able to make tx") - - reReg.KeyregTxnFields = transactions.KeyregTxnFields{ - VotePK: account.VoteID, - SelectionPK: account.SelectionID, - StateProofPK: account.StateProofID, - VoteFirst: account.VoteFirstValid, - VoteLast: account.VoteLastValid, - VoteKeyDilution: account.VoteKeyDilution, - } - - wh, err := n15c.GetUnencryptedWalletHandle() - require.NoError(t, err, "should be able to get unencrypted wallet handle") - onlineTxID, err := n15c.SignAndBroadcastTransaction(wh, nil, reReg) - require.NoError(t, err, "should be no errors when going online") - - fixture.WaitForConfirmedTxn(uint64(reReg.LastValid), onlineTxID) - account, err = fixture.LibGoalClient.AccountData(address) - - a.NoError(err) - a.Equal(basics.Online, account.Status) - a.True(account.IncentiveEligible) // eligible! - } diff --git a/test/framework/fixtures/libgoalFixture.go b/test/framework/fixtures/libgoalFixture.go index 474c902186..04e0dc8bc6 100644 --- a/test/framework/fixtures/libgoalFixture.go +++ b/test/framework/fixtures/libgoalFixture.go @@ -22,6 +22,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "syscall" "testing" @@ -65,6 +66,24 @@ func (f *RestClientFixture) SetConsensus(consensus config.ConsensusProtocols) { f.consensus = consensus } +// FasterConsensus speeds up the given consensus version in two ways. The seed +// refresh lookback is set tp 8 (instead of 80), so the 320 round balance +// lookback becomes 32. And, if the architecture implies it can be handled, +// round times are shortened by lowering vote timeouts. +func (f *RestClientFixture) FasterConsensus(ver protocol.ConsensusVersion) { + if f.consensus == nil { + f.consensus = make(config.ConsensusProtocols) + } + fast := config.Consensus[ver] + fast.SeedRefreshInterval = 8 // so balanceRound ends up 2 * 8 * 2 = 32 + // and speed up the rounds while we're at it + if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" { + fast.AgreementFilterTimeoutPeriod0 = time.Second / 2 + fast.AgreementFilterTimeout = time.Second / 2 + } + f.consensus[ver] = fast +} + // Setup is called to initialize the test fixture for the test(s) func (f *LibGoalFixture) Setup(t TestingTB, templateFile string) { f.setup(t, t.Name(), templateFile, true) diff --git a/test/testdata/nettemplates/Mining.json b/test/testdata/nettemplates/Mining.json new file mode 100644 index 0000000000..586370921e --- /dev/null +++ b/test/testdata/nettemplates/Mining.json @@ -0,0 +1,28 @@ +{ + "Genesis": { + "NetworkName": "tbd", + "ConsensusProtocol": "future", + "LastPartKeyRound": 500, + "Wallets": [ + { "Name": "Relay", "Stake": 84, "Online": false }, + { "Name": "Wallet15", "Stake": 15, "Online": true }, + { "Name": "Wallet01", "Stake": 1, "Online": true } + ], + "RewardsPoolBalance": 0 + }, + "Nodes": [ + { + "Name": "Relay", + "Wallets": [{ "Name": "Relay", "ParticipationOnly": false }], + "IsRelay": true + }, + { + "Name": "Node15", + "Wallets": [{ "Name": "Wallet15", "ParticipationOnly": false }] + }, + { + "Name": "Node01", + "Wallets": [{ "Name": "Wallet01", "ParticipationOnly": false }] + } + ] +} diff --git a/test/testdata/nettemplates/Suspension.json b/test/testdata/nettemplates/Suspension.json index 586370921e..439cbb888b 100644 --- a/test/testdata/nettemplates/Suspension.json +++ b/test/testdata/nettemplates/Suspension.json @@ -4,9 +4,9 @@ "ConsensusProtocol": "future", "LastPartKeyRound": 500, "Wallets": [ - { "Name": "Relay", "Stake": 84, "Online": false }, - { "Name": "Wallet15", "Stake": 15, "Online": true }, - { "Name": "Wallet01", "Stake": 1, "Online": true } + { "Name": "Relay", "Stake": 70, "Online": true }, + { "Name": "Wallet20", "Stake": 20, "Online": true }, + { "Name": "Wallet10", "Stake": 10, "Online": true } ], "RewardsPoolBalance": 0 }, @@ -17,12 +17,12 @@ "IsRelay": true }, { - "Name": "Node15", - "Wallets": [{ "Name": "Wallet15", "ParticipationOnly": false }] + "Name": "Node20", + "Wallets": [{ "Name": "Wallet20", "ParticipationOnly": false }] }, { - "Name": "Node01", - "Wallets": [{ "Name": "Wallet01", "ParticipationOnly": false }] + "Name": "Node10", + "Wallets": [{ "Name": "Wallet10", "ParticipationOnly": false }] } ] } From 7dbd49756eff2ec911665e9b7a11a79fdb972854 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 18 Mar 2024 15:06:46 -0400 Subject: [PATCH 057/117] Reduce duplicative calls --- agreement/proposal.go | 63 ++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index 07133967ec..b538f6eb74 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -21,6 +21,7 @@ import ( "fmt" "time" + "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" @@ -141,15 +142,10 @@ func (i seedInput) ToBeHashed() (protocol.HashID, []byte) { return protocol.ProposerSeed, protocol.Encode(&i) } -func deriveNewSeed(address basics.Address, vrf *crypto.VRFSecrets, rnd round, period period, ledger LedgerReader) (newSeed committee.Seed, seedProof crypto.VRFProof, reterr error) { +func deriveNewSeed(address basics.Address, vrf *crypto.VRFSecrets, rnd round, period period, ledger LedgerReader, cparams config.ConsensusParams) (newSeed committee.Seed, seedProof crypto.VRFProof, reterr error) { var ok bool var vrfOut crypto.VrfOutput - cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) - if err != nil { - reterr = fmt.Errorf("failed to obtain consensus parameters in round %d: %v", ParamsRound(rnd), err) - return - } var alpha crypto.Digest prevSeed, err := ledger.Seed(seedRound(rnd, cparams)) if err != nil { @@ -198,16 +194,21 @@ func verifyHeader(p unauthenticatedProposal, ledger LedgerReader) error { // ledger.ConsensusParams(rnd) is not allowed because rnd isn't committed. // The BlockHeader isn't trustworthy yet, since we haven't checked the - // upgrade state. So, for now, we confirm that the Proposer is *either* - // correct or missing. `eval` package will using Mining().Enabled to confirm - // which it should be. + // upgrade state. So, lacking the current consensus params, we confirm that + // the Proposer is *either* correct or missing. `eval` package will using + // Mining().Enabled to confirm which it should be. if !p.BlockHeader.Proposer.IsZero() && p.BlockHeader.Proposer != value.OriginalProposer { return fmt.Errorf("wrong proposer (%v != %v)", p.Proposer, value.OriginalProposer) } + cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) + if err != nil { + return fmt.Errorf("failed to obtain consensus parameters in round %d: %w", ParamsRound(rnd), err) + } + // Similarly, we only check here that the payout is zero if - // ineligibile. `eval` code must check that it is correct if > 0. - eligible, err := payoutEligible(rnd, p.Proposer, ledger) + // ineligible. `eval` code must check that it is correct if > 0. + eligible, proposerRecord, err := payoutEligible(rnd, p.Proposer, ledger, cparams) if err != nil { return fmt.Errorf("failed to determine incentive eligibility %w", err) } @@ -216,17 +217,6 @@ func verifyHeader(p unauthenticatedProposal, ledger LedgerReader) error { p.BlockHeader.ProposerPayout.Raw, p.Proposer) } - cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) - if err != nil { - return fmt.Errorf("failed to obtain consensus parameters in round %d: %v", ParamsRound(rnd), err) - } - - balanceRound := balanceRound(rnd, cparams) - proposerRecord, err := ledger.LookupAgreement(balanceRound, value.OriginalProposer) - if err != nil { - return fmt.Errorf("failed to obtain balance record for address %v in round %d: %v", value.OriginalProposer, balanceRound, err) - } - var alpha crypto.Digest prevSeed, err := ledger.Seed(seedRound(rnd, cparams)) if err != nil { @@ -269,19 +259,12 @@ func verifyHeader(p unauthenticatedProposal, ledger LedgerReader) error { } // payoutEligible determines whether the proposer is eligible for block incentive payout. -func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerReader) (bool, error) { - // We want to check eligibility of the online balance in the round that - // mattered to select this proposer. - agreementParams, err := ledger.ConsensusParams(ParamsRound(rnd)) - if err != nil { - return false, err - } - +func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerReader, cparams config.ConsensusParams) (bool, basics.OnlineAccountData, error) { // Check the balance from the agreement round - balanceRound := balanceRound(rnd, agreementParams) + balanceRound := balanceRound(rnd, cparams) balanceRecord, err := ledger.LookupAgreement(balanceRound, proposer) if err != nil { - return false, err + return false, basics.OnlineAccountData{}, err } // It's only fair to compare balances from 320 rounds ago to the mining @@ -289,22 +272,28 @@ func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerRead // when mining begins, the miningRules[0] in consensus.go has Min and Max. balanceParams, err := ledger.ConsensusParams(balanceRound) if err != nil { - return false, err + return false, basics.OnlineAccountData{}, err } eligible := balanceRecord.IncentiveEligible && balanceRecord.MicroAlgosWithRewards.Raw >= balanceParams.Mining().MinBalance && balanceRecord.MicroAlgosWithRewards.Raw <= balanceParams.Mining().MaxBalance - return eligible, nil + return eligible, balanceRecord, nil } func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve ValidatedBlock, period period, ledger LedgerReader) (proposal, proposalValue, error) { - blk := ve.Block() - newSeed, seedProof, err := deriveNewSeed(address, vrf, blk.Round(), period, ledger) + rnd := ve.Block().Round() + + cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) + if err != nil { + return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: no consensus parameters for round %d: %w", ParamsRound(rnd), err) + } + + newSeed, seedProof, err := deriveNewSeed(address, vrf, rnd, period, ledger, cparams) if err != nil { return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could not derive new seed: %w", err) } - eligible, err := payoutEligible(blk.Round(), address, ledger) + eligible, _, err := payoutEligible(rnd, address, ledger, cparams) if err != nil { return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could determine eligibility: %w", err) } From 13af5f02030b3a1be14820c6d3a133b44df73c24 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 10:22:05 -0400 Subject: [PATCH 058/117] Comment clarification Co-authored-by: cce <51567+cce@users.noreply.github.com> --- data/bookkeeping/block.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 13ba40d170..acd329a5f6 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -58,9 +58,9 @@ type ( // Genesis hash to which this block belongs. GenesisHash crypto.Digest `codec:"gh"` - // Proposer is the proposer of this block. Like the Seed, algod adds - // this after the block is built, so that the same block can be prepared - // for multiple Players in the same node. Therefore, it can not be used + // Proposer is the proposer of this block. Like the Seed, agreement adds + // this after the block is assembled by the transaction pool, so that the same block can be prepared + // for multiple participating accounts in the same node. Therefore, it can not be used // to influence block evaluation. Populated if proto.EnableMining Proposer basics.Address `codec:"prp"` From 0aa7d5fbcf5ac1a9b4d77f25d79006f302012b14 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 10:49:29 -0400 Subject: [PATCH 059/117] sanity check Co-authored-by: cce <51567+cce@users.noreply.github.com> --- ledger/double_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ledger/double_test.go b/ledger/double_test.go index 9db07bcf4d..a3083b71db 100644 --- a/ledger/double_test.go +++ b/ledger/double_test.go @@ -140,6 +140,7 @@ func (dl *DoubleLedger) fullBlock(txs ...*txntest.Txn) *ledgercore.ValidatedBloc func (dl *DoubleLedger) endBlock(proposer ...basics.Address) *ledgercore.ValidatedBlock { prp := dl.proposer if len(proposer) > 0 { + require.Len(dl.t, proposer, 1, "endBlock() cannot specify multiple proposers") prp = proposer[0] } vb := endBlock(dl.t, dl.generator, dl.eval, prp) From c49946f8eba718e79c1ebc733726198ae6af7cd7 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 11:07:39 -0400 Subject: [PATCH 060/117] CR updates --- data/bookkeeping/block.go | 3 ++- data/transactions/payment.go | 2 +- data/transactions/transaction.go | 2 +- ledger/apply/keyreg.go | 3 ++- ledger/apply/payment.go | 18 ------------------ ledger/apply/payment_test.go | 10 +++++----- 6 files changed, 11 insertions(+), 27 deletions(-) diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index acd329a5f6..0653d57767 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -532,7 +532,8 @@ func computeBonus(current basics.Round, prevBonus basics.MicroAlgos, curPlan bon // Set the amount if it's non-zero... if !curPlan.baseAmount.IsZero() { upgrading := curPlan != prevPlan || current == 1 - // and the time has come. When the baseRound arrives, or at upgrade time is already passed. + // The time has come if the baseRound arrives, or at upgrade time if + // baseRound has already passed. if current == curPlan.baseRound || (upgrading && current > curPlan.baseRound) { return curPlan.baseAmount } diff --git a/data/transactions/payment.go b/data/transactions/payment.go index 464028be35..97efeb1c5c 100644 --- a/data/transactions/payment.go +++ b/data/transactions/payment.go @@ -37,7 +37,7 @@ type PaymentTxnFields struct { CloseRemainderTo basics.Address `codec:"close"` } -func (payment PaymentTxnFields) checkSpender(header Header, spec SpecialAddresses, proto config.ConsensusParams) error { +func (payment PaymentTxnFields) CheckSpender(header Header, spec SpecialAddresses, proto config.ConsensusParams) error { if header.Sender == payment.CloseRemainderTo { return fmt.Errorf("transaction cannot close account to its sender %v", header.Sender) } diff --git a/data/transactions/transaction.go b/data/transactions/transaction.go index 06ae38c0d6..04a258633f 100644 --- a/data/transactions/transaction.go +++ b/data/transactions/transaction.go @@ -352,7 +352,7 @@ func (tx Transaction) WellFormed(spec SpecialAddresses, proto config.ConsensusPa switch tx.Type { case protocol.PaymentTx: // in case that the fee sink is spending, check that this spend is to a valid address - err := tx.checkSpender(tx.Header, spec, proto) + err := tx.CheckSpender(tx.Header, spec, proto) if err != nil { return err } diff --git a/ledger/apply/keyreg.go b/ledger/apply/keyreg.go index b5cb1380bd..eefdf657f2 100644 --- a/ledger/apply/keyreg.go +++ b/ledger/apply/keyreg.go @@ -66,7 +66,8 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal record.VoteFirstValid = 0 record.VoteLastValid = 0 record.VoteKeyDilution = 0 - record.IncentiveEligible = false + // IncentiveEligible is not reset, because the account has gracefully + // gone offline. They should be able to get back online without paying again. } else { if params.EnableKeyregCoherencyCheck { if keyreg.VoteLast <= round { diff --git a/ledger/apply/payment.go b/ledger/apply/payment.go index 8483bae92b..908e8eb926 100644 --- a/ledger/apply/payment.go +++ b/ledger/apply/payment.go @@ -19,28 +19,10 @@ package apply import ( "fmt" - "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/transactions" ) -func checkSpender(payment transactions.PaymentTxnFields, header transactions.Header, spec transactions.SpecialAddresses, proto config.ConsensusParams) error { - if header.Sender == payment.CloseRemainderTo { - return fmt.Errorf("transaction cannot close account to its sender %v", header.Sender) - } - - // the FeeSink account may only spend to the IncentivePool - if header.Sender == spec.FeeSink { - if payment.Receiver != spec.RewardsPool { - return fmt.Errorf("cannot spend from fee sink's address %v to non incentive pool address %v", header.Sender, payment.Receiver) - } - if payment.CloseRemainderTo != (basics.Address{}) { - return fmt.Errorf("cannot close fee sink %v to %v", header.Sender, payment.CloseRemainderTo) - } - } - return nil -} - // Payment changes the balances according to this transaction. // The ApplyData argument should reflect the changes made by // apply(). It may already include changes made by the caller diff --git a/ledger/apply/payment_test.go b/ledger/apply/payment_test.go index a0b5976d70..a24118aa25 100644 --- a/ledger/apply/payment_test.go +++ b/ledger/apply/payment_test.go @@ -135,18 +135,18 @@ func TestCheckSpender(t *testing.T) { } tx.Sender = basics.Address(feeSink) - require.Error(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV0.ConsensusParams())) + require.Error(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV0.ConsensusParams())) poolAddr := basics.Address(poolAddr) tx.Receiver = poolAddr - require.NoError(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV0.ConsensusParams())) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV0.ConsensusParams())) tx.CloseRemainderTo = poolAddr - require.Error(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV0.ConsensusParams())) - require.Error(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV7.ConsensusParams())) + require.Error(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV0.ConsensusParams())) + require.Error(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams())) tx.Sender = src - require.NoError(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV7.ConsensusParams())) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams())) } func TestPaymentValidation(t *testing.T) { From 76af3a357dfa3ed456ed04ecfe4d7e382857e777 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 16:24:25 -0400 Subject: [PATCH 061/117] Absent list generation tests --- ledger/eval/eval.go | 15 +- ledger/eval/eval_test.go | 185 ++++++++++++++++-- .../prefetcher/prefetcher_alignment_test.go | 4 - ledger/store/trackerdb/data.go | 2 +- 4 files changed, 183 insertions(+), 23 deletions(-) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 4f48858753..80ccef5f9f 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1514,7 +1514,8 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { ch := activeChallenge(&eval.proto, uint64(eval.Round()), eval.state) for _, accountAddr := range eval.state.modifiedAccounts() { - acctDelta, found := eval.state.mods.Accts.GetData(accountAddr) + fmt.Printf("Check %v\n", accountAddr) + acctData, found := eval.state.mods.Accts.GetData(accountAddr) if !found { continue } @@ -1522,8 +1523,8 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { // Regardless of being online or suspended, if voting data exists, the // account can be expired to remove it. This means an offline account // can be expired (because it was already suspended). - if !acctDelta.VoteID.IsEmpty() { - expiresBeforeCurrent := acctDelta.VoteLastValid < current + if !acctData.VoteID.IsEmpty() { + expiresBeforeCurrent := acctData.VoteLastValid < current if expiresBeforeCurrent && len(updates.ExpiredParticipationAccounts) < maxExpirations { updates.ExpiredParticipationAccounts = append( @@ -1538,9 +1539,9 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { continue // no more room (don't break the loop, since we may have more expiries) } - if acctDelta.Status == basics.Online { - lastSeen := max(acctDelta.LastProposed, acctDelta.LastHeartbeat) - if isAbsent(eval.state.prevTotals.Online.Money, acctDelta.MicroAlgos, lastSeen, current) || + if acctData.Status == basics.Online { + lastSeen := max(acctData.LastProposed, acctData.LastHeartbeat) + if isAbsent(eval.state.prevTotals.Online.Money, acctData.MicroAlgos, lastSeen, current) || failsChallenge(ch, accountAddr, lastSeen) { updates.AbsentParticipationAccounts = append( updates.AbsentParticipationAccounts, @@ -1712,7 +1713,7 @@ func (eval *BlockEvaluator) validateAbsentOnlineAccounts() error { } if acctData.Status != basics.Online { - return fmt.Errorf("proposed absent acct %v was not online but %v", accountAddr, acctData.Status) + return fmt.Errorf("proposed absent account %v was %v, not Online", accountAddr, acctData.Status) } lastSeen := max(acctData.LastProposed, acctData.LastHeartbeat) diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 9bcb131fe1..3e8c2c2418 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -30,7 +30,6 @@ import ( "github.com/algorand/go-algorand/agreement" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" - "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/data/basics" basics_testing "github.com/algorand/go-algorand/data/basics/testing" "github.com/algorand/go-algorand/data/bookkeeping" @@ -946,7 +945,7 @@ func (ledger *evalTestLedger) BlockHdr(rnd basics.Round) (bookkeeping.BlockHeade } func (ledger *evalTestLedger) VotersForStateProof(rnd basics.Round) (*ledgercore.VotersForRound, error) { - return nil, errors.New("untested code path") + return nil, nil } // GetCreator is like GetCreatorForRound, but for the latest round and race-free @@ -988,6 +987,10 @@ func (ledger *evalTestLedger) nextBlock(t testing.TB) *BlockEvaluator { func (ledger *evalTestLedger) endBlock(t testing.TB, eval *BlockEvaluator) *ledgercore.ValidatedBlock { validatedBlock, err := eval.GenerateBlock() require.NoError(t, err) + // fake agreement's setting of header fields so later validates work. + seed := committee.Seed{} + crypto.RandBytes(seed[:]) + *validatedBlock = validatedBlock.WithProposal(seed, testPoolAddr, true) err = ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) require.NoError(t, err) return validatedBlock @@ -1078,6 +1081,7 @@ func (l *testCowBaseLedger) GetCreatorForRound(_ basics.Round, cindex basics.Cre func TestCowBaseCreatorsCache(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() addresses := make([]basics.Address, 3) for i := 0; i < len(addresses); i++ { @@ -1121,6 +1125,7 @@ func TestCowBaseCreatorsCache(t *testing.T) { // TestEvalFunctionForExpiredAccounts tests that the eval function will correctly mark accounts as offline func TestEvalFunctionForExpiredAccounts(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() genesisInitState, addrs, keys := ledgertesting.GenesisWithProto(10, protocol.ConsensusFuture) @@ -1272,6 +1277,7 @@ func (p *failRoundCowParent) lookup(basics.Address) (ledgercore.AccountData, err // TestExpiredAccountGenerationWithDiskFailure tests edge cases where disk failures can lead to ledger look up failures func TestExpiredAccountGenerationWithDiskFailure(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() genesisInitState, addrs, keys := ledgertesting.GenesisWithProto(10, protocol.ConsensusFuture) @@ -1357,9 +1363,167 @@ func TestExpiredAccountGenerationWithDiskFailure(t *testing.T) { require.ErrorContains(t, err, "disk I/O fail (on purpose)") } +// TestAbsenteeChecks tests that the eval function will correctly mark accounts as absent +func TestAbsenteeChecks(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + genesisInitState, addrs, keys := ledgertesting.GenesisWithProto(10, protocol.ConsensusFuture) + + // add 32 more addresses, we can get a suspension by challenge + for i := 0; i < 32; i++ { + addrs = append(addrs, basics.Address{byte(i << 3), 0xaa}) + } + + // Set all addrs to online + for i, addr := range addrs { + tmp := genesisInitState.Accounts[addr] + tmp.Status = basics.Online + crypto.RandBytes(tmp.StateProofID[:]) + crypto.RandBytes(tmp.SelectionID[:]) + crypto.RandBytes(tmp.VoteID[:]) + tmp.VoteFirstValid = 1 + tmp.VoteLastValid = 1500 // large enough to avoid EXPIRATION, so we can see SUSPENSION + tmp.LastHeartbeat = 1 // non-zero allows suspensions + switch i { + case 1: + tmp.LastHeartbeat = 1150 // lie hear so that addr[1] won't be suspended + case 2: + tmp.LastProposed = 1150 // lie hear so that addr[2] won't be suspended + } + + genesisInitState.Accounts[addr] = tmp + } + + l := newTestLedger(t, bookkeeping.GenesisBalances{ + Balances: genesisInitState.Accounts, + FeeSink: testSinkAddr, + RewardsPool: testPoolAddr, + }) + + newBlock := bookkeeping.MakeBlock(l.blocks[0].BlockHeader) + + blkEval, err := l.StartEvaluator(newBlock.BlockHeader, 0, 0, nil) + require.NoError(t, err) + + // Advance the evaluator, watching for lack of suspensions since we don't + // suspend until a txn with a suspendable account appears + challenge := byte(0) + for i := uint64(0); i < uint64(1210); i++ { // A bit past one grace period (200) past challenge at 1000. + vb := l.endBlock(t, blkEval) + blkEval = l.nextBlock(t) + require.Zero(t, vb.Block().AbsentParticipationAccounts) + if vb.Block().Round() == 1000 { + challenge = vb.Block().BlockHeader.Seed[0] + } + } + challenged := basics.Address{(challenge >> 3) << 3, 0xaa} + fmt.Printf("challenge = %x challenged = %v\n", challenge, challenged) + + pay := func(i int, a basics.Address) transactions.Transaction { + return transactions.Transaction{ + Type: protocol.PaymentTx, + Header: transactions.Header{ + Sender: addrs[i], + Fee: minFee, + LastValid: blkEval.Round(), + GenesisHash: l.GenesisHash(), + }, + PaymentTxnFields: transactions.PaymentTxnFields{ + Receiver: a, + Amount: basics.MicroAlgos{Raw: 100_000}, + }, + } + } + + selfpay := func(i int) transactions.SignedTxn { + return pay(i, addrs[i]).Sign(keys[i]) + } + + require.NoError(t, blkEval.Transaction(selfpay(0), transactions.ApplyData{})) + require.NoError(t, blkEval.Transaction(selfpay(1), transactions.ApplyData{})) + require.NoError(t, blkEval.Transaction(selfpay(2), transactions.ApplyData{})) + for i := 0; i < 32; i++ { + require.NoError(t, blkEval.Transaction(pay(0, basics.Address{byte(i << 3), 0xaa}).Sign(keys[0]), + transactions.ApplyData{})) + } + + // Make sure we validate our block as well + blkEval.validate = true + + validatedBlock, err := blkEval.GenerateBlock() + require.NoError(t, err) + + // fake agreement's setting of header fields so later validates work + *validatedBlock = validatedBlock.WithProposal(committee.Seed{}, testPoolAddr, true) + + require.Zero(t, validatedBlock.Block().ExpiredParticipationAccounts) + require.Contains(t, validatedBlock.Block().AbsentParticipationAccounts, addrs[0], addrs[0].String()) + require.NotContains(t, validatedBlock.Block().AbsentParticipationAccounts, addrs[1], addrs[1].String()) + require.NotContains(t, validatedBlock.Block().AbsentParticipationAccounts, addrs[2], addrs[2].String()) + + // Of the 32 extra accounts, make sure only the one matching the challenge is suspended + require.Contains(t, validatedBlock.Block().AbsentParticipationAccounts, challenged, challenged.String()) + for i := byte(0); i < 32; i++ { + if i == challenge>>3 { + continue + } + require.NotContains(t, validatedBlock.Block().AbsentParticipationAccounts, basics.Address{i << 3, 0xaa}) + } + + _, err = Eval(context.Background(), l, validatedBlock.Block(), false, nil, nil, l.tracer) + require.NoError(t, err) + + acctData, _ := blkEval.state.lookup(addrs[0]) + + require.NotZero(t, acctData.StateProofID) + require.NotZero(t, acctData.SelectionID) + require.NotZero(t, acctData.VoteID) + goodBlock := validatedBlock.Block() + + // First validate that it's fine if we dont touch it. + _, err = Eval(context.Background(), l, goodBlock, true, verify.GetMockedCache(true), nil, l.tracer) + require.NoError(t, err) + + // Introduce an address that shouldn't be suspended + badBlock := goodBlock + badBlock.AbsentParticipationAccounts = append(badBlock.AbsentParticipationAccounts, addrs[1]) + _, err = Eval(context.Background(), l, badBlock, true, verify.GetMockedCache(true), nil, l.tracer) + require.ErrorContains(t, err, "not absent") + + // An account that isn't even online + badBlock = goodBlock + badBlock.AbsentParticipationAccounts = append(badBlock.AbsentParticipationAccounts, basics.Address{0x01}) + _, err = Eval(context.Background(), l, badBlock, true, verify.GetMockedCache(true), nil, l.tracer) + require.ErrorContains(t, err, "not Online") + + // Add more than the expected number of accounts + badBlock = goodBlock + addressToCopy := badBlock.AbsentParticipationAccounts[0] + for i := 0; i < blkEval.proto.MaxProposedExpiredOnlineAccounts+1; i++ { + badBlock.AbsentParticipationAccounts = append(badBlock.AbsentParticipationAccounts, addressToCopy) + } + + _, err = Eval(context.Background(), l, badBlock, true, verify.GetMockedCache(true), nil, l.tracer) + require.ErrorContains(t, err, "length of absent accounts") + + // Duplicate an address + badBlock = goodBlock + badBlock.AbsentParticipationAccounts = append(badBlock.AbsentParticipationAccounts, addressToCopy) + + _, err = Eval(context.Background(), l, badBlock, true, verify.GetMockedCache(true), nil, l.tracer) + require.ErrorContains(t, err, "duplicate address found") + + badBlock = goodBlock + // sanity check that bad block is being actually copied and not just the pointer + _, err = Eval(context.Background(), l, badBlock, true, verify.GetMockedCache(true), nil, l.tracer) + require.NoError(t, err) +} + // TestExpiredAccountGeneration test that expired accounts are added to a block header and validated func TestExpiredAccountGeneration(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() genesisInitState, addrs, keys := ledgertesting.GenesisWithProto(10, protocol.ConsensusFuture) @@ -1446,19 +1610,18 @@ func TestExpiredAccountGeneration(t *testing.T) { listOfExpiredAccounts := validatedBlock.Block().ParticipationUpdates.ExpiredParticipationAccounts - require.Equal(t, 1, len(listOfExpiredAccounts)) - expiredAccount := listOfExpiredAccounts[0] - require.Equal(t, expiredAccount, recvAddr) + require.Len(t, listOfExpiredAccounts, 1) + require.Equal(t, listOfExpiredAccounts[0], recvAddr) recvAcct, err := eval.state.lookup(recvAddr) require.NoError(t, err) require.Equal(t, basics.Offline, recvAcct.Status) - require.Equal(t, basics.Round(0), recvAcct.VoteFirstValid) - require.Equal(t, basics.Round(0), recvAcct.VoteLastValid) - require.Equal(t, uint64(0), recvAcct.VoteKeyDilution) - require.Equal(t, crypto.OneTimeSignatureVerifier{}, recvAcct.VoteID) - require.Equal(t, crypto.VRFVerifier{}, recvAcct.SelectionID) - require.Equal(t, merklesignature.Verifier{}.Commitment, recvAcct.StateProofID) + require.Zero(t, recvAcct.VoteFirstValid) + require.Zero(t, recvAcct.VoteLastValid) + require.Zero(t, recvAcct.VoteKeyDilution) + require.Zero(t, recvAcct.VoteID) + require.Zero(t, recvAcct.SelectionID) + require.Zero(t, recvAcct.StateProofID) } func TestBitsMatch(t *testing.T) { diff --git a/ledger/eval/prefetcher/prefetcher_alignment_test.go b/ledger/eval/prefetcher/prefetcher_alignment_test.go index b6e85a21a2..cb4b165c94 100644 --- a/ledger/eval/prefetcher/prefetcher_alignment_test.go +++ b/ledger/eval/prefetcher/prefetcher_alignment_test.go @@ -58,10 +58,6 @@ func rewardsPool() basics.Address { return makeAddress(101) } -func proposer() basics.Address { - return basics.Address{} -} - func genesisBlock() (bookkeeping.Block, error) { block, err := bookkeeping.MakeGenesisBlock( proto, diff --git a/ledger/store/trackerdb/data.go b/ledger/store/trackerdb/data.go index c1d5c52d93..da2cf3eeab 100644 --- a/ledger/store/trackerdb/data.go +++ b/ledger/store/trackerdb/data.go @@ -152,7 +152,7 @@ type BaseOnlineAccountData struct { BaseVotingData - IncentiveEligible bool `codec:"W"` + IncentiveEligible bool `codec:"X"` MicroAlgos basics.MicroAlgos `codec:"Y"` RewardsBase uint64 `codec:"Z"` } From a61a0ef8ac98bcf820773b2ee6329a31d7c56902 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 16:32:24 -0400 Subject: [PATCH 062/117] Add methods for some reason --- agreement/proposal.go | 8 ++++---- data/bookkeeping/block.go | 12 +++++++++++- ledger/eval/eval.go | 12 ++++++------ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index b538f6eb74..066f78824b 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -197,7 +197,7 @@ func verifyHeader(p unauthenticatedProposal, ledger LedgerReader) error { // upgrade state. So, lacking the current consensus params, we confirm that // the Proposer is *either* correct or missing. `eval` package will using // Mining().Enabled to confirm which it should be. - if !p.BlockHeader.Proposer.IsZero() && p.BlockHeader.Proposer != value.OriginalProposer { + if !p.Proposer().IsZero() && p.Proposer() != value.OriginalProposer { return fmt.Errorf("wrong proposer (%v != %v)", p.Proposer, value.OriginalProposer) } @@ -208,13 +208,13 @@ func verifyHeader(p unauthenticatedProposal, ledger LedgerReader) error { // Similarly, we only check here that the payout is zero if // ineligible. `eval` code must check that it is correct if > 0. - eligible, proposerRecord, err := payoutEligible(rnd, p.Proposer, ledger, cparams) + eligible, proposerRecord, err := payoutEligible(rnd, p.Proposer(), ledger, cparams) if err != nil { return fmt.Errorf("failed to determine incentive eligibility %w", err) } - if !eligible && p.BlockHeader.ProposerPayout.Raw > 0 { + if !eligible && p.ProposerPayout().Raw > 0 { return fmt.Errorf("proposer payout (%d) for ineligible Proposer %v", - p.BlockHeader.ProposerPayout.Raw, p.Proposer) + p.ProposerPayout().Raw, p.Proposer) } var alpha crypto.Digest diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index 0653d57767..c09beee1da 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -299,10 +299,20 @@ func (block Block) GenesisHash() crypto.Digest { } // Seed returns the Block's random seed. -func (block *Block) Seed() committee.Seed { +func (block Block) Seed() committee.Seed { return block.BlockHeader.Seed } +// Proposer returns the Block's proposer. +func (block Block) Proposer() basics.Address { + return block.BlockHeader.Proposer +} + +// ProposerPayout returns the Block's proposer payout. +func (block Block) ProposerPayout() basics.MicroAlgos { + return block.BlockHeader.ProposerPayout +} + // NextRewardsState computes the RewardsState of the subsequent round // given the subsequent consensus parameters, along with the incentive pool // balance and the total reward units in the system as of the current round. diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 80ccef5f9f..b4e0f3f5b7 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1328,7 +1328,7 @@ func (eval *BlockEvaluator) endOfBlock() error { // can cancel this payment by zero'ing the ProposerPayout if the // proposer is found to be ineligible. See WithProposal(). eval.block.FeesCollected = eval.state.feesCollected - eval.block.ProposerPayout, err = eval.proposerPayout() + eval.block.BlockHeader.ProposerPayout, err = eval.proposerPayout() if err != nil { return err } @@ -1395,12 +1395,12 @@ func (eval *BlockEvaluator) endOfBlock() error { // even if Mining is not enabled (and unset any time). So make sure // it's set iff it should be. if eval.proto.Mining().Enabled { - if eval.block.Proposer.IsZero() && !eval.generate { // if generating, proposer is set later by agreement + if eval.block.Proposer().IsZero() && !eval.generate { // if generating, proposer is set later by agreement return fmt.Errorf("proposer missing when mining enabled") } } else { - if !eval.block.Proposer.IsZero() { - return fmt.Errorf("proposer %v present when mining disabled", eval.block.Proposer) + if !eval.block.Proposer().IsZero() { + return fmt.Errorf("proposer %v present when mining disabled", eval.block.Proposer()) } } @@ -1415,8 +1415,8 @@ func (eval *BlockEvaluator) endOfBlock() error { } maxPayout = payout.Raw } - if eval.block.ProposerPayout.Raw > maxPayout { - return fmt.Errorf("proposal wants %d payout, %d is allowed", eval.block.ProposerPayout.Raw, maxPayout) + if eval.block.ProposerPayout().Raw > maxPayout { + return fmt.Errorf("proposal wants %d payout, %d is allowed", eval.block.ProposerPayout().Raw, maxPayout) } expectedVoters, expectedVotersWeight, err2 := eval.stateProofVotersAndTotal() From 74ede811cccd1c776f90fc56f40ff7759764cee9 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 16:38:03 -0400 Subject: [PATCH 063/117] WithProposER --- agreement/abstractions.go | 4 ++-- agreement/agreementtest/simulate_test.go | 2 +- agreement/common_test.go | 2 +- agreement/fuzzer/ledger_test.go | 2 +- agreement/proposal.go | 8 ++++---- data/datatest/impls.go | 4 ++-- ledger/eval/eval.go | 2 +- ledger/eval/eval_test.go | 6 +++--- ledger/ledgercore/validatedBlock.go | 4 ++-- ledger/simple_test.go | 4 ++-- node/node.go | 6 +++--- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/agreement/abstractions.go b/agreement/abstractions.go index 9f3341aaed..de3e438912 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -54,7 +54,7 @@ type BlockValidator interface { // and can now be recorded in the ledger. This is an optimized version of // calling EnsureBlock() on the Ledger. type ValidatedBlock interface { - // WithProposal creates a copy of this ValidatedBlock with its + // WithProposer creates a copy of this ValidatedBlock with its // cryptographically random seed and proposer set. The block's // ProposerPayout is zero'd if !eligible. Abstractly, it is how the // agreement code "finishes" a block and makes it a proposal for a specific @@ -62,7 +62,7 @@ type ValidatedBlock interface { // // Calls to Seed() or to Digest() on the copy's Block must // reflect the value of the new seed. - WithProposal(seed committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock + WithProposer(seed committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock // Block returns the underlying block that has been validated. Block() bookkeeping.Block diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index df346098a1..ed1bcd736f 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -79,7 +79,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { diff --git a/agreement/common_test.go b/agreement/common_test.go index c7e39f132a..1c961bc3ff 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -165,7 +165,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go index c184623120..e080142d9e 100644 --- a/agreement/fuzzer/ledger_test.go +++ b/agreement/fuzzer/ledger_test.go @@ -93,7 +93,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { diff --git a/agreement/proposal.go b/agreement/proposal.go index 066f78824b..3c56f5bf8a 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -185,10 +185,10 @@ func deriveNewSeed(address basics.Address, vrf *crypto.VRFSecrets, rnd round, pe return } -// verifyHeader checks the things in the header that can only be confirmed by +// verifyProposer checks the things in the header that can only be confirmed by // looking into the unauthenticatedProposal or using LookupAgreement. The // Proposer, ProposerPayout, and Seed. -func verifyHeader(p unauthenticatedProposal, ledger LedgerReader) error { +func verifyProposer(p unauthenticatedProposal, ledger LedgerReader) error { value := p.value() rnd := p.Round() @@ -298,7 +298,7 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve Validat return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could determine eligibility: %w", err) } - ve = ve.WithProposal(newSeed, address, eligible) + ve = ve.WithProposer(newSeed, address, eligible) proposal := makeProposal(ve, seedProof, period, address) value := proposalValue{ OriginalPeriod: period, @@ -319,7 +319,7 @@ func (p unauthenticatedProposal) validate(ctx context.Context, current round, le return invalid, fmt.Errorf("proposed entry from wrong round: entry.Round() != current: %v != %v", entry.Round(), current) } - err := verifyHeader(p, ledger) + err := verifyProposer(p, ledger) if err != nil { return invalid, fmt.Errorf("unable to verify header: %w", err) } diff --git a/data/datatest/impls.go b/data/datatest/impls.go index c4c251c726..f1c41bc0cc 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -64,8 +64,8 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated return validatedBlock{blk: &b}, nil } -// WithProposal implements the agreement.ValidatedBlock interface. -func (ve validatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { +// WithProposer implements the agreement.ValidatedBlock interface. +func (ve validatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { newblock := *ve.blk newblock.BlockHeader.Seed = s newblock.BlockHeader.Proposer = proposer diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index b4e0f3f5b7..3ecf2e18b8 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1326,7 +1326,7 @@ func (eval *BlockEvaluator) endOfBlock() error { if eval.proto.Mining().Enabled { // Determine how much the proposer should be paid. Agreement code // can cancel this payment by zero'ing the ProposerPayout if the - // proposer is found to be ineligible. See WithProposal(). + // proposer is found to be ineligible. See WithProposer(). eval.block.FeesCollected = eval.state.feesCollected eval.block.BlockHeader.ProposerPayout, err = eval.proposerPayout() if err != nil { diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 3e8c2c2418..8fe4a385ef 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -990,7 +990,7 @@ func (ledger *evalTestLedger) endBlock(t testing.TB, eval *BlockEvaluator) *ledg // fake agreement's setting of header fields so later validates work. seed := committee.Seed{} crypto.RandBytes(seed[:]) - *validatedBlock = validatedBlock.WithProposal(seed, testPoolAddr, true) + *validatedBlock = validatedBlock.WithProposer(seed, testPoolAddr, true) err = ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) require.NoError(t, err) return validatedBlock @@ -1212,7 +1212,7 @@ func TestEvalFunctionForExpiredAccounts(t *testing.T) { require.NoError(t, err) // fake agreement's setting of header fields so later validates work - *validatedBlock = validatedBlock.WithProposal(committee.Seed{}, testPoolAddr, true) + *validatedBlock = validatedBlock.WithProposer(committee.Seed{}, testPoolAddr, true) expired := false for _, acct := range validatedBlock.Block().ExpiredParticipationAccounts { @@ -1455,7 +1455,7 @@ func TestAbsenteeChecks(t *testing.T) { require.NoError(t, err) // fake agreement's setting of header fields so later validates work - *validatedBlock = validatedBlock.WithProposal(committee.Seed{}, testPoolAddr, true) + *validatedBlock = validatedBlock.WithProposer(committee.Seed{}, testPoolAddr, true) require.Zero(t, validatedBlock.Block().ExpiredParticipationAccounts) require.Contains(t, validatedBlock.Block().AbsentParticipationAccounts, addrs[0], addrs[0].String()) diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 975973f50c..cf034ee0e6 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -41,8 +41,8 @@ func (vb ValidatedBlock) Delta() StateDelta { return vb.delta } -// WithProposal returns a copy of the ValidatedBlock with a modified seed and associated proposer -func (vb ValidatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { +// WithProposer returns a copy of the ValidatedBlock with a modified seed and associated proposer +func (vb ValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { newblock := vb.blk newblock.BlockHeader.Seed = s // agreement is telling us who the proposer is and if they're eligible, but diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 9db2d90c37..b054606ce5 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -157,11 +157,11 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer // can't call the agreement code, the eligibility of the prp is not // considered. if ledger.GenesisProto().Mining().Enabled { - *vb = vb.WithProposal(committee.Seed(prp), prp, true) + *vb = vb.WithProposer(committee.Seed(prp), prp, true) } else { // To more closely mimic the agreement code, we don't // write the proposer when !Mining().Enabled. - *vb = vb.WithProposal(committee.Seed(prp), basics.Address{}, false) + *vb = vb.WithProposer(committee.Seed(prp), basics.Address{}, false) } err = ledger.AddValidatedBlock(*vb, agreement.Certificate{}) diff --git a/node/node.go b/node/node.go index 1224f55147..8703bd6175 100644 --- a/node/node.go +++ b/node/node.go @@ -1290,9 +1290,9 @@ type validatedBlock struct { vb *ledgercore.ValidatedBlock } -// WithProposal satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) WithProposal(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { - lvb := vb.vb.WithProposal(s, proposer, eligible) +// WithProposer satisfies the agreement.ValidatedBlock interface. +func (vb validatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { + lvb := vb.vb.WithProposer(s, proposer, eligible) return validatedBlock{vb: &lvb} } From fd1c88b83ce25233d7c59b4863c5efb2c8e0e9de Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 16:44:31 -0400 Subject: [PATCH 064/117] Copy IncentiveEligible in ledgercore function --- ledger/ledgercore/accountdata.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 60c9b9645a..92efa394a1 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -213,6 +213,7 @@ func (u AccountData) OnlineAccountData(proto config.ConsensusParams, rewardsLeve VoteLastValid: u.VoteLastValid, VoteKeyDilution: u.VoteKeyDilution, }, + IncentiveEligible: u.IncentiveEligible, } } From 9d536a758a5d0b7cef5fc2a92dbeb35103e9cec7 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 17:29:09 -0400 Subject: [PATCH 065/117] comment updates --- test/e2e-go/features/incentives/mining_test.go | 6 +++--- test/framework/fixtures/libgoalFixture.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 353b161988..30d701da72 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -75,9 +75,9 @@ func TestBasicMining(t *testing.T) { data01, err = c01.AccountData(account01.Address) a.NoError(err) - // Go 31 rounds after the burn happened. rounds. During this time, - // incentive eligibility is not in effect yet, so regardless of who - // proposes, they won't earn anything. + // Go 31 rounds after the burn happened. During this time, incentive + // eligibility is not in effect yet, so regardless of who proposes, they + // won't earn anything. client := fixture.LibGoalClient status, err := client.Status() diff --git a/test/framework/fixtures/libgoalFixture.go b/test/framework/fixtures/libgoalFixture.go index 04e0dc8bc6..fbfb7bd9b1 100644 --- a/test/framework/fixtures/libgoalFixture.go +++ b/test/framework/fixtures/libgoalFixture.go @@ -67,7 +67,7 @@ func (f *RestClientFixture) SetConsensus(consensus config.ConsensusProtocols) { } // FasterConsensus speeds up the given consensus version in two ways. The seed -// refresh lookback is set tp 8 (instead of 80), so the 320 round balance +// refresh lookback is set to 8 (instead of 80), so the 320 round balance // lookback becomes 32. And, if the architecture implies it can be handled, // round times are shortened by lowering vote timeouts. func (f *RestClientFixture) FasterConsensus(ver protocol.ConsensusVersion) { From a7e701063e99563dd7d4e26fa8fe24e345d54220 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 17:36:52 -0400 Subject: [PATCH 066/117] More explicitly set the returned stackValue --- data/transactions/logic/eval.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index d8757d1917..3eba36bcfd 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -5701,17 +5701,16 @@ func opBlock(cx *EvalContext) error { case BlkSeed: cx.Stack[last].Bytes = hdr.Seed[:] case BlkTimestamp: - cx.Stack[last].Bytes = nil if hdr.TimeStamp < 0 { return fmt.Errorf("block(%d) timestamp %d < 0", round, hdr.TimeStamp) } - cx.Stack[last].Uint = uint64(hdr.TimeStamp) + cx.Stack[last] = stackValue{Uint: uint64(hdr.TimeStamp)} case BlkProposer: cx.Stack[last].Bytes = hdr.Proposer[:] case BlkFeesCollected: - cx.Stack[last].Uint = hdr.FeesCollected.Raw + cx.Stack[last] = stackValue{Uint: hdr.FeesCollected.Raw} case BlkBonus: - cx.Stack[last].Uint = hdr.Bonus.Raw + cx.Stack[last] = stackValue{Uint: hdr.Bonus.Raw} default: return fmt.Errorf("invalid block field %s", fs.field) } From 1b0d633e2f6b64da54268ec920987aa0b42bd5e0 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 20 Mar 2024 17:55:36 -0400 Subject: [PATCH 067/117] more friendly fire from adding methods --- ledger/eval_simple_test.go | 2 +- ledger/ledger_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 3f1af17d75..fee4789dfb 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -270,7 +270,7 @@ func TestMiningFees(t *testing.T) { // new fields are in the header require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) require.EqualValues(t, bonus1, vb.Block().Bonus.Raw) - require.EqualValues(t, bonus1+1_500, vb.Block().ProposerPayout.Raw) + require.EqualValues(t, bonus1+1_500, vb.Block().ProposerPayout().Raw) } else { require.False(t, dl.generator.GenesisProto().Mining().Enabled) require.Zero(t, dl.generator.GenesisProto().Mining().Percent) // version sanity check diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go index a402549dd3..92f709baa9 100644 --- a/ledger/ledger_test.go +++ b/ledger/ledger_test.go @@ -109,7 +109,7 @@ func endOfBlock(blk *bookkeeping.Block) error { blk.FeesCollected.Raw += txn.Txn.Fee.Raw } // blk.ProposerPayout is allowed to be zero, so don't reproduce the calc here. - blk.Proposer = basics.Address{0x01} // Must be set to _something_. + blk.BlockHeader.Proposer = basics.Address{0x01} // Must be set to _something_. } var err error blk.TxnCommitments, err = blk.PaysetCommit() From f64c143ec7fba92e53be7de2406fe7c73c0bd63d Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 21 Mar 2024 13:09:13 -0400 Subject: [PATCH 068/117] Move the mining and bonus stuff into consensus also, removing the name "mining" in favor of just "payouts" --- agreement/proposal.go | 15 +++-- config/config_test.go | 12 ---- config/consensus.go | 91 ++++++++++++++++------------- data/bookkeeping/block.go | 50 +++------------- data/bookkeeping/block_test.go | 35 +++++------ data/transactions/payment.go | 2 +- ledger/apply/keyreg.go | 4 +- ledger/apptxn_test.go | 4 +- ledger/eval/eval.go | 19 +++--- ledger/eval/eval_test.go | 8 +-- ledger/eval_simple_test.go | 8 +-- ledger/ledger_test.go | 2 +- ledger/ledgercore/validatedBlock.go | 4 +- ledger/simple_test.go | 4 +- 14 files changed, 104 insertions(+), 154 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index 3c56f5bf8a..46f39edef1 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -196,9 +196,9 @@ func verifyProposer(p unauthenticatedProposal, ledger LedgerReader) error { // The BlockHeader isn't trustworthy yet, since we haven't checked the // upgrade state. So, lacking the current consensus params, we confirm that // the Proposer is *either* correct or missing. `eval` package will using - // Mining().Enabled to confirm which it should be. + // Mining.Enabled to confirm which it should be. if !p.Proposer().IsZero() && p.Proposer() != value.OriginalProposer { - return fmt.Errorf("wrong proposer (%v != %v)", p.Proposer, value.OriginalProposer) + return fmt.Errorf("wrong proposer (%v != %v)", p.Proposer(), value.OriginalProposer) } cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) @@ -214,7 +214,7 @@ func verifyProposer(p unauthenticatedProposal, ledger LedgerReader) error { } if !eligible && p.ProposerPayout().Raw > 0 { return fmt.Errorf("proposer payout (%d) for ineligible Proposer %v", - p.ProposerPayout().Raw, p.Proposer) + p.ProposerPayout().Raw, p.Proposer()) } var alpha crypto.Digest @@ -267,16 +267,15 @@ func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerRead return false, basics.OnlineAccountData{}, err } - // It's only fair to compare balances from 320 rounds ago to the mining - // rules that were in effect then. To make this work in a reasonable way - // when mining begins, the miningRules[0] in consensus.go has Min and Max. + // When mining begins, nobody could possible have IncentiveEligible set in + // the blanceRound rounds ago, so the min/max check is irrelevant. balanceParams, err := ledger.ConsensusParams(balanceRound) if err != nil { return false, basics.OnlineAccountData{}, err } eligible := balanceRecord.IncentiveEligible && - balanceRecord.MicroAlgosWithRewards.Raw >= balanceParams.Mining().MinBalance && - balanceRecord.MicroAlgosWithRewards.Raw <= balanceParams.Mining().MaxBalance + balanceRecord.MicroAlgosWithRewards.Raw >= balanceParams.Payouts.MinBalance && + balanceRecord.MicroAlgosWithRewards.Raw <= balanceParams.Payouts.MaxBalance return eligible, balanceRecord, nil } diff --git a/config/config_test.go b/config/config_test.go index 3e5ae33f8e..432c0f9281 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -996,15 +996,3 @@ func TestTracksCatchpointsWithoutStoring(t *testing.T) { require.Equal(t, true, cfg.TracksCatchpoints()) require.Equal(t, false, cfg.StoresCatchpoints()) } - -func TestAllProtocolMiningVersions(t *testing.T) { - // in reality, this test will never even get a chance to run if it would - // fail, since checkSetMax will panic. - partitiontest.PartitionTest(t) - t.Parallel() - a := assert.New(t) - - for _, p := range Consensus { - a.Less(int(p.MiningRulesVer), len(miningRules)) - } -} diff --git a/config/consensus.go b/config/consensus.go index b515c0c9e1..b3bfc7e2e3 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -530,18 +530,21 @@ type ConsensusParams struct { // dynamic filter, it will be calculated and logged (but not used). DynamicFilterTimeout bool - // MiningRulesVer is the version of mining related rules. It excludes anything - // related to block "bonuses" - extra payments made beyond what fees could - // provide. 0 disables mining and related tracking - MiningRulesVer uint8 - - // BonusPlanVer is the version of the block bonus plan. 0 indicates no block bonuses. - BonusPlanVer uint8 + // Payouts contains parameters for amounts and eligibility for block proposer + // payouts. It excludes information about the "unsustainable" payouts + // described in BonusPlan. + Payouts ProposerPayoutRules + + // Bonus contains parameters related to the extra payout made to block + // proposers, unrelated to the fees paid in that block. For it to actually + // occur, extra funds need to be put into the FeeSink. The bonus amount + // decays exponentially. + Bonus BonusPlan } -// MiningRules puts several related consensus parameters in one place. The same +// ProposerPayoutRules puts several related consensus parameters in one place. The same // care for backward compatibility with old blocks must be taken. -type MiningRules struct { +type ProposerPayoutRules struct { // Enabled turns on several things needed for paying block incentives, // including tracking of the proposer and fees collected. Enabled bool @@ -589,35 +592,30 @@ type MiningRules struct { ChallengeBits int } -// miningRules should be extended, never changed, since old blocks must retain -// their behavior. -var miningRules = [...]MiningRules{ - { - Enabled: false, - // Because the eligibility check has a lookback, we need these set even - // here before mining begins. - MinBalance: 30_000_000_000, // 30,000 algos - MaxBalance: 50_000_000_000_000, // 50M algos - }, - { - Enabled: true, - Percent: 75, - GoOnlineFee: 2_000_000, // 2 algos - MinBalance: 30_000_000_000, // 30,000 algos - MaxBalance: 50_000_000_000_000, // 50M algos - MaxMarkAbsent: 32, - ChallengeInterval: 1000, - ChallengeGracePeriod: 200, - ChallengeBits: 5, - // With about 31k rounds per day, we expect about (31k/interval)/2^bits ~= 1 challenge / day / account) - }, -} - -// Mining returns the MiningRules of the ConsensusParams. These are the -// consensus params related to tracking proposers and paying a portion of fees -// to eligible recipients. -func (cp ConsensusParams) Mining() MiningRules { - return miningRules[cp.MiningRulesVer] +// If we need to change the decay rate (only), we would create a new plan like: +// BaseAmount: 0, DecayInterval: XXX +// by using a zero baseAmount, the amount not affected. +// For a bigger change, we'd use a plan like: +// BaseRound: , BaseAmount: , DecayInterval: +// or just +// BaseAmount: , DecayInterval: +// the new decay rate would go into effect at upgrade time, and the new +// amount would be set at baseRound or at upgrade time. + +type BonusPlan struct { + // BonusBaseRound is the earliest round this plan can apply. Of course, the + // consensus update must also have happened. So using a low value makes it + // go into effect immediately upon upgrade. + BaseRound uint64 + // BonusBaseAmount is the bonus to be paid when this plan first applies (see + // baseRound). If it is zero, then no explicit change is made to the bonus + // (useful for only changing the decay rate). + BaseAmount uint64 + // BonusDecayInterval is the time in rounds between 1% decays. For simplicity, + // decay occurs based on round % BonusDecayInterval, so a decay can happen right + // after going into effect. The BonusDecayInterval goes into effect at upgrade + // time, regardless of `baseRound`. + DecayInterval uint64 } // PaysetCommitType enumerates possible ways for the block header to commit to @@ -773,7 +771,7 @@ func checkSetAllocBounds(p ConsensusParams) { checkSetMax(p.MaxAppProgramLen, &MaxLogCalls) checkSetMax(p.MaxInnerTransactions*p.MaxTxGroupSize, &MaxInnerTransactionsPerDelta) checkSetMax(p.MaxProposedExpiredOnlineAccounts, &MaxProposedExpiredOnlineAccounts) - checkSetMax(p.Mining().MaxMarkAbsent, &MaxMarkAbsent) + checkSetMax(p.Payouts.MaxMarkAbsent, &MaxMarkAbsent) // These bounds are exported to make them available to the msgp generator for calculating // maximum valid message size for each message going across the wire. @@ -1506,8 +1504,19 @@ func initConsensusProtocols() { vFuture.LogicSigVersion = 11 // When moving this to a release, put a new higher LogicSigVersion here - vFuture.MiningRulesVer = 1 - vFuture.BonusPlanVer = 1 + vFuture.Payouts.Enabled = true + vFuture.Payouts.Percent = 75 + vFuture.Payouts.GoOnlineFee = 2_000_000 // 2 algos + vFuture.Payouts.MinBalance = 30_000_000_000 // 30,000 algos + vFuture.Payouts.MaxBalance = 50_000_000_000_000 // 50M algos + vFuture.Payouts.MaxMarkAbsent = 32 + vFuture.Payouts.ChallengeInterval = 1000 + vFuture.Payouts.ChallengeGracePeriod = 200 + vFuture.Payouts.ChallengeBits = 5 + + vFuture.Bonus.BaseAmount = 5_000_000 // 5 Algos + // 2.9 sec rounds gives about 10.8M rounds per year. + vFuture.Bonus.DecayInterval = 1_000_000 // .99^(10.8/1) ~ .897 ~ 10% decay per year Consensus[protocol.ConsensusFuture] = vFuture diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index c09beee1da..e7540fa3f4 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -494,62 +494,26 @@ func ProcessUpgradeParams(prev BlockHeader) (uv UpgradeVote, us UpgradeState, er return upgradeVote, upgradeState, err } -type bonusPlan struct { - // baseRound is the earliest round this plan can apply. Of course, the - // consensus update must also have happened. So using a low value makes it - // go into effect immediately upon upgrade. - baseRound basics.Round - // baseAmount is the bonus to be paid when this plan first applies (see - // baseRound). If it is zero, then no explicit change is made to the bonus - // (useful for only changing the decay rate). - baseAmount basics.MicroAlgos - // decayInterval is the time in rounds between 1% decays. For simplicity, - // decay occurs based on round % decayInterval, so a decay can happen right - // after going into effect. The decayInterval goes into effect at upgrade - // time, regardless of `baseRound`. - decayInterval basics.Round -} - -// bonusPlans should be extended, never changed, since old blocks must retain -// their behavior. -var bonusPlans = []bonusPlan{ - 0: {}, - 1: { - baseRound: 0, // goes into effect with upgrade - baseAmount: basics.MicroAlgos{Raw: 5_000_000}, - // 2.9 sec rounds gives about 10.8M rounds per year. - decayInterval: 1_000_000, // .99^(10.8/1) ~ .897 ~ 10% decay per year - }, - // If we need to change the decay rate (only), we would create a new plan like: - // { decayInterval: XXX} by using a zero baseAmount, the amount not affected. - // For a bigger change, we'd use a plan like: - // { baseRound: , baseAmount: , decayInterval: } - // or just - // { baseAmount: , decayInterval: } - // the new decay rate would go into effect at upgrade time, and the new - // amount would be set at baseRound or at upgrade time. -} - // NextBonus determines the bonus that should be paid out for proposing the next block. func NextBonus(prev BlockHeader, params *config.ConsensusParams) basics.MicroAlgos { - current := prev.Round + 1 + current := uint64(prev.Round + 1) prevParams := config.Consensus[prev.CurrentProtocol] // presence ensured by ProcessUpgradeParams - return computeBonus(current, prev.Bonus, bonusPlans[params.BonusPlanVer], bonusPlans[prevParams.BonusPlanVer]) + return computeBonus(current, prev.Bonus, params.Bonus, prevParams.Bonus) } // computeBonus is the guts of NextBonus that can be unit tested more effectively. -func computeBonus(current basics.Round, prevBonus basics.MicroAlgos, curPlan bonusPlan, prevPlan bonusPlan) basics.MicroAlgos { +func computeBonus(current uint64, prevBonus basics.MicroAlgos, curPlan config.BonusPlan, prevPlan config.BonusPlan) basics.MicroAlgos { // Set the amount if it's non-zero... - if !curPlan.baseAmount.IsZero() { + if curPlan.BaseAmount != 0 { upgrading := curPlan != prevPlan || current == 1 // The time has come if the baseRound arrives, or at upgrade time if // baseRound has already passed. - if current == curPlan.baseRound || (upgrading && current > curPlan.baseRound) { - return curPlan.baseAmount + if current == curPlan.BaseRound || (upgrading && current > curPlan.BaseRound) { + return basics.MicroAlgos{Raw: curPlan.BaseAmount} } } - if curPlan.decayInterval != 0 && current%curPlan.decayInterval == 0 { + if curPlan.DecayInterval != 0 && current%curPlan.DecayInterval == 0 { // decay keep, _ := basics.NewPercent(99).DivvyAlgos(prevBonus) return keep diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index e9e1ec0247..d56658cd33 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -51,12 +51,12 @@ func init() { } params1.MinUpgradeWaitRounds = 0 params1.MaxUpgradeWaitRounds = 0 - params1.BonusPlanVer = 0 config.Consensus[proto1] = params1 params2 := config.Consensus[protocol.ConsensusCurrentVersion] params2.ApprovedUpgrades = map[protocol.ConsensusVersion]uint64{} - params2.BonusPlanVer = 1 + params2.Bonus.BaseAmount = 5_000_000 + params2.Bonus.DecayInterval = 1_000_000 config.Consensus[proto2] = params2 paramsDelay := config.Consensus[protocol.ConsensusCurrentVersion] @@ -946,15 +946,15 @@ func TestBonusUpgrades(t *testing.T) { ma198 := basics.MicroAlgos{Raw: 198} ma200 := basics.MicroAlgos{Raw: 200} - old := bonusPlan{} - plan := bonusPlan{} + old := config.BonusPlan{} + plan := config.BonusPlan{} // Nothing happens with empty plans a.Equal(ma0, computeBonus(1, ma0, plan, old)) a.Equal(ma100, computeBonus(1, ma100, plan, old)) // When plan doesn't change, just expect decay on the intervals - plan.decayInterval = 100 + plan.DecayInterval = 100 a.Equal(ma100, computeBonus(1, ma100, plan, plan)) a.Equal(ma100, computeBonus(99, ma100, plan, plan)) a.Equal(ma99, computeBonus(100, ma100, plan, plan)) @@ -962,19 +962,19 @@ func TestBonusUpgrades(t *testing.T) { a.Equal(ma99, computeBonus(10000, ma100, plan, plan)) // When plan changes, the new decay is in effect - d90 := bonusPlan{decayInterval: 90} + d90 := config.BonusPlan{DecayInterval: 90} a.Equal(ma100, computeBonus(100, ma100, d90, plan)) // no decay a.Equal(ma99, computeBonus(180, ma100, d90, plan)) // decay // When plan changes and amount is present, it is installed - d90.baseAmount = ma200 + d90.BaseAmount = 200 a.Equal(ma200, computeBonus(100, ma100, d90, plan)) // no decay (wrong round and upgrade anyway) a.Equal(ma200, computeBonus(180, ma100, d90, plan)) // no decay (upgrade) a.Equal(ma198, computeBonus(180, ma200, d90, d90)) // decay a.Equal(ma99, computeBonus(180, ma100, d90, d90)) // decay (no install) // If there's a baseRound, the amount is installed accordingly - d90.baseRound = 150 + d90.BaseRound = 150 a.Equal(ma99, computeBonus(90, ma100, d90, plan)) // decay because baseRound delays install a.Equal(ma100, computeBonus(149, ma100, d90, plan)) // no decay (interval) but also not installed yet a.Equal(ma200, computeBonus(150, ma100, d90, plan)) // no decay (upgrade and immediate change) @@ -996,9 +996,10 @@ func TestFirstYearBonus(t *testing.T) { yearSeconds := 365 * 24 * 60 * 60 yearRounds := int(float64(yearSeconds) / 2.9) + plan := config.Consensus[protocol.ConsensusFuture].Bonus sum := uint64(0) - bonus := bonusPlans[1].baseAmount.Raw - interval := int(bonusPlans[1].decayInterval) + bonus := plan.BaseAmount + interval := int(plan.DecayInterval) for i := 0; i < yearRounds; i++ { sum += bonus if i%interval == 0 { @@ -1008,22 +1009,12 @@ func TestFirstYearBonus(t *testing.T) { sum /= 1_000_000 // micro to Algos fmt.Printf("paid %d algos\n", sum) - fmt.Printf("bonus start: %d end: %d\n", bonusPlans[1].baseAmount.Raw, bonus) + fmt.Printf("bonus start: %d end: %d\n", plan.BaseAmount, bonus) // pays about 51M algos a.InDelta(51_000_000, sum, 500_000) // decline about 10% - a.InDelta(0.90, float64(bonus)/float64(bonusPlans[1].baseAmount.Raw), 0.01) + a.InDelta(0.90, float64(bonus)/float64(plan.BaseAmount), 0.01) } - -func TestAllProtocolBonusPlans(t *testing.T) { - partitiontest.PartitionTest(t) - t.Parallel() - a := assert.New(t) - - for _, p := range config.Consensus { - a.Less(int(p.BonusPlanVer), len(bonusPlans)) - } -} diff --git a/data/transactions/payment.go b/data/transactions/payment.go index 97efeb1c5c..b1a75d94db 100644 --- a/data/transactions/payment.go +++ b/data/transactions/payment.go @@ -44,7 +44,7 @@ func (payment PaymentTxnFields) CheckSpender(header Header, spec SpecialAddresse // the FeeSink account may only spend to the IncentivePool (not at all, if EnableMining) if header.Sender == spec.FeeSink { - if proto.Mining().Enabled { + if proto.Payouts.Enabled { return fmt.Errorf("cannot spend from fee sink address %v", header.Sender) } if payment.Receiver != spec.RewardsPool { diff --git a/ledger/apply/keyreg.go b/ledger/apply/keyreg.go index eefdf657f2..f5326f8240 100644 --- a/ledger/apply/keyreg.go +++ b/ledger/apply/keyreg.go @@ -78,13 +78,13 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal } } record.Status = basics.Online - if params.Mining().Enabled { + if params.Payouts.Enabled { record.LastHeartbeat = header.FirstValid } record.VoteFirstValid = keyreg.VoteFirst record.VoteLastValid = keyreg.VoteLast record.VoteKeyDilution = keyreg.VoteKeyDilution - if header.Fee.Raw >= params.Mining().GoOnlineFee && params.Mining().Enabled { + if header.Fee.Raw >= params.Payouts.GoOnlineFee && params.Payouts.Enabled { record.IncentiveEligible = true } } diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index ab1600e87e..f1442fb691 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -92,10 +92,10 @@ func TestPayAction(t *testing.T) { vb := dl.endBlock(proposer) const miningVer = 40 if ver >= miningVer { - require.True(t, dl.generator.GenesisProto().Mining().Enabled) + require.True(t, dl.generator.GenesisProto().Payouts.Enabled) require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) } else { - require.False(t, dl.generator.GenesisProto().Mining().Enabled) + require.False(t, dl.generator.GenesisProto().Payouts.Enabled) require.Zero(t, vb.Block().FeesCollected) } diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 3ecf2e18b8..4dcbeeaf0f 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1323,7 +1323,7 @@ func (eval *BlockEvaluator) endOfBlock() error { eval.block.TxnCounter = 0 } - if eval.proto.Mining().Enabled { + if eval.proto.Payouts.Enabled { // Determine how much the proposer should be paid. Agreement code // can cancel this payment by zero'ing the ProposerPayout if the // proposer is found to be ineligible. See WithProposer(). @@ -1383,7 +1383,7 @@ func (eval *BlockEvaluator) endOfBlock() error { } var expectedFeesCollected basics.MicroAlgos - if eval.proto.Mining().Enabled { + if eval.proto.Payouts.Enabled { expectedFeesCollected = eval.state.feesCollected } if eval.block.FeesCollected != expectedFeesCollected { @@ -1394,7 +1394,7 @@ func (eval *BlockEvaluator) endOfBlock() error { // we don't see the bundle), but agreement allows the proposer to be set // even if Mining is not enabled (and unset any time). So make sure // it's set iff it should be. - if eval.proto.Mining().Enabled { + if eval.proto.Payouts.Enabled { if eval.block.Proposer().IsZero() && !eval.generate { // if generating, proposer is set later by agreement return fmt.Errorf("proposer missing when mining enabled") } @@ -1408,7 +1408,7 @@ func (eval *BlockEvaluator) endOfBlock() error { // ineligible, but we must check that it is correct if non-zero. We allow it // to be too low. A proposer can be algruistic. maxPayout := uint64(0) - if eval.proto.Mining().Enabled { + if eval.proto.Payouts.Enabled { payout, err := eval.proposerPayout() if err != nil { return err @@ -1473,7 +1473,7 @@ func (eval *BlockEvaluator) endOfBlock() error { } func (eval *BlockEvaluator) proposerPayout() (basics.MicroAlgos, error) { - incentive, _ := basics.NewPercent(eval.proto.Mining().Percent).DivvyAlgos(eval.block.FeesCollected) + incentive, _ := basics.NewPercent(eval.proto.Payouts.Percent).DivvyAlgos(eval.block.FeesCollected) total, o := basics.OAddA(incentive, eval.block.Bonus) if o { return basics.MicroAlgos{}, fmt.Errorf("payout overflowed adding bonus incentive %d %d", incentive, eval.block.Bonus) @@ -1507,14 +1507,13 @@ func (eval *BlockEvaluator) generateKnockOfflineAccountsList() { current := eval.Round() maxExpirations := eval.proto.MaxProposedExpiredOnlineAccounts - maxSuspensions := eval.proto.Mining().MaxMarkAbsent + maxSuspensions := eval.proto.Payouts.MaxMarkAbsent updates := &eval.block.ParticipationUpdates ch := activeChallenge(&eval.proto, uint64(eval.Round()), eval.state) for _, accountAddr := range eval.state.modifiedAccounts() { - fmt.Printf("Check %v\n", accountAddr) acctData, found := eval.state.mods.Accts.GetData(accountAddr) if !found { continue @@ -1604,7 +1603,7 @@ type headerSource interface { } func activeChallenge(proto *config.ConsensusParams, current uint64, headers headerSource) challenge { - rules := proto.Mining() + rules := proto.Payouts // are challenges active? if rules.ChallengeInterval == 0 || current < rules.ChallengeInterval { return challenge{} @@ -1622,7 +1621,7 @@ func activeChallenge(proto *config.ConsensusParams, current uint64, headers head } challengeProto := config.Consensus[challengeHdr.CurrentProtocol] // challenge is not considered if rules have changed since that round - if challengeProto.Mining() != rules { + if challengeProto.Payouts != rules { return challenge{} } return challenge{round, challengeHdr.Seed, rules.ChallengeBits} @@ -1686,7 +1685,7 @@ func (eval *BlockEvaluator) validateAbsentOnlineAccounts() error { if !eval.validate { return nil } - maxSuspensions := eval.proto.Mining().MaxMarkAbsent + maxSuspensions := eval.proto.Payouts.MaxMarkAbsent suspensionCount := len(eval.block.ParticipationUpdates.AbsentParticipationAccounts) // If the length of the array is strictly greater than our max then we have an error. diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 8fe4a385ef..97ed6e639a 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -1704,7 +1704,7 @@ func TestActiveChallenge(t *testing.T) { now := config.Consensus[nowHeader.CurrentProtocol] // simplest test. when interval=X and grace=G, X+G+1 is a challenge - inChallenge := now.Mining().ChallengeInterval + now.Mining().ChallengeGracePeriod + 1 + inChallenge := now.Payouts.ChallengeInterval + now.Payouts.ChallengeGracePeriod + 1 ch := activeChallenge(&now, inChallenge, singleSource(nowHeader)) a.NotZero(ch.round) @@ -1715,13 +1715,13 @@ func TestActiveChallenge(t *testing.T) { } // ChallengeGracePeriod rounds allow challenges starting with inChallenge - for r := inChallenge; r < inChallenge+now.Mining().ChallengeGracePeriod; r++ { + for r := inChallenge; r < inChallenge+now.Payouts.ChallengeGracePeriod; r++ { ch := activeChallenge(&now, r, singleSource(nowHeader)) - a.EqualValues(ch.round, now.Mining().ChallengeInterval) + a.EqualValues(ch.round, now.Payouts.ChallengeInterval) } // And the next round is again challenge-less - ch = activeChallenge(&now, inChallenge+now.Mining().ChallengeGracePeriod, singleSource(nowHeader)) + ch = activeChallenge(&now, inChallenge+now.Payouts.ChallengeGracePeriod, singleSource(nowHeader)) a.Zero(ch.round) // ignore challenge if upgrade happened diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index fee4789dfb..c6a6f22d55 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -265,15 +265,15 @@ func TestMiningFees(t *testing.T) { const bonus1 = 5_000_000 // the first bonus value, set in if ver >= miningBegins { - require.True(t, dl.generator.GenesisProto().Mining().Enabled) // version sanity check - require.NotZero(t, dl.generator.GenesisProto().Mining().Percent) // version sanity check + require.True(t, dl.generator.GenesisProto().Payouts.Enabled) // version sanity check + require.NotZero(t, dl.generator.GenesisProto().Payouts.Percent) // version sanity check // new fields are in the header require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) require.EqualValues(t, bonus1, vb.Block().Bonus.Raw) require.EqualValues(t, bonus1+1_500, vb.Block().ProposerPayout().Raw) } else { - require.False(t, dl.generator.GenesisProto().Mining().Enabled) - require.Zero(t, dl.generator.GenesisProto().Mining().Percent) // version sanity check + require.False(t, dl.generator.GenesisProto().Payouts.Enabled) + require.Zero(t, dl.generator.GenesisProto().Payouts.Percent) // version sanity check require.Zero(t, vb.Block().FeesCollected) require.Zero(t, vb.Block().Bonus) require.Zero(t, vb.Block().ProposerPayout) diff --git a/ledger/ledger_test.go b/ledger/ledger_test.go index 92f709baa9..8d5bfbdfd7 100644 --- a/ledger/ledger_test.go +++ b/ledger/ledger_test.go @@ -103,7 +103,7 @@ func initNextBlockHeader(correctHeader *bookkeeping.BlockHeader, lastBlock bookk // endOfBlock is simplified implementation of BlockEvaluator.endOfBlock so that // our test blocks can pass validation. func endOfBlock(blk *bookkeeping.Block) error { - if blk.ConsensusProtocol().Mining().Enabled { + if blk.ConsensusProtocol().Payouts.Enabled { // This won't work for inner fees, and it's not bothering with overflow for _, txn := range blk.Payset { blk.FeesCollected.Raw += txn.Txn.Fee.Raw diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index cf034ee0e6..44cf41e2fa 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -49,10 +49,10 @@ func (vb ValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, // agreement does not consider the current config params, so here we decide // what really goes into the BlockHeader. proto := config.Consensus[vb.blk.CurrentProtocol] - if proto.Mining().Enabled { + if proto.Payouts.Enabled { newblock.BlockHeader.Proposer = proposer } - if !proto.Mining().Enabled || !eligible { + if !proto.Payouts.Enabled || !eligible { newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} } diff --git a/ledger/simple_test.go b/ledger/simple_test.go index b054606ce5..fd8396d956 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -156,11 +156,11 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer // for tests. Doesn't matter that it makes them both the same. Since this // can't call the agreement code, the eligibility of the prp is not // considered. - if ledger.GenesisProto().Mining().Enabled { + if ledger.GenesisProto().Payouts.Enabled { *vb = vb.WithProposer(committee.Seed(prp), prp, true) } else { // To more closely mimic the agreement code, we don't - // write the proposer when !Mining().Enabled. + // write the proposer when !Mining.Enabled. *vb = vb.WithProposer(committee.Seed(prp), basics.Address{}, false) } From 53b9cca78e4e3a23ff63631c0c5bfb504ff2d78f Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 21 Mar 2024 13:20:03 -0400 Subject: [PATCH 069/117] lint docs --- config/consensus.go | 16 ++++++++++++---- data/transactions/payment.go | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/config/consensus.go b/config/consensus.go index b3bfc7e2e3..e7e169de19 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -592,16 +592,24 @@ type ProposerPayoutRules struct { ChallengeBits int } +// BonusPlan describes how the "extra" proposer payouts are to be made. It +// specifices an exponential decay in which the bonus decreases by 1% every n +// rounds. // If we need to change the decay rate (only), we would create a new plan like: -// BaseAmount: 0, DecayInterval: XXX +// +// BaseAmount: 0, DecayInterval: XXX +// // by using a zero baseAmount, the amount not affected. // For a bigger change, we'd use a plan like: -// BaseRound: , BaseAmount: , DecayInterval: +// +// BaseRound: , BaseAmount: , DecayInterval: +// // or just -// BaseAmount: , DecayInterval: +// +// BaseAmount: , DecayInterval: +// // the new decay rate would go into effect at upgrade time, and the new // amount would be set at baseRound or at upgrade time. - type BonusPlan struct { // BonusBaseRound is the earliest round this plan can apply. Of course, the // consensus update must also have happened. So using a low value makes it diff --git a/data/transactions/payment.go b/data/transactions/payment.go index b1a75d94db..eed247744d 100644 --- a/data/transactions/payment.go +++ b/data/transactions/payment.go @@ -37,6 +37,7 @@ type PaymentTxnFields struct { CloseRemainderTo basics.Address `codec:"close"` } +// CheckSpender performs some stateless checks on the Sender of a pay transaction func (payment PaymentTxnFields) CheckSpender(header Header, spec SpecialAddresses, proto config.ConsensusParams) error { if header.Sender == payment.CloseRemainderTo { return fmt.Errorf("transaction cannot close account to its sender %v", header.Sender) From 613d7d4118281c0896b4a49191f5bdbbdd987ce7 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 21 Mar 2024 13:27:21 -0400 Subject: [PATCH 070/117] spelling --- config/consensus.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/consensus.go b/config/consensus.go index e7e169de19..718f7f70a3 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -593,9 +593,9 @@ type ProposerPayoutRules struct { } // BonusPlan describes how the "extra" proposer payouts are to be made. It -// specifices an exponential decay in which the bonus decreases by 1% every n -// rounds. -// If we need to change the decay rate (only), we would create a new plan like: +// specifies an exponential decay in which the bonus decreases by 1% every n +// rounds. If we need to change the decay rate (only), we would create a new +// plan like: // // BaseAmount: 0, DecayInterval: XXX // From 961ae74814741108f59fe929514bb71a3323ef21 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 21 Mar 2024 13:38:17 -0400 Subject: [PATCH 071/117] make msgp --- ledger/store/trackerdb/msgp_gen.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ledger/store/trackerdb/msgp_gen.go b/ledger/store/trackerdb/msgp_gen.go index 21d0b677c9..465248e93d 100644 --- a/ledger/store/trackerdb/msgp_gen.go +++ b/ledger/store/trackerdb/msgp_gen.go @@ -821,8 +821,8 @@ func (z *BaseOnlineAccountData) MarshalMsg(b []byte) (o []byte) { o = (*z).BaseVotingData.StateProofID.MarshalMsg(o) } if (zb0001Mask & 0x40) == 0 { // if not empty - // string "W" - o = append(o, 0xa1, 0x57) + // string "X" + o = append(o, 0xa1, 0x58) o = msgp.AppendBool(o, (*z).IncentiveEligible) } if (zb0001Mask & 0x80) == 0 { // if not empty @@ -993,7 +993,7 @@ func (z *BaseOnlineAccountData) UnmarshalMsgWithState(bts []byte, st msgp.Unmars err = msgp.WrapError(err, "StateProofID") return } - case "W": + case "X": (*z).IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) if err != nil { err = msgp.WrapError(err, "IncentiveEligible") From 88e719215e0ebab2819a70f14161f81c3df056a3 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 21 Mar 2024 13:56:16 -0400 Subject: [PATCH 072/117] collateral damage --- config/consensus.go | 2 +- ledger/eval_simple_test.go | 6 +++--- test/e2e-go/features/incentives/mining_test.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config/consensus.go b/config/consensus.go index 718f7f70a3..ef73505111 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -1516,7 +1516,7 @@ func initConsensusProtocols() { vFuture.Payouts.Percent = 75 vFuture.Payouts.GoOnlineFee = 2_000_000 // 2 algos vFuture.Payouts.MinBalance = 30_000_000_000 // 30,000 algos - vFuture.Payouts.MaxBalance = 50_000_000_000_000 // 50M algos + vFuture.Payouts.MaxBalance = 70_000_000_000_000 // 70M algos vFuture.Payouts.MaxMarkAbsent = 32 vFuture.Payouts.ChallengeInterval = 1000 vFuture.Payouts.ChallengeGracePeriod = 200 diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index c6a6f22d55..7db66e2379 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -228,7 +228,7 @@ func TestMiningFees(t *testing.T) { const eFee = 3_000_000 dl.txn( &txntest.Txn{Type: "pay", Sender: addrs[1], - Receiver: proposer, Amount: eFee + 50_000_000*1_000_000 + 1}, + Receiver: proposer, Amount: eFee + 50_000_000}, ) prp := lookup(dl.t, dl.generator, proposer) @@ -263,7 +263,7 @@ func TestMiningFees(t *testing.T) { dl.txns(&pay, pay.Args("again")) vb := dl.endBlock(proposer) - const bonus1 = 5_000_000 // the first bonus value, set in + const bonus1 = 5_000_000 // the first bonus value, set in config/consensus.go if ver >= miningBegins { require.True(t, dl.generator.GenesisProto().Payouts.Enabled) // version sanity check require.NotZero(t, dl.generator.GenesisProto().Payouts.Percent) // version sanity check @@ -276,7 +276,7 @@ func TestMiningFees(t *testing.T) { require.Zero(t, dl.generator.GenesisProto().Payouts.Percent) // version sanity check require.Zero(t, vb.Block().FeesCollected) require.Zero(t, vb.Block().Bonus) - require.Zero(t, vb.Block().ProposerPayout) + require.Zero(t, vb.Block().ProposerPayout()) } postsink := micros(dl.t, dl.generator, genBalances.FeeSink) diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 30d701da72..ccefc95ce5 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -87,7 +87,7 @@ func TestBasicMining(t *testing.T) { a.NoError(err) fmt.Printf(" 1 block %d proposed by %v\n", status.LastRound, block.Proposer) - a.Zero(block.ProposerPayout) // nobody is eligible yet (hasn't worked back to balance round) + a.Zero(block.ProposerPayout()) // nobody is eligible yet (hasn't worked back to balance round) a.EqualValues(5_000_000, block.Bonus.Raw) fixture.WaitForRoundWithTimeout(status.LastRound + 1) @@ -129,12 +129,12 @@ func TestBasicMining(t *testing.T) { // 01 would get paid (because under balance cap) 15 would not switch block.Proposer.String() { case account01.Address: - a.NotZero(block.ProposerPayout) + a.NotZero(block.ProposerPayout()) a.NotEqual(data01.MicroAlgos, next.MicroAlgos) proposed01 = true data01 = next case account15.Address: - a.Zero(block.ProposerPayout) + a.Zero(block.ProposerPayout()) a.Equal(data15.MicroAlgos, next.MicroAlgos) data15 = next proposed15 = true From 1ec4f3176c570e1dd648d6339efc4783d9fc96da Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 21 Mar 2024 14:12:05 -0400 Subject: [PATCH 073/117] Remove "mining" wording from comments and variable names --- agreement/proposal.go | 4 ++-- data/bookkeeping/block.go | 6 ++--- data/transactions/logic/fields.go | 4 ++-- data/transactions/payment.go | 2 +- ledger/apptxn_test.go | 14 ++++++------ ledger/eval/eval.go | 8 +++---- ledger/eval/eval_test.go | 2 +- ledger/eval_simple_test.go | 22 +++++++++---------- ledger/simple_test.go | 2 +- .../e2e-go/features/incentives/mining_test.go | 6 ++--- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index 46f39edef1..01ad8e25db 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -196,7 +196,7 @@ func verifyProposer(p unauthenticatedProposal, ledger LedgerReader) error { // The BlockHeader isn't trustworthy yet, since we haven't checked the // upgrade state. So, lacking the current consensus params, we confirm that // the Proposer is *either* correct or missing. `eval` package will using - // Mining.Enabled to confirm which it should be. + // Payouts.Enabled to confirm which it should be. if !p.Proposer().IsZero() && p.Proposer() != value.OriginalProposer { return fmt.Errorf("wrong proposer (%v != %v)", p.Proposer(), value.OriginalProposer) } @@ -267,7 +267,7 @@ func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerRead return false, basics.OnlineAccountData{}, err } - // When mining begins, nobody could possible have IncentiveEligible set in + // When payouts begin, nobody could possible have IncentiveEligible set in // the blanceRound rounds ago, so the min/max check is irrelevant. balanceParams, err := ledger.ConsensusParams(balanceRound) if err != nil { diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index e7540fa3f4..b16ee08758 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -61,11 +61,11 @@ type ( // Proposer is the proposer of this block. Like the Seed, agreement adds // this after the block is assembled by the transaction pool, so that the same block can be prepared // for multiple participating accounts in the same node. Therefore, it can not be used - // to influence block evaluation. Populated if proto.EnableMining + // to influence block evaluation. Populated if proto.Payouts.Enabled Proposer basics.Address `codec:"prp"` // FeesCollected is the sum of all fees paid by transactions in this - // block. Populated if proto.EnableMining. + // block. Populated if proto.Payouts.Enabled FeesCollected basics.MicroAlgos `codec:"fc"` // Bonus is the bonus incentive to be paid for proposing this block. It @@ -74,7 +74,7 @@ type ( // ProposerPayout is the amount that should be moved from the FeeSink to // the Proposer at the start of the next block. It is basically the - // bonus + the mining fraction of FeesCollected, but may be zero'd by + // bonus + the payouts percent of FeesCollected, but may be zero'd by // proposer ineligibility. ProposerPayout basics.MicroAlgos `codec:"pp"` diff --git a/data/transactions/logic/fields.go b/data/transactions/logic/fields.go index 2b35305607..d0603d0ffc 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -965,9 +965,9 @@ const ( BlkSeed BlockField = iota // BlkTimestamp is the Block's timestamp, seconds from epoch BlkTimestamp - // BlkProposer is the Block's proposer, or ZeroAddress, pre EnableMining + // BlkProposer is the Block's proposer, or ZeroAddress, pre Payouts.Enabled BlkProposer - // BlkFeesCollected is the sum of fees for the block, or 0, pre EnableMining + // BlkFeesCollected is the sum of fees for the block, or 0, pre Payouts.Enabled BlkFeesCollected // BlkBonus is the extra amount to be paid for the given block (from FeeSink) BlkBonus diff --git a/data/transactions/payment.go b/data/transactions/payment.go index eed247744d..2b52b4b385 100644 --- a/data/transactions/payment.go +++ b/data/transactions/payment.go @@ -43,7 +43,7 @@ func (payment PaymentTxnFields) CheckSpender(header Header, spec SpecialAddresse return fmt.Errorf("transaction cannot close account to its sender %v", header.Sender) } - // the FeeSink account may only spend to the IncentivePool (not at all, if EnableMining) + // the FeeSink account may only spend to the IncentivePool (not at all, if Payouts.Enabled) if header.Sender == spec.FeeSink { if proto.Payouts.Enabled { return fmt.Errorf("cannot spend from fee sink address %v", header.Sender) diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index f1442fb691..167ff32543 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -60,7 +60,7 @@ func TestPayAction(t *testing.T) { itxn_submit `)) - // We're going to test some mining effects here too, so that we have an inner transaction example. + // We're going to test some payout effects here too, so that we have an inner transaction example. proposer := basics.Address{0x01, 0x02, 0x03} dl.txns(&txntest.Txn{ Type: "pay", @@ -90,8 +90,8 @@ func TestPayAction(t *testing.T) { dl.beginBlock() dl.txns(&payout1) vb := dl.endBlock(proposer) - const miningVer = 40 - if ver >= miningVer { + const payoutsVer = 40 + if ver >= payoutsVer { require.True(t, dl.generator.GenesisProto().Payouts.Enabled) require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) } else { @@ -102,20 +102,20 @@ func TestPayAction(t *testing.T) { postsink := micros(dl.t, dl.generator, genBalances.FeeSink) postprop := micros(dl.t, dl.generator, proposer) - // Mining checks - require.EqualValues(t, 0, postprop-preprop) // mining not moved yet + // Payout checks + require.EqualValues(t, 0, postprop-preprop) // payout not moved yet require.EqualValues(t, 2000, postsink-presink) dl.fullBlock() postsink = micros(dl.t, dl.generator, genBalances.FeeSink) postprop = micros(dl.t, dl.generator, proposer) dl.t.Log("postsink", postsink, "postprop", postprop) - if ver >= miningVer { + if ver >= payoutsVer { bonus := 5_000_000 // block.go assert.EqualValues(t, bonus-500, presink-postsink) // based on 75% in config/consensus.go require.EqualValues(t, bonus+1500, postprop-preprop) } else { - require.EqualValues(t, 2000, postsink-presink) // no mining yet + require.EqualValues(t, 2000, postsink-presink) // no payouts yet } ad0 := micros(dl.t, dl.generator, addrs[0]) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 4dcbeeaf0f..f05ee661f6 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1392,15 +1392,15 @@ func (eval *BlockEvaluator) endOfBlock() error { // agreement will check that the proposer is correct (we can't because // we don't see the bundle), but agreement allows the proposer to be set - // even if Mining is not enabled (and unset any time). So make sure + // even if Payouts is not enabled (and unset any time). So make sure // it's set iff it should be. if eval.proto.Payouts.Enabled { if eval.block.Proposer().IsZero() && !eval.generate { // if generating, proposer is set later by agreement - return fmt.Errorf("proposer missing when mining enabled") + return fmt.Errorf("proposer missing when payouts enabled") } } else { if !eval.block.Proposer().IsZero() { - return fmt.Errorf("proposer %v present when mining disabled", eval.block.Proposer()) + return fmt.Errorf("proposer %v present when payouts disabled", eval.block.Proposer()) } } @@ -1582,7 +1582,7 @@ func bitsMatch(a, b []byte, n int) bool { } func isAbsent(totalOnlineStake basics.MicroAlgos, acctStake basics.MicroAlgos, lastSeen basics.Round, current basics.Round) bool { - // Don't consider accounts that were online when mining went into effect as + // Don't consider accounts that were online when payouts went into effect as // absent. They get noticed the next time they propose or keyreg, which // ought to be soon, if they are high stake or want to earn incentives. if lastSeen == 0 { diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 97ed6e639a..8ddcabcfc8 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -1727,7 +1727,7 @@ func TestActiveChallenge(t *testing.T) { // ignore challenge if upgrade happened oldHeader := bookkeeping.BlockHeader{ UpgradeState: bookkeeping.UpgradeState{ - // We need a version from before mining rules got turned on + // We need a version from before payouts got turned on CurrentProtocol: protocol.ConsensusV39, }, } diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 7db66e2379..659d2b5afc 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -212,15 +212,15 @@ func TestBlockEvaluator(t *testing.T) { require.Equal(t, bal2new.MicroAlgos.Raw, bal2.MicroAlgos.Raw-minFee.Raw) } -// TestMiningFees ensures that the proper portion of tx fees go to the proposer -func TestMiningFees(t *testing.T) { +// TestPayoutFees ensures that the proper portion of tx fees go to the proposer +func TestPayoutFees(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() // Lots of balance checks that would be messed up by rewards genBalances, addrs, _ := ledgertesting.NewTestGenesis(ledgertesting.TurnOffRewards) - miningBegins := 40 - ledgertesting.TestConsensusRange(t, miningBegins-1, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { + payoutsBegin := 40 + ledgertesting.TestConsensusRange(t, payoutsBegin-1, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { dl := NewDoubleLedger(t, genBalances, cv, cfg) defer dl.Close() @@ -245,9 +245,9 @@ func TestMiningFees(t *testing.T) { }) prp = lookup(dl.t, dl.generator, proposer) - require.Equal(t, ver >= miningBegins, prp.IncentiveEligible) + require.Equal(t, ver >= payoutsBegin, prp.IncentiveEligible) - dl.fullBlock() // start with an empty block, so no mining fees are paid at start of next one + dl.fullBlock() // start with an empty block, so no payouts from fees are paid at start of next one presink := micros(dl.t, dl.generator, genBalances.FeeSink) preprop := micros(dl.t, dl.generator, proposer) @@ -264,7 +264,7 @@ func TestMiningFees(t *testing.T) { vb := dl.endBlock(proposer) const bonus1 = 5_000_000 // the first bonus value, set in config/consensus.go - if ver >= miningBegins { + if ver >= payoutsBegin { require.True(t, dl.generator.GenesisProto().Payouts.Enabled) // version sanity check require.NotZero(t, dl.generator.GenesisProto().Payouts.Percent) // version sanity check // new fields are in the header @@ -295,7 +295,7 @@ func TestMiningFees(t *testing.T) { t.Log(" postsink2", postsink) t.Log(" postprop2", postprop) - if ver >= miningBegins { + if ver >= payoutsBegin { require.EqualValues(t, bonus1+1500, postprop-preprop) // based on 75% in config/consensus.go require.EqualValues(t, bonus1-500, presink-postsink) } else { @@ -312,8 +312,8 @@ func TestIncentiveEligible(t *testing.T) { t.Parallel() genBalances, addrs, _ := ledgertesting.NewTestGenesis() - miningBegins := 40 - ledgertesting.TestConsensusRange(t, miningBegins-1, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { + payoutsBegin := 40 + ledgertesting.TestConsensusRange(t, payoutsBegin-1, 0, func(t *testing.T, ver int, cv protocol.ConsensusVersion, cfg config.Local) { dl := NewDoubleLedger(t, genBalances, cv, cfg) defer dl.Close() @@ -364,7 +364,7 @@ func TestIncentiveEligible(t *testing.T) { require.False(t, a.IncentiveEligible) a, _, _, err = dl.generator.LookupLatest(smallest) require.NoError(t, err) - require.Equal(t, a.IncentiveEligible, ver >= miningBegins) + require.Equal(t, a.IncentiveEligible, ver >= payoutsBegin) }) } diff --git a/ledger/simple_test.go b/ledger/simple_test.go index fd8396d956..90d0694691 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -160,7 +160,7 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer *vb = vb.WithProposer(committee.Seed(prp), prp, true) } else { // To more closely mimic the agreement code, we don't - // write the proposer when !Mining.Enabled. + // write the proposer when !Payouts.Enabled. *vb = vb.WithProposer(committee.Seed(prp), basics.Address{}, false) } diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index ccefc95ce5..237ad79a80 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -32,8 +32,8 @@ import ( "github.com/algorand/go-algorand/test/partitiontest" ) -// TestBasicMining shows proposers getting paid -func TestBasicMining(t *testing.T) { +// TestBasicPayouts shows proposers getting paid +func TestBasicPayouts(t *testing.T) { partitiontest.PartitionTest(t) defer fixtures.ShutdownSynchronizedTest(t) @@ -48,7 +48,7 @@ func TestBasicMining(t *testing.T) { // Overview of this test: // rereg to become eligible (must pay extra fee) - // show incentives are paid (mining and bonuses) + // show payouts are paid (from fees and bonuses) // deplete feesink to ensure it's graceful clientAndAccount := func(name string) (libgoal.Client, model.Account) { From dcb4636e04c010d5933d07b1ff4fdc2520142225 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 22 Mar 2024 10:41:54 -0400 Subject: [PATCH 074/117] func, field, whatever --- ledger/eval/eval_test.go | 1 - .../e2e-go/features/incentives/mining_test.go | 20 ++++++++++--------- .../features/incentives/suspension_test.go | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 8ddcabcfc8..fefa936f2b 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -1418,7 +1418,6 @@ func TestAbsenteeChecks(t *testing.T) { } } challenged := basics.Address{(challenge >> 3) << 3, 0xaa} - fmt.Printf("challenge = %x challenged = %v\n", challenge, challenged) pay := func(i int, a basics.Address) transactions.Transaction { return transactions.Transaction{ diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 237ad79a80..3d46c02b8b 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -86,16 +86,16 @@ func TestBasicPayouts(t *testing.T) { block, err := client.BookkeepingBlock(status.LastRound) a.NoError(err) - fmt.Printf(" 1 block %d proposed by %v\n", status.LastRound, block.Proposer) + fmt.Printf(" 1 block %d proposed by %v\n", status.LastRound, block.Proposer()) a.Zero(block.ProposerPayout()) // nobody is eligible yet (hasn't worked back to balance round) a.EqualValues(5_000_000, block.Bonus.Raw) fixture.WaitForRoundWithTimeout(status.LastRound + 1) // incentives would pay out in the next round (they won't here, but makes the test realistic) - next, err := client.AccountData(block.Proposer.String()) + next, err := client.AccountData(block.Proposer().String()) a.EqualValues(next.LastProposed, status.LastRound) // regardless of proposer, nobody gets paid - switch block.Proposer.String() { + switch block.Proposer().String() { case account01.Address: a.Equal(data01.MicroAlgos, next.MicroAlgos) data01 = next @@ -118,16 +118,16 @@ func TestBasicPayouts(t *testing.T) { block, err := client.BookkeepingBlock(status.LastRound) a.NoError(err) - fmt.Printf(" 3 block %d proposed by %v\n", status.LastRound, block.Proposer) + fmt.Printf(" 3 block %d proposed by %v\n", status.LastRound, block.Proposer()) a.EqualValues(5_000_000, block.Bonus.Raw) // incentives would pay out in the next round so wait to see them fixture.WaitForRoundWithTimeout(status.LastRound + 1) - next, err := client.AccountData(block.Proposer.String()) - fmt.Printf(" proposer %v has %d at round %d\n", block.Proposer, next.MicroAlgos.Raw, status.LastRound) + next, err := client.AccountData(block.Proposer().String()) + fmt.Printf(" proposer %v has %d at round %d\n", block.Proposer(), next.MicroAlgos.Raw, status.LastRound) // 01 would get paid (because under balance cap) 15 would not - switch block.Proposer.String() { + switch block.Proposer().String() { case account01.Address: a.NotZero(block.ProposerPayout()) a.NotEqual(data01.MicroAlgos, next.MicroAlgos) @@ -188,8 +188,10 @@ func rekeyreg(f *fixtures.RestClientFixture, a *require.Assertions, client libgo a.NoError(err) data, err := client.AccountData(address) - a.True(data.LastHeartbeat == 0) a.NoError(err) + a.Equal(basics.Online, data.Status) // must already be online for this to work + a.True(data.LastHeartbeat == 0) + a.False(data.IncentiveEligible) reReg.KeyregTxnFields = transactions.KeyregTxnFields{ VotePK: data.VoteID, SelectionPK: data.SelectionID, @@ -208,8 +210,8 @@ func rekeyreg(f *fixtures.RestClientFixture, a *require.Assertions, client libgo data, err = client.AccountData(address) a.NoError(err) a.Equal(basics.Online, data.Status) - a.True(data.IncentiveEligible) a.True(data.LastHeartbeat > 0) + a.True(data.IncentiveEligible) fmt.Printf(" %v has %v in round %d\n", address, data.MicroAlgos.Raw, *txn.ConfirmedRound) return data } diff --git a/test/e2e-go/features/incentives/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go index a5a0c41415..4d23c11310 100644 --- a/test/e2e-go/features/incentives/suspension_test.go +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -80,11 +80,11 @@ func TestBasicSuspension(t *testing.T) { block, err := c10.BookkeepingBlock(status.LastRound) a.NoError(err) - fmt.Printf(" block %d proposed by %v\n", status.LastRound, block.Proposer) + fmt.Printf(" block %d proposed by %v\n", status.LastRound, block.Proposer()) fixture.WaitForRoundWithTimeout(status.LastRound + 1) - switch block.Proposer.String() { + switch block.Proposer().String() { case account10.Address: proposed10 = true case account20.Address: From 648f95eef7d41d1b634e4e75f5bf1e5c0c81f316 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 25 Mar 2024 12:01:57 -0400 Subject: [PATCH 075/117] Pass the actual proposer into payoutEligible, even before payouts Doing so allows the propoer record to be returned. --- agreement/proposal.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index 01ad8e25db..602d5b3ef0 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -207,8 +207,11 @@ func verifyProposer(p unauthenticatedProposal, ledger LedgerReader) error { } // Similarly, we only check here that the payout is zero if - // ineligible. `eval` code must check that it is correct if > 0. - eligible, proposerRecord, err := payoutEligible(rnd, p.Proposer(), ledger, cparams) + // ineligible. `eval` code must check that it is correct if > 0. We pass + // OriginalProposer instead of p.Proposer so that the call returns the + // proper record, even before Payouts.Enabled (it will be used below to + // check the Seed). + eligible, proposerRecord, err := payoutEligible(rnd, value.OriginalProposer, ledger, cparams) if err != nil { return fmt.Errorf("failed to determine incentive eligibility %w", err) } @@ -258,7 +261,10 @@ func verifyProposer(p unauthenticatedProposal, ledger LedgerReader) error { return nil } -// payoutEligible determines whether the proposer is eligible for block incentive payout. +// payoutEligible determines whether the proposer is eligible for block +// incentive payout. It will return false before payouts begin since no record +// will be IncentiveEligible. But, since we feed the true proposer in even if +// the header lacks it, the returned balanceRecord will be the right record. func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerReader, cparams config.ConsensusParams) (bool, basics.OnlineAccountData, error) { // Check the balance from the agreement round balanceRound := balanceRound(rnd, cparams) @@ -268,7 +274,7 @@ func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerRead } // When payouts begin, nobody could possible have IncentiveEligible set in - // the blanceRound rounds ago, so the min/max check is irrelevant. + // the balanceRound, so the min/max check is irrelevant. balanceParams, err := ledger.ConsensusParams(balanceRound) if err != nil { return false, basics.OnlineAccountData{}, err From 9c8211cdda673c2fe762973e4e96a2116da5f6f8 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 25 Mar 2024 14:42:41 -0400 Subject: [PATCH 076/117] Trying to track down a failure --- test/e2e-go/features/incentives/suspension_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/e2e-go/features/incentives/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go index 4d23c11310..04072cd0e3 100644 --- a/test/e2e-go/features/incentives/suspension_test.go +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -122,7 +122,7 @@ func TestBasicSuspension(t *testing.T) { a.NotZero(account.VoteID) a.False(account.IncentiveEligible) // suspension turns off flag - // n10's account is still online, because it's got less stake, not absent 10 x interval. + // n10's account is still online, because it's got less stake, has not been absent 10 x interval. account, err = c10.AccountData(account10.Address) a.NoError(err) a.Equal(basics.Online, account.Status) @@ -135,10 +135,12 @@ func TestBasicSuspension(t *testing.T) { lg, err := fixture.StartNode(c20.DataDir()) a.NoError(err) - // Wait for newly restarted node to start. Presumably it'll catchup in - // seconds, and propose by round 90 + // Wait for newly restarted node to start. stat, err := lg.Status() a.NoError(err) + // Waiting for this round should show it has started and caught up. + stat, err = lg.WaitForRound(afterStop.LastRound + 55) + a.NoError(err) // Proceed until a round is proposed by n20. (Stop at 50 rounds, that's more likely a bug than luck) for r := stat.LastRound; r < stat.LastRound+50; r++ { @@ -154,9 +156,11 @@ func TestBasicSuspension(t *testing.T) { } } // n20's account is back online, with same voting material - account, err = fixture.LibGoalClient.AccountData(account20.Address) + account, err = c10.AccountData(account20.Address) a.NoError(err) a.Equal(basics.Online, account.Status) + a.Greater(account.LastProposed, stat.LastRound) + a.Equal(voteID, account.VoteID) a.False(account.IncentiveEligible) } From c054b3cf44bd6d26e5c2f5765ee8211ea90ddb93 Mon Sep 17 00:00:00 2001 From: chris erway Date: Wed, 27 Mar 2024 12:11:34 -0400 Subject: [PATCH 077/117] split ValidatedBlock and AssembledBlock interfaces --- agreement/abstractions.go | 29 ++++++++++++++--------- agreement/agreementtest/simulate_test.go | 4 ++-- agreement/common_test.go | 4 ++-- agreement/fuzzer/ledger_test.go | 4 ++-- agreement/proposal.go | 30 ++++++++++++++++-------- data/bookkeeping/block.go | 18 ++++++++++++++ data/datatest/impls.go | 6 ++--- ledger/ledgercore/validatedBlock.go | 24 ------------------- node/node.go | 26 +++++++++++++------- 9 files changed, 83 insertions(+), 62 deletions(-) diff --git a/agreement/abstractions.go b/agreement/abstractions.go index de3e438912..2a1e247d2d 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -54,16 +54,6 @@ type BlockValidator interface { // and can now be recorded in the ledger. This is an optimized version of // calling EnsureBlock() on the Ledger. type ValidatedBlock interface { - // WithProposer creates a copy of this ValidatedBlock with its - // cryptographically random seed and proposer set. The block's - // ProposerPayout is zero'd if !eligible. Abstractly, it is how the - // agreement code "finishes" a block and makes it a proposal for a specific - // account. - // - // Calls to Seed() or to Digest() on the copy's Block must - // reflect the value of the new seed. - WithProposer(seed committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock - // Block returns the underlying block that has been validated. Block() bookkeeping.Block } @@ -87,7 +77,24 @@ type BlockFactory interface { // produce a ValidatedBlock for the given round. If an insufficient number of // nodes on the network can assemble entries, the agreement protocol may // lose liveness. - AssembleBlock(basics.Round) (ValidatedBlock, error) + AssembleBlock(basics.Round) (AssembledBlock, error) +} + +// An AssembledBlock represents a Block produced by a BlockFactory +// to be included in a proposal by agreement. +type AssembledBlock interface { + // WithProposer creates a copy of this AssembledBlock with its + // cryptographically random seed and proposer set. The block's + // ProposerPayout is zero'd if !eligible. Abstractly, it is how the + // agreement code "finishes" a block and makes it a proposal for a specific + // account. + // + // Calls to Seed() or to Digest() on the copy's Block must + // reflect the value of the new seed. + WithProposer(seed committee.Seed, proposer basics.Address, eligible bool) AssembledBlock + + // Block returns the underlying block that has been assembled. + Block() bookkeeping.Block } // A Ledger represents the sequence of Entries agreed upon by the protocol. diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index ed1bcd736f..c048b61668 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -79,7 +79,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -98,7 +98,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.ValidatedBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.AssembledBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } diff --git a/agreement/common_test.go b/agreement/common_test.go index 1c961bc3ff..eb37490440 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -165,7 +165,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) AssembledBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -184,7 +184,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (ValidatedBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round) (AssembledBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go index e080142d9e..36c5da277a 100644 --- a/agreement/fuzzer/ledger_test.go +++ b/agreement/fuzzer/ledger_test.go @@ -93,7 +93,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -112,7 +112,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.ValidatedBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.AssembledBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } diff --git a/agreement/proposal.go b/agreement/proposal.go index 602d5b3ef0..257fd482b2 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -102,14 +102,24 @@ type proposal struct { validatedAt time.Duration } -func makeProposal(ve ValidatedBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { +func makeProposalFromAssembledBlock(blk AssembledBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { + e := blk.Block() + var payload unauthenticatedProposal + payload.Block = e + payload.SeedProof = pf + payload.OriginalPeriod = origPer + payload.OriginalProposer = origProp + return proposal{unauthenticatedProposal: payload} +} + +func makeProposalFromValidatedBlock(ve ValidatedBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { e := ve.Block() var payload unauthenticatedProposal payload.Block = e payload.SeedProof = pf payload.OriginalPeriod = origPer payload.OriginalProposer = origProp - return proposal{unauthenticatedProposal: payload, ve: ve} + return proposal{unauthenticatedProposal: payload, ve: ve} // store ve to use when calling Ledger.EnsureValidatedBlock } func (p proposal) u() unauthenticatedProposal { @@ -285,8 +295,8 @@ func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerRead return eligible, balanceRecord, nil } -func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve ValidatedBlock, period period, ledger LedgerReader) (proposal, proposalValue, error) { - rnd := ve.Block().Round() +func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, blk AssembledBlock, period period, ledger LedgerReader) (proposal, proposalValue, error) { + rnd := blk.Block().Round() cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) if err != nil { @@ -303,15 +313,15 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve Validat return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could determine eligibility: %w", err) } - ve = ve.WithProposer(newSeed, address, eligible) - proposal := makeProposal(ve, seedProof, period, address) + blk = blk.WithProposer(newSeed, address, eligible) + prop := makeProposalFromAssembledBlock(blk, seedProof, period, address) value := proposalValue{ OriginalPeriod: period, OriginalProposer: address, - BlockDigest: proposal.Block.Digest(), - EncodingDigest: crypto.HashObj(proposal), + BlockDigest: prop.Block.Digest(), + EncodingDigest: crypto.HashObj(prop), } - return proposal, value, nil + return prop, value, nil } // validate returns true if the proposal is valid. @@ -334,5 +344,5 @@ func (p unauthenticatedProposal) validate(ctx context.Context, current round, le return invalid, fmt.Errorf("EntryValidator rejected entry: %w", err) } - return makeProposal(ve, p.SeedProof, p.OriginalPeriod, p.OriginalProposer), nil + return makeProposalFromValidatedBlock(ve, p.SeedProof, p.OriginalPeriod, p.OriginalProposer), nil } diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index b16ee08758..7f2632f3f0 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -313,6 +313,24 @@ func (block Block) ProposerPayout() basics.MicroAlgos { return block.BlockHeader.ProposerPayout } +// WithProposer returns a copy of the Block with a modified seed and associated proposer +func (block Block) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) Block { + newblock := block + newblock.BlockHeader.Seed = s + // agreement is telling us who the proposer is and if they're eligible, but + // agreement does not consider the current config params, so here we decide + // what really goes into the BlockHeader. + proto := config.Consensus[block.CurrentProtocol] + if proto.Payouts.Enabled { + newblock.BlockHeader.Proposer = proposer + } + if !proto.Payouts.Enabled || !eligible { + newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } + + return newblock +} + // NextRewardsState computes the RewardsState of the subsequent round // given the subsequent consensus parameters, along with the incentive pool // balance and the total reward units in the system as of the current round. diff --git a/data/datatest/impls.go b/data/datatest/impls.go index f1c41bc0cc..c834f99d2d 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -53,7 +53,7 @@ type entryFactoryImpl struct { } // AssembleBlock implements Ledger.AssembleBlock. -func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.ValidatedBlock, error) { +func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.AssembledBlock, error) { prev, err := i.l.BlockHdr(round - 1) if err != nil { return nil, fmt.Errorf("could not make proposals: could not read block from ledger at round %v: %v", round, err) @@ -64,8 +64,8 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated return validatedBlock{blk: &b}, nil } -// WithProposer implements the agreement.ValidatedBlock interface. -func (ve validatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { +// WithProposer implements the agreement.AssembledBlock interface. +func (ve validatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { newblock := *ve.blk newblock.BlockHeader.Seed = s newblock.BlockHeader.Proposer = proposer diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 44cf41e2fa..8d4962fd3b 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -17,10 +17,7 @@ package ledgercore import ( - "github.com/algorand/go-algorand/config" - "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" - "github.com/algorand/go-algorand/data/committee" ) // ValidatedBlock represents the result of a block validation. It can @@ -41,27 +38,6 @@ func (vb ValidatedBlock) Delta() StateDelta { return vb.delta } -// WithProposer returns a copy of the ValidatedBlock with a modified seed and associated proposer -func (vb ValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { - newblock := vb.blk - newblock.BlockHeader.Seed = s - // agreement is telling us who the proposer is and if they're eligible, but - // agreement does not consider the current config params, so here we decide - // what really goes into the BlockHeader. - proto := config.Consensus[vb.blk.CurrentProtocol] - if proto.Payouts.Enabled { - newblock.BlockHeader.Proposer = proposer - } - if !proto.Payouts.Enabled || !eligible { - newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} - } - - return ValidatedBlock{ - blk: newblock, - delta: vb.delta, - } -} - // MakeValidatedBlock creates a validated block. func MakeValidatedBlock(blk bookkeeping.Block, delta StateDelta) ValidatedBlock { return ValidatedBlock{ diff --git a/node/node.go b/node/node.go index 8703bd6175..95f3eb257e 100644 --- a/node/node.go +++ b/node/node.go @@ -1290,20 +1290,30 @@ type validatedBlock struct { vb *ledgercore.ValidatedBlock } -// WithProposer satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { - lvb := vb.vb.WithProposer(s, proposer, eligible) - return validatedBlock{vb: &lvb} -} - // Block satisfies the agreement.ValidatedBlock interface. func (vb validatedBlock) Block() bookkeeping.Block { blk := vb.vb.Block() return blk } +// assembledBlock satisfies agreement.AssembledBlock +type assembledBlock struct { + blk bookkeeping.Block +} + +// WithProposer satisfies the agreement.ValidatedBlock interface. +func (ab assembledBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { + nb := ab.blk.WithProposer(s, proposer, eligible) + return assembledBlock{blk: nb} +} + +// Block satisfies the agreement.AssembledBlock interface. +func (ab assembledBlock) Block() bookkeeping.Block { + return ab.blk +} + // AssembleBlock implements Ledger.AssembleBlock. -func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.ValidatedBlock, error) { +func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.AssembledBlock, error) { deadline := time.Now().Add(node.config.ProposalAssemblyTime) lvb, err := node.transactionPool.AssembleBlock(round, deadline) if err != nil { @@ -1324,7 +1334,7 @@ func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.Valid } return nil, err } - return validatedBlock{vb: lvb}, nil + return assembledBlock{blk: lvb.Block()}, nil } // getOfflineClosedStatus will return an int with the appropriate bit(s) set if it is offline and/or online From 630581946ac5d700ef878ed3db478fad6004c5ee Mon Sep 17 00:00:00 2001 From: chris erway Date: Wed, 27 Mar 2024 12:20:14 -0400 Subject: [PATCH 078/117] fix new tests --- ledger/eval/eval_test.go | 6 +++--- ledger/simple_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index fefa936f2b..b45556aebf 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -990,7 +990,7 @@ func (ledger *evalTestLedger) endBlock(t testing.TB, eval *BlockEvaluator) *ledg // fake agreement's setting of header fields so later validates work. seed := committee.Seed{} crypto.RandBytes(seed[:]) - *validatedBlock = validatedBlock.WithProposer(seed, testPoolAddr, true) + *validatedBlock = ledgercore.MakeValidatedBlock(validatedBlock.Block().WithProposer(seed, testPoolAddr, true), validatedBlock.Delta()) err = ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) require.NoError(t, err) return validatedBlock @@ -1212,7 +1212,7 @@ func TestEvalFunctionForExpiredAccounts(t *testing.T) { require.NoError(t, err) // fake agreement's setting of header fields so later validates work - *validatedBlock = validatedBlock.WithProposer(committee.Seed{}, testPoolAddr, true) + *validatedBlock = ledgercore.MakeValidatedBlock(validatedBlock.Block().WithProposer(committee.Seed{}, testPoolAddr, true), validatedBlock.Delta()) expired := false for _, acct := range validatedBlock.Block().ExpiredParticipationAccounts { @@ -1454,7 +1454,7 @@ func TestAbsenteeChecks(t *testing.T) { require.NoError(t, err) // fake agreement's setting of header fields so later validates work - *validatedBlock = validatedBlock.WithProposer(committee.Seed{}, testPoolAddr, true) + *validatedBlock = ledgercore.MakeValidatedBlock(validatedBlock.Block().WithProposer(committee.Seed{}, testPoolAddr, true), validatedBlock.Delta()) require.Zero(t, validatedBlock.Block().ExpiredParticipationAccounts) require.Contains(t, validatedBlock.Block().AbsentParticipationAccounts, addrs[0], addrs[0].String()) diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 90d0694691..c6a49b60e1 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -157,11 +157,11 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer // can't call the agreement code, the eligibility of the prp is not // considered. if ledger.GenesisProto().Payouts.Enabled { - *vb = vb.WithProposer(committee.Seed(prp), prp, true) + *vb = ledgercore.MakeValidatedBlock(vb.Block().WithProposer(committee.Seed(prp), prp, true), vb.Delta()) } else { // To more closely mimic the agreement code, we don't // write the proposer when !Payouts.Enabled. - *vb = vb.WithProposer(committee.Seed(prp), basics.Address{}, false) + *vb = ledgercore.MakeValidatedBlock(vb.Block().WithProposer(committee.Seed(prp), basics.Address{}, false), vb.Delta()) } err = ledger.AddValidatedBlock(*vb, agreement.Certificate{}) From 216ea21b66610577874df270473cac12de9fc976 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 28 Mar 2024 10:28:58 -0400 Subject: [PATCH 079/117] checkpoint better testing before n+1 to n --- ledger/apptxn_test.go | 3 +- ledger/double_test.go | 68 +++++++++++++-------------------- ledger/eval_simple_test.go | 3 ++ ledger/ledgercore/statedelta.go | 2 +- ledger/simple_test.go | 31 +++++++++++---- 5 files changed, 56 insertions(+), 51 deletions(-) diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index 167ff32543..cadf4fda02 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -3395,7 +3395,8 @@ ok: vb := dl.endBlock() deltas := vb.Delta() - params, _ := deltas.Accts.GetAppParams(addrs[0], appID) + params, ok := deltas.Accts.GetAppParams(addrs[0], appID) + require.True(t, ok) require.Equal(t, basics.TealKeyValue{ "caller": {Type: basics.TealBytesType, Bytes: string(addrs[0][:])}, "creator": {Type: basics.TealBytesType, Bytes: string(addrs[0][:])}, diff --git a/ledger/double_test.go b/ledger/double_test.go index a3083b71db..0854943636 100644 --- a/ledger/double_test.go +++ b/ledger/double_test.go @@ -19,6 +19,7 @@ package ledger import ( "testing" + "github.com/algorand/go-algorand/agreement" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" @@ -62,7 +63,8 @@ func (dl DoubleLedger) Close() { func NewDoubleLedger(t testing.TB, balances bookkeeping.GenesisBalances, cv protocol.ConsensusVersion, cfg config.Local, opts ...simpleLedgerOption) DoubleLedger { g := newSimpleLedgerWithConsensusVersion(t, balances, cv, cfg, opts...) v := newSimpleLedgerFull(t, balances, cv, g.GenesisHash(), cfg, opts...) - return DoubleLedger{t, g, v, nil, balances.FeeSink} // FeeSink as proposer will make old code work as expected + // FeeSink as proposer will make old tests work as expected, because payouts will stay put. + return DoubleLedger{t, g, v, nil, balances.FeeSink} } func (dl *DoubleLedger) beginBlock() *eval.BlockEvaluator { @@ -176,52 +178,34 @@ func (dl *DoubleLedger) reloadLedgers() { require.NoError(dl.t, dl.validator.reloadLedger()) } -func checkBlock(t testing.TB, checkLedger *Ledger, vb *ledgercore.ValidatedBlock) { - bl := vb.Block() +func checkBlock(t testing.TB, checkLedger *Ledger, gvb *ledgercore.ValidatedBlock) { + bl := gvb.Block() msg := bl.MarshalMsg(nil) var reconstituted bookkeeping.Block _, err := reconstituted.UnmarshalMsg(msg) require.NoError(t, err) - check := nextCheckBlock(t, checkLedger, reconstituted.RewardsState) - var group []transactions.SignedTxnWithAD - for _, stib := range reconstituted.Payset { - stxn, ad, err := reconstituted.BlockHeader.DecodeSignedTxn(stib) - require.NoError(t, err) - stad := transactions.SignedTxnWithAD{SignedTxn: stxn, ApplyData: ad} - // If txn we're looking at belongs in the current group, append - if group == nil || (!stxn.Txn.Group.IsZero() && group[0].Txn.Group == stxn.Txn.Group) { - group = append(group, stad) - } else if group != nil { - err := check.TransactionGroup(group) - require.NoError(t, err) - group = []transactions.SignedTxnWithAD{stad} - } - } - if group != nil { - err := check.TransactionGroup(group) - require.NoError(t, err, "%+v", reconstituted.Payset) - } - check.SetGenerateForTesting(true) - // We use the same value for seed and proposer. But the proposer is - // sometimes zero'd to account for mining being disabled. So we get the - // blocks to match by providing the Seed as the proposer. - cb := endBlock(t, checkLedger, check, basics.Address(vb.Block().BlockHeader.Seed)) - check.SetGenerateForTesting(false) - require.Equal(t, vb.Block(), cb.Block()) - - // vb.Delta() need not actually be Equal, in the sense of require.Equal - // because the order of the records in Accts is determined by the way the - // cb.sdeltas map (and then the maps in there) is iterated when the - // StateDelta is constructed by roundCowState.deltas(). They should be - // semantically equivalent, but those fields are not exported, so checking - // equivalence is hard. If vb.Delta() is, in fact, different, even though - // vb.Block() is the same, then there is something seriously broken going - // on, that is unlikely to have anything to do with these tests. So upshot: - // we skip trying a complicated equality check. - - // This is the part of checking Delta() equality that wouldn't work right. - // require.Equal(t, vb.Delta().Accts, cb.Delta().Accts) + cvb, err := validateWithoutSignatures(t, checkLedger, reconstituted) + require.NoError(t, err) + cvbd := cvb.Delta() + cvbd.Dehydrate() + gvbd := gvb.Delta() + gvbd.Dehydrate() + + // There are some things in the deltas that won't be identical. Erase them. + // Hdr was put in here at _start_ of block, and not updated. So gvb is in + // initial state, cvd got to see the whole thing. + gvbd.Hdr = nil + cvbd.Hdr = nil + + require.Equal(t, gvbd, cvbd) + + // Hydration/Dehydration is done in-place, so rehydrate so to avoid external evidence + cvbd.Hydrate() + gvbd.Hydrate() + + err = checkLedger.AddValidatedBlock(*cvb, agreement.Certificate{}) + require.NoError(t, err) } func nextCheckBlock(t testing.TB, ledger *Ledger, rs bookkeeping.RewardsState) *eval.BlockEvaluator { diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 659d2b5afc..034a8f2eaf 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -271,6 +271,9 @@ func TestPayoutFees(t *testing.T) { require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) require.EqualValues(t, bonus1, vb.Block().Bonus.Raw) require.EqualValues(t, bonus1+1_500, vb.Block().ProposerPayout().Raw) + // This last one is really only testing the "fake" agreement that + // happens in dl.endBlock(). + require.EqualValues(t, proposer, vb.Block().Proposer()) } else { require.False(t, dl.generator.GenesisProto().Payouts.Enabled) require.Zero(t, dl.generator.GenesisProto().Payouts.Percent) // version sanity check diff --git a/ledger/ledgercore/statedelta.go b/ledger/ledgercore/statedelta.go index b735d391fe..1d2562ca4f 100644 --- a/ledger/ledgercore/statedelta.go +++ b/ledger/ledgercore/statedelta.go @@ -279,7 +279,7 @@ func (ad *AccountDeltas) Hydrate() { } } -// Dehydrate normalized the fields of this AccountDeltas, and clears any redundant internal caching. +// Dehydrate normalizes the fields of this AccountDeltas, and clears any redundant internal caching. // This is useful for comparing AccountDeltas objects for equality. func (ad *AccountDeltas) Dehydrate() { if ad.Accts == nil { diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 90d0694691..555736dbf3 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -17,6 +17,7 @@ package ledger import ( + "context" "fmt" "strings" "testing" @@ -28,6 +29,7 @@ import ( "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/committee" "github.com/algorand/go-algorand/data/transactions" + "github.com/algorand/go-algorand/data/transactions/verify" "github.com/algorand/go-algorand/data/txntest" "github.com/algorand/go-algorand/ledger/eval" "github.com/algorand/go-algorand/ledger/ledgercore" @@ -140,14 +142,16 @@ func txgroup(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, txns ...*t return eval.TransactionGroup(transactions.WrapSignedTxnsWithAD(txgroup)) } -// endBlock completes the block being created, returns the ValidatedBlock for +// endBlock completes the block being created, returning the ValidatedBlock for // inspection. Proposer is optional - if unset, blocks will be finished with // ZeroAddress proposer. func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer ...basics.Address) *ledgercore.ValidatedBlock { - vb, err := eval.GenerateBlock() + gvb, err := eval.GenerateBlock() require.NoError(t, err) - var prp basics.Address + // Making the proposer the feesink unless specified causes less disruption + // to existing tests. (Because block payouts don't change balances.) + prp := gvb.Block().BlockHeader.FeeSink if len(proposer) > 0 { prp = proposer[0] } @@ -157,14 +161,20 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer // can't call the agreement code, the eligibility of the prp is not // considered. if ledger.GenesisProto().Payouts.Enabled { - *vb = vb.WithProposer(committee.Seed(prp), prp, true) + *gvb = gvb.WithProposer(committee.Seed(prp), prp, true) } else { // To more closely mimic the agreement code, we don't // write the proposer when !Payouts.Enabled. - *vb = vb.WithProposer(committee.Seed(prp), basics.Address{}, false) + *gvb = gvb.WithProposer(committee.Seed(prp), basics.Address{}, false) } - err = ledger.AddValidatedBlock(*vb, agreement.Certificate{}) + vvb, err := validateWithoutSignatures(t, ledger, gvb.Block()) + require.NoError(t, err) + + // we could add some checks that ensure gvb and vvb are quite similar, but + // they will differ a bit, as noted above. + + err = ledger.AddValidatedBlock(*vvb, agreement.Certificate{}) require.NoError(t, err) // `rndBQ` gives the latest known block round added to the ledger // we should wait until `rndBQ` block to be committed to blockQueue, @@ -176,7 +186,14 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer // then we return the result and continue the execution. rndBQ := ledger.Latest() ledger.WaitForCommit(rndBQ) - return vb + return vvb +} + +func validateWithoutSignatures(t testing.TB, ledger *Ledger, blk bookkeeping.Block) (*ledgercore.ValidatedBlock, error) { + save := ledger.verifiedTxnCache + defer func() { ledger.verifiedTxnCache = save }() + ledger.verifiedTxnCache = verify.GetMockedCache(true) // validate the txns, but not signatures + return ledger.Validate(context.Background(), blk, nil) } // main wraps up some TEAL source in a header and footer so that it is From e0658cd17d4fdbf897bbcda98999e96a974f53d2 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 28 Mar 2024 10:30:45 -0400 Subject: [PATCH 080/117] bonus constant changes --- config/consensus.go | 4 ++-- data/bookkeeping/block_test.go | 4 ++-- ledger/apptxn_test.go | 2 +- ledger/eval_simple_test.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/consensus.go b/config/consensus.go index ef73505111..a8c9c30fbd 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -1522,9 +1522,9 @@ func initConsensusProtocols() { vFuture.Payouts.ChallengeGracePeriod = 200 vFuture.Payouts.ChallengeBits = 5 - vFuture.Bonus.BaseAmount = 5_000_000 // 5 Algos + vFuture.Bonus.BaseAmount = 10_000_000 // 10 Algos // 2.9 sec rounds gives about 10.8M rounds per year. - vFuture.Bonus.DecayInterval = 1_000_000 // .99^(10.8/1) ~ .897 ~ 10% decay per year + vFuture.Bonus.DecayInterval = 250_000 // .99^(10.8/0.25) ~ .648. So 35% decay per year Consensus[protocol.ConsensusFuture] = vFuture diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index d56658cd33..a8f7a8cfdb 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -987,8 +987,8 @@ func TestBonusUpgrades(t *testing.T) { a.Equal(ma100, computeBonus(151, ma100, d90, d90)) // no decay (interval) } -// TestFirstYearBonus shows what about a year's worth of block bonuses would pay out. -func TestFirstYearBonus(t *testing.T) { +// TestFirstYearsBonus shows what the bonuses look like +func TestFirstYearsBonus(t *testing.T) { partitiontest.PartitionTest(t) t.Parallel() a := require.New(t) diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index cadf4fda02..f031309c30 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -111,7 +111,7 @@ func TestPayAction(t *testing.T) { postprop = micros(dl.t, dl.generator, proposer) dl.t.Log("postsink", postsink, "postprop", postprop) if ver >= payoutsVer { - bonus := 5_000_000 // block.go + bonus := 10_000_000 // config/consensus.go assert.EqualValues(t, bonus-500, presink-postsink) // based on 75% in config/consensus.go require.EqualValues(t, bonus+1500, postprop-preprop) } else { diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 034a8f2eaf..ba58654a3c 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -263,7 +263,7 @@ func TestPayoutFees(t *testing.T) { dl.txns(&pay, pay.Args("again")) vb := dl.endBlock(proposer) - const bonus1 = 5_000_000 // the first bonus value, set in config/consensus.go + const bonus1 = 10_000_000 // the first bonus value, set in config/consensus.go if ver >= payoutsBegin { require.True(t, dl.generator.GenesisProto().Payouts.Enabled) // version sanity check require.NotZero(t, dl.generator.GenesisProto().Payouts.Percent) // version sanity check From b3c26ca6d6fe4cb05fabcf019264fb9f6378b848 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 29 Mar 2024 10:14:50 -0400 Subject: [PATCH 081/117] Make the proposer payouts happen in same block they are proposed. --- data/bookkeeping/block_test.go | 55 +++++++-- ledger/apptxn_test.go | 7 -- ledger/eval/eval.go | 114 ++++++++++-------- ledger/eval_simple_test.go | 91 ++++++++------ .../e2e-go/features/incentives/mining_test.go | 52 ++++---- 5 files changed, 191 insertions(+), 128 deletions(-) diff --git a/data/bookkeeping/block_test.go b/data/bookkeeping/block_test.go index a8f7a8cfdb..3c305b3c3b 100644 --- a/data/bookkeeping/block_test.go +++ b/data/bookkeeping/block_test.go @@ -1000,21 +1000,62 @@ func TestFirstYearsBonus(t *testing.T) { sum := uint64(0) bonus := plan.BaseAmount interval := int(plan.DecayInterval) + r := 0 for i := 0; i < yearRounds; i++ { + r++ sum += bonus - if i%interval == 0 { + if r%interval == 0 { bonus, _ = basics.Muldiv(bonus, 99, 100) } } - sum /= 1_000_000 // micro to Algos + suma := sum / 1_000_000 // micro to Algos - fmt.Printf("paid %d algos\n", sum) + fmt.Printf("paid %d algos\n", suma) fmt.Printf("bonus start: %d end: %d\n", plan.BaseAmount, bonus) - // pays about 51M algos - a.InDelta(51_000_000, sum, 500_000) + // pays about 88M algos + a.InDelta(88_500_000, suma, 100_000) - // decline about 10% - a.InDelta(0.90, float64(bonus)/float64(plan.BaseAmount), 0.01) + // decline about 35% + a.InDelta(0.65, float64(bonus)/float64(plan.BaseAmount), 0.01) + // year 2 + for i := 0; i < yearRounds; i++ { + r++ + sum += bonus + if r%interval == 0 { + bonus, _ = basics.Muldiv(bonus, 99, 100) + } + } + + sum2 := sum / 1_000_000 // micro to Algos + + fmt.Printf("paid %d algos after 2 years\n", sum2) + fmt.Printf("bonus end: %d\n", bonus) + + // pays about 146M algos (total for 2 years) + a.InDelta(145_700_000, sum2, 100_000) + + // decline about 58% + a.InDelta(0.42, float64(bonus)/float64(plan.BaseAmount), 0.01) + + // year 3 + for i := 0; i < yearRounds; i++ { + r++ + sum += bonus + if r%interval == 0 { + bonus, _ = basics.Muldiv(bonus, 99, 100) + } + } + + sum3 := sum / 1_000_000 // micro to Algos + + fmt.Printf("paid %d algos after 3 years\n", sum3) + fmt.Printf("bonus end: %d\n", bonus) + + // pays about 182M algos (total for 3 years) + a.InDelta(182_600_000, sum3, 100_000) + + // declined to about 27% (but foundation funding probably gone anyway) + a.InDelta(0.27, float64(bonus)/float64(plan.BaseAmount), 0.01) } diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index f031309c30..8e40d327e9 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -102,13 +102,6 @@ func TestPayAction(t *testing.T) { postsink := micros(dl.t, dl.generator, genBalances.FeeSink) postprop := micros(dl.t, dl.generator, proposer) - // Payout checks - require.EqualValues(t, 0, postprop-preprop) // payout not moved yet - require.EqualValues(t, 2000, postsink-presink) - - dl.fullBlock() - postsink = micros(dl.t, dl.generator, genBalances.FeeSink) - postprop = micros(dl.t, dl.generator, proposer) dl.t.Log("postsink", postsink, "postprop", postprop) if ver >= payoutsVer { bonus := 10_000_000 // config/consensus.go diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index f05ee661f6..1818a6f04b 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -791,39 +791,6 @@ func StartEvaluator(l LedgerForEvaluator, hdr bookkeeping.BlockHeader, evalOpts return nil, fmt.Errorf("overflowed subtracting rewards for block %v", hdr.Round) } - // Move last block's proposer payout to the proposer - if !prevHeader.Proposer.IsZero() { - // Payout the previous proposer. - if !prevHeader.ProposerPayout.IsZero() { - // Use the FeeSink from that header, since that's the checked - // balance. (Though we have no expectation it will ever change.) - err = eval.state.Move(prevHeader.FeeSink, prevHeader.Proposer, prevHeader.ProposerPayout, nil, nil) - if err != nil { - // Should be impossible, it was checked when put into the block - return nil, fmt.Errorf("unable to payout block incentive: %v", err) - } - } - - // Increment LastProposed on the proposer account - prp, err := eval.state.Get(prevHeader.Proposer, false) - if err != nil { - return nil, err - } - prp.LastProposed = hdr.Round - 1 - - // An account could propose, even while suspended, because of the 320 - // round lookback. Doing so is evidence the account is - // back. Unsuspend. But the account will remain not IncentiveElgible - // until they repay the initial fee. - if prp.Suspended() { - prp.Status = basics.Online - } - err = eval.state.Put(prevHeader.Proposer, prp) - if err != nil { - return nil, err - } - } - if eval.Tracer != nil { eval.Tracer.BeforeBlock(&eval.block.BlockHeader) } @@ -1390,33 +1357,35 @@ func (eval *BlockEvaluator) endOfBlock() error { return fmt.Errorf("fees collected wrong: %v != %v", eval.block.FeesCollected, expectedFeesCollected) } - // agreement will check that the proposer is correct (we can't because - // we don't see the bundle), but agreement allows the proposer to be set - // even if Payouts is not enabled (and unset any time). So make sure - // it's set iff it should be. if eval.proto.Payouts.Enabled { - if eval.block.Proposer().IsZero() && !eval.generate { // if generating, proposer is set later by agreement - return fmt.Errorf("proposer missing when payouts enabled") + // agreement will check that the payout is zero if the proposer is + // ineligible, but we must check that it is correct if non-zero. We + // allow it to be too low. A proposer can be algruistic. + expectedPayout, err := eval.proposerPayout() + if err != nil { + return err + } + payout := eval.block.ProposerPayout() + if payout.Raw > expectedPayout.Raw { + return fmt.Errorf("proposal wants %d payout, %d is allowed", payout.Raw, expectedPayout.Raw) + } + // agreement will check that the proposer is correct (we can't because + // we don't see the bundle), but agreement allows the proposer to be set + // even if Payouts is not enabled (and unset any time). So make sure + // it's set only if it should be. + if !eval.generate { // if generating, proposer is set later by agreement + proposer := eval.block.Proposer() + if proposer.IsZero() { + return fmt.Errorf("proposer missing when payouts enabled") + } } } else { if !eval.block.Proposer().IsZero() { return fmt.Errorf("proposer %v present when payouts disabled", eval.block.Proposer()) } - } - - // agreement will check that the payout is zero if the proposer is - // ineligible, but we must check that it is correct if non-zero. We allow it - // to be too low. A proposer can be algruistic. - maxPayout := uint64(0) - if eval.proto.Payouts.Enabled { - payout, err := eval.proposerPayout() - if err != nil { - return err + if !eval.block.ProposerPayout().IsZero() { + return fmt.Errorf("payout %d present when payouts disabled", eval.block.ProposerPayout().Raw) } - maxPayout = payout.Raw - } - if eval.block.ProposerPayout().Raw > maxPayout { - return fmt.Errorf("proposal wants %d payout, %d is allowed", eval.block.ProposerPayout().Raw, maxPayout) } expectedVoters, expectedVotersWeight, err2 := eval.stateProofVotersAndTotal() @@ -1461,6 +1430,45 @@ func (eval *BlockEvaluator) endOfBlock() error { } } + // Try to pay the proposer. + { + proposer := eval.block.Proposer() + payout := eval.block.ProposerPayout() + // In effect, these checks mean we're only performing the payout when + // !generate, since the proposer won't be present yet. + if !proposer.IsZero() { + // We don't propagate the error here, + // we simply declare that an illegal payout is not made. This + // protects us from stalling if there is ever an error in which + // the generation code thinks the payout will be legal, but it + // turns out not to be. That would be a programming error in + // algod, but not worth stalling over. + if !payout.IsZero() { + err2 := eval.state.Move(eval.block.FeeSink, proposer, payout, nil, nil) + if err2 != nil { + logging.Base().Warnf("Unable to payout %d to %v: %s", + payout, proposer, err2) + } + } + prp, err2 := eval.state.Get(proposer, false) + if err2 != nil { + return err2 + } + prp.LastProposed = eval.Round() + // An account could propose, even while suspended, because of the 320 + // round lookback. Doing so is evidence the account is + // back. Unsuspend. But the account will remain not IncentiveElgible + // until they keyreg again with the extra fee. + if prp.Suspended() { + prp.Status = basics.Online + } + err2 = eval.state.Put(proposer, prp) + if err2 != nil { + return err2 + } + } + } + if err := eval.state.CalculateTotals(); err != nil { return err } diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index ba58654a3c..60c31419f9 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -263,6 +263,13 @@ func TestPayoutFees(t *testing.T) { dl.txns(&pay, pay.Args("again")) vb := dl.endBlock(proposer) + postsink := micros(dl.t, dl.generator, genBalances.FeeSink) + postprop := micros(dl.t, dl.generator, proposer) + t.Log(" postsink", postsink) + t.Log(" postprop", postprop) + + prp = lookup(dl.t, dl.generator, proposer) + const bonus1 = 10_000_000 // the first bonus value, set in config/consensus.go if ver >= payoutsBegin { require.True(t, dl.generator.GenesisProto().Payouts.Enabled) // version sanity check @@ -274,38 +281,55 @@ func TestPayoutFees(t *testing.T) { // This last one is really only testing the "fake" agreement that // happens in dl.endBlock(). require.EqualValues(t, proposer, vb.Block().Proposer()) + + // At the end of the block, part of the fees + bonus have been moved to + // the proposer. + require.EqualValues(t, bonus1+1500, postprop-preprop) // based on 75% in config/consensus.go + require.EqualValues(t, bonus1-500, presink-postsink) + require.Equal(t, prp.LastProposed, dl.generator.Latest()) } else { require.False(t, dl.generator.GenesisProto().Payouts.Enabled) require.Zero(t, dl.generator.GenesisProto().Payouts.Percent) // version sanity check require.Zero(t, vb.Block().FeesCollected) require.Zero(t, vb.Block().Bonus) require.Zero(t, vb.Block().ProposerPayout()) + // fees stayed in the feesink + require.EqualValues(t, 0, postprop-preprop, "%v", proposer) + require.EqualValues(t, 2000, postsink-presink) + require.Zero(t, prp.LastProposed) } - postsink := micros(dl.t, dl.generator, genBalances.FeeSink) - postprop := micros(dl.t, dl.generator, proposer) - t.Log(" postsink", postsink) - t.Log(" postprop", postprop) + // Do another block, make sure proposer doesn't get paid again. (Sanity + // check. The code used to award the payout used to be in block n+1). + vb = dl.fullBlock() + require.Equal(t, postsink, micros(dl.t, dl.generator, genBalances.FeeSink)) + require.Equal(t, postprop, micros(dl.t, dl.generator, proposer)) - // At the end of the block, all fees are still in the sink. - require.EqualValues(t, 2000, postsink-presink) - require.EqualValues(t, 0, postprop-preprop) + // Rest of the tests only make sense with payout active + if ver < payoutsBegin { + return + } - // Do the next block, which moves part of the fees + bonus to proposer - dl.fullBlock() - postsink = micros(dl.t, dl.generator, genBalances.FeeSink) - postprop = micros(dl.t, dl.generator, proposer) - t.Log(" postsink2", postsink) - t.Log(" postprop2", postprop) + // Get the feesink down low, then drain it by proposing. + feesink := vb.Block().FeeSink + data := lookup(t, dl.generator, feesink) + dl.txn(&txntest.Txn{ + Type: "pay", + Sender: feesink, + Receiver: addrs[1], + Amount: data.MicroAlgos.Raw - 12_000_000, + }) + dl.beginBlock() + dl.endBlock(proposer) + require.EqualValues(t, micros(t, dl.generator, feesink), 2_000_000) - if ver >= payoutsBegin { - require.EqualValues(t, bonus1+1500, postprop-preprop) // based on 75% in config/consensus.go - require.EqualValues(t, bonus1-500, presink-postsink) - } else { - // stayed in the feesink - require.EqualValues(t, 0, postprop-preprop, "%v", proposer) - require.EqualValues(t, 2000, postsink-presink) - } + dl.beginBlock() + dl.endBlock(proposer) + require.EqualValues(t, micros(t, dl.generator, feesink), 100_000) + + dl.beginBlock() + dl.endBlock(proposer) + require.EqualValues(t, micros(t, dl.generator, feesink), 100_000) }) } @@ -419,9 +443,12 @@ func TestAbsentTracking(t *testing.T) { }) dl.endBlock(proposer) - // no changes until the next block prp := lookup(t, dl.validator, proposer) - require.Zero(t, prp.LastProposed) + if ver >= checkingBegins { + require.Equal(t, prp.LastProposed, dl.validator.Latest()) + } else { + require.Zero(t, prp.LastProposed) + } require.Zero(t, prp.LastHeartbeat) require.False(t, prp.IncentiveEligible) @@ -434,18 +461,6 @@ func TestAbsentTracking(t *testing.T) { dl.fullBlock() - prp = lookup(t, dl.validator, proposer) - require.False(t, prp.IncentiveEligible) - - if ver >= checkingBegins { - // version sanity check - require.NotZero(t, prp.LastProposed) - require.Zero(t, prp.LastHeartbeat) // genesis participants have never hb - } else { - require.Zero(t, prp.LastProposed) - require.Zero(t, prp.LastHeartbeat) - } - // addrs[2] was already offline dl.txns(&txntest.Txn{Type: "keyreg", Sender: addrs[2]}) // OFFLINE keyreg regger := lookup(t, dl.validator, addrs[2]) @@ -552,13 +567,11 @@ func TestAbsentTracking(t *testing.T) { require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).Status == basics.Offline) require.False(t, lookup(t, dl.generator, addrs[2]).IncentiveEligible) - // now, addrs[2] proposes, so it gets back online, but not immediately, - // because processing happens after the proposing block + // now, addrs[2] proposes, so it gets back online, but stays ineligible dl.proposer = addrs[2] dl.fullBlock() - require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).Status == basics.Offline) - dl.fullBlock() require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[2]).Status) + require.False(t, lookup(t, dl.generator, addrs[2]).IncentiveEligible) }) } diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 3d46c02b8b..83a9e20c54 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -32,6 +32,9 @@ import ( "github.com/algorand/go-algorand/test/partitiontest" ) +// first bonus payout, set in config/consensus.go +const bonus1 = 10_000_000 + // TestBasicPayouts shows proposers getting paid func TestBasicPayouts(t *testing.T) { partitiontest.PartitionTest(t) @@ -61,12 +64,13 @@ func TestBasicPayouts(t *testing.T) { c15, account15 := clientAndAccount("Node15") c01, account01 := clientAndAccount("Node01") + relay, _ := clientAndAccount("Relay") data01 := rekeyreg(&fixture, a, c01, account01.Address) data15 := rekeyreg(&fixture, a, c15, account15.Address) - // have account01 burn some money to get below to eligibility cap - // Starts with 100M, so burn 60M and get under 50M cap. + // have account01 burn some money to get below the eligibility cap + // Starts with 100M, so burn 60M and get under 70M cap. txn, err := c01.SendPaymentFromUnencryptedWallet(account01.Address, basics.Address{}.String(), 1000, 60_000_000_000_000, nil) a.NoError(err) @@ -86,12 +90,10 @@ func TestBasicPayouts(t *testing.T) { block, err := client.BookkeepingBlock(status.LastRound) a.NoError(err) - fmt.Printf(" 1 block %d proposed by %v\n", status.LastRound, block.Proposer()) + fmt.Printf("block %d proposed by %v\n", status.LastRound, block.Proposer()) a.Zero(block.ProposerPayout()) // nobody is eligible yet (hasn't worked back to balance round) - a.EqualValues(5_000_000, block.Bonus.Raw) - fixture.WaitForRoundWithTimeout(status.LastRound + 1) + a.EqualValues(bonus1, block.Bonus.Raw) - // incentives would pay out in the next round (they won't here, but makes the test realistic) next, err := client.AccountData(block.Proposer().String()) a.EqualValues(next.LastProposed, status.LastRound) // regardless of proposer, nobody gets paid @@ -105,6 +107,7 @@ func TestBasicPayouts(t *testing.T) { default: a.Fail("bad proposer", "%v proposed", block.Proposer) } + fixture.WaitForRoundWithTimeout(status.LastRound + 1) status, err = client.Status() a.NoError(err) } @@ -117,33 +120,28 @@ func TestBasicPayouts(t *testing.T) { a.NoError(err) block, err := client.BookkeepingBlock(status.LastRound) a.NoError(err) + a.EqualValues(bonus1, block.Bonus.Raw) - fmt.Printf(" 3 block %d proposed by %v\n", status.LastRound, block.Proposer()) - a.EqualValues(5_000_000, block.Bonus.Raw) - - // incentives would pay out in the next round so wait to see them - fixture.WaitForRoundWithTimeout(status.LastRound + 1) next, err := client.AccountData(block.Proposer().String()) - fmt.Printf(" proposer %v has %d at round %d\n", block.Proposer(), next.MicroAlgos.Raw, status.LastRound) + fmt.Printf(" proposer %v has %d after proposing round %d\n", block.Proposer(), next.MicroAlgos.Raw, status.LastRound) // 01 would get paid (because under balance cap) 15 would not switch block.Proposer().String() { case account01.Address: - a.NotZero(block.ProposerPayout()) - a.NotEqual(data01.MicroAlgos, next.MicroAlgos) + a.EqualValues(bonus1, block.ProposerPayout().Raw) + a.EqualValues(data01.MicroAlgos.Raw+bonus1, next.MicroAlgos.Raw) // 01 earns proposed01 = true data01 = next case account15.Address: a.Zero(block.ProposerPayout()) - a.Equal(data15.MicroAlgos, next.MicroAlgos) + a.Equal(data15.MicroAlgos, next.MicroAlgos) // didn't earn data15 = next proposed15 = true default: a.Fail("bad proposer", "%v proposed", block.Proposer) } + fixture.WaitForRoundWithTimeout(status.LastRound + 1) } - a.True(proposed15) - a.True(proposed01) // There's some chance of this triggering flakily // Now that we've proven incentives get paid, let's drain the FeeSink and // ensure it happens gracefully. Have account15 go offline so that (after @@ -163,17 +161,27 @@ func TestBasicPayouts(t *testing.T) { block, err := client.BookkeepingBlock(status.LastRound) a.NoError(err) - feesink := block.BlockHeader.FeeSink - err = fixture.WaitForRoundWithTimeout(status.LastRound + 1) - a.NoError(err) - data, err := client.AccountData(feesink.String()) + a.EqualValues(bonus1, block.Bonus.Raw) + + data, err := client.AccountData(block.Proposer().String()) a.NoError(err) - fmt.Printf(" feesink has %d at round %d\n", data.MicroAlgos.Raw, status.LastRound) + fmt.Printf(" proposer %v has %d after proposing round %d\n", block.Proposer(), data.MicroAlgos.Raw, status.LastRound) + + feesink := block.BlockHeader.FeeSink + // show all the node's belief about feesink, for debugging the payout + // effects appearing locally but not elsewhere (or vice versa) + for i, c := range []libgoal.Client{c01, c15, relay} { + data, err = c.AccountData(feesink.String()) + a.NoError(err) + fmt.Printf(" feesink %d has %d at round %d\n", i, data.MicroAlgos.Raw, status.LastRound) + } a.LessOrEqual(100000, int(data.MicroAlgos.Raw)) // won't go below minfee if data.MicroAlgos.Raw == 100000 { break } a.Less(i, 32+20) + err = fixture.WaitForRoundWithTimeout(status.LastRound + 1) + a.NoError(err) } } From 73798d1f2d9638996bbbfa239ad9a1535ac9d58b Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 29 Mar 2024 12:14:54 -0400 Subject: [PATCH 082/117] No idea why this is failing --- test/scripts/e2e_subs/absentee.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/scripts/e2e_subs/absentee.py b/test/scripts/e2e_subs/absentee.py index 3a1419d547..af3e94d88c 100755 --- a/test/scripts/e2e_subs/absentee.py +++ b/test/scripts/e2e_subs/absentee.py @@ -1,10 +1,8 @@ #!/usr/bin/env python -import base64 import os import sys from goal import Goal -import algosdk.encoding as enc from datetime import datetime From a914ee177e4cd3e4b853091dc84845973f38764e Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 29 Mar 2024 14:13:58 -0400 Subject: [PATCH 083/117] Don't save the deltas after generating a proposal Those deltas are incomplete, don't cache and use them. --- agreement/proposal.go | 1 + ledger/eval/eval.go | 14 ++++---- .../e2e-go/features/incentives/mining_test.go | 33 ++++++++++++++++++- test/scripts/e2e_subs/absentee.py | 1 + 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index 602d5b3ef0..b28a05f9e2 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -305,6 +305,7 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve Validat ve = ve.WithProposer(newSeed, address, eligible) proposal := makeProposal(ve, seedProof, period, address) + proposal.ve = nil value := proposalValue{ OriginalPeriod: period, OriginalProposer: address, diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 1818a6f04b..ac72efe160 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1434,15 +1434,13 @@ func (eval *BlockEvaluator) endOfBlock() error { { proposer := eval.block.Proposer() payout := eval.block.ProposerPayout() - // In effect, these checks mean we're only performing the payout when - // !generate, since the proposer won't be present yet. + // The proposer won't be present yet when generating a block if !proposer.IsZero() { - // We don't propagate the error here, - // we simply declare that an illegal payout is not made. This - // protects us from stalling if there is ever an error in which - // the generation code thinks the payout will be legal, but it - // turns out not to be. That would be a programming error in - // algod, but not worth stalling over. + // We don't propagate the error here, we simply declare that an + // illegal payout is not made. This protects us from stalling if + // there is ever an error in which the generation code thinks the + // payout will be legal, but it turns out not to be. That would be + // a programming error in algod, but not worth stalling over. if !payout.IsZero() { err2 := eval.state.Move(eval.block.FeeSink, proposer, payout, nil, nil) if err2 != nil { diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 83a9e20c54..79e9039ded 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -59,6 +59,7 @@ func TestBasicPayouts(t *testing.T) { accounts, err := fixture.GetNodeWalletsSortedByBalance(c) a.NoError(err) a.Len(accounts, 1) + fmt.Printf("Client %s is %v\n", name, accounts[0].Address) return c, accounts[0] } @@ -94,6 +95,25 @@ func TestBasicPayouts(t *testing.T) { a.Zero(block.ProposerPayout()) // nobody is eligible yet (hasn't worked back to balance round) a.EqualValues(bonus1, block.Bonus.Raw) + // all nodes agree the proposer proposed. The paranoia here is + // justified. Block incentives are the first time we're making changes + // to the Delta in the "second" evaluation of the block. That is, the + // payment and LastProposed change happen only if evaluating a block + // that `agreement` has already added to. An easy bug to have is an + // optimization that avoids this re-evaluation in the algod that + // proposed the block. We had such an optimization, and it would cause + // failures here. The fix is throwing away the ValidatedBlock in + // proposalForBlock() after makeProposal. + for i, c := range []libgoal.Client{c15, c01, relay} { + fmt.Printf("checking block %v\n", block.Round()) + data, err := c.AccountData(block.Proposer().String()) + a.NoError(err) + bb, err := c.BookkeepingBlock(status.LastRound) + a.NoError(err) + a.Equal(block.Proposer(), bb.Proposer()) + a.Equal(block.Round(), data.LastProposed, "client %d thinks %v", i, block.Proposer()) + } + next, err := client.AccountData(block.Proposer().String()) a.EqualValues(next.LastProposed, status.LastRound) // regardless of proposer, nobody gets paid @@ -125,6 +145,13 @@ func TestBasicPayouts(t *testing.T) { next, err := client.AccountData(block.Proposer().String()) fmt.Printf(" proposer %v has %d after proposing round %d\n", block.Proposer(), next.MicroAlgos.Raw, status.LastRound) + // all nodes agree the proposer proposed + for i, c := range []libgoal.Client{c15, c01, relay} { + data, err := c.AccountData(block.Proposer().String()) + a.NoError(err) + a.Equal(block.Round(), data.LastProposed, i) + } + // 01 would get paid (because under balance cap) 15 would not switch block.Proposer().String() { case account01.Address: @@ -170,7 +197,11 @@ func TestBasicPayouts(t *testing.T) { feesink := block.BlockHeader.FeeSink // show all the node's belief about feesink, for debugging the payout // effects appearing locally but not elsewhere (or vice versa) - for i, c := range []libgoal.Client{c01, c15, relay} { + for i, c := range []libgoal.Client{relay, c01, c15} { + data, err = c.AccountData(block.Proposer().String()) + a.NoError(err) + a.Equal(block.Round(), data.LastProposed) + data, err = c.AccountData(feesink.String()) a.NoError(err) fmt.Printf(" feesink %d has %d at round %d\n", i, data.MicroAlgos.Raw, status.LastRound) diff --git a/test/scripts/e2e_subs/absentee.py b/test/scripts/e2e_subs/absentee.py index af3e94d88c..7e72d8a966 100755 --- a/test/scripts/e2e_subs/absentee.py +++ b/test/scripts/e2e_subs/absentee.py @@ -25,6 +25,7 @@ pblock = goal.algod.block_info(txinfo['confirmed-round'])['block'] assert pblock["prp"] != "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ" prp_info = goal.algod.account_info(pblock["prp"]) +assert prp_info["round"] == pblock["rnd"], pblock assert "last-proposed" in prp_info, prp_info # they just did! assert prp_info["last-proposed"] > 0 assert "last-heartbeat" not in prp_info, prp_info # was a genesis account From 4e2313aea6698957dafc8e89882ef4ef0ff2dccd Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 29 Mar 2024 15:20:49 -0400 Subject: [PATCH 084/117] correct type in merge conflict resolution --- ledger/simple_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 456d1c56fa..e2e9f524b8 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -161,11 +161,11 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer // can't call the agreement code, the eligibility of the prp is not // considered. if ledger.GenesisProto().Payouts.Enabled { - *gvb = ledgercore.MakeValidatedBlock(vb.Block().WithProposer(committee.Seed(prp), prp, true), vb.Delta()) + *gvb = ledgercore.MakeValidatedBlock(gvb.Block().WithProposer(committee.Seed(prp), prp, true), gvb.Delta()) } else { // To more closely mimic the agreement code, we don't // write the proposer when !Payouts.Enabled. - *gvb = ledgercore.MakeValidatedBlock(vb.Block().WithProposer(committee.Seed(prp), basics.Address{}, false), vb.Delta()) + *gvb = ledgercore.MakeValidatedBlock(gvb.Block().WithProposer(committee.Seed(prp), basics.Address{}, false), gvb.Delta()) } vvb, err := validateWithoutSignatures(t, ledger, gvb.Block()) From 2c401257dc170cf8929e9aa73d0742236c06cc1f Mon Sep 17 00:00:00 2001 From: chris erway Date: Wed, 3 Apr 2024 13:09:46 -0400 Subject: [PATCH 085/117] first step of applying unfinishedBlock changes from algorand/go-algorand#5967 to algorand/go-algorand#5757 --- agreement/abstractions.go | 33 ++++++++++++++---------- agreement/agreementtest/simulate_test.go | 4 +-- agreement/common_test.go | 13 +++++++--- agreement/fuzzer/ledger_test.go | 8 ++++-- agreement/player_permutation_test.go | 4 ++- agreement/proposal.go | 10 +++---- agreement/proposalStore_test.go | 16 ++++++------ agreement/proposal_test.go | 6 ++--- agreement/pseudonode.go | 6 ++++- data/datatest/impls.go | 11 +++++--- node/node.go | 28 +++++++++++++------- 11 files changed, 87 insertions(+), 52 deletions(-) diff --git a/agreement/abstractions.go b/agreement/abstractions.go index 2a1e247d2d..e093e944fc 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -65,34 +65,39 @@ var ErrAssembleBlockRoundStale = errors.New("requested round for AssembleBlock i // An BlockFactory produces an Block which is suitable for proposal for a given // Round. type BlockFactory interface { - // AssembleBlock produces a new ValidatedBlock which is suitable for proposal - // at a given Round. + // AssembleBlock produces a new UnfinishedBlock for a given Round. + // It must be finalized before proposed by agreement. It is provided + // a list of participating addresses that may propose this block. // - // AssembleBlock should produce a ValidatedBlock for which the corresponding + // AssembleBlock should produce a block for which the corresponding // BlockValidator validates (i.e. for which BlockValidator.Validate // returns true). If an insufficient number of nodes can assemble valid // entries, the agreement protocol may lose liveness. // // AssembleBlock may return an error if the BlockFactory is unable to - // produce a ValidatedBlock for the given round. If an insufficient number of + // produce an UnfinishedBlock for the given round. If an insufficient number of // nodes on the network can assemble entries, the agreement protocol may // lose liveness. - AssembleBlock(basics.Round) (AssembledBlock, error) + AssembleBlock(rnd basics.Round, partAddresses []basics.Address) (UnfinishedBlock, error) } -// An AssembledBlock represents a Block produced by a BlockFactory -// to be included in a proposal by agreement. -type AssembledBlock interface { - // WithProposer creates a copy of this AssembledBlock with its - // cryptographically random seed and proposer set. The block's - // ProposerPayout is zero'd if !eligible. Abstractly, it is how the - // agreement code "finishes" a block and makes it a proposal for a specific - // account. +// An UnfinishedBlock represents a Block produced by a BlockFactory +// and must be finalized before being proposed by agreement. +type UnfinishedBlock interface { + // WithSeed creates a copy of this UnfinishedBlock with its + // cryptographically random seed set to the given value. // // Calls to Seed() or to Digest() on the copy's Block must // reflect the value of the new seed. - WithProposer(seed committee.Seed, proposer basics.Address, eligible bool) AssembledBlock + FinishBlock(seed committee.Seed, proposer basics.Address, eligible bool) ProposableBlock + Round() basics.Round +} + +// An ProposableBlock represents a Block produced by a BlockFactory, +// that was later finalized by providing the seed and the proposer, +// and can now be proposed by agreement. +type ProposableBlock interface { // Block returns the underlying block that has been assembled. Block() bookkeeping.Block } diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index c048b61668..3911696c57 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -79,7 +79,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.UnfinishedBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -98,7 +98,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.AssembledBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.UnfinishedBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } diff --git a/agreement/common_test.go b/agreement/common_test.go index eb37490440..d4f3a55541 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -165,7 +165,11 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) AssembledBlock { +func (b testValidatedBlock) Round() basics.Round { + return b.Inside.Round() +} + +func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) ProposableBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -184,7 +188,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (AssembledBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round, _ []basics.Address) (UnfinishedBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } @@ -417,7 +421,7 @@ type testAccountData struct { } func makeProposalsTesting(accs testAccountData, round basics.Round, period period, factory BlockFactory, ledger Ledger) (ps []proposal, vs []vote) { - ve, err := factory.AssembleBlock(round) + ve, err := factory.AssembleBlock(round, accs.addresses) if err != nil { logging.Base().Errorf("Could not generate a proposal for round %d: %v", round, err) return nil, nil @@ -529,8 +533,9 @@ func (v *voteMakerHelper) MakeRandomProposalValue() *proposalValue { func (v *voteMakerHelper) MakeRandomProposalPayload(t *testing.T, r round) (*proposal, *proposalValue) { f := testBlockFactory{Owner: 1} - ve, err := f.AssembleBlock(r) + ue, err := f.AssembleBlock(r, nil) require.NoError(t, err) + ve := ue.FinishBlock(committee.Seed{}, basics.Address{}, false) var payload unauthenticatedProposal payload.Block = ve.Block() diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go index 36c5da277a..6d5c91c28c 100644 --- a/agreement/fuzzer/ledger_test.go +++ b/agreement/fuzzer/ledger_test.go @@ -93,7 +93,11 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { +func (b testValidatedBlock) Round() basics.Round { + return b.Inside.Round() +} + +func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.ProposableBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -112,7 +116,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.AssembledBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round, _ []basics.Address) (agreement.UnfinishedBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } diff --git a/agreement/player_permutation_test.go b/agreement/player_permutation_test.go index e41195d1f1..da6fe902a1 100644 --- a/agreement/player_permutation_test.go +++ b/agreement/player_permutation_test.go @@ -25,13 +25,15 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/data/committee" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" ) func makeRandomProposalPayload(r round) *proposal { f := testBlockFactory{Owner: 1} - ve, _ := f.AssembleBlock(r) + ue, _ := f.AssembleBlock(r, nil) + ve := ue.FinishBlock(committee.Seed{}, basics.Address{}, false) var payload unauthenticatedProposal payload.Block = ve.Block() diff --git a/agreement/proposal.go b/agreement/proposal.go index 58647f0a8a..c14c2d8d5d 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -102,7 +102,7 @@ type proposal struct { validatedAt time.Duration } -func makeProposalFromAssembledBlock(blk AssembledBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { +func makeProposalFromProposableBlock(blk ProposableBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { e := blk.Block() var payload unauthenticatedProposal payload.Block = e @@ -295,8 +295,8 @@ func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerRead return eligible, balanceRecord, nil } -func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, blk AssembledBlock, period period, ledger LedgerReader) (proposal, proposalValue, error) { - rnd := blk.Block().Round() +func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, blk UnfinishedBlock, period period, ledger LedgerReader) (proposal, proposalValue, error) { + rnd := blk.Round() cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) if err != nil { @@ -313,8 +313,8 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, blk Assemb return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could determine eligibility: %w", err) } - blk = blk.WithProposer(newSeed, address, eligible) - prop := makeProposalFromAssembledBlock(blk, seedProof, period, address) + proposableBlock := blk.FinishBlock(newSeed, address, eligible) + prop := makeProposalFromProposableBlock(proposableBlock, seedProof, period, address) value := proposalValue{ OriginalPeriod: period, diff --git a/agreement/proposalStore_test.go b/agreement/proposalStore_test.go index 2ce8c23753..d434a3cca8 100644 --- a/agreement/proposalStore_test.go +++ b/agreement/proposalStore_test.go @@ -64,7 +64,7 @@ func TestBlockAssemblerPipeline(t *testing.T) { round := player.Round period := player.Period - testBlockFactory, err := factory.AssembleBlock(player.Round) + testBlockFactory, err := factory.AssembleBlock(player.Round, accounts.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", round, err) accountIndex := 0 @@ -132,7 +132,7 @@ func TestBlockAssemblerBind(t *testing.T) { player, _, accounts, factory, ledger := testSetup(0) - testBlockFactory, err := factory.AssembleBlock(player.Round) + testBlockFactory, err := factory.AssembleBlock(player.Round, accounts.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", player.Round, err) accountIndex := 0 @@ -200,7 +200,7 @@ func TestBlockAssemblerAuthenticator(t *testing.T) { player, _, accounts, factory, ledger := testSetup(0) - testBlockFactory, err := factory.AssembleBlock(player.Round) + testBlockFactory, err := factory.AssembleBlock(player.Round, accounts.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", player.Round, err) accountIndex := 0 proposalPayload, _, _ := proposalForBlock(accounts.addresses[accountIndex], accounts.vrfs[accountIndex], testBlockFactory, player.Period, ledger) @@ -266,7 +266,7 @@ func TestBlockAssemblerTrim(t *testing.T) { player, _, accounts, factory, ledger := testSetup(0) - testBlockFactory, err := factory.AssembleBlock(player.Round) + testBlockFactory, err := factory.AssembleBlock(player.Round, accounts.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", player.Round, err) accountIndex := 0 proposalPayload, _, _ := proposalForBlock(accounts.addresses[accountIndex], accounts.vrfs[accountIndex], testBlockFactory, player.Period, ledger) @@ -339,7 +339,7 @@ func TestProposalStoreT(t *testing.T) { player, _, accounts, factory, ledger := testSetup(0) - testBlockFactory, err := factory.AssembleBlock(player.Round) + testBlockFactory, err := factory.AssembleBlock(player.Round, accounts.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", player.Round, err) accountIndex := 0 proposalPayload, proposalV, _ := proposalForBlock(accounts.addresses[accountIndex], accounts.vrfs[accountIndex], testBlockFactory, player.Period, ledger) @@ -413,7 +413,7 @@ func TestProposalStoreUnderlying(t *testing.T) { player, _, accounts, factory, ledger := testSetup(0) - testBlockFactory, err := factory.AssembleBlock(player.Round) + testBlockFactory, err := factory.AssembleBlock(player.Round, accounts.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", player.Round, err) accountIndex := 0 proposalPayload, proposalV, _ := proposalForBlock(accounts.addresses[accountIndex], accounts.vrfs[accountIndex], testBlockFactory, player.Period, ledger) @@ -477,7 +477,7 @@ func TestProposalStoreHandle(t *testing.T) { proposalVoteEventBatch, proposalPayloadEventBatch, _ := generateProposalEvents(t, player, accounts, factory, ledger) - testBlockFactory, err := factory.AssembleBlock(player.Round) + testBlockFactory, err := factory.AssembleBlock(player.Round, accounts.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", player.Round, err) accountIndex := 0 _, proposalV0, _ := proposalForBlock(accounts.addresses[accountIndex], accounts.vrfs[accountIndex], testBlockFactory, player.Period, ledger) @@ -661,7 +661,7 @@ func TestProposalStoreGetPinnedValue(t *testing.T) { // create proposal Store player, router, accounts, factory, ledger := testPlayerSetup() - testBlockFactory, err := factory.AssembleBlock(player.Round) + testBlockFactory, err := factory.AssembleBlock(player.Round, accounts.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", player.Round, err) accountIndex := 0 // create a route handler for the proposal store diff --git a/agreement/proposal_test.go b/agreement/proposal_test.go index 98cb177073..31b8d83050 100644 --- a/agreement/proposal_test.go +++ b/agreement/proposal_test.go @@ -46,7 +46,7 @@ func testSetup(periodCount uint64) (player, rootRouter, testAccountData, testBlo } func createProposalsTesting(accs testAccountData, round basics.Round, period period, factory BlockFactory, ledger Ledger) (ps []proposal, vs []vote) { - ve, err := factory.AssembleBlock(round) + ve, err := factory.AssembleBlock(round, accs.addresses) if err != nil { logging.Base().Errorf("Could not generate a proposal for round %d: %v", round, err) return nil, nil @@ -122,7 +122,7 @@ func TestProposalFunctions(t *testing.T) { player, _, accs, factory, ledger := testSetup(0) round := player.Round period := player.Period - ve, err := factory.AssembleBlock(player.Round) + ve, err := factory.AssembleBlock(player.Round, accs.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", round, err) validator := testBlockValidator{} @@ -162,7 +162,7 @@ func TestProposalUnauthenticated(t *testing.T) { round := player.Round period := player.Period - testBlockFactory, err := factory.AssembleBlock(player.Round) + testBlockFactory, err := factory.AssembleBlock(player.Round, accounts.addresses) require.NoError(t, err, "Could not generate a proposal for round %d: %v", round, err) validator := testBlockValidator{} diff --git a/agreement/pseudonode.go b/agreement/pseudonode.go index e0bfb326bd..fe5423c025 100644 --- a/agreement/pseudonode.go +++ b/agreement/pseudonode.go @@ -284,7 +284,11 @@ func (n asyncPseudonode) makePseudonodeVerifier(voteVerifier *AsyncVoteVerifier) // makeProposals creates a slice of block proposals for the given round and period. func (n asyncPseudonode) makeProposals(round basics.Round, period period, accounts []account.ParticipationRecordForRound) ([]proposal, []unauthenticatedVote) { - ve, err := n.factory.AssembleBlock(round) + addresses := make([]basics.Address, len(accounts)) + for i := range accounts { + addresses[i] = accounts[i].Account + } + ve, err := n.factory.AssembleBlock(round, addresses) if err != nil { if err != ErrAssembleBlockRoundStale { n.log.Errorf("pseudonode.makeProposals: could not generate a proposal for round %d: %v", round, err) diff --git a/data/datatest/impls.go b/data/datatest/impls.go index c834f99d2d..f7268744c6 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -53,7 +53,7 @@ type entryFactoryImpl struct { } // AssembleBlock implements Ledger.AssembleBlock. -func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.AssembledBlock, error) { +func (i entryFactoryImpl) AssembleBlock(round basics.Round, _ []basics.Address) (agreement.UnfinishedBlock, error) { prev, err := i.l.BlockHdr(round - 1) if err != nil { return nil, fmt.Errorf("could not make proposals: could not read block from ledger at round %v: %v", round, err) @@ -64,8 +64,8 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Assembled return validatedBlock{blk: &b}, nil } -// WithProposer implements the agreement.AssembledBlock interface. -func (ve validatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { +// FinishBlock implements the agreement.UnfinishedBlock interface. +func (ve validatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.ProposableBlock { newblock := *ve.blk newblock.BlockHeader.Seed = s newblock.BlockHeader.Proposer = proposer @@ -80,6 +80,11 @@ func (ve validatedBlock) Block() bookkeeping.Block { return *ve.blk } +// Round implements the agreement.UnfinishedBlock interface. +func (ve validatedBlock) Round() basics.Round { + return ve.blk.Round() +} + type ledgerImpl struct { l *data.Ledger } diff --git a/node/node.go b/node/node.go index bae3a45bd8..1c57ffdf8a 100644 --- a/node/node.go +++ b/node/node.go @@ -1301,24 +1301,34 @@ func (vb validatedBlock) Block() bookkeeping.Block { return blk } -// assembledBlock satisfies agreement.AssembledBlock -type assembledBlock struct { +// unfinishedBlock satisfies agreement.UnfinishedBlock +type unfinishedBlock struct { blk bookkeeping.Block } -// WithProposer satisfies the agreement.ValidatedBlock interface. -func (ab assembledBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { +// proposableBlock satisfies agreement.ProposableBlock +type proposableBlock struct { + blk bookkeeping.Block +} + +// FinishBlock satisfies the agreement.UnfinishedBlock interface. +func (ab unfinishedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.ProposableBlock { nb := ab.blk.WithProposer(s, proposer, eligible) - return assembledBlock{blk: nb} + return proposableBlock{blk: nb} +} + +// Round satisfies the agreement.UnfinishedBlock interface. +func (ab unfinishedBlock) Round() basics.Round { + return ab.blk.Round() } -// Block satisfies the agreement.AssembledBlock interface. -func (ab assembledBlock) Block() bookkeeping.Block { +// Block satisfies the agreement.ProposableBlock interface. +func (ab proposableBlock) Block() bookkeeping.Block { return ab.blk } // AssembleBlock implements Ledger.AssembleBlock. -func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.AssembledBlock, error) { +func (node *AlgorandFullNode) AssembleBlock(round basics.Round, addrs []basics.Address) (agreement.UnfinishedBlock, error) { deadline := time.Now().Add(node.config.ProposalAssemblyTime) lvb, err := node.transactionPool.AssembleBlock(round, deadline) if err != nil { @@ -1339,7 +1349,7 @@ func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.Assem } return nil, err } - return assembledBlock{blk: lvb.Block()}, nil + return unfinishedBlock{blk: lvb.Block()}, nil } // getOfflineClosedStatus will return an int with the appropriate bit(s) set if it is offline and/or online From f2c4f29f163e0b532048c274214328c189beec9b Mon Sep 17 00:00:00 2001 From: chris erway Date: Thu, 4 Apr 2024 14:12:36 -0400 Subject: [PATCH 086/117] add ledgercore.UnfinishedBlock and provide VotingAccountChecker to transactionPool --- agreement/proposal.go | 2 +- data/pools/transactionPool.go | 46 +++++++++++++++++++---------- ledger/eval/eval.go | 14 +++++++-- ledger/ledgercore/validatedBlock.go | 30 +++++++++++++++++++ node/node.go | 18 +++++++++-- 5 files changed, 89 insertions(+), 21 deletions(-) diff --git a/agreement/proposal.go b/agreement/proposal.go index c14c2d8d5d..73261dabd8 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -109,7 +109,7 @@ func makeProposalFromProposableBlock(blk ProposableBlock, pf crypto.VrfProof, or payload.SeedProof = pf payload.OriginalPeriod = origPer payload.OriginalProposer = origProp - return proposal{unauthenticatedProposal: payload} + return proposal{unauthenticatedProposal: payload} // ve set to nil -- won't cache deltas } func makeProposalFromValidatedBlock(ve ValidatedBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { diff --git a/data/pools/transactionPool.go b/data/pools/transactionPool.go index 2eb787eafd..c2533edb7a 100644 --- a/data/pools/transactionPool.go +++ b/data/pools/transactionPool.go @@ -70,8 +70,10 @@ type TransactionPool struct { assemblyCond sync.Cond assemblyDeadline time.Time // assemblyRound indicates which round number we're currently waiting for or waited for last. - assemblyRound basics.Round - assemblyResults poolAsmResults + assemblyRound basics.Round + // assemblyPartAddresses indicates which participating accounts we're currently using, or last used. + assemblyPartAddresses []basics.Address + assemblyResults poolAsmResults // pendingMu protects pendingTxGroups and pendingTxids pendingMu deadlock.RWMutex @@ -87,6 +89,7 @@ type TransactionPool struct { rememberedTxids map[transactions.Txid]transactions.SignedTxn log logging.Logger + vac VotingAccountChecker // proposalAssemblyTime is the ProposalAssemblyTime configured for this node. proposalAssemblyTime time.Duration @@ -103,12 +106,20 @@ type BlockEvaluator interface { PaySetSize() int TransactionGroup(txads []transactions.SignedTxnWithAD) error Transaction(txn transactions.SignedTxn, ad transactions.ApplyData) error - GenerateBlock() (*ledgercore.ValidatedBlock, error) + GenerateBlock(addrs []basics.Address) (*ledgercore.UnfinishedBlock, error) ResetTxnBytes() } +type UnfinishedBlock interface { + FinishBlock(proposer basics.Address) bookkeeping.Block +} + +type VotingAccountChecker interface { + VotingAccountsForRound(basics.Round) []basics.Address +} + // MakeTransactionPool makes a transaction pool. -func MakeTransactionPool(ledger *ledger.Ledger, cfg config.Local, log logging.Logger) *TransactionPool { +func MakeTransactionPool(ledger *ledger.Ledger, cfg config.Local, log logging.Logger, vac VotingAccountChecker) *TransactionPool { if cfg.TxPoolExponentialIncreaseFactor < 1 { cfg.TxPoolExponentialIncreaseFactor = 1 } @@ -124,6 +135,7 @@ func MakeTransactionPool(ledger *ledger.Ledger, cfg config.Local, log logging.Lo txPoolMaxSize: cfg.TxPoolSize, proposalAssemblyTime: cfg.ProposalAssemblyTime, log: log, + vac: vac, } pool.cond.L = &pool.mu pool.assemblyCond.L = &pool.assemblyMu @@ -137,7 +149,7 @@ type poolAsmResults struct { // the ok variable indicates whether the assembly for the block roundStartedEvaluating was complete ( i.e. ok == true ) or // whether it's still in-progress. ok bool - blk *ledgercore.ValidatedBlock + blk *ledgercore.UnfinishedBlock stats telemetryspec.AssembleBlockMetrics err error // roundStartedEvaluating is the round which we were attempted to evaluate last. It's a good measure for @@ -602,7 +614,7 @@ func (pool *TransactionPool) addToPendingBlockEvaluatorOnce(txgroup []transactio transactionGroupDuration := time.Since(transactionGroupStartsTime) pool.assemblyMu.Lock() defer pool.assemblyMu.Unlock() - if pool.assemblyRound > pool.pendingBlockEvaluator.Round() { + if evalRnd := pool.pendingBlockEvaluator.Round(); pool.assemblyRound > evalRnd { // the block we're assembling now isn't the one the the AssembleBlock is waiting for. While it would be really cool // to finish generating the block, it would also be pointless to spend time on it. // we're going to set the ok and assemblyCompletedOrAbandoned to "true" so we can complete this loop asap @@ -623,7 +635,7 @@ func (pool *TransactionPool) addToPendingBlockEvaluatorOnce(txgroup []transactio } blockGenerationStarts := time.Now() - lvb, gerr := pool.pendingBlockEvaluator.GenerateBlock() + lvb, gerr := pool.pendingBlockEvaluator.GenerateBlock(pool.vac.VotingAccountsForRound(evalRnd)) if gerr != nil { pool.assemblyResults.err = fmt.Errorf("could not generate block for %d: %v", pool.assemblyResults.roundStartedEvaluating, gerr) } else { @@ -773,11 +785,11 @@ func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIds map[transact asmStats.TransactionsLoopStartTime = int64(firstTxnGrpTime.Sub(pool.assemblyDeadline.Add(-pool.proposalAssemblyTime))) } - if !pool.assemblyResults.ok && pool.assemblyRound <= pool.pendingBlockEvaluator.Round() { + if evalRnd := pool.pendingBlockEvaluator.Round(); !pool.assemblyResults.ok && pool.assemblyRound <= evalRnd { pool.assemblyResults.ok = true pool.assemblyResults.assemblyCompletedOrAbandoned = true // this is not strictly needed, since the value would only get inspected by this go-routine, but we'll adjust it along with "ok" for consistency blockGenerationStarts := time.Now() - lvb, err := pool.pendingBlockEvaluator.GenerateBlock() + lvb, err := pool.pendingBlockEvaluator.GenerateBlock(pool.vac.VotingAccountsForRound(evalRnd)) if err != nil { pool.assemblyResults.err = fmt.Errorf("could not generate block for %d (end): %v", pool.assemblyResults.roundStartedEvaluating, err) } else { @@ -815,7 +827,7 @@ func (pool *TransactionPool) getStateProofStats(txib *transactions.SignedTxnInBl // AssembleBlock assembles a block for a given round, trying not to // take longer than deadline to finish. -func (pool *TransactionPool) AssembleBlock(round basics.Round, deadline time.Time) (assembled *ledgercore.ValidatedBlock, err error) { +func (pool *TransactionPool) AssembleBlock(round basics.Round, addrs []basics.Address, deadline time.Time) (assembled *ledgercore.UnfinishedBlock, err error) { var stats telemetryspec.AssembleBlockMetrics if pool.logAssembleStats { @@ -829,7 +841,7 @@ func (pool *TransactionPool) AssembleBlock(round basics.Round, deadline time.Tim dt := time.Since(start) stats.Nanoseconds = dt.Nanoseconds() - payset := assembled.Block().Payset + payset := assembled.UnfinishedBlock().Payset if len(payset) != 0 { totalFees := uint64(0) @@ -864,7 +876,7 @@ func (pool *TransactionPool) AssembleBlock(round basics.Round, deadline time.Tim } stats.AverageFee = totalFees / uint64(stats.IncludedCount) } - stats.StateProofNextRound = uint64(assembled.Block().StateProofTracking[protocol.StateProofBasic].StateProofNextRound) + stats.StateProofNextRound = uint64(assembled.UnfinishedBlock().StateProofTracking[protocol.StateProofBasic].StateProofNextRound) var details struct { Round uint64 } @@ -896,6 +908,8 @@ func (pool *TransactionPool) AssembleBlock(round basics.Round, deadline time.Tim pool.assemblyDeadline = deadline pool.assemblyRound = round + pool.assemblyPartAddresses = addrs + for time.Now().Before(deadline) && (!pool.assemblyResults.ok || pool.assemblyResults.roundStartedEvaluating != round) { condvar.TimedWait(&pool.assemblyCond, time.Until(deadline)) } @@ -958,7 +972,7 @@ func (pool *TransactionPool) AssembleBlock(round basics.Round, deadline time.Tim // assembleEmptyBlock construct a new block for the given round. Internally it's using the ledger database calls, so callers // need to be aware that it might take a while before it would return. -func (pool *TransactionPool) assembleEmptyBlock(round basics.Round) (assembled *ledgercore.ValidatedBlock, err error) { +func (pool *TransactionPool) assembleEmptyBlock(round basics.Round) (assembled *ledgercore.UnfinishedBlock, err error) { prevRound := round - 1 prev, err := pool.ledger.BlockHdr(prevRound) if err != nil { @@ -979,11 +993,11 @@ func (pool *TransactionPool) assembleEmptyBlock(round basics.Round) (assembled * err = fmt.Errorf("TransactionPool.assembleEmptyBlock: cannot start evaluator for %d: %w", round, err) return nil, err } - return blockEval.GenerateBlock() + return blockEval.GenerateBlock(pool.vac.VotingAccountsForRound(round)) } // AssembleDevModeBlock assemble a new block from the existing transaction pool. The pending evaluator is being -func (pool *TransactionPool) AssembleDevModeBlock() (assembled *ledgercore.ValidatedBlock, err error) { +func (pool *TransactionPool) AssembleDevModeBlock() (assembled *ledgercore.UnfinishedBlock, err error) { pool.mu.Lock() defer pool.mu.Unlock() @@ -992,6 +1006,6 @@ func (pool *TransactionPool) AssembleDevModeBlock() (assembled *ledgercore.Valid // The above was already pregenerating the entire block, // so there won't be any waiting on this call. - assembled, err = pool.AssembleBlock(pool.pendingBlockEvaluator.Round(), time.Now().Add(pool.proposalAssemblyTime)) + assembled, err = pool.AssembleBlock(pool.pendingBlockEvaluator.Round(), nil, time.Now().Add(pool.proposalAssemblyTime)) return } diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index ac72efe160..ec5f8a987a 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1789,7 +1789,7 @@ func (eval *BlockEvaluator) suspendAbsentAccounts() error { // After a call to GenerateBlock, the BlockEvaluator can still be used to // accept transactions. However, to guard against reuse, subsequent calls // to GenerateBlock on the same BlockEvaluator will fail. -func (eval *BlockEvaluator) GenerateBlock() (*ledgercore.ValidatedBlock, error) { +func (eval *BlockEvaluator) GenerateBlock(addrs []basics.Address) (*ledgercore.UnfinishedBlock, error) { if !eval.generate { logging.Base().Panicf("GenerateBlock() called but generate is false") } @@ -1803,7 +1803,17 @@ func (eval *BlockEvaluator) GenerateBlock() (*ledgercore.ValidatedBlock, error) return nil, err } - vb := ledgercore.MakeValidatedBlock(eval.block, eval.state.deltas()) + // look up set of participation accounts passed to GenerateBlock (possible proposers) + finalAccounts := make(map[basics.Address]ledgercore.AccountData, len(addrs)) + for i := range addrs { + acct, err := eval.state.lookup(addrs[i]) + if err != nil { + return nil, err + } + finalAccounts[addrs[i]] = acct + } + + vb := ledgercore.MakeUnfinishedBlock(eval.block, eval.state.deltas(), finalAccounts) eval.blockGenerated = true proto, ok := config.Consensus[eval.block.BlockHeader.CurrentProtocol] if !ok { diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 8d4962fd3b..9b41283b80 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -17,6 +17,7 @@ package ledgercore import ( + "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" ) @@ -45,3 +46,32 @@ func MakeValidatedBlock(blk bookkeeping.Block, delta StateDelta) ValidatedBlock delta: delta, } } + +// UnfinishedBlock represents a block that has been generated, but is +// not yet ready for proposing until FinishBlock is called. +type UnfinishedBlock struct { + finalAccounts map[basics.Address]AccountData // status of selected accounts at end of block + blk bookkeeping.Block + deltas StateDelta +} + +// MakeUnfinishedBlock creates an unfinished block. +func MakeUnfinishedBlock(blk bookkeeping.Block, deltas StateDelta, finalAccounts map[basics.Address]AccountData) UnfinishedBlock { + return UnfinishedBlock{ + finalAccounts: finalAccounts, + blk: blk, + deltas: deltas, + } +} + +// UnfinishedBlock returns the underlying Block. It should only be used for statistics and testing purposes, +// as the block is not yet finished and ready for proposing. +func (ub UnfinishedBlock) UnfinishedBlock() bookkeeping.Block { + return ub.blk +} + +// UnfinishedDeltas returns the unfinished deltas. It should only be used for statistics and testing purposes, +// as the block is not yet finished and ready for proposing. +func (ub UnfinishedBlock) UnfinishedDeltas() StateDelta { + return ub.deltas +} diff --git a/node/node.go b/node/node.go index 1c57ffdf8a..c2b31834f6 100644 --- a/node/node.go +++ b/node/node.go @@ -228,7 +228,7 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd return nil, err } - node.transactionPool = pools.MakeTransactionPool(node.ledger.Ledger, cfg, node.log) + node.transactionPool = pools.MakeTransactionPool(node.ledger.Ledger, cfg, node.log, node) blockListeners := []ledgercore.BlockListener{ node.transactionPool, @@ -1330,7 +1330,7 @@ func (ab proposableBlock) Block() bookkeeping.Block { // AssembleBlock implements Ledger.AssembleBlock. func (node *AlgorandFullNode) AssembleBlock(round basics.Round, addrs []basics.Address) (agreement.UnfinishedBlock, error) { deadline := time.Now().Add(node.config.ProposalAssemblyTime) - lvb, err := node.transactionPool.AssembleBlock(round, deadline) + lvb, err := node.transactionPool.AssembleBlock(round, addrs, deadline) if err != nil { if errors.Is(err, pools.ErrStaleBlockAssemblyRequest) { // convert specific error to one that would have special handling in the agreement code. @@ -1369,6 +1369,20 @@ func getOfflineClosedStatus(acctData basics.OnlineAccountData) int { return rval } +// VotingAccountsForRound provides a list of addresses that have participation keys valid for the given round. +// These accounts may not all be eligible to propose, but they are a superset of eligible proposers. +func (node *AlgorandFullNode) VotingAccountsForRound(round basics.Round) []basics.Address { + if node.devMode { + return []basics.Address{} + } + parts := node.accountManager.Keys(round) + accounts := make([]basics.Address, len(parts)) + for i, p := range parts { + accounts[i] = p.Account + } + return accounts +} + // VotingKeys implements the key manager's VotingKeys method, and provides additional validation with the ledger. // that allows us to load multiple overlapping keys for the same account, and filter these per-round basis. func (node *AlgorandFullNode) VotingKeys(votingRound, keysRound basics.Round) []account.ParticipationRecordForRound { From 43f74e58658dc883e8e3567efa177c7c7a40922f Mon Sep 17 00:00:00 2001 From: chris erway Date: Thu, 4 Apr 2024 15:10:15 -0400 Subject: [PATCH 087/117] wire up ledgercore.UnfinishedBlock to node AssembleBlock implementation, and update tests that call GenerateBlock --- data/pools/transactionPool.go | 24 +++++++------ ledger/eval/eval_test.go | 20 +++++------ ledger/eval_simple_test.go | 12 +++++-- ledger/evalbench_test.go | 12 +++++-- ledger/fullblock_perf_test.go | 6 ++-- ledger/ledger_perf_test.go | 10 +++--- ledger/ledgercore/validatedBlock.go | 17 +++++++++ ledger/simple_test.go | 6 +++- ledger/simulation/simulator.go | 7 ++-- ledger/simulation/testing/utils.go | 8 +++-- node/node.go | 53 +++++++++++++++-------------- tools/debug/transplanter/main.go | 8 +++-- 12 files changed, 114 insertions(+), 69 deletions(-) diff --git a/data/pools/transactionPool.go b/data/pools/transactionPool.go index c2533edb7a..52fa188d6d 100644 --- a/data/pools/transactionPool.go +++ b/data/pools/transactionPool.go @@ -70,10 +70,8 @@ type TransactionPool struct { assemblyCond sync.Cond assemblyDeadline time.Time // assemblyRound indicates which round number we're currently waiting for or waited for last. - assemblyRound basics.Round - // assemblyPartAddresses indicates which participating accounts we're currently using, or last used. - assemblyPartAddresses []basics.Address - assemblyResults poolAsmResults + assemblyRound basics.Round + assemblyResults poolAsmResults // pendingMu protects pendingTxGroups and pendingTxids pendingMu deadlock.RWMutex @@ -194,6 +192,13 @@ func (pool *TransactionPool) Reset() { pool.recomputeBlockEvaluator(nil, 0) } +func (pool *TransactionPool) getVotingAccountsForRound(rnd basics.Round) []basics.Address { + if pool.vac == nil { + return nil + } + return pool.vac.VotingAccountsForRound(rnd) +} + // NumExpired returns the number of transactions that expired at the // end of a round (only meaningful if cleanup has been called for that // round). @@ -635,7 +640,7 @@ func (pool *TransactionPool) addToPendingBlockEvaluatorOnce(txgroup []transactio } blockGenerationStarts := time.Now() - lvb, gerr := pool.pendingBlockEvaluator.GenerateBlock(pool.vac.VotingAccountsForRound(evalRnd)) + lvb, gerr := pool.pendingBlockEvaluator.GenerateBlock(pool.getVotingAccountsForRound(evalRnd)) if gerr != nil { pool.assemblyResults.err = fmt.Errorf("could not generate block for %d: %v", pool.assemblyResults.roundStartedEvaluating, gerr) } else { @@ -789,7 +794,7 @@ func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIds map[transact pool.assemblyResults.ok = true pool.assemblyResults.assemblyCompletedOrAbandoned = true // this is not strictly needed, since the value would only get inspected by this go-routine, but we'll adjust it along with "ok" for consistency blockGenerationStarts := time.Now() - lvb, err := pool.pendingBlockEvaluator.GenerateBlock(pool.vac.VotingAccountsForRound(evalRnd)) + lvb, err := pool.pendingBlockEvaluator.GenerateBlock(pool.getVotingAccountsForRound(evalRnd)) if err != nil { pool.assemblyResults.err = fmt.Errorf("could not generate block for %d (end): %v", pool.assemblyResults.roundStartedEvaluating, err) } else { @@ -827,7 +832,7 @@ func (pool *TransactionPool) getStateProofStats(txib *transactions.SignedTxnInBl // AssembleBlock assembles a block for a given round, trying not to // take longer than deadline to finish. -func (pool *TransactionPool) AssembleBlock(round basics.Round, addrs []basics.Address, deadline time.Time) (assembled *ledgercore.UnfinishedBlock, err error) { +func (pool *TransactionPool) AssembleBlock(round basics.Round, deadline time.Time) (assembled *ledgercore.UnfinishedBlock, err error) { var stats telemetryspec.AssembleBlockMetrics if pool.logAssembleStats { @@ -908,7 +913,6 @@ func (pool *TransactionPool) AssembleBlock(round basics.Round, addrs []basics.Ad pool.assemblyDeadline = deadline pool.assemblyRound = round - pool.assemblyPartAddresses = addrs for time.Now().Before(deadline) && (!pool.assemblyResults.ok || pool.assemblyResults.roundStartedEvaluating != round) { condvar.TimedWait(&pool.assemblyCond, time.Until(deadline)) @@ -993,7 +997,7 @@ func (pool *TransactionPool) assembleEmptyBlock(round basics.Round) (assembled * err = fmt.Errorf("TransactionPool.assembleEmptyBlock: cannot start evaluator for %d: %w", round, err) return nil, err } - return blockEval.GenerateBlock(pool.vac.VotingAccountsForRound(round)) + return blockEval.GenerateBlock(pool.getVotingAccountsForRound(round)) } // AssembleDevModeBlock assemble a new block from the existing transaction pool. The pending evaluator is being @@ -1006,6 +1010,6 @@ func (pool *TransactionPool) AssembleDevModeBlock() (assembled *ledgercore.Unfin // The above was already pregenerating the entire block, // so there won't be any waiting on this call. - assembled, err = pool.AssembleBlock(pool.pendingBlockEvaluator.Round(), nil, time.Now().Add(pool.proposalAssemblyTime)) + assembled, err = pool.AssembleBlock(pool.pendingBlockEvaluator.Round(), time.Now().Add(pool.proposalAssemblyTime)) return } diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index b45556aebf..ee3838dad4 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -985,15 +985,15 @@ func (ledger *evalTestLedger) nextBlock(t testing.TB) *BlockEvaluator { // endBlock completes the block being created, returns the ValidatedBlock for inspection func (ledger *evalTestLedger) endBlock(t testing.TB, eval *BlockEvaluator) *ledgercore.ValidatedBlock { - validatedBlock, err := eval.GenerateBlock() + unfinishedBlock, err := eval.GenerateBlock(nil) require.NoError(t, err) // fake agreement's setting of header fields so later validates work. seed := committee.Seed{} crypto.RandBytes(seed[:]) - *validatedBlock = ledgercore.MakeValidatedBlock(validatedBlock.Block().WithProposer(seed, testPoolAddr, true), validatedBlock.Delta()) - err = ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) + validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock().WithProposer(seed, testPoolAddr, true), unfinishedBlock.UnfinishedDeltas()) + err = ledger.AddValidatedBlock(validatedBlock, agreement.Certificate{}) require.NoError(t, err) - return validatedBlock + return &validatedBlock } // lookup gets the current accountdata for an address @@ -1208,11 +1208,11 @@ func TestEvalFunctionForExpiredAccounts(t *testing.T) { // Make sure we validate our block as well blkEval.validate = true - validatedBlock, err := blkEval.GenerateBlock() + unfinishedBlock, err := blkEval.GenerateBlock(nil) require.NoError(t, err) // fake agreement's setting of header fields so later validates work - *validatedBlock = ledgercore.MakeValidatedBlock(validatedBlock.Block().WithProposer(committee.Seed{}, testPoolAddr, true), validatedBlock.Delta()) + validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock().WithProposer(committee.Seed{}, testPoolAddr, true), unfinishedBlock.UnfinishedDeltas()) expired := false for _, acct := range validatedBlock.Block().ExpiredParticipationAccounts { @@ -1450,11 +1450,11 @@ func TestAbsenteeChecks(t *testing.T) { // Make sure we validate our block as well blkEval.validate = true - validatedBlock, err := blkEval.GenerateBlock() + unfinishedBlock, err := blkEval.GenerateBlock(nil) require.NoError(t, err) // fake agreement's setting of header fields so later validates work - *validatedBlock = ledgercore.MakeValidatedBlock(validatedBlock.Block().WithProposer(committee.Seed{}, testPoolAddr, true), validatedBlock.Delta()) + validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock().WithProposer(committee.Seed{}, testPoolAddr, true), unfinishedBlock.UnfinishedDeltas()) require.Zero(t, validatedBlock.Block().ExpiredParticipationAccounts) require.Contains(t, validatedBlock.Block().AbsentParticipationAccounts, addrs[0], addrs[0].String()) @@ -1604,10 +1604,10 @@ func TestExpiredAccountGeneration(t *testing.T) { // Make sure we validate our block as well eval.validate = true - validatedBlock, err := eval.GenerateBlock() + unfinishedBlock, err := eval.GenerateBlock(nil) require.NoError(t, err) - listOfExpiredAccounts := validatedBlock.Block().ParticipationUpdates.ExpiredParticipationAccounts + listOfExpiredAccounts := unfinishedBlock.UnfinishedBlock().ParticipationUpdates.ExpiredParticipationAccounts require.Len(t, listOfExpiredAccounts, 1) require.Equal(t, listOfExpiredAccounts[0], recvAddr) diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index 60c31419f9..cde5ea0bb2 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -34,6 +34,7 @@ import ( "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/txntest" + "github.com/algorand/go-algorand/ledger/ledgercore" ledgertesting "github.com/algorand/go-algorand/ledger/testing" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/protocol" @@ -190,15 +191,18 @@ func TestBlockEvaluator(t *testing.T) { err = eval.TestTransactionGroup(txgroup) require.Error(t, err) - validatedBlock, err := eval.GenerateBlock() + unfinishedBlock, err := eval.GenerateBlock(nil) // XXX not providing proposer addresses require.NoError(t, err) + // XXX not setting seed & proposer details with FinishBlock/WithProposer + validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) + accts := genesisInitState.Accounts bal0 := accts[addrs[0]] bal1 := accts[addrs[1]] bal2 := accts[addrs[2]] - l.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) + l.AddValidatedBlock(validatedBlock, agreement.Certificate{}) bal0new, _, _, err := l.LookupAccount(newBlock.Round(), addrs[0]) require.NoError(t, err) @@ -935,10 +939,12 @@ func TestRekeying(t *testing.T) { return err } } - validatedBlock, err := eval.GenerateBlock() + unfinishedBlock, err := eval.GenerateBlock(nil) // XXX not providing proposer addresses if err != nil { return err } + // XXX not setting seed & proposer details with FinishBlock/WithProposer + validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) backlogPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, nil) defer backlogPool.Shutdown() diff --git a/ledger/evalbench_test.go b/ledger/evalbench_test.go index 371cdad87d..e42771d009 100644 --- a/ledger/evalbench_test.go +++ b/ledger/evalbench_test.go @@ -514,6 +514,7 @@ func benchmarkPreparePaymentTransactionsTesting(b *testing.B, numTxns int, txnSo if len(initSignedTxns) > 0 { var numBlocks uint64 = 0 + var unfinishedBlock *ledgercore.UnfinishedBlock var validatedBlock *ledgercore.ValidatedBlock // there are might more transactions than MaxTxnBytesPerBlock allows @@ -522,8 +523,11 @@ func benchmarkPreparePaymentTransactionsTesting(b *testing.B, numTxns int, txnSo err := bev.Transaction(stxn, transactions.ApplyData{}) require.NoError(b, err) if maxTxnPerBlock > 0 && i%maxTxnPerBlock == 0 || i == len(initSignedTxns)-1 { - validatedBlock, err = bev.GenerateBlock() + unfinishedBlock, err = bev.GenerateBlock(nil) require.NoError(b, err) + // XXX not setting seed & proposer details with FinishBlock/WithProposer + vb := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) + validatedBlock = &vb for _, l := range []*Ledger{l, l2} { err = l.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) require.NoError(b, err) @@ -562,12 +566,14 @@ func benchmarkPreparePaymentTransactionsTesting(b *testing.B, numTxns int, txnSo require.NoError(b, err) } - validatedBlock, err := bev.GenerateBlock() + unfinishedBlock, err := bev.GenerateBlock(nil) require.NoError(b, err) + // XXX not setting seed & proposer details with FinishBlock/WithProposer + validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) blockBuildDone := time.Now() blockBuildTime := blockBuildDone.Sub(setupDone) b.ReportMetric(float64(blockBuildTime)/float64(numTxns), "ns/block_build_tx") - return validatedBlock + return &validatedBlock } diff --git a/ledger/fullblock_perf_test.go b/ledger/fullblock_perf_test.go index c45963cb1c..b0fef304f3 100644 --- a/ledger/fullblock_perf_test.go +++ b/ledger/fullblock_perf_test.go @@ -314,12 +314,12 @@ func addTransaction(bc *benchConfig, stxn transactions.SignedTxn) uint64 { } func addBlock(bc *benchConfig) { - vblk, err := bc.eval.GenerateBlock() + vblk, err := bc.eval.GenerateBlock(nil) cert := agreement.Certificate{} require.NoError(bc.b, err) - bc.blocks = append(bc.blocks, vblk.Block()) + bc.blocks = append(bc.blocks, vblk.UnfinishedBlock()) - err = bc.l0.AddBlock(vblk.Block(), cert) + err = bc.l0.AddBlock(vblk.UnfinishedBlock(), cert) require.NoError(bc.b, err) _, last := bc.l0.LatestCommitted() diff --git a/ledger/ledger_perf_test.go b/ledger/ledger_perf_test.go index f160838ab1..b34877aed5 100644 --- a/ledger/ledger_perf_test.go +++ b/ledger/ledger_perf_test.go @@ -293,24 +293,24 @@ func benchmarkFullBlocks(params testParams, b *testing.B) { } } - lvb, err := eval.GenerateBlock() + lvb, err := eval.GenerateBlock(nil) require.NoError(b, err) // If this is the app creation block, add to both ledgers if i == 1 { - err = l0.AddBlock(lvb.Block(), cert) + err = l0.AddBlock(lvb.UnfinishedBlock(), cert) require.NoError(b, err) - err = l1.AddBlock(lvb.Block(), cert) + err = l1.AddBlock(lvb.UnfinishedBlock(), cert) require.NoError(b, err) continue } // For all other blocks, add just to the first ledger, and stash // away to be replayed in the second ledger while running timer - err = l0.AddBlock(lvb.Block(), cert) + err = l0.AddBlock(lvb.UnfinishedBlock(), cert) require.NoError(b, err) - blocks = append(blocks, lvb.Block()) + blocks = append(blocks, lvb.UnfinishedBlock()) } b.Logf("built %d blocks, each with %d txns", numBlocks, txPerBlock) diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 9b41283b80..a9b81fb183 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -19,6 +19,7 @@ package ledgercore import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" + "github.com/algorand/go-algorand/data/committee" ) // ValidatedBlock represents the result of a block validation. It can @@ -75,3 +76,19 @@ func (ub UnfinishedBlock) UnfinishedBlock() bookkeeping.Block { func (ub UnfinishedBlock) UnfinishedDeltas() StateDelta { return ub.deltas } + +// ContainsAddress returns true if the balance data about the given address is present in the unfinished block. +func (ub UnfinishedBlock) ContainsAddress(addr basics.Address) bool { + _, ok := ub.finalAccounts[addr] + return ok +} + +// FinishBlock completes the block and returns a proposable block. +func (ub UnfinishedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) bookkeeping.Block { + return ub.blk.WithProposer(s, proposer, eligible) +} + +// Round returns the round of the block. +func (ub UnfinishedBlock) Round() basics.Round { + return ub.blk.Round() +} diff --git a/ledger/simple_test.go b/ledger/simple_test.go index e2e9f524b8..9cd1c5a692 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -146,9 +146,13 @@ func txgroup(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, txns ...*t // inspection. Proposer is optional - if unset, blocks will be finished with // ZeroAddress proposer. func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer ...basics.Address) *ledgercore.ValidatedBlock { - gvb, err := eval.GenerateBlock() + ub, err := eval.GenerateBlock(nil) require.NoError(t, err) + // XXX not setting seed & proposer details with FinishBlock/WithProposer + validatedBlock := ledgercore.MakeValidatedBlock(ub.UnfinishedBlock(), ub.UnfinishedDeltas()) + gvb := &validatedBlock + // Making the proposer the feesink unless specified causes less disruption // to existing tests. (Because block payouts don't change balances.) prp := gvb.Block().BlockHeader.FeeSink diff --git a/ledger/simulation/simulator.go b/ledger/simulation/simulator.go index 0a33e5f12b..db019d6763 100644 --- a/ledger/simulation/simulator.go +++ b/ledger/simulation/simulator.go @@ -197,12 +197,15 @@ func (s Simulator) evaluate(hdr bookkeeping.BlockHeader, stxns []transactions.Si } // Finally, process any pending end-of-block state changes. - vb, err := eval.GenerateBlock() + ub, err := eval.GenerateBlock(nil) // XXX not fetching proposer addresses if err != nil { return nil, err } - return vb, nil + // XXX not setting seed & proposer details with FinishBlock/WithProposer + vb := ledgercore.MakeValidatedBlock(ub.UnfinishedBlock(), ub.UnfinishedDeltas()) + + return &vb, nil } func (s Simulator) simulateWithTracer(txgroup []transactions.SignedTxn, tracer logic.EvalTracer, overrides ResultEvalOverrides) (*ledgercore.ValidatedBlock, error) { diff --git a/ledger/simulation/testing/utils.go b/ledger/simulation/testing/utils.go index ae43fe72fc..50facac59c 100644 --- a/ledger/simulation/testing/utils.go +++ b/ledger/simulation/testing/utils.go @@ -104,11 +104,13 @@ func (env *Environment) nextBlock() *eval.BlockEvaluator { // endBlock completes the block being created, returns the ValidatedBlock for inspection func (env *Environment) endBlock(evaluator *eval.BlockEvaluator) *ledgercore.ValidatedBlock { env.t.Helper() - validatedBlock, err := evaluator.GenerateBlock() + unfinishedBlock, err := evaluator.GenerateBlock(nil) // XXX not providing proposer addresses require.NoError(env.t, err) - err = env.Ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) + // XXX not setting seed & proposer details with FinishBlock/WithProposer + validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) + err = env.Ledger.AddValidatedBlock(validatedBlock, agreement.Certificate{}) require.NoError(env.t, err) - return validatedBlock + return &validatedBlock } // Txn creates and executes a new block with the given transaction and returns its ApplyData diff --git a/node/node.go b/node/node.go index c2b31834f6..9d6c5c47bf 100644 --- a/node/node.go +++ b/node/node.go @@ -455,20 +455,20 @@ func (node *AlgorandFullNode) Ledger() *data.Ledger { // writeDevmodeBlock generates a new block for a devmode, and write it to the ledger. func (node *AlgorandFullNode) writeDevmodeBlock() (err error) { - var vb *ledgercore.ValidatedBlock + var vb *ledgercore.UnfinishedBlock vb, err = node.transactionPool.AssembleDevModeBlock() if err != nil || vb == nil { return } - // Make a new validated block. - prevRound := vb.Block().Round() - 1 + // Make a new validated block from this UnfinishedBlock. + prevRound := vb.UnfinishedBlock().Round() - 1 prev, err := node.ledger.BlockHdr(prevRound) if err != nil { return err } - blk := vb.Block() + blk := vb.UnfinishedBlock() // Set block timestamp based on offset, if set. // Make sure block timestamp is not greater than MaxInt64. @@ -476,11 +476,10 @@ func (node *AlgorandFullNode) writeDevmodeBlock() (err error) { blk.TimeStamp = prev.TimeStamp + *node.timestampOffset } blk.BlockHeader.Seed = committee.Seed(prev.Hash()) - vb2 := ledgercore.MakeValidatedBlock(blk, vb.Delta()) - vb = &vb2 + vb2 := ledgercore.MakeValidatedBlock(blk, vb.UnfinishedDeltas()) // add the newly generated block to the ledger - err = node.ledger.AddValidatedBlock(*vb, agreement.Certificate{Round: vb.Block().Round()}) + err = node.ledger.AddValidatedBlock(vb2, agreement.Certificate{Round: vb2.Block().Round()}) return err } @@ -1295,15 +1294,9 @@ type validatedBlock struct { vb *ledgercore.ValidatedBlock } -// Block satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) Block() bookkeeping.Block { - blk := vb.vb.Block() - return blk -} - // unfinishedBlock satisfies agreement.UnfinishedBlock type unfinishedBlock struct { - blk bookkeeping.Block + blk *ledgercore.UnfinishedBlock } // proposableBlock satisfies agreement.ProposableBlock @@ -1311,26 +1304,24 @@ type proposableBlock struct { blk bookkeeping.Block } -// FinishBlock satisfies the agreement.UnfinishedBlock interface. -func (ab unfinishedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.ProposableBlock { - nb := ab.blk.WithProposer(s, proposer, eligible) - return proposableBlock{blk: nb} -} +// Block satisfies the agreement.ValidatedBlock interface. +func (vb validatedBlock) Block() bookkeeping.Block { return vb.vb.Block() } // Round satisfies the agreement.UnfinishedBlock interface. -func (ab unfinishedBlock) Round() basics.Round { - return ab.blk.Round() -} +func (ub unfinishedBlock) Round() basics.Round { return ub.blk.Round() } // Block satisfies the agreement.ProposableBlock interface. -func (ab proposableBlock) Block() bookkeeping.Block { - return ab.blk +func (ab proposableBlock) Block() bookkeeping.Block { return ab.blk } + +// FinishBlock satisfies the agreement.UnfinishedBlock interface. +func (ub unfinishedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.ProposableBlock { + return proposableBlock{blk: ub.blk.FinishBlock(s, proposer, eligible)} } // AssembleBlock implements Ledger.AssembleBlock. func (node *AlgorandFullNode) AssembleBlock(round basics.Round, addrs []basics.Address) (agreement.UnfinishedBlock, error) { deadline := time.Now().Add(node.config.ProposalAssemblyTime) - lvb, err := node.transactionPool.AssembleBlock(round, addrs, deadline) + ub, err := node.transactionPool.AssembleBlock(round, deadline) if err != nil { if errors.Is(err, pools.ErrStaleBlockAssemblyRequest) { // convert specific error to one that would have special handling in the agreement code. @@ -1349,7 +1340,17 @@ func (node *AlgorandFullNode) AssembleBlock(round basics.Round, addrs []basics.A } return nil, err } - return unfinishedBlock{blk: lvb.Block()}, nil + + // ensure UnfinishedBlock contains provided addresses + for _, addr := range addrs { + if !ub.ContainsAddress(addr) { + // this should not happen: VotingKeys() and VotingAccountsForRound() should be in sync + node.log.Errorf("AlgorandFullNode.AssembleBlock: could not generate a proposal for round %d, proposer %s not in UnfinishedBlock", round, addr) + return nil, agreement.ErrAssembleBlockRoundStale + } + } + + return unfinishedBlock{blk: ub}, nil } // getOfflineClosedStatus will return an int with the appropriate bit(s) set if it is offline and/or online diff --git a/tools/debug/transplanter/main.go b/tools/debug/transplanter/main.go index 0d2880412d..1a41504c99 100644 --- a/tools/debug/transplanter/main.go +++ b/tools/debug/transplanter/main.go @@ -421,7 +421,7 @@ func main() { txCount := 0 totalTxCount := 0 blockCount := 0 - pool := pools.MakeTransactionPool(l, cfg, log) + pool := pools.MakeTransactionPool(l, cfg, log, nil) hdr, err := l.BlockHdr(l.Latest()) if err != nil { fmt.Fprintf(os.Stderr, "Cannot get latest block header: %v", err) @@ -455,13 +455,15 @@ func main() { if txCount >= *blockSize { deadline := time.Now().Add(100 * time.Millisecond) - vb, err := pool.AssembleBlock(nextRound, deadline) + ab, err := pool.AssembleBlock(nextRound, deadline) if err != nil { fmt.Fprintf(os.Stderr, "ERR: Cannot assemble block %d: %v\n", nextRound, err) break } + // make validated block without calling FinishBlock + vb := ledgercore.MakeValidatedBlock(ab.UnfinishedBlock(), ab.UnfinishedDeltas()) - err = l.AddValidatedBlock(*vb, agreement.Certificate{}) + err = l.AddValidatedBlock(vb, agreement.Certificate{}) if err != nil { fmt.Fprintf(os.Stderr, "ERR: Cannot add block %d: %v\n", nextRound, err) break From 0e3034af575a540939dfaa543beab2ef51e8b341 Mon Sep 17 00:00:00 2001 From: chris erway Date: Thu, 4 Apr 2024 16:42:56 -0400 Subject: [PATCH 088/117] fix tests & generator --- agreement/agreementtest/simulate_test.go | 8 ++++++-- tools/block-generator/generator/generator_ledger.go | 13 +++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index 3911696c57..f528ffca35 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -79,7 +79,11 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.UnfinishedBlock { +func (b testValidatedBlock) Round() basics.Round { + return b.Inside.Round() +} + +func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.ProposableBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -98,7 +102,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.UnfinishedBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round, addrs []basics.Address) (agreement.UnfinishedBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } diff --git a/tools/block-generator/generator/generator_ledger.go b/tools/block-generator/generator/generator_ledger.go index f841b574f3..472ce3f0d5 100644 --- a/tools/block-generator/generator/generator_ledger.go +++ b/tools/block-generator/generator/generator_ledger.go @@ -178,21 +178,22 @@ func (g *generator) evaluateBlock(hdr bookkeeping.BlockHeader, txGroups [][]txn. } for i, txGroup := range txGroups { for { - err := eval.TransactionGroup(txGroup) - if err != nil { - if strings.Contains(err.Error(), "database table is locked") { + txErr := eval.TransactionGroup(txGroup) + if txErr != nil { + if strings.Contains(txErr.Error(), "database table is locked") { time.Sleep(waitDelay) commitWaitTime += waitDelay // sometimes the database is locked, so we retry continue } - return nil, 0, 0, fmt.Errorf("could not evaluate transaction group %d: %w", i, err) + return nil, 0, 0, fmt.Errorf("could not evaluate transaction group %d: %w", i, txErr) } break } } - lvb, err := eval.GenerateBlock() - return lvb, eval.TestingTxnCounter(), commitWaitTime, err + ub, err := eval.GenerateBlock(nil) + lvb := ledgercore.MakeValidatedBlock(ub.UnfinishedBlock(), ub.UnfinishedDeltas()) + return &lvb, eval.TestingTxnCounter(), commitWaitTime, err } func countInners(ad txn.ApplyData) int { From 5dbe3c1650b0849a4e36a5419730b069d9b1e7e9 Mon Sep 17 00:00:00 2001 From: chris erway Date: Thu, 4 Apr 2024 16:52:20 -0400 Subject: [PATCH 089/117] update remaining broken tests to use GenerateBlock and MakeValidatedBlock --- data/pools/transactionPool.go | 5 +-- data/pools/transactionPool_test.go | 72 ++++++++++++++++-------------- data/txHandler_test.go | 2 +- node/assemble_test.go | 8 ++-- 4 files changed, 45 insertions(+), 42 deletions(-) diff --git a/data/pools/transactionPool.go b/data/pools/transactionPool.go index 52fa188d6d..7545acf134 100644 --- a/data/pools/transactionPool.go +++ b/data/pools/transactionPool.go @@ -108,10 +108,7 @@ type BlockEvaluator interface { ResetTxnBytes() } -type UnfinishedBlock interface { - FinishBlock(proposer basics.Address) bookkeeping.Block -} - +// VotingAccountChecker provides a list of possible participating account addresses valid for a given round. type VotingAccountChecker interface { VotingAccountsForRound(basics.Round) []basics.Address } diff --git a/data/pools/transactionPool_test.go b/data/pools/transactionPool_test.go index 4229b0510d..72c757aa2d 100644 --- a/data/pools/transactionPool_test.go +++ b/data/pools/transactionPool_test.go @@ -168,7 +168,7 @@ func TestMinBalanceOK(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) // sender goes below min tx := transactions.Transaction{ @@ -211,7 +211,7 @@ func TestSenderGoesBelowMinBalance(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) // sender goes below min tx := transactions.Transaction{ @@ -255,7 +255,7 @@ func TestSenderGoesBelowMinBalanceDueToAssets(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) assetTx := transactions.Transaction{ Type: protocol.AssetConfigTx, @@ -326,7 +326,7 @@ func TestCloseAccount(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) // sender goes below min closeTx := transactions.Transaction{ @@ -389,7 +389,7 @@ func TestCloseAccountWhileTxIsPending(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) // sender goes below min tx := transactions.Transaction{ @@ -453,7 +453,7 @@ func TestClosingAccountBelowMinBalance(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) // sender goes below min closeTx := transactions.Transaction{ @@ -497,7 +497,7 @@ func TestRecipientGoesBelowMinBalance(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) // sender goes below min tx := transactions.Transaction{ @@ -538,7 +538,7 @@ func TestRememberForget(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(mockLedger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(mockLedger, cfg, logging.Base(), nil) eval := newBlockEvaluator(t, mockLedger) @@ -574,10 +574,11 @@ func TestRememberForget(t *testing.T) { numberOfTxns := numOfAccounts*numOfAccounts - numOfAccounts require.Len(t, pending, numberOfTxns) - blk, err := eval.GenerateBlock() + ufblk, err := eval.GenerateBlock(nil) require.NoError(t, err) - err = mockLedger.AddValidatedBlock(*blk, agreement.Certificate{}) + blk := ledgercore.MakeValidatedBlock(ufblk.UnfinishedBlock(), ufblk.UnfinishedDeltas()) + err = mockLedger.AddValidatedBlock(blk, agreement.Certificate{}) require.NoError(t, err) transactionPool.OnNewBlock(blk.Block(), ledgercore.StateDelta{}) @@ -605,7 +606,7 @@ func TestCleanUp(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(mockLedger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(mockLedger, cfg, logging.Base(), nil) issuedTransactions := 0 for i, sender := range addresses { @@ -637,10 +638,11 @@ func TestCleanUp(t *testing.T) { for mockLedger.Latest() < 6 { eval := newBlockEvaluator(t, mockLedger) - blk, err := eval.GenerateBlock() + ufblk, err := eval.GenerateBlock(nil) require.NoError(t, err) - err = mockLedger.AddValidatedBlock(*blk, agreement.Certificate{}) + blk := ledgercore.MakeValidatedBlock(ufblk.UnfinishedBlock(), ufblk.UnfinishedDeltas()) + err = mockLedger.AddValidatedBlock(blk, agreement.Certificate{}) require.NoError(t, err) transactionPool.OnNewBlock(blk.Block(), ledgercore.StateDelta{}) @@ -653,10 +655,11 @@ func TestCleanUp(t *testing.T) { for mockLedger.Latest() < 6+basics.Round(expiredHistory*proto.MaxTxnLife) { eval := newBlockEvaluator(t, mockLedger) - blk, err := eval.GenerateBlock() + ufblk, err := eval.GenerateBlock(nil) require.NoError(t, err) - err = mockLedger.AddValidatedBlock(*blk, agreement.Certificate{}) + blk := ledgercore.MakeValidatedBlock(ufblk.UnfinishedBlock(), ufblk.UnfinishedDeltas()) + err = mockLedger.AddValidatedBlock(blk, agreement.Certificate{}) require.NoError(t, err) transactionPool.OnNewBlock(blk.Block(), ledgercore.StateDelta{}) @@ -684,7 +687,7 @@ func TestFixOverflowOnNewBlock(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(mockLedger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(mockLedger, cfg, logging.Base(), nil) overSpender := addresses[0] var overSpenderAmount uint64 @@ -748,10 +751,11 @@ func TestFixOverflowOnNewBlock(t *testing.T) { require.NoError(t, err) // simulate this transaction was applied - block, err := blockEval.GenerateBlock() + ufblk, err := blockEval.GenerateBlock(nil) require.NoError(t, err) - err = mockLedger.AddValidatedBlock(*block, agreement.Certificate{}) + block := ledgercore.MakeValidatedBlock(ufblk.UnfinishedBlock(), ufblk.UnfinishedDeltas()) + err = mockLedger.AddValidatedBlock(block, agreement.Certificate{}) require.NoError(t, err) transactionPool.OnNewBlock(block.Block(), ledgercore.StateDelta{}) @@ -781,7 +785,7 @@ func TestOverspender(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) receiver := addresses[1] tx := transactions.Transaction{ @@ -843,7 +847,7 @@ func TestRemove(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) sender := addresses[0] receiver := addresses[1] @@ -900,7 +904,7 @@ func TestLogicSigOK(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) // sender goes below min tx := transactions.Transaction{ @@ -946,7 +950,7 @@ func TestTransactionPool_CurrentFeePerByte(t *testing.T) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = testPoolSize * 15 cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(l, cfg, logging.Base()) + transactionPool := MakeTransactionPool(l, cfg, logging.Base(), nil) for i, sender := range addresses { for j := 0; j < testPoolSize*15/len(addresses); j++ { @@ -997,7 +1001,7 @@ func BenchmarkTransactionPoolRememberOne(b *testing.B) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = b.N cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) signedTransactions := make([]transactions.SignedTxn, 0, b.N) for i, sender := range addresses { for j := 0; j < b.N/len(addresses); j++ { @@ -1029,7 +1033,7 @@ func BenchmarkTransactionPoolRememberOne(b *testing.B) { b.StopTimer() b.ResetTimer() ledger = makeMockLedger(b, initAccFixed(addresses, 1<<32)) - transactionPool = MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool = MakeTransactionPool(ledger, cfg, logging.Base(), nil) b.StartTimer() for _, signedTx := range signedTransactions { @@ -1058,7 +1062,7 @@ func BenchmarkTransactionPoolPending(b *testing.B) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = benchPoolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) var block bookkeeping.Block block.Payset = make(transactions.Payset, 0) @@ -1136,7 +1140,7 @@ func BenchmarkTransactionPoolRecompute(b *testing.B) { cfg.EnableProcessBlockStats = false setupPool := func() (*TransactionPool, map[transactions.Txid]ledgercore.IncludedTransactions, uint) { - transactionPool := MakeTransactionPool(l, cfg, logging.Base()) + transactionPool := MakeTransactionPool(l, cfg, logging.Base(), nil) // make some transactions var signedTransactions []transactions.SignedTxn @@ -1227,7 +1231,7 @@ func BenchmarkTransactionPoolSteadyState(b *testing.B) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = poolSize cfg.EnableProcessBlockStats = false - transactionPool := MakeTransactionPool(l, cfg, logging.Base()) + transactionPool := MakeTransactionPool(l, cfg, logging.Base(), nil) var signedTransactions []transactions.SignedTxn for i := 0; i < b.N; i++ { @@ -1290,10 +1294,11 @@ func BenchmarkTransactionPoolSteadyState(b *testing.B) { ledgerTxnQueue = ledgerTxnQueue[1:] } - blk, err := eval.GenerateBlock() + ufblk, err := eval.GenerateBlock(nil) require.NoError(b, err) - err = l.AddValidatedBlock(*blk, agreement.Certificate{}) + blk := ledgercore.MakeValidatedBlock(ufblk.UnfinishedBlock(), ufblk.UnfinishedDeltas()) + err = l.AddValidatedBlock(blk, agreement.Certificate{}) require.NoError(b, err) transactionPool.OnNewBlock(blk.Block(), ledgercore.StateDelta{}) @@ -1324,7 +1329,7 @@ func TestTxPoolSizeLimits(t *testing.T) { ledger := makeMockLedger(t, initAcc(map[basics.Address]uint64{firstAddress: proto.MinBalance + 2*proto.MinTxnFee*uint64(cfg.TxPoolSize)})) - transactionPool := MakeTransactionPool(ledger, cfg, logging.Base()) + transactionPool := MakeTransactionPool(ledger, cfg, logging.Base(), nil) receiver := addresses[1] @@ -1439,7 +1444,7 @@ func TestStateProofLogging(t *testing.T) { // Set the ledger and the transaction pool mockLedger := makeMockLedger(t, initAccounts) - transactionPool := MakeTransactionPool(mockLedger, cfg, logger) + transactionPool := MakeTransactionPool(mockLedger, cfg, logger, nil) transactionPool.logAssembleStats = true // Set the first round block @@ -1458,10 +1463,11 @@ func TestStateProofLogging(t *testing.T) { // Simulate the blocks up to round 512 without any transactions for i := 1; true; i++ { - blk, err := transactionPool.AssembleBlock(basics.Round(i), time.Time{}) + ufblk, err := transactionPool.AssembleBlock(basics.Round(i), time.Time{}) require.NoError(t, err) - err = mockLedger.AddValidatedBlock(*blk, agreement.Certificate{}) + blk := ledgercore.MakeValidatedBlock(ufblk.UnfinishedBlock(), ufblk.UnfinishedDeltas()) + err = mockLedger.AddValidatedBlock(blk, agreement.Certificate{}) require.NoError(t, err) // Move to the next round diff --git a/data/txHandler_test.go b/data/txHandler_test.go index 3f567554bf..5eb40741ee 100644 --- a/data/txHandler_test.go +++ b/data/txHandler_test.go @@ -820,7 +820,7 @@ func makeTestTxHandlerOrphanedWithContext(ctx context.Context, backlogSize int, } func makeTestTxHandler(dl *Ledger, cfg config.Local) (*TxHandler, error) { - tp := pools.MakeTransactionPool(dl.Ledger, cfg, logging.Base()) + tp := pools.MakeTransactionPool(dl.Ledger, cfg, logging.Base(), nil) backlogPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, nil) opts := TxHandlerOpts{ tp, backlogPool, dl, &mocks.MockNetwork{}, "", crypto.Digest{}, cfg, diff --git a/node/assemble_test.go b/node/assemble_test.go index 6954d99400..51ff7d8edc 100644 --- a/node/assemble_test.go +++ b/node/assemble_test.go @@ -99,7 +99,7 @@ func BenchmarkAssembleBlock(b *testing.B) { cfg := config.GetDefaultLocal() cfg.TxPoolSize = txPoolSize cfg.EnableAssembleStats = false - tp := pools.MakeTransactionPool(l.Ledger, cfg, logging.Base()) + tp := pools.MakeTransactionPool(l.Ledger, cfg, logging.Base(), nil) errcount := 0 okcount := 0 var worstTxID transactions.Txid @@ -220,13 +220,13 @@ func TestAssembleBlockTransactionPoolBehind(t *testing.T) { cfg = config.GetDefaultLocal() cfg.TxPoolSize = txPoolSize cfg.EnableAssembleStats = false - tp := pools.MakeTransactionPool(l.Ledger, cfg, log) + tp := pools.MakeTransactionPool(l.Ledger, cfg, log, nil) next := l.NextRound() deadline := time.Now().Add(time.Second) block, err := tp.AssembleBlock(next, deadline) require.NoError(t, err) - require.NoError(t, ledger.AddBlock(block.Block(), agreement.Certificate{Round: next})) + require.NoError(t, ledger.AddBlock(block.UnfinishedBlock(), agreement.Certificate{Round: next})) expectingLog = true @@ -234,7 +234,7 @@ func TestAssembleBlockTransactionPoolBehind(t *testing.T) { deadline = time.Now().Add(time.Second) block, err = tp.AssembleBlock(next, deadline) require.NoError(t, err) - require.NoError(t, ledger.AddBlock(block.Block(), agreement.Certificate{Round: next})) + require.NoError(t, ledger.AddBlock(block.UnfinishedBlock(), agreement.Certificate{Round: next})) require.False(t, expectingLog) } From b7c7fe017ac15cbf33328d0b31cabad82e4bddea Mon Sep 17 00:00:00 2001 From: chris erway Date: Fri, 5 Apr 2024 09:18:21 -0400 Subject: [PATCH 090/117] move MakeAccountManager to be above MakeTransactionPool --- node/node.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/node/node.go b/node/node.go index 9d6c5c47bf..abbf298f51 100644 --- a/node/node.go +++ b/node/node.go @@ -228,6 +228,21 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd return nil, err } + registry, err := ensureParticipationDB(node.genesisDirs.ColdGenesisDir, node.log) + if err != nil { + log.Errorf("unable to initialize the participation registry database: %v", err) + return nil, err + } + node.accountManager = data.MakeAccountManager(log, registry) + + err = node.loadParticipationKeys() + if err != nil { + log.Errorf("Cannot load participation keys: %v", err) + return nil, err + } + + node.oldKeyDeletionNotify = make(chan struct{}, 1) + node.transactionPool = pools.MakeTransactionPool(node.ledger.Ledger, cfg, node.log, node) blockListeners := []ledgercore.BlockListener{ @@ -300,21 +315,6 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd node.catchupService = catchup.MakeService(node.log, node.config, p2pNode, node.ledger, node.catchupBlockAuth, agreementLedger.UnmatchedPendingCertificates, node.lowPriorityCryptoVerificationPool) node.txPoolSyncerService = rpcs.MakeTxSyncer(node.transactionPool, node.net, node.txHandler.SolicitedTxHandler(), time.Duration(cfg.TxSyncIntervalSeconds)*time.Second, time.Duration(cfg.TxSyncTimeoutSeconds)*time.Second, cfg.TxSyncServeResponseSize) - registry, err := ensureParticipationDB(node.genesisDirs.ColdGenesisDir, node.log) - if err != nil { - log.Errorf("unable to initialize the participation registry database: %v", err) - return nil, err - } - node.accountManager = data.MakeAccountManager(log, registry) - - err = node.loadParticipationKeys() - if err != nil { - log.Errorf("Cannot load participation keys: %v", err) - return nil, err - } - - node.oldKeyDeletionNotify = make(chan struct{}, 1) - catchpointCatchupState, err := node.ledger.GetCatchpointCatchupState(context.Background()) if err != nil { log.Errorf("unable to determine catchpoint catchup state: %v", err) From 8893086ce660622f825733fce5c70cd829931fed Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 9 Apr 2024 15:43:03 -0400 Subject: [PATCH 091/117] CR changes --- test/e2e-go/features/incentives/mining_test.go | 12 ++++++++---- test/e2e-go/features/incentives/suspension_test.go | 11 ++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 79e9039ded..92c887c9f6 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -194,17 +194,21 @@ func TestBasicPayouts(t *testing.T) { a.NoError(err) fmt.Printf(" proposer %v has %d after proposing round %d\n", block.Proposer(), data.MicroAlgos.Raw, status.LastRound) + pdata, err := c15.AccountData(block.Proposer().String()) + a.NoError(err) feesink := block.BlockHeader.FeeSink - // show all the node's belief about feesink, for debugging the payout - // effects appearing locally but not elsewhere (or vice versa) - for i, c := range []libgoal.Client{relay, c01, c15} { + fdata, err := c15.AccountData(feesink.String()) + a.NoError(err) + + for _, c := range []libgoal.Client{c15, c01, relay} { data, err = c.AccountData(block.Proposer().String()) a.NoError(err) a.Equal(block.Round(), data.LastProposed) + a.Equal(pdata, data) data, err = c.AccountData(feesink.String()) a.NoError(err) - fmt.Printf(" feesink %d has %d at round %d\n", i, data.MicroAlgos.Raw, status.LastRound) + a.Equal(fdata, data) } a.LessOrEqual(100000, int(data.MicroAlgos.Raw)) // won't go below minfee if data.MicroAlgos.Raw == 100000 { diff --git a/test/e2e-go/features/incentives/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go index 04072cd0e3..0766744b09 100644 --- a/test/e2e-go/features/incentives/suspension_test.go +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -50,6 +50,7 @@ func TestBasicSuspension(t *testing.T) { // Run for 55 rounds, which is enough for 20% node to be suspended, but not 10% // check neither suspended, send a tx from 20% to 10%, only 20% gets suspended // TODO once we have heartbeats: bring them back up, make sure 20% gets back online + const suspend20 = 55 var fixture fixtures.RestClientFixture // Make the seed lookback shorter, so the test runs faster @@ -98,7 +99,7 @@ func TestBasicSuspension(t *testing.T) { a.NoError(err) // Advance 55 rounds - err = fixture.WaitForRoundWithTimeout(afterStop.LastRound + 55) + err = fixture.WaitForRoundWithTimeout(afterStop.LastRound + suspend20) a.NoError(err) // n20 is still online after 55 rounds of absence (the node is off, but the @@ -112,8 +113,8 @@ func TestBasicSuspension(t *testing.T) { // pay n10 & n20, so both could be noticed richAccount, err := fixture.GetRichestAccount() a.NoError(err) - fixture.SendMoneyAndWait(afterStop.LastRound+55, 5, 1000, richAccount.Address, account10.Address, "") - fixture.SendMoneyAndWait(afterStop.LastRound+55, 5, 1000, richAccount.Address, account20.Address, "") + fixture.SendMoneyAndWait(afterStop.LastRound+suspend20, 5, 1000, richAccount.Address, account10.Address, "") + fixture.SendMoneyAndWait(afterStop.LastRound+suspend20, 5, 1000, richAccount.Address, account20.Address, "") // n20's account is now offline, but has voting key material (suspended) account, err = c10.AccountData(account20.Address) @@ -139,7 +140,7 @@ func TestBasicSuspension(t *testing.T) { stat, err := lg.Status() a.NoError(err) // Waiting for this round should show it has started and caught up. - stat, err = lg.WaitForRound(afterStop.LastRound + 55) + stat, err = lg.WaitForRound(afterStop.LastRound + suspend20) a.NoError(err) // Proceed until a round is proposed by n20. (Stop at 50 rounds, that's more likely a bug than luck) @@ -155,7 +156,7 @@ func TestBasicSuspension(t *testing.T) { break } } - // n20's account is back online, with same voting material + // account20 is back online, with same voting material account, err = c10.AccountData(account20.Address) a.NoError(err) a.Equal(basics.Online, account.Status) From 906c8aae79aa18367e20502e27473d79b5e3e7ff Mon Sep 17 00:00:00 2001 From: chris erway Date: Tue, 9 Apr 2024 16:12:37 -0400 Subject: [PATCH 092/117] rename VotingAccountChecker => Supplier --- data/pools/transactionPool.go | 8 ++++---- ledger/ledgercore/validatedBlock.go | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/data/pools/transactionPool.go b/data/pools/transactionPool.go index 7545acf134..31ad805d68 100644 --- a/data/pools/transactionPool.go +++ b/data/pools/transactionPool.go @@ -87,7 +87,7 @@ type TransactionPool struct { rememberedTxids map[transactions.Txid]transactions.SignedTxn log logging.Logger - vac VotingAccountChecker + vac VotingAccountSupplier // proposalAssemblyTime is the ProposalAssemblyTime configured for this node. proposalAssemblyTime time.Duration @@ -108,13 +108,13 @@ type BlockEvaluator interface { ResetTxnBytes() } -// VotingAccountChecker provides a list of possible participating account addresses valid for a given round. -type VotingAccountChecker interface { +// VotingAccountSupplier provides a list of possible participating account addresses valid for a given round. +type VotingAccountSupplier interface { VotingAccountsForRound(basics.Round) []basics.Address } // MakeTransactionPool makes a transaction pool. -func MakeTransactionPool(ledger *ledger.Ledger, cfg config.Local, log logging.Logger, vac VotingAccountChecker) *TransactionPool { +func MakeTransactionPool(ledger *ledger.Ledger, cfg config.Local, log logging.Logger, vac VotingAccountSupplier) *TransactionPool { if cfg.TxPoolExponentialIncreaseFactor < 1 { cfg.TxPoolExponentialIncreaseFactor = 1 } diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index a9b81fb183..a4c45b6d83 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -85,6 +85,7 @@ func (ub UnfinishedBlock) ContainsAddress(addr basics.Address) bool { // FinishBlock completes the block and returns a proposable block. func (ub UnfinishedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) bookkeeping.Block { + // XXX here is where we could look up the proposer's balance return ub.blk.WithProposer(s, proposer, eligible) } From d8ad692ddeb2fb9b6aaa94d97f279e10fe1336df Mon Sep 17 00:00:00 2001 From: chris erway Date: Tue, 9 Apr 2024 16:15:47 -0400 Subject: [PATCH 093/117] rename ue, ve, to ub, pb --- agreement/common_test.go | 8 ++++---- agreement/player_permutation_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/agreement/common_test.go b/agreement/common_test.go index d4f3a55541..3a3b0a6594 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -533,12 +533,12 @@ func (v *voteMakerHelper) MakeRandomProposalValue() *proposalValue { func (v *voteMakerHelper) MakeRandomProposalPayload(t *testing.T, r round) (*proposal, *proposalValue) { f := testBlockFactory{Owner: 1} - ue, err := f.AssembleBlock(r, nil) + ub, err := f.AssembleBlock(r, nil) require.NoError(t, err) - ve := ue.FinishBlock(committee.Seed{}, basics.Address{}, false) + pb := ub.FinishBlock(committee.Seed{}, basics.Address{}, false) var payload unauthenticatedProposal - payload.Block = ve.Block() + payload.Block = pb.Block() payload.SeedProof = randomVRFProof() propVal := proposalValue{ @@ -546,7 +546,7 @@ func (v *voteMakerHelper) MakeRandomProposalPayload(t *testing.T, r round) (*pro EncodingDigest: crypto.HashObj(payload), } - return &proposal{unauthenticatedProposal: payload, ve: ve}, &propVal + return &proposal{unauthenticatedProposal: payload, ve: pb}, &propVal } // make a vote for a fixed proposal value diff --git a/agreement/player_permutation_test.go b/agreement/player_permutation_test.go index da6fe902a1..f697e4db25 100644 --- a/agreement/player_permutation_test.go +++ b/agreement/player_permutation_test.go @@ -32,14 +32,14 @@ import ( func makeRandomProposalPayload(r round) *proposal { f := testBlockFactory{Owner: 1} - ue, _ := f.AssembleBlock(r, nil) - ve := ue.FinishBlock(committee.Seed{}, basics.Address{}, false) + ub, _ := f.AssembleBlock(r, nil) + pb := ub.FinishBlock(committee.Seed{}, basics.Address{}, false) var payload unauthenticatedProposal - payload.Block = ve.Block() + payload.Block = pb.Block() payload.SeedProof = crypto.VRFProof{} - return &proposal{unauthenticatedProposal: payload, ve: ve} + return &proposal{unauthenticatedProposal: payload, ve: pb} } var errTestVerifyFailed = makeSerErrStr("test error") From 65510961cf6b1703e144f4445d484214f9f324f5 Mon Sep 17 00:00:00 2001 From: chris erway Date: Tue, 9 Apr 2024 16:40:03 -0400 Subject: [PATCH 094/117] don't assign ProposableBlock to ValidatedBlock in agreement tests --- agreement/common_test.go | 2 +- agreement/player_permutation_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agreement/common_test.go b/agreement/common_test.go index 3a3b0a6594..f011bc26a6 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -546,7 +546,7 @@ func (v *voteMakerHelper) MakeRandomProposalPayload(t *testing.T, r round) (*pro EncodingDigest: crypto.HashObj(payload), } - return &proposal{unauthenticatedProposal: payload, ve: pb}, &propVal + return &proposal{unauthenticatedProposal: payload}, &propVal } // make a vote for a fixed proposal value diff --git a/agreement/player_permutation_test.go b/agreement/player_permutation_test.go index f697e4db25..59d92e015f 100644 --- a/agreement/player_permutation_test.go +++ b/agreement/player_permutation_test.go @@ -39,7 +39,7 @@ func makeRandomProposalPayload(r round) *proposal { payload.Block = pb.Block() payload.SeedProof = crypto.VRFProof{} - return &proposal{unauthenticatedProposal: payload, ve: pb} + return &proposal{unauthenticatedProposal: payload} } var errTestVerifyFailed = makeSerErrStr("test error") From 72957a030c57a5354ecac766fa84ca9d83571d09 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 10 Apr 2024 16:59:42 -0400 Subject: [PATCH 095/117] Don't pay closed proposers. --- ledger/eval/eval.go | 39 +++++------ ledger/ledgercore/validatedBlock.go | 5 +- .../e2e-go/features/incentives/mining_test.go | 69 +++++++++++++++++-- 3 files changed, 88 insertions(+), 25 deletions(-) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index ec5f8a987a..1011df823e 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1436,33 +1436,32 @@ func (eval *BlockEvaluator) endOfBlock() error { payout := eval.block.ProposerPayout() // The proposer won't be present yet when generating a block if !proposer.IsZero() { - // We don't propagate the error here, we simply declare that an - // illegal payout is not made. This protects us from stalling if - // there is ever an error in which the generation code thinks the - // payout will be legal, but it turns out not to be. That would be - // a programming error in algod, but not worth stalling over. if !payout.IsZero() { - err2 := eval.state.Move(eval.block.FeeSink, proposer, payout, nil, nil) - if err2 != nil { - logging.Base().Warnf("Unable to payout %d to %v: %s", - payout, proposer, err2) + err := eval.state.Move(eval.block.FeeSink, proposer, payout, nil, nil) + if err != nil { + return err } } - prp, err2 := eval.state.Get(proposer, false) - if err2 != nil { - return err2 + prp, err := eval.state.Get(proposer, false) + if err != nil { + return err + } + // Record the LastProposed round, except in the unlikely case that a + // proposer has closed their account, but is still voting (it takes + // 320 rounds to be effective). Recording would prevent GC. + if !prp.IsZero() { + prp.LastProposed = eval.Round() } - prp.LastProposed = eval.Round() - // An account could propose, even while suspended, because of the 320 - // round lookback. Doing so is evidence the account is - // back. Unsuspend. But the account will remain not IncentiveElgible - // until they keyreg again with the extra fee. + // An account could propose, even while suspended, because of the + // 320 round lookback. Doing so is evidence the account is + // operational. Unsuspend. But the account will remain not + // IncentiveElgible until they keyreg again with the extra fee. if prp.Suspended() { prp.Status = basics.Online } - err2 = eval.state.Put(proposer, prp) - if err2 != nil { - return err2 + err = eval.state.Put(proposer, prp) + if err != nil { + return err } } } diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index a4c45b6d83..c29c16cf7f 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -85,7 +85,10 @@ func (ub UnfinishedBlock) ContainsAddress(addr basics.Address) bool { // FinishBlock completes the block and returns a proposable block. func (ub UnfinishedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) bookkeeping.Block { - // XXX here is where we could look up the proposer's balance + propData, ok := ub.finalAccounts[proposer] + if !ok || propData.MicroAlgos.IsZero() { + eligible = false + } return ub.blk.WithProposer(s, proposer, eligible) } diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 92c887c9f6..93bb8ddef1 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -172,16 +172,21 @@ func TestBasicPayouts(t *testing.T) { // Now that we've proven incentives get paid, let's drain the FeeSink and // ensure it happens gracefully. Have account15 go offline so that (after - // 32 rounds) only account01 is proposing. It is eligible and will drain the - // fee sink. + // 32 rounds) only account01 (who is eligible) is proposing, so drainage + // will happen soon after. offline, err := c15.MakeUnsignedGoOfflineTx(account15.Address, 0, 0, 1000, [32]byte{}) a.NoError(err) wh, err := c15.GetUnencryptedWalletHandle() a.NoError(err) - _, err = c15.SignAndBroadcastTransaction(wh, nil, offline) + offlineTxId, err := c15.SignAndBroadcastTransaction(wh, nil, offline) a.NoError(err) + offTxn, err := fixture.WaitForConfirmedTxn(uint64(offline.LastValid), offlineTxId) + a.NoError(err) + + fmt.Printf(" c15 (%s) will be truly offline (not proposing) after round %d\n", account15.Address, *offTxn.ConfirmedRound+32) + var feesink basics.Address for i := 0; i < 100; i++ { status, err := client.Status() a.NoError(err) @@ -196,7 +201,7 @@ func TestBasicPayouts(t *testing.T) { pdata, err := c15.AccountData(block.Proposer().String()) a.NoError(err) - feesink := block.BlockHeader.FeeSink + feesink = block.BlockHeader.FeeSink fdata, err := c15.AccountData(feesink.String()) a.NoError(err) @@ -218,6 +223,62 @@ func TestBasicPayouts(t *testing.T) { err = fixture.WaitForRoundWithTimeout(status.LastRound + 1) a.NoError(err) } + // maybe it got drained before c15 stops proposing. wait. + err = fixture.WaitForRoundWithTimeout(*offTxn.ConfirmedRound + 32) + a.NoError(err) + + // put 20 algos back into the feesink, show it pays out again + txn, err = c01.SendPaymentFromUnencryptedWallet(account01.Address, feesink.String(), 1000, 50_000_000, nil) + a.NoError(err) + refill, err := fixture.WaitForConfirmedTxn(uint64(txn.LastValid), txn.ID().String()) + fmt.Printf("refilled fee sink in %d\n", *refill.ConfirmedRound) + a.NoError(err) + block, err := client.BookkeepingBlock(*refill.ConfirmedRound) + a.NoError(err) + // 01 is the only one online, so it proposed the block + require.Equal(t, account01.Address, block.Proposer().String()) + // and therefore feesink is already down to ~40 + data, err := relay.AccountData(feesink.String()) + a.NoError(err) + a.Less(int(data.MicroAlgos.Raw), 41_000_000) + a.Greater(int(data.MicroAlgos.Raw), 39_000_000) + + // Closeout c01. This is pretty weird, it means nobody will be online. But + // that will take 32 rounds. We will stop the test before then, we just + // want to show that c01 does not get paid if it has closed. + wh, err = c01.GetUnencryptedWalletHandle() + a.NoError(err) + junk := basics.Address{0x01, 0x01}.String() + txn, err = c01.SendPaymentFromWallet(wh, nil, account01.Address, junk, 1000, 0, nil, junk /* close to */, 0, 0) + a.NoError(err) + close, err := fixture.WaitForConfirmedTxn(uint64(txn.LastValid), txn.ID().String()) + a.NoError(err) + fmt.Printf("closed c01 in %d\n", *close.ConfirmedRound) + block, err = client.BookkeepingBlock(*close.ConfirmedRound) + a.NoError(err) + // 01 is the only one online, so it proposed the block + require.Equal(t, account01.Address, block.Proposer().String()) + + // The feesink got was 0.1A, and got 50A in refill.ConfirmedRound. c01 + // closed out in close.ConfirmedRound. So the feesink should have about: + expected := 100_000 + 1_000_000*(50-10*(*close.ConfirmedRound-*refill.ConfirmedRound)) + + // account is gone anyway (it didn't get paid) + data, err = relay.AccountData(account01.Address) + a.Zero(data, "%+v", data) + + data, err = relay.AccountData(feesink.String()) + a.NoError(err) + // Don't want to bother dealing with the exact fees paid in/out. + a.Less(data.MicroAlgos.Raw, expected+5000) + a.Greater(data.MicroAlgos.Raw, expected-5000) + + // Lest one be concerned about that cavalier attitude, wait for a few more + // rounds, and show feesink is unchanged. + a.NoError(fixture.WaitForRoundWithTimeout(*close.ConfirmedRound + 5)) + after, err := relay.AccountData(feesink.String()) + a.NoError(err) + a.Equal(data.MicroAlgos, after.MicroAlgos) } func rekeyreg(f *fixtures.RestClientFixture, a *require.Assertions, client libgoal.Client, address string) basics.AccountData { From 569a48ea586d7b3a6f4f247d01c90393a501b745 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 10 Apr 2024 17:53:57 -0400 Subject: [PATCH 096/117] linter --- test/e2e-go/features/incentives/mining_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index 93bb8ddef1..fab188a10e 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -179,9 +179,9 @@ func TestBasicPayouts(t *testing.T) { a.NoError(err) wh, err := c15.GetUnencryptedWalletHandle() a.NoError(err) - offlineTxId, err := c15.SignAndBroadcastTransaction(wh, nil, offline) + offlineTxID, err := c15.SignAndBroadcastTransaction(wh, nil, offline) a.NoError(err) - offTxn, err := fixture.WaitForConfirmedTxn(uint64(offline.LastValid), offlineTxId) + offTxn, err := fixture.WaitForConfirmedTxn(uint64(offline.LastValid), offlineTxID) a.NoError(err) fmt.Printf(" c15 (%s) will be truly offline (not proposing) after round %d\n", account15.Address, *offTxn.ConfirmedRound+32) From 6bff63958455c12b5f10c48acd87b09ca672f47c Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 11 Apr 2024 11:59:09 -0400 Subject: [PATCH 097/117] Prevent payouts to closed account --- ledger/eval/eval.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 1011df823e..2bbef59ffc 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1378,6 +1378,16 @@ func (eval *BlockEvaluator) endOfBlock() error { if proposer.IsZero() { return fmt.Errorf("proposer missing when payouts enabled") } + // a closed account cannot get payout + if !payout.IsZero() { + prp, err := eval.state.Get(proposer, false) + if err != nil { + return err + } + if prp.IsZero() { + return fmt.Errorf("proposer %v is closed but expects payout %d", proposer, payout.Raw) + } + } } } else { if !eval.block.Proposer().IsZero() { From aceef41c0f37411f5441a903771f1a14beec7fd7 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 11 Apr 2024 14:59:58 -0400 Subject: [PATCH 098/117] Fixup for rule that proposer must exist to be paid --- ledger/eval_simple_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index cde5ea0bb2..db38d0bf47 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -594,13 +594,18 @@ func TestAbsenteeChallenges(t *testing.T) { dl := NewDoubleLedger(t, genBalances, cv, cfg) defer dl.Close() + // This address ends up being used as a proposer, because that's how we + // jam a specific seed into the block to control the challenge. + // Therefore, it must be an existing account. + seedAndProp := basics.Address{0xaa} + // We'll generate a challenge for accounts that start with 0xaa. propguy := basics.Address{0xaa, 0xaa, 0xaa} // Will propose during the challenge window regguy := basics.Address{0xaa, 0xbb, 0xbb} // Will re-reg during the challenge window badguy := basics.Address{0xaa, 0x11, 0x11} // Will ignore the challenge // Fund them all and have them go online. That makes them eligible to be challenged - for i, guy := range []basics.Address{propguy, regguy, badguy} { + for i, guy := range []basics.Address{seedAndProp, propguy, regguy, badguy} { dl.txns(&txntest.Txn{ Type: "pay", Sender: addrs[0], @@ -629,7 +634,7 @@ func TestAbsenteeChallenges(t *testing.T) { } // make the BlockSeed start with 0xa in the challenge round dl.beginBlock() - dl.endBlock(basics.Address{0xaa}) // This becomes the seed, which is used for the challenge + dl.endBlock(seedAndProp) // This becomes the seed, which is used for the challenge for vb := dl.fullBlock(); vb.Block().Round() < 1200; vb = dl.fullBlock() { // advance through first grace period From 6540c05493ee26465a5038a6b62ac9b2ceefceda Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 11 Apr 2024 15:47:38 -0400 Subject: [PATCH 099/117] Insert paranoid debug check to investivate failure in CI --- .../features/incentives/suspension_test.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/e2e-go/features/incentives/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go index 0766744b09..e2dacf0a9d 100644 --- a/test/e2e-go/features/incentives/suspension_test.go +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -63,6 +63,7 @@ func TestBasicSuspension(t *testing.T) { accounts, err := fixture.GetNodeWalletsSortedByBalance(c) a.NoError(err) a.Len(accounts, 1) + fmt.Printf("Client %s is %v\n", name, accounts[0].Address) return c, accounts[0] } @@ -150,18 +151,22 @@ func TestBasicSuspension(t *testing.T) { // Once n20 proposes, break out early if fixture.VerifyBlockProposed(account20.Address, 1) { + fmt.Printf("account20 proposed at round %d\n", r) // wait one extra round, because changes are processed in block n+1. err = fixture.WaitForRoundWithTimeout(r + 1) a.NoError(err) break } } - // account20 is back online, with same voting material - account, err = c10.AccountData(account20.Address) - a.NoError(err) - a.Equal(basics.Online, account.Status) - a.Greater(account.LastProposed, stat.LastRound) + // paranoia. see mining_test.go for more details. + r := require.New(t) + for i, c := range []libgoal.Client{c10, c20} { + account, err = c.AccountData(account20.Address) + a.NoError(err) + r.Equal(basics.Online, account.Status, i) + r.Greater(account.LastProposed, stat.LastRound, i) - a.Equal(voteID, account.VoteID) - a.False(account.IncentiveEligible) + r.Equal(voteID, account.VoteID, i) + r.False(account.IncentiveEligible, i) + } } From 73fff7be711aad280f78a0f21c69b4c29180eea5 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 12 Apr 2024 09:09:33 -0400 Subject: [PATCH 100/117] very old typo fix --- test/scripts/e2e_subs/htlc-teal-test.sh | 2 +- test/scripts/e2e_subs/periodic-teal-test.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/scripts/e2e_subs/htlc-teal-test.sh b/test/scripts/e2e_subs/htlc-teal-test.sh index 9dfd62a574..d2b70c533d 100755 --- a/test/scripts/e2e_subs/htlc-teal-test.sh +++ b/test/scripts/e2e_subs/htlc-teal-test.sh @@ -47,7 +47,7 @@ ${gcmd} clerk send --fee=1000 --from-program ${TEMPDIR}/atomic.teal -a=0 -t=${ZE # Check balance BALANCEB=$(${gcmd} account balance -a ${ACCOUNTB} | awk '{ print $1 }') if [ $BALANCEB -ne 9999000 ]; then - date '+htlc-teal-test FAIL wanted balance=9999000 but got ${BALANCEB} %Y%m%d_%H%M%S' + date "+htlc-teal-test FAIL wanted balance=9999000 but got ${BALANCEB} %Y%m%d_%H%M%S" false fi diff --git a/test/scripts/e2e_subs/periodic-teal-test.sh b/test/scripts/e2e_subs/periodic-teal-test.sh index 7ec6512ac1..6e95e7658d 100755 --- a/test/scripts/e2e_subs/periodic-teal-test.sh +++ b/test/scripts/e2e_subs/periodic-teal-test.sh @@ -48,7 +48,7 @@ done BALANCEB=$(${gcmd} account balance -a ${ACCOUNTB}|awk '{ print $1 }') if [ $BALANCEB -ne 300000 ]; then - date '+periodic-teal-test FAIL wanted balance=3000000 but got ${BALANCEB} %Y%m%d_%H%M%S' + date "+periodic-teal-test FAIL wanted balance=3000000 but got ${BALANCEB} %Y%m%d_%H%M%S" false fi From 82e244dd18078414b6e638a837d5f9a5d5039999 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 12 Apr 2024 09:58:09 -0400 Subject: [PATCH 101/117] Pavel CR --- ledger/eval/eval.go | 13 ++++++------- node/node.go | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index 2bbef59ffc..cba58b86fe 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1349,15 +1349,11 @@ func (eval *BlockEvaluator) endOfBlock() error { return fmt.Errorf("txn count wrong: %d != %d", eval.block.TxnCounter, expectedTxnCount) } - var expectedFeesCollected basics.MicroAlgos if eval.proto.Payouts.Enabled { - expectedFeesCollected = eval.state.feesCollected - } - if eval.block.FeesCollected != expectedFeesCollected { - return fmt.Errorf("fees collected wrong: %v != %v", eval.block.FeesCollected, expectedFeesCollected) - } + if eval.block.FeesCollected != eval.state.feesCollected { + return fmt.Errorf("fees collected wrong: %v != %v", eval.block.FeesCollected, eval.state.feesCollected) + } - if eval.proto.Payouts.Enabled { // agreement will check that the payout is zero if the proposer is // ineligible, but we must check that it is correct if non-zero. We // allow it to be too low. A proposer can be algruistic. @@ -1390,6 +1386,9 @@ func (eval *BlockEvaluator) endOfBlock() error { } } } else { + if !eval.block.FeesCollected.IsZero() { + return fmt.Errorf("feesCollected %d present when payouts disabled", eval.block.FeesCollected.Raw) + } if !eval.block.Proposer().IsZero() { return fmt.Errorf("proposer %v present when payouts disabled", eval.block.Proposer()) } diff --git a/node/node.go b/node/node.go index abbf298f51..a608d4a5a1 100644 --- a/node/node.go +++ b/node/node.go @@ -462,7 +462,7 @@ func (node *AlgorandFullNode) writeDevmodeBlock() (err error) { } // Make a new validated block from this UnfinishedBlock. - prevRound := vb.UnfinishedBlock().Round() - 1 + prevRound := vb.Round() - 1 prev, err := node.ledger.BlockHdr(prevRound) if err != nil { return err From c27f7b3f6fff1660da6b5e40ded6fa69bdbc2efe Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 12 Apr 2024 10:12:50 -0400 Subject: [PATCH 102/117] explain the paranoia differently after interface changes --- test/e2e-go/features/incentives/mining_test.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/mining_test.go index fab188a10e..7abb9a0871 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/mining_test.go @@ -96,14 +96,12 @@ func TestBasicPayouts(t *testing.T) { a.EqualValues(bonus1, block.Bonus.Raw) // all nodes agree the proposer proposed. The paranoia here is - // justified. Block incentives are the first time we're making changes - // to the Delta in the "second" evaluation of the block. That is, the - // payment and LastProposed change happen only if evaluating a block - // that `agreement` has already added to. An easy bug to have is an - // optimization that avoids this re-evaluation in the algod that - // proposed the block. We had such an optimization, and it would cause - // failures here. The fix is throwing away the ValidatedBlock in - // proposalForBlock() after makeProposal. + // justified. Block incentives are computed in two stages. A little bit + // of extra work is done when agreement "Finishes" the block. An easy + // bug to have using the block the Deltas() computed on the block + // without the changes that come after agreement runs. We had such an + // optimization, and it would cause failures here. Interface changes + // made since they should make such a problem impossible, but... for i, c := range []libgoal.Client{c15, c01, relay} { fmt.Printf("checking block %v\n", block.Round()) data, err := c.AccountData(block.Proposer().String()) From 58a70e66f06be014cc6d59ab5e77fa6524b2a0e5 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 12 Apr 2024 12:52:28 -0400 Subject: [PATCH 103/117] Tidy up endOfBlock() --- ledger/eval/eval.go | 184 ++++++++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 77 deletions(-) diff --git a/ledger/eval/eval.go b/ledger/eval/eval.go index cba58b86fe..6599dd5918 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -1349,52 +1349,8 @@ func (eval *BlockEvaluator) endOfBlock() error { return fmt.Errorf("txn count wrong: %d != %d", eval.block.TxnCounter, expectedTxnCount) } - if eval.proto.Payouts.Enabled { - if eval.block.FeesCollected != eval.state.feesCollected { - return fmt.Errorf("fees collected wrong: %v != %v", eval.block.FeesCollected, eval.state.feesCollected) - } - - // agreement will check that the payout is zero if the proposer is - // ineligible, but we must check that it is correct if non-zero. We - // allow it to be too low. A proposer can be algruistic. - expectedPayout, err := eval.proposerPayout() - if err != nil { - return err - } - payout := eval.block.ProposerPayout() - if payout.Raw > expectedPayout.Raw { - return fmt.Errorf("proposal wants %d payout, %d is allowed", payout.Raw, expectedPayout.Raw) - } - // agreement will check that the proposer is correct (we can't because - // we don't see the bundle), but agreement allows the proposer to be set - // even if Payouts is not enabled (and unset any time). So make sure - // it's set only if it should be. - if !eval.generate { // if generating, proposer is set later by agreement - proposer := eval.block.Proposer() - if proposer.IsZero() { - return fmt.Errorf("proposer missing when payouts enabled") - } - // a closed account cannot get payout - if !payout.IsZero() { - prp, err := eval.state.Get(proposer, false) - if err != nil { - return err - } - if prp.IsZero() { - return fmt.Errorf("proposer %v is closed but expects payout %d", proposer, payout.Raw) - } - } - } - } else { - if !eval.block.FeesCollected.IsZero() { - return fmt.Errorf("feesCollected %d present when payouts disabled", eval.block.FeesCollected.Raw) - } - if !eval.block.Proposer().IsZero() { - return fmt.Errorf("proposer %v present when payouts disabled", eval.block.Proposer()) - } - if !eval.block.ProposerPayout().IsZero() { - return fmt.Errorf("payout %d present when payouts disabled", eval.block.ProposerPayout().Raw) - } + if err := eval.validateForPayouts(); err != nil { + return err } expectedVoters, expectedVotersWeight, err2 := eval.stateProofVotersAndTotal() @@ -1439,50 +1395,124 @@ func (eval *BlockEvaluator) endOfBlock() error { } } - // Try to pay the proposer. - { + if err := eval.performPayout(); err != nil { + return err + } + + if err := eval.recordProposal(); err != nil { + return err + } + + if err := eval.state.CalculateTotals(); err != nil { + return err + } + + if eval.Tracer != nil { + eval.Tracer.AfterBlock(&eval.block.BlockHeader) + } + + return nil +} + +func (eval *BlockEvaluator) validateForPayouts() error { + if !eval.proto.Payouts.Enabled { + if !eval.block.FeesCollected.IsZero() { + return fmt.Errorf("feesCollected %d present when payouts disabled", eval.block.FeesCollected.Raw) + } + if !eval.block.Proposer().IsZero() { + return fmt.Errorf("proposer %v present when payouts disabled", eval.block.Proposer()) + } + if !eval.block.ProposerPayout().IsZero() { + return fmt.Errorf("payout %d present when payouts disabled", eval.block.ProposerPayout().Raw) + } + return nil + } + + if eval.block.FeesCollected != eval.state.feesCollected { + return fmt.Errorf("fees collected wrong: %v != %v", eval.block.FeesCollected, eval.state.feesCollected) + } + + // agreement will check that the payout is zero if the proposer is + // ineligible, but we must check that it is correct if non-zero. We + // allow it to be too low. A proposer can be algruistic. + expectedPayout, err := eval.proposerPayout() + if err != nil { + return err + } + payout := eval.block.ProposerPayout() + if payout.Raw > expectedPayout.Raw { + return fmt.Errorf("proposal wants %d payout, %d is allowed", payout.Raw, expectedPayout.Raw) + } + + // agreement will check that the proposer is correct (we can't because + // we don't see the bundle), but agreement allows the proposer to be set + // even if Payouts is not enabled (and unset any time). So make sure + // it's set only if it should be. + if !eval.generate { // if generating, proposer is set later by agreement proposer := eval.block.Proposer() - payout := eval.block.ProposerPayout() - // The proposer won't be present yet when generating a block - if !proposer.IsZero() { - if !payout.IsZero() { - err := eval.state.Move(eval.block.FeeSink, proposer, payout, nil, nil) - if err != nil { - return err - } - } + if proposer.IsZero() { + return fmt.Errorf("proposer missing when payouts enabled") + } + // a closed account cannot get payout + if !payout.IsZero() { prp, err := eval.state.Get(proposer, false) if err != nil { return err } - // Record the LastProposed round, except in the unlikely case that a - // proposer has closed their account, but is still voting (it takes - // 320 rounds to be effective). Recording would prevent GC. - if !prp.IsZero() { - prp.LastProposed = eval.Round() - } - // An account could propose, even while suspended, because of the - // 320 round lookback. Doing so is evidence the account is - // operational. Unsuspend. But the account will remain not - // IncentiveElgible until they keyreg again with the extra fee. - if prp.Suspended() { - prp.Status = basics.Online - } - err = eval.state.Put(proposer, prp) - if err != nil { - return err + if prp.IsZero() { + return fmt.Errorf("proposer %v is closed but expects payout %d", proposer, payout.Raw) } } } + return nil +} - if err := eval.state.CalculateTotals(); err != nil { - return err +func (eval *BlockEvaluator) performPayout() error { + proposer := eval.block.Proposer() + // The proposer won't be present yet when generating a block, nor before enabled + if proposer.IsZero() { + return nil } - if eval.Tracer != nil { - eval.Tracer.AfterBlock(&eval.block.BlockHeader) + payout := eval.block.ProposerPayout() + + if !payout.IsZero() { + err := eval.state.Move(eval.block.FeeSink, proposer, payout, nil, nil) + if err != nil { + return err + } } + return nil +} +func (eval *BlockEvaluator) recordProposal() error { + proposer := eval.block.Proposer() + // The proposer won't be present yet when generating a block, nor before enabled + if proposer.IsZero() { + return nil + } + + prp, err := eval.state.Get(proposer, false) + if err != nil { + return err + } + // Record the LastProposed round, except in the unlikely case that a + // proposer has closed their account, but is still voting (it takes + // 320 rounds to be effective). Recording would prevent GC. + if !prp.IsZero() { + prp.LastProposed = eval.Round() + } + // An account could propose, even while suspended, because of the + // 320 round lookback. Doing so is evidence the account is + // operational. Unsuspend. But the account will remain not + // IncentiveElgible until they keyreg again with the extra fee. + if prp.Suspended() { + prp.Status = basics.Online + } + err = eval.state.Put(proposer, prp) + if err != nil { + return err + } return nil } From a46890d3a4f3d67bc2d0c13b69972051748d58cf Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 12 Apr 2024 14:49:31 -0400 Subject: [PATCH 104/117] Reduce "shim types", introduce agreement.Block agreement.Block is similar to what was agreement.ProposableBlock, but rather than being an interface it is a concrete type. Therefore, there can be no confusion: A validated block is not an agreement.Block just because it has a Block() method. Instead, FinalizeBlock methods explicit copy their internal Block() objects into agreement.Block objects during finalization. This PR also eliminates node.validatedBlock as *ledgercore.ValidatedBlock implements agreement.ValidatedBlock already, no wrapper is needed. Blocks remain as immutable as before. --- agreement/abstractions.go | 17 +++++++---------- agreement/agreementtest/simulate_test.go | 4 ++-- agreement/common_test.go | 6 +++--- agreement/fuzzer/ledger_test.go | 4 ++-- agreement/player_permutation_test.go | 3 ++- agreement/proposal.go | 4 ++-- node/impls.go | 5 +++-- node/node.go | 20 ++------------------ 8 files changed, 23 insertions(+), 40 deletions(-) diff --git a/agreement/abstractions.go b/agreement/abstractions.go index e093e944fc..50ee6c10a3 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -84,23 +84,20 @@ type BlockFactory interface { // An UnfinishedBlock represents a Block produced by a BlockFactory // and must be finalized before being proposed by agreement. type UnfinishedBlock interface { - // WithSeed creates a copy of this UnfinishedBlock with its - // cryptographically random seed set to the given value. + // FinishBlock creates a Proposaable block, having set the cryptographically + // random seed and payout related fields. // // Calls to Seed() or to Digest() on the copy's Block must // reflect the value of the new seed. - FinishBlock(seed committee.Seed, proposer basics.Address, eligible bool) ProposableBlock + FinishBlock(seed committee.Seed, proposer basics.Address, eligible bool) Block Round() basics.Round } -// An ProposableBlock represents a Block produced by a BlockFactory, -// that was later finalized by providing the seed and the proposer, -// and can now be proposed by agreement. -type ProposableBlock interface { - // Block returns the underlying block that has been assembled. - Block() bookkeeping.Block -} +// A Block (in agreement) represents an UnfinishedBlock produced by a +// BlockFactory, that was later finalized by providing the seed and the +// proposer, and can now be proposed by agreement. +type Block bookkeeping.Block // A Ledger represents the sequence of Entries agreed upon by the protocol. // The Ledger consists of two parts: a LedgerReader and a LedgerWriter, which diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index f528ffca35..6c071ed8a8 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -83,13 +83,13 @@ func (b testValidatedBlock) Round() basics.Round { return b.Inside.Round() } -func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.ProposableBlock { +func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.Block { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { b.Inside.BlockHeader.ProposerPayout = basics.MicroAlgos{} } - return b + return agreement.Block(b.Inside) } type testBlockValidator struct{} diff --git a/agreement/common_test.go b/agreement/common_test.go index f011bc26a6..ca8983705e 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -169,13 +169,13 @@ func (b testValidatedBlock) Round() basics.Round { return b.Inside.Round() } -func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) ProposableBlock { +func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) Block { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { b.Inside.BlockHeader.ProposerPayout = basics.MicroAlgos{} } - return b + return Block(b.Inside) } type testBlockValidator struct{} @@ -538,7 +538,7 @@ func (v *voteMakerHelper) MakeRandomProposalPayload(t *testing.T, r round) (*pro pb := ub.FinishBlock(committee.Seed{}, basics.Address{}, false) var payload unauthenticatedProposal - payload.Block = pb.Block() + payload.Block = bookkeeping.Block(pb) payload.SeedProof = randomVRFProof() propVal := proposalValue{ diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go index 6d5c91c28c..efd68ae087 100644 --- a/agreement/fuzzer/ledger_test.go +++ b/agreement/fuzzer/ledger_test.go @@ -97,13 +97,13 @@ func (b testValidatedBlock) Round() basics.Round { return b.Inside.Round() } -func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.ProposableBlock { +func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.Block { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { b.Inside.BlockHeader.ProposerPayout = basics.MicroAlgos{} } - return b + return agreement.Block(b.Inside) } type testBlockValidator struct{} diff --git a/agreement/player_permutation_test.go b/agreement/player_permutation_test.go index 59d92e015f..216316d8bd 100644 --- a/agreement/player_permutation_test.go +++ b/agreement/player_permutation_test.go @@ -25,6 +25,7 @@ import ( "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/committee" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/partitiontest" @@ -36,7 +37,7 @@ func makeRandomProposalPayload(r round) *proposal { pb := ub.FinishBlock(committee.Seed{}, basics.Address{}, false) var payload unauthenticatedProposal - payload.Block = pb.Block() + payload.Block = bookkeeping.Block(pb) payload.SeedProof = crypto.VRFProof{} return &proposal{unauthenticatedProposal: payload} diff --git a/agreement/proposal.go b/agreement/proposal.go index 73261dabd8..e696bfeb4b 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -102,8 +102,8 @@ type proposal struct { validatedAt time.Duration } -func makeProposalFromProposableBlock(blk ProposableBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { - e := blk.Block() +func makeProposalFromProposableBlock(blk Block, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { + e := bookkeeping.Block(blk) var payload unauthenticatedProposal payload.Block = e payload.SeedProof = pf diff --git a/node/impls.go b/node/impls.go index b7ee54fab7..826f0399c4 100644 --- a/node/impls.go +++ b/node/impls.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/ledger" + "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/network" "github.com/algorand/go-algorand/util/execpool" @@ -59,7 +60,7 @@ func (i blockValidatorImpl) Validate(ctx context.Context, e bookkeeping.Block) ( return nil, err } - return validatedBlock{vb: lvb}, nil + return lvb, nil } // agreementLedger implements the agreement.Ledger interface. @@ -86,7 +87,7 @@ func (l agreementLedger) EnsureBlock(e bookkeeping.Block, c agreement.Certificat // EnsureValidatedBlock implements agreement.LedgerWriter.EnsureValidatedBlock. func (l agreementLedger) EnsureValidatedBlock(ve agreement.ValidatedBlock, c agreement.Certificate) { - l.Ledger.EnsureValidatedBlock(ve.(validatedBlock).vb, c) + l.Ledger.EnsureValidatedBlock(ve.(*ledgercore.ValidatedBlock), c) // let the network know that we've made some progress. l.n.OnNetworkAdvance() } diff --git a/node/node.go b/node/node.go index a608d4a5a1..88766877dc 100644 --- a/node/node.go +++ b/node/node.go @@ -1289,33 +1289,17 @@ func (node *AlgorandFullNode) SetCatchpointCatchupMode(catchpointCatchupMode boo } -// validatedBlock satisfies agreement.ValidatedBlock -type validatedBlock struct { - vb *ledgercore.ValidatedBlock -} - // unfinishedBlock satisfies agreement.UnfinishedBlock type unfinishedBlock struct { blk *ledgercore.UnfinishedBlock } -// proposableBlock satisfies agreement.ProposableBlock -type proposableBlock struct { - blk bookkeeping.Block -} - -// Block satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) Block() bookkeeping.Block { return vb.vb.Block() } - // Round satisfies the agreement.UnfinishedBlock interface. func (ub unfinishedBlock) Round() basics.Round { return ub.blk.Round() } -// Block satisfies the agreement.ProposableBlock interface. -func (ab proposableBlock) Block() bookkeeping.Block { return ab.blk } - // FinishBlock satisfies the agreement.UnfinishedBlock interface. -func (ub unfinishedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.ProposableBlock { - return proposableBlock{blk: ub.blk.FinishBlock(s, proposer, eligible)} +func (ub unfinishedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.Block { + return agreement.Block(ub.blk.FinishBlock(s, proposer, eligible)) } // AssembleBlock implements Ledger.AssembleBlock. From 8baf0f2125a90b509c482fb20f7cb9e0131b56b1 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Fri, 12 Apr 2024 15:46:36 -0400 Subject: [PATCH 105/117] Removed XXX in favor of explaining that we are skipping agreement --- ledger/eval_simple_test.go | 6 ++---- ledger/evalbench_test.go | 10 ++++++---- ledger/simple_test.go | 10 +++++----- ledger/simulation/simulator.go | 4 ++-- ledger/simulation/testing/utils.go | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ledger/eval_simple_test.go b/ledger/eval_simple_test.go index db38d0bf47..9ac67d2fb7 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -191,10 +191,9 @@ func TestBlockEvaluator(t *testing.T) { err = eval.TestTransactionGroup(txgroup) require.Error(t, err) - unfinishedBlock, err := eval.GenerateBlock(nil) // XXX not providing proposer addresses + unfinishedBlock, err := eval.GenerateBlock(nil) require.NoError(t, err) - // XXX not setting seed & proposer details with FinishBlock/WithProposer validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) accts := genesisInitState.Accounts @@ -944,11 +943,10 @@ func TestRekeying(t *testing.T) { return err } } - unfinishedBlock, err := eval.GenerateBlock(nil) // XXX not providing proposer addresses + unfinishedBlock, err := eval.GenerateBlock(nil) if err != nil { return err } - // XXX not setting seed & proposer details with FinishBlock/WithProposer validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) backlogPool := execpool.MakeBacklog(nil, 0, execpool.LowPriority, nil) diff --git a/ledger/evalbench_test.go b/ledger/evalbench_test.go index e42771d009..c70795f4d2 100644 --- a/ledger/evalbench_test.go +++ b/ledger/evalbench_test.go @@ -517,15 +517,17 @@ func benchmarkPreparePaymentTransactionsTesting(b *testing.B, numTxns int, txnSo var unfinishedBlock *ledgercore.UnfinishedBlock var validatedBlock *ledgercore.ValidatedBlock - // there are might more transactions than MaxTxnBytesPerBlock allows - // so make smaller blocks to fit + // there might be more transactions than MaxTxnBytesPerBlock allows so + // make smaller blocks to fit for i, stxn := range initSignedTxns { err := bev.Transaction(stxn, transactions.ApplyData{}) require.NoError(b, err) if maxTxnPerBlock > 0 && i%maxTxnPerBlock == 0 || i == len(initSignedTxns)-1 { unfinishedBlock, err = bev.GenerateBlock(nil) require.NoError(b, err) - // XXX not setting seed & proposer details with FinishBlock/WithProposer + // We are not setting seed & proposer details with + // FinishBlock/WithProposer. When agreement actually does that, + // it surely has some cost. vb := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) validatedBlock = &vb for _, l := range []*Ledger{l, l2} { @@ -566,9 +568,9 @@ func benchmarkPreparePaymentTransactionsTesting(b *testing.B, numTxns int, txnSo require.NoError(b, err) } + // as above - this might be an underestimate because we skip agreement unfinishedBlock, err := bev.GenerateBlock(nil) require.NoError(b, err) - // XXX not setting seed & proposer details with FinishBlock/WithProposer validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) blockBuildDone := time.Now() diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 9cd1c5a692..8af40eaaf3 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -149,7 +149,7 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer ub, err := eval.GenerateBlock(nil) require.NoError(t, err) - // XXX not setting seed & proposer details with FinishBlock/WithProposer + // We fake some thigns that agreement would do, like setting proposer validatedBlock := ledgercore.MakeValidatedBlock(ub.UnfinishedBlock(), ub.UnfinishedDeltas()) gvb := &validatedBlock @@ -160,10 +160,10 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer prp = proposer[0] } - // We have this backdoor way to install a proposer or seed into the header - // for tests. Doesn't matter that it makes them both the same. Since this - // can't call the agreement code, the eligibility of the prp is not - // considered. + // Since we can't do agreement, we have this backdoor way to install a + // proposer or seed into the header for tests. Doesn't matter that it makes + // them both the same. Since this can't call the agreement code, the + // eligibility of the prp is not considered. if ledger.GenesisProto().Payouts.Enabled { *gvb = ledgercore.MakeValidatedBlock(gvb.Block().WithProposer(committee.Seed(prp), prp, true), gvb.Delta()) } else { diff --git a/ledger/simulation/simulator.go b/ledger/simulation/simulator.go index db019d6763..c7c722686d 100644 --- a/ledger/simulation/simulator.go +++ b/ledger/simulation/simulator.go @@ -197,12 +197,12 @@ func (s Simulator) evaluate(hdr bookkeeping.BlockHeader, stxns []transactions.Si } // Finally, process any pending end-of-block state changes. - ub, err := eval.GenerateBlock(nil) // XXX not fetching proposer addresses + ub, err := eval.GenerateBlock(nil) if err != nil { return nil, err } - // XXX not setting seed & proposer details with FinishBlock/WithProposer + // Since we skip agreement, this block is imperfect w/ respect to seed/proposer/payouts vb := ledgercore.MakeValidatedBlock(ub.UnfinishedBlock(), ub.UnfinishedDeltas()) return &vb, nil diff --git a/ledger/simulation/testing/utils.go b/ledger/simulation/testing/utils.go index 50facac59c..3a6bbe0edf 100644 --- a/ledger/simulation/testing/utils.go +++ b/ledger/simulation/testing/utils.go @@ -104,9 +104,9 @@ func (env *Environment) nextBlock() *eval.BlockEvaluator { // endBlock completes the block being created, returns the ValidatedBlock for inspection func (env *Environment) endBlock(evaluator *eval.BlockEvaluator) *ledgercore.ValidatedBlock { env.t.Helper() - unfinishedBlock, err := evaluator.GenerateBlock(nil) // XXX not providing proposer addresses + unfinishedBlock, err := evaluator.GenerateBlock(nil) require.NoError(env.t, err) - // XXX not setting seed & proposer details with FinishBlock/WithProposer + // Since we skip agreement, this block is imperfect w/ respect to seed/proposer/payouts validatedBlock := ledgercore.MakeValidatedBlock(unfinishedBlock.UnfinishedBlock(), unfinishedBlock.UnfinishedDeltas()) err = env.Ledger.AddValidatedBlock(validatedBlock, agreement.Certificate{}) require.NoError(env.t, err) From 7b1560dc93008af4bfc391a7397559959706e504 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 16 Apr 2024 10:00:59 -0400 Subject: [PATCH 106/117] CR cleanup --- .../features/incentives/{mining_test.go => payouts_test.go} | 2 +- test/e2e-go/features/incentives/suspension_test.go | 5 +---- test/testdata/nettemplates/{Mining.json => Payouts.json} | 0 3 files changed, 2 insertions(+), 5 deletions(-) rename test/e2e-go/features/incentives/{mining_test.go => payouts_test.go} (99%) rename test/testdata/nettemplates/{Mining.json => Payouts.json} (100%) diff --git a/test/e2e-go/features/incentives/mining_test.go b/test/e2e-go/features/incentives/payouts_test.go similarity index 99% rename from test/e2e-go/features/incentives/mining_test.go rename to test/e2e-go/features/incentives/payouts_test.go index 7abb9a0871..045d05822a 100644 --- a/test/e2e-go/features/incentives/mining_test.go +++ b/test/e2e-go/features/incentives/payouts_test.go @@ -46,7 +46,7 @@ func TestBasicPayouts(t *testing.T) { var fixture fixtures.RestClientFixture // Make the seed lookback shorter, otherwise we need to wait 320 rounds to become IncentiveEligible. fixture.FasterConsensus(protocol.ConsensusFuture) - fixture.Setup(t, filepath.Join("nettemplates", "Mining.json")) + fixture.Setup(t, filepath.Join("nettemplates", "Payouts.json")) defer fixture.Shutdown() // Overview of this test: diff --git a/test/e2e-go/features/incentives/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go index e2dacf0a9d..06db1ccaad 100644 --- a/test/e2e-go/features/incentives/suspension_test.go +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -20,7 +20,6 @@ import ( "fmt" "path/filepath" "testing" - "time" "github.com/stretchr/testify/require" @@ -32,8 +31,6 @@ import ( "github.com/algorand/go-algorand/test/partitiontest" ) -const roundTime = 2 * time.Second // with speedup below, what's a good value? - // TestBasicSuspension confirms that accounts that don't propose get suspended // (when a tx naming them occurs) func TestBasicSuspension(t *testing.T) { @@ -158,7 +155,7 @@ func TestBasicSuspension(t *testing.T) { break } } - // paranoia. see mining_test.go for more details. + // paranoia. see payouts_test.go for more details. r := require.New(t) for i, c := range []libgoal.Client{c10, c20} { account, err = c.AccountData(account20.Address) diff --git a/test/testdata/nettemplates/Mining.json b/test/testdata/nettemplates/Payouts.json similarity index 100% rename from test/testdata/nettemplates/Mining.json rename to test/testdata/nettemplates/Payouts.json From 015379bd1b3607e8e79a337fd1a2401cd9dc4fa1 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 16 Apr 2024 10:50:17 -0400 Subject: [PATCH 107/117] Make it clearer where the delays come from --- test/e2e-go/features/incentives/payouts_test.go | 15 ++++++++------- test/framework/fixtures/libgoalFixture.go | 3 ++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/test/e2e-go/features/incentives/payouts_test.go b/test/e2e-go/features/incentives/payouts_test.go index 045d05822a..3497bd2375 100644 --- a/test/e2e-go/features/incentives/payouts_test.go +++ b/test/e2e-go/features/incentives/payouts_test.go @@ -45,7 +45,9 @@ func TestBasicPayouts(t *testing.T) { var fixture fixtures.RestClientFixture // Make the seed lookback shorter, otherwise we need to wait 320 rounds to become IncentiveEligible. - fixture.FasterConsensus(protocol.ConsensusFuture) + faster := fixture.FasterConsensus(protocol.ConsensusFuture) + lookback := 4 * faster.SeedRefreshInterval + fmt.Printf("lookback is %d\n", lookback) fixture.Setup(t, filepath.Join("nettemplates", "Payouts.json")) defer fixture.Shutdown() @@ -83,11 +85,10 @@ func TestBasicPayouts(t *testing.T) { // Go 31 rounds after the burn happened. During this time, incentive // eligibility is not in effect yet, so regardless of who proposes, they // won't earn anything. - client := fixture.LibGoalClient status, err := client.Status() a.NoError(err) - for status.LastRound < *burn.ConfirmedRound+31 { + for status.LastRound < *burn.ConfirmedRound+lookback-1 { block, err := client.BookkeepingBlock(status.LastRound) a.NoError(err) @@ -182,7 +183,7 @@ func TestBasicPayouts(t *testing.T) { offTxn, err := fixture.WaitForConfirmedTxn(uint64(offline.LastValid), offlineTxID) a.NoError(err) - fmt.Printf(" c15 (%s) will be truly offline (not proposing) after round %d\n", account15.Address, *offTxn.ConfirmedRound+32) + fmt.Printf(" c15 (%s) will be truly offline (not proposing) after round %d\n", account15.Address, *offTxn.ConfirmedRound+lookback) var feesink basics.Address for i := 0; i < 100; i++ { @@ -217,12 +218,12 @@ func TestBasicPayouts(t *testing.T) { if data.MicroAlgos.Raw == 100000 { break } - a.Less(i, 32+20) + a.Less(i, int(lookback+20)) err = fixture.WaitForRoundWithTimeout(status.LastRound + 1) a.NoError(err) } // maybe it got drained before c15 stops proposing. wait. - err = fixture.WaitForRoundWithTimeout(*offTxn.ConfirmedRound + 32) + err = fixture.WaitForRoundWithTimeout(*offTxn.ConfirmedRound + lookback) a.NoError(err) // put 20 algos back into the feesink, show it pays out again @@ -242,7 +243,7 @@ func TestBasicPayouts(t *testing.T) { a.Greater(int(data.MicroAlgos.Raw), 39_000_000) // Closeout c01. This is pretty weird, it means nobody will be online. But - // that will take 32 rounds. We will stop the test before then, we just + // that will take `lookback` rounds. We will stop the test before then, we just // want to show that c01 does not get paid if it has closed. wh, err = c01.GetUnencryptedWalletHandle() a.NoError(err) diff --git a/test/framework/fixtures/libgoalFixture.go b/test/framework/fixtures/libgoalFixture.go index 9faa342cde..61e2b6dbec 100644 --- a/test/framework/fixtures/libgoalFixture.go +++ b/test/framework/fixtures/libgoalFixture.go @@ -70,7 +70,7 @@ func (f *RestClientFixture) SetConsensus(consensus config.ConsensusProtocols) { // refresh lookback is set to 8 (instead of 80), so the 320 round balance // lookback becomes 32. And, if the architecture implies it can be handled, // round times are shortened by lowering vote timeouts. -func (f *RestClientFixture) FasterConsensus(ver protocol.ConsensusVersion) { +func (f *RestClientFixture) FasterConsensus(ver protocol.ConsensusVersion) config.ConsensusParams { if f.consensus == nil { f.consensus = make(config.ConsensusProtocols) } @@ -82,6 +82,7 @@ func (f *RestClientFixture) FasterConsensus(ver protocol.ConsensusVersion) { fast.AgreementFilterTimeout = time.Second / 2 } f.consensus[ver] = fast + return fast } // Setup is called to initialize the test fixture for the test(s) From e0c5d4f101588d6f054d49d8f4b544530937c326 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 16 Apr 2024 13:01:27 -0400 Subject: [PATCH 108/117] mining -> payouts --- test/scripts/e2e_subs/{mining.py => payouts.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/scripts/e2e_subs/{mining.py => payouts.py} (100%) diff --git a/test/scripts/e2e_subs/mining.py b/test/scripts/e2e_subs/payouts.py similarity index 100% rename from test/scripts/e2e_subs/mining.py rename to test/scripts/e2e_subs/payouts.py From f1082f5ff53579c1446c4b8f19650d9cbaf9361c Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 17 Apr 2024 15:21:23 -0400 Subject: [PATCH 109/117] Leave lookback at 320 for suspension test Part of the test requires the suspended node to re-propose after suispension. That can only happen when the balance lookback is considerably bigger than the suspension interval (here, 5*10==50) Fortunately, this test doesn't have to wait for lookback, so the benefit of short filter timeout is all we need for a quick test. --- .../features/incentives/payouts_test.go | 5 +-- .../features/incentives/suspension_test.go | 36 ++++++++++--------- test/framework/fixtures/libgoalFixture.go | 14 +++++--- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/test/e2e-go/features/incentives/payouts_test.go b/test/e2e-go/features/incentives/payouts_test.go index 3497bd2375..a7b6567d87 100644 --- a/test/e2e-go/features/incentives/payouts_test.go +++ b/test/e2e-go/features/incentives/payouts_test.go @@ -20,6 +20,7 @@ import ( "fmt" "path/filepath" "testing" + "time" "github.com/stretchr/testify/require" @@ -45,8 +46,8 @@ func TestBasicPayouts(t *testing.T) { var fixture fixtures.RestClientFixture // Make the seed lookback shorter, otherwise we need to wait 320 rounds to become IncentiveEligible. - faster := fixture.FasterConsensus(protocol.ConsensusFuture) - lookback := 4 * faster.SeedRefreshInterval + const lookback = 32 + fixture.FasterConsensus(protocol.ConsensusFuture, time.Second/2, 32) fmt.Printf("lookback is %d\n", lookback) fixture.Setup(t, filepath.Join("nettemplates", "Payouts.json")) defer fixture.Shutdown() diff --git a/test/e2e-go/features/incentives/suspension_test.go b/test/e2e-go/features/incentives/suspension_test.go index 06db1ccaad..3d3f0954f6 100644 --- a/test/e2e-go/features/incentives/suspension_test.go +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -20,6 +20,7 @@ import ( "fmt" "path/filepath" "testing" + "time" "github.com/stretchr/testify/require" @@ -50,8 +51,9 @@ func TestBasicSuspension(t *testing.T) { const suspend20 = 55 var fixture fixtures.RestClientFixture - // Make the seed lookback shorter, so the test runs faster - fixture.FasterConsensus(protocol.ConsensusFuture) + // Speed up rounds, but keep long lookback, so 20% node has a chance to get + // back online after being suspended. + fixture.FasterConsensus(protocol.ConsensusFuture, time.Second/2, 320) fixture.Setup(t, filepath.Join("nettemplates", "Suspension.json")) defer fixture.Shutdown() @@ -137,23 +139,23 @@ func TestBasicSuspension(t *testing.T) { // Wait for newly restarted node to start. stat, err := lg.Status() a.NoError(err) - // Waiting for this round should show it has started and caught up. - stat, err = lg.WaitForRound(afterStop.LastRound + suspend20) + + // Get the current round, and wait for the restarted node to get there. + stat, err = fixture.AlgodClient.Status() a.NoError(err) - // Proceed until a round is proposed by n20. (Stop at 50 rounds, that's more likely a bug than luck) - for r := stat.LastRound; r < stat.LastRound+50; r++ { - err = fixture.WaitForRoundWithTimeout(r) - a.NoError(err) + // Wait for latest round to show n20 has started and caught up. + restartRound := stat.LastRound + stat, err = lg.WaitForRound(restartRound) + a.NoError(err) - // Once n20 proposes, break out early - if fixture.VerifyBlockProposed(account20.Address, 1) { - fmt.Printf("account20 proposed at round %d\n", r) - // wait one extra round, because changes are processed in block n+1. - err = fixture.WaitForRoundWithTimeout(r + 1) - a.NoError(err) - break - } + // Proceed until a round is proposed by n20. + attempts := 0 + for !fixture.VerifyBlockProposed(account20.Address, 1) { + stat, err = lg.WaitForRound(stat.LastRound + 1) + a.NoError(err) + attempts++ + a.Less(attempts, suspend20, "n20 didn't propose\n") } // paranoia. see payouts_test.go for more details. r := require.New(t) @@ -161,7 +163,7 @@ func TestBasicSuspension(t *testing.T) { account, err = c.AccountData(account20.Address) a.NoError(err) r.Equal(basics.Online, account.Status, i) - r.Greater(account.LastProposed, stat.LastRound, i) + r.Greater(account.LastProposed, restartRound, i) r.Equal(voteID, account.VoteID, i) r.False(account.IncentiveEligible, i) diff --git a/test/framework/fixtures/libgoalFixture.go b/test/framework/fixtures/libgoalFixture.go index 61e2b6dbec..1ffc6493c0 100644 --- a/test/framework/fixtures/libgoalFixture.go +++ b/test/framework/fixtures/libgoalFixture.go @@ -36,6 +36,7 @@ import ( "github.com/algorand/go-algorand/crypto/merklearray" "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/account" + "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/gen" "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/netdeploy" @@ -70,19 +71,22 @@ func (f *RestClientFixture) SetConsensus(consensus config.ConsensusProtocols) { // refresh lookback is set to 8 (instead of 80), so the 320 round balance // lookback becomes 32. And, if the architecture implies it can be handled, // round times are shortened by lowering vote timeouts. -func (f *RestClientFixture) FasterConsensus(ver protocol.ConsensusVersion) config.ConsensusParams { +func (f *RestClientFixture) FasterConsensus(ver protocol.ConsensusVersion, timeout time.Duration, lookback basics.Round) { if f.consensus == nil { f.consensus = make(config.ConsensusProtocols) } fast := config.Consensus[ver] - fast.SeedRefreshInterval = 8 // so balanceRound ends up 2 * 8 * 2 = 32 + // balanceRound is 4 * SeedRefreshInterval + if lookback%4 != 0 { + panic(fmt.Sprintf("lookback must be a multiple of 4, got %d", lookback)) + } + fast.SeedRefreshInterval = uint64(lookback) / 4 // and speed up the rounds while we're at it if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" { - fast.AgreementFilterTimeoutPeriod0 = time.Second / 2 - fast.AgreementFilterTimeout = time.Second / 2 + fast.AgreementFilterTimeoutPeriod0 = timeout + fast.AgreementFilterTimeout = timeout } f.consensus[ver] = fast - return fast } // Setup is called to initialize the test fixture for the test(s) From ba5efdaa200990911618248d5ddd0d739b667396 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Thu, 18 Apr 2024 14:09:32 -0400 Subject: [PATCH 110/117] Accomodate talking to slow clients --- .../features/incentives/payouts_test.go | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/test/e2e-go/features/incentives/payouts_test.go b/test/e2e-go/features/incentives/payouts_test.go index a7b6567d87..3a5d6bd2a4 100644 --- a/test/e2e-go/features/incentives/payouts_test.go +++ b/test/e2e-go/features/incentives/payouts_test.go @@ -26,6 +26,7 @@ import ( "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/protocol" @@ -106,16 +107,20 @@ func TestBasicPayouts(t *testing.T) { // made since they should make such a problem impossible, but... for i, c := range []libgoal.Client{c15, c01, relay} { fmt.Printf("checking block %v\n", block.Round()) - data, err := c.AccountData(block.Proposer().String()) - a.NoError(err) - bb, err := c.BookkeepingBlock(status.LastRound) + bb, err := getblock(c, status.LastRound) a.NoError(err) a.Equal(block.Proposer(), bb.Proposer()) - a.Equal(block.Round(), data.LastProposed, "client %d thinks %v", i, block.Proposer()) + + // check that the LastProposed for the proposer has been incremented + data, err := c.AccountData(block.Proposer().String()) + a.NoError(err) + // We use LOE instead of Equal because it's possible that by now + // the proposer has proposed again! + a.LessOrEqual(block.Round(), data.LastProposed, "client %d thinks %v", i, block.Proposer()) } next, err := client.AccountData(block.Proposer().String()) - a.EqualValues(next.LastProposed, status.LastRound) + a.LessOrEqual(int(status.LastRound), int(next.LastProposed)) // regardless of proposer, nobody gets paid switch block.Proposer().String() { case account01.Address: @@ -281,6 +286,15 @@ func TestBasicPayouts(t *testing.T) { a.Equal(data.MicroAlgos, after.MicroAlgos) } +// getblock waits for the given block because we use when we might be talking to +// a client that is behind the network (since it has low stake) +func getblock(client libgoal.Client, round uint64) (bookkeeping.Block, error) { + if _, err := client.WaitForRound(round); err != nil { + return bookkeeping.Block{}, err + } + return client.BookkeepingBlock(round) +} + func rekeyreg(f *fixtures.RestClientFixture, a *require.Assertions, client libgoal.Client, address string) basics.AccountData { // we start by making an _offline_ tx here, because we want to populate the // key material ourself with a copy of the account's existing material. That From 40ce14a4ed3600e6fe75b975ea650d6700370431 Mon Sep 17 00:00:00 2001 From: chris erway Date: Fri, 19 Apr 2024 10:07:04 -0400 Subject: [PATCH 111/117] add mismatched proposer check to increase coverage a tiny bit --- agreement/proposal_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/agreement/proposal_test.go b/agreement/proposal_test.go index 31b8d83050..f325c507d6 100644 --- a/agreement/proposal_test.go +++ b/agreement/proposal_test.go @@ -201,6 +201,15 @@ func TestProposalUnauthenticated(t *testing.T) { unauthenticatedProposal3.SeedProof = unauthenticatedProposal.SeedProof _, err = unauthenticatedProposal3.validate(context.Background(), round, ledger, validator) require.Error(t, err) + + // validate mismatch proposer address between block and unauthenticatedProposal + proposal4, _, _ := proposalForBlock(accounts.addresses[accountIndex], accounts.vrfs[accountIndex], testBlockFactory, period, ledger) + accountIndex++ + unauthenticatedProposal4 := proposal4.u() + unauthenticatedProposal4.OriginalProposer = accounts.addresses[0] // set to the wrong address + require.NotEqual(t, unauthenticatedProposal4.OriginalProposer, unauthenticatedProposal4.Block.Proposer()) + _, err = unauthenticatedProposal4.validate(context.Background(), round, ledger, validator) + require.ErrorContains(t, err, "wrong proposer") } func unauthenticatedProposalBlockPanicWrapper(t *testing.T, message string, uap unauthenticatedProposal, validator BlockValidator) (block bookkeeping.Block) { From 1037ad50dfca7f8cd860a25f544bbececb65660b Mon Sep 17 00:00:00 2001 From: cce <51567+cce@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:21:55 -0400 Subject: [PATCH 112/117] Apply suggestions from code review Batch of comment tweaks from CR Co-authored-by: Jason Paulos --- agreement/abstractions.go | 2 +- config/consensus.go | 6 +++--- test/e2e-go/features/incentives/payouts_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/agreement/abstractions.go b/agreement/abstractions.go index a0ace29ac5..ea0a4b1eee 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -84,7 +84,7 @@ type BlockFactory interface { // An UnfinishedBlock represents a Block produced by a BlockFactory // and must be finalized before being proposed by agreement. type UnfinishedBlock interface { - // FinishBlock creates a Proposaable block, having set the cryptographically + // FinishBlock creates a proposable block, having set the cryptographically // random seed and payout related fields. // // Calls to Seed() or to Digest() on the copy's Block must diff --git a/config/consensus.go b/config/consensus.go index a8c9c30fbd..f86e45e831 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -611,15 +611,15 @@ type ProposerPayoutRules struct { // the new decay rate would go into effect at upgrade time, and the new // amount would be set at baseRound or at upgrade time. type BonusPlan struct { - // BonusBaseRound is the earliest round this plan can apply. Of course, the + // BaseRound is the earliest round this plan can apply. Of course, the // consensus update must also have happened. So using a low value makes it // go into effect immediately upon upgrade. BaseRound uint64 - // BonusBaseAmount is the bonus to be paid when this plan first applies (see + // BaseAmount is the bonus to be paid when this plan first applies (see // baseRound). If it is zero, then no explicit change is made to the bonus // (useful for only changing the decay rate). BaseAmount uint64 - // BonusDecayInterval is the time in rounds between 1% decays. For simplicity, + // DecayInterval is the time in rounds between 1% decays. For simplicity, // decay occurs based on round % BonusDecayInterval, so a decay can happen right // after going into effect. The BonusDecayInterval goes into effect at upgrade // time, regardless of `baseRound`. diff --git a/test/e2e-go/features/incentives/payouts_test.go b/test/e2e-go/features/incentives/payouts_test.go index 3a5d6bd2a4..28fc91089e 100644 --- a/test/e2e-go/features/incentives/payouts_test.go +++ b/test/e2e-go/features/incentives/payouts_test.go @@ -101,7 +101,7 @@ func TestBasicPayouts(t *testing.T) { // all nodes agree the proposer proposed. The paranoia here is // justified. Block incentives are computed in two stages. A little bit // of extra work is done when agreement "Finishes" the block. An easy - // bug to have using the block the Deltas() computed on the block + // bug to have is using the block the Deltas() computed on the block // without the changes that come after agreement runs. We had such an // optimization, and it would cause failures here. Interface changes // made since they should make such a problem impossible, but... @@ -232,7 +232,7 @@ func TestBasicPayouts(t *testing.T) { err = fixture.WaitForRoundWithTimeout(*offTxn.ConfirmedRound + lookback) a.NoError(err) - // put 20 algos back into the feesink, show it pays out again + // put 50 algos back into the feesink, show it pays out again txn, err = c01.SendPaymentFromUnencryptedWallet(account01.Address, feesink.String(), 1000, 50_000_000, nil) a.NoError(err) refill, err := fixture.WaitForConfirmedTxn(uint64(txn.LastValid), txn.ID().String()) From 87a20c54d731f5e1385841c150208ec295533ab7 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 22 Apr 2024 14:47:00 -0400 Subject: [PATCH 113/117] exercise inability to pay from fee sink --- data/transactions/payment.go | 2 +- ledger/apply/payment_test.go | 38 ++++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/data/transactions/payment.go b/data/transactions/payment.go index 2b52b4b385..d51e4ed1d6 100644 --- a/data/transactions/payment.go +++ b/data/transactions/payment.go @@ -51,7 +51,7 @@ func (payment PaymentTxnFields) CheckSpender(header Header, spec SpecialAddresse if payment.Receiver != spec.RewardsPool { return fmt.Errorf("cannot spend from fee sink's address %v to non incentive pool address %v", header.Sender, payment.Receiver) } - if payment.CloseRemainderTo != (basics.Address{}) { + if !payment.CloseRemainderTo.IsZero() { return fmt.Errorf("cannot close fee sink %v to %v", header.Sender, payment.CloseRemainderTo) } } diff --git a/ledger/apply/payment_test.go b/ledger/apply/payment_test.go index a24118aa25..5cc436740d 100644 --- a/ledger/apply/payment_test.go +++ b/ledger/apply/payment_test.go @@ -111,8 +111,10 @@ func TestPaymentApply(t *testing.T) { func TestCheckSpender(t *testing.T) { partitiontest.PartitionTest(t) - mockBalV0 := makeMockBalances(protocol.ConsensusCurrentVersion) mockBalV7 := makeMockBalances(protocol.ConsensusV7) + mockBalV39 := makeMockBalances(protocol.ConsensusV39) + mockBalCurrent := makeMockBalances(protocol.ConsensusCurrentVersion) + mockBalFuture := makeMockBalances(protocol.ConsensusFuture) secretSrc := keypair() src := basics.Address(secretSrc.SignatureVerifier) @@ -134,19 +136,39 @@ func TestCheckSpender(t *testing.T) { }, } - tx.Sender = basics.Address(feeSink) - require.Error(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV0.ConsensusParams())) + tx.Sender = feeSink + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams()), + "to non incentive pool address") + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV39.ConsensusParams()), + "to non incentive pool address") + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalCurrent.ConsensusParams()), + "to non incentive pool address") + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalFuture.ConsensusParams()), + "cannot spend from fee sink") - poolAddr := basics.Address(poolAddr) tx.Receiver = poolAddr - require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV0.ConsensusParams())) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalCurrent.ConsensusParams())) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams())) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV39.ConsensusParams())) + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalFuture.ConsensusParams()), + "cannot spend from fee sink") tx.CloseRemainderTo = poolAddr - require.Error(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV0.ConsensusParams())) - require.Error(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams())) - + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams()), + "cannot close fee sink") + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV39.ConsensusParams()), + "cannot close fee sink") + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalCurrent.ConsensusParams()), + "cannot close fee sink") + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalFuture.ConsensusParams()), + "cannot spend from fee sink") + + // When not sending from fee sink, everything's fine tx.Sender = src require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams())) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV39.ConsensusParams())) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalCurrent.ConsensusParams())) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalFuture.ConsensusParams())) } func TestPaymentValidation(t *testing.T) { From 7ddccf307842d9167f368501f3e904a578922648 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 22 Apr 2024 15:27:51 -0400 Subject: [PATCH 114/117] Simplifiy tests. No "current" test, and no mockBalances --- ledger/apply/payment_test.go | 37 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/ledger/apply/payment_test.go b/ledger/apply/payment_test.go index 5cc436740d..6e756350fb 100644 --- a/ledger/apply/payment_test.go +++ b/ledger/apply/payment_test.go @@ -111,10 +111,9 @@ func TestPaymentApply(t *testing.T) { func TestCheckSpender(t *testing.T) { partitiontest.PartitionTest(t) - mockBalV7 := makeMockBalances(protocol.ConsensusV7) - mockBalV39 := makeMockBalances(protocol.ConsensusV39) - mockBalCurrent := makeMockBalances(protocol.ConsensusCurrentVersion) - mockBalFuture := makeMockBalances(protocol.ConsensusFuture) + v7 := config.Consensus[protocol.ConsensusV7] + v39 := config.Consensus[protocol.ConsensusV39] + vFuture := config.Consensus[protocol.ConsensusFuture] secretSrc := keypair() src := basics.Address(secretSrc.SignatureVerifier) @@ -137,38 +136,32 @@ func TestCheckSpender(t *testing.T) { } tx.Sender = feeSink - require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams()), + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v7), "to non incentive pool address") - require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV39.ConsensusParams()), + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v39), "to non incentive pool address") - require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalCurrent.ConsensusParams()), - "to non incentive pool address") - require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalFuture.ConsensusParams()), + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, vFuture), "cannot spend from fee sink") tx.Receiver = poolAddr - require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalCurrent.ConsensusParams())) - require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams())) - require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV39.ConsensusParams())) - require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalFuture.ConsensusParams()), + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v7)) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v39)) + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, vFuture), "cannot spend from fee sink") tx.CloseRemainderTo = poolAddr - require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams()), - "cannot close fee sink") - require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV39.ConsensusParams()), + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v7), "cannot close fee sink") - require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalCurrent.ConsensusParams()), + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v39), "cannot close fee sink") - require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalFuture.ConsensusParams()), + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, vFuture), "cannot spend from fee sink") // When not sending from fee sink, everything's fine tx.Sender = src - require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV7.ConsensusParams())) - require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalV39.ConsensusParams())) - require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalCurrent.ConsensusParams())) - require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, mockBalFuture.ConsensusParams())) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v7)) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v39)) + require.NoError(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, vFuture)) } func TestPaymentValidation(t *testing.T) { From 480f0f95d46bd9acd87c395c9823c82e2df31f45 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 22 Apr 2024 23:09:54 -0400 Subject: [PATCH 115/117] Typo Co-authored-by: cce <51567+cce@users.noreply.github.com> --- ledger/eval/eval_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index ee3838dad4..0e7cd4fd72 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -1387,9 +1387,9 @@ func TestAbsenteeChecks(t *testing.T) { tmp.LastHeartbeat = 1 // non-zero allows suspensions switch i { case 1: - tmp.LastHeartbeat = 1150 // lie hear so that addr[1] won't be suspended + tmp.LastHeartbeat = 1150 // lie here so that addr[1] won't be suspended case 2: - tmp.LastProposed = 1150 // lie hear so that addr[2] won't be suspended + tmp.LastProposed = 1150 // lie here so that addr[2] won't be suspended } genesisInitState.Accounts[addr] = tmp From 762e15b77ad65d2d0f1bef6dc617a2cca1af2c5d Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Mon, 22 Apr 2024 23:11:08 -0400 Subject: [PATCH 116/117] comment Co-authored-by: cce <51567+cce@users.noreply.github.com> --- ledger/ledgercore/validatedBlock.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index c29c16cf7f..0d7704710d 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -85,7 +85,9 @@ func (ub UnfinishedBlock) ContainsAddress(addr basics.Address) bool { // FinishBlock completes the block and returns a proposable block. func (ub UnfinishedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) bookkeeping.Block { + // Look up the given proposer's balance by the end of this block propData, ok := ub.finalAccounts[proposer] + // This proposer has closed their account and is not eligible for rewards if !ok || propData.MicroAlgos.IsZero() { eligible = false } From 3d2fbc463361523a3ed80230c61f99d859071707 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 23 Apr 2024 08:54:23 -0400 Subject: [PATCH 117/117] Ensure only the address we thought would get suspended did. Co-authored-by: cce <51567+cce@users.noreply.github.com> --- ledger/eval/eval_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index 0e7cd4fd72..994bedd561 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -1465,6 +1465,7 @@ func TestAbsenteeChecks(t *testing.T) { require.Contains(t, validatedBlock.Block().AbsentParticipationAccounts, challenged, challenged.String()) for i := byte(0); i < 32; i++ { if i == challenge>>3 { + require.Equal(t, basics.Address{i << 3, 0xaa}, challenged) continue } require.NotContains(t, validatedBlock.Block().AbsentParticipationAccounts, basics.Address{i << 3, 0xaa})