diff --git a/agreement/abstractions.go b/agreement/abstractions.go index a22a3a0526..ea0a4b1eee 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -54,13 +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 { - // WithSeed creates a copy of this ValidatedBlock 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. - WithSeed(committee.Seed) ValidatedBlock - // Block returns the underlying block that has been validated. Block() bookkeeping.Block } @@ -72,21 +65,42 @@ 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) (ValidatedBlock, error) + AssembleBlock(rnd basics.Round, partAddresses []basics.Address) (UnfinishedBlock, error) +} + +// An UnfinishedBlock represents a Block produced by a BlockFactory +// and must be finalized before being proposed by agreement. +type UnfinishedBlock interface { + // 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 + // reflect the value of the new seed. + FinishBlock(seed committee.Seed, proposer basics.Address, eligible bool) Block + + Round() basics.Round } +// 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. +// +//msgp:ignore Block +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 // provide read and write access to the ledger, respectively. diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index a0afca4d46..6c071ed8a8 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -79,9 +79,17 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithSeed(s committee.Seed) agreement.ValidatedBlock { +func (b testValidatedBlock) Round() basics.Round { + return b.Inside.Round() +} + +func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.Block { b.Inside.BlockHeader.Seed = s - return b + b.Inside.BlockHeader.Proposer = proposer + if !eligible { + b.Inside.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } + return agreement.Block(b.Inside) } type testBlockValidator struct{} @@ -94,7 +102,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.ValidatedBlock, 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/agreement/common_test.go b/agreement/common_test.go index 9ec85618e8..ca8983705e 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -165,9 +165,17 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithSeed(s committee.Seed) ValidatedBlock { +func (b testValidatedBlock) Round() basics.Round { + return b.Inside.Round() +} + +func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) Block { b.Inside.BlockHeader.Seed = s - return b + b.Inside.BlockHeader.Proposer = proposer + if !eligible { + b.Inside.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } + return Block(b.Inside) } type testBlockValidator struct{} @@ -180,7 +188,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (ValidatedBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round, _ []basics.Address) (UnfinishedBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } @@ -413,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 @@ -525,11 +533,12 @@ 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) + ub, err := f.AssembleBlock(r, nil) require.NoError(t, err) + pb := ub.FinishBlock(committee.Seed{}, basics.Address{}, false) var payload unauthenticatedProposal - payload.Block = ve.Block() + payload.Block = bookkeeping.Block(pb) payload.SeedProof = randomVRFProof() propVal := proposalValue{ @@ -537,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}, &propVal } // make a vote for a fixed proposal value diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go index 00ba132294..efd68ae087 100644 --- a/agreement/fuzzer/ledger_test.go +++ b/agreement/fuzzer/ledger_test.go @@ -93,9 +93,17 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithSeed(s committee.Seed) agreement.ValidatedBlock { +func (b testValidatedBlock) Round() basics.Round { + return b.Inside.Round() +} + +func (b testValidatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.Block { b.Inside.BlockHeader.Seed = s - return b + b.Inside.BlockHeader.Proposer = proposer + if !eligible { + b.Inside.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } + return agreement.Block(b.Inside) } type testBlockValidator struct{} @@ -108,7 +116,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.ValidatedBlock, 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/msgp_gen.go b/agreement/msgp_gen.go index 16679ce797..b012f66da2 100644 --- a/agreement/msgp_gen.go +++ b/agreement/msgp_gen.go @@ -4493,183 +4493,225 @@ 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 */ + zb0005Len := uint32(34) + var zb0005Mask uint64 /* 43 bits */ + if (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x40 + } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x40 + zb0005Len-- + zb0005Mask |= 0x80 + } + if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x100 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x4000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x8000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x10000 } if (*z).unauthenticatedProposal.OriginalPeriod == 0 { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x20000 } if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000 + 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 |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x100000 + } + if (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x200000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x800000 + } + if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x1000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800000 + zb0005Len-- + zb0005Mask |= 0x10000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x200000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000000 + zb0005Len-- + zb0005Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000000 + zb0005Len-- + zb0005Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000000 + zb0005Len-- + zb0005Mask |= 0x2000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000000 + zb0005Len-- + zb0005Mask |= 0x4000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000000 + zb0005Len-- + zb0005Mask |= 0x8000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x800000000 - } - // variable map header, size zb0004Len - o = msgp.AppendMapHeader(o, zb0004Len) - if zb0004Len != 0 { - if (zb0004Mask & 0x40) == 0 { // if not empty + zb0005Len-- + zb0005Mask |= 0x10000000000 + } + // 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 (zb0004Mask & 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 & 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 & 0x100) == 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 & 0x200) == 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 & 0x400) == 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 & 0x800) == 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 & 0x1000) == 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 & 0x2000) == 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 & 0x4000) == 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 & 0x8000) == 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 & 0x10000) == 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 & 0x20000) == 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 { @@ -4681,47 +4723,57 @@ 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 (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 (zb0004Mask & 0x80000) == 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 (zb0004Mask & 0x100000) == 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 & 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 & 0x400000) == 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 & 0x800000) == 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 & 0x1000000) == 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 & 0x2000000) == 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 & 0x4000000) == 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 & 0x8000000) == 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 { @@ -4741,42 +4793,42 @@ func (z *proposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x10000000) == 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 & 0x20000000) == 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 & 0x40000000) == 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 & 0x80000000) == 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 & 0x100000000) == 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 & 0x200000000) == 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 & 0x400000000) == 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 & 0x800000000) == 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) @@ -4799,73 +4851,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) @@ -4874,157 +4926,189 @@ 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 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 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.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) 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") @@ -5038,26 +5122,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) @@ -5067,44 +5151,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) + 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 @@ -5115,11 +5228,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) @@ -5163,14 +5276,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) @@ -5184,6 +5297,30 @@ 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 "bi": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + 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 { @@ -5275,27 +5412,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") @@ -5309,24 +5446,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) @@ -5335,6 +5472,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxMarkAbsent)) + 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 { @@ -5349,13 +5513,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) @@ -5386,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() + 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 @@ -5398,18 +5562,22 @@ 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.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.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() + 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()) @@ -5418,6 +5586,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.MaxMarkAbsent) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.unauthenticatedProposal.Block.Payset s += config.MaxTxnBytesPerBlock @@ -8856,187 +9027,229 @@ 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 */ + zb0005Len := uint32(35) + var zb0005Mask uint64 /* 43 bits */ + if (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x80 + } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x100 + } + if (*z).unauthenticatedProposal.Block.BlockHeader.FeesCollected.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).unauthenticatedProposal.Block.BlockHeader.GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x4000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x8000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x10000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x20000 } if (*z).unauthenticatedProposal.OriginalPeriod == 0 { - zb0004Len-- - zb0004Mask |= 0x10000 + zb0005Len-- + zb0005Mask |= 0x40000 } if (*z).unauthenticatedProposal.OriginalProposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x80000 + } + if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + zb0005Len-- + zb0005Mask |= 0x100000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x200000 + } + if (*z).unauthenticatedProposal.Block.BlockHeader.ProposerPayout.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x800000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x1000000 + } + if (*z).unauthenticatedProposal.Block.BlockHeader.Proposer.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).PriorVote.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000 + zb0005Len-- + zb0005Mask |= 0x4000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if (*z).unauthenticatedProposal.SeedProof.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x200000000 } if len((*z).unauthenticatedProposal.Block.BlockHeader.StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x400000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x40000000 + zb0005Len-- + zb0005Mask |= 0x800000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x80000000 + zb0005Len-- + zb0005Mask |= 0x1000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000000 + zb0005Len-- + zb0005Mask |= 0x2000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000000 + zb0005Len-- + zb0005Mask |= 0x4000000000 } if (*z).unauthenticatedProposal.Block.Payset.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000000 + zb0005Len-- + zb0005Mask |= 0x8000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800000000 + zb0005Len-- + zb0005Mask |= 0x10000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000000000 + zb0005Len-- + zb0005Mask |= 0x20000000000 } if (*z).unauthenticatedProposal.Block.BlockHeader.UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x2000000000 - } - // variable map header, size zb0004Len - o = msgp.AppendMapHeader(o, zb0004Len) - if zb0004Len != 0 { - if (zb0004Mask & 0x80) == 0 { // if not empty + zb0005Len-- + zb0005Mask |= 0x40000000000 + } + // 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 (zb0004Mask & 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 & 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 (zb0004Mask & 0x200) == 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 (zb0004Mask & 0x400) == 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 (zb0004Mask & 0x800) == 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 (zb0004Mask & 0x1000) == 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 (zb0004Mask & 0x2000) == 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 (zb0004Mask & 0x4000) == 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 (zb0004Mask & 0x8000) == 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 (zb0004Mask & 0x10000) == 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 (zb0004Mask & 0x20000) == 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 (zb0004Mask & 0x40000) == 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 { + 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 & 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 { @@ -9048,52 +9261,62 @@ 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 (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 (zb0004Mask & 0x100000) == 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 (zb0004Mask & 0x200000) == 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 & 0x4000000) == 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 (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 (zb0004Mask & 0x1000000) == 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 (zb0004Mask & 0x2000000) == 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 (zb0004Mask & 0x4000000) == 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 (zb0004Mask & 0x8000000) == 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 (zb0004Mask & 0x10000000) == 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 (zb0004Mask & 0x20000000) == 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 { @@ -9113,42 +9336,42 @@ func (z *transmittedPayload) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x40000000) == 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 (zb0004Mask & 0x80000000) == 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 (zb0004Mask & 0x100000000) == 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 (zb0004Mask & 0x200000000) == 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 (zb0004Mask & 0x400000000) == 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 (zb0004Mask & 0x800000000) == 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 (zb0004Mask & 0x1000000000) == 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 (zb0004Mask & 0x2000000000) == 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) @@ -9171,73 +9394,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) @@ -9246,157 +9469,189 @@ 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 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 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.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) 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") @@ -9410,26 +9665,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) @@ -9439,52 +9694,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) + 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 @@ -9495,11 +9779,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) @@ -9543,14 +9827,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) @@ -9564,6 +9848,30 @@ 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 "bi": + bts, err = (*z).unauthenticatedProposal.Block.BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + 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 { @@ -9655,27 +9963,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") @@ -9689,24 +9997,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) @@ -9715,6 +10023,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxMarkAbsent)) + 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 { @@ -9729,13 +10064,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) @@ -9772,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() + 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 @@ -9784,18 +10119,22 @@ 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.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.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() + 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()) @@ -9804,6 +10143,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.MaxMarkAbsent) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.unauthenticatedProposal.Block.Payset s += config.MaxTxnBytesPerBlock @@ -10516,183 +10858,225 @@ 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 */ + zb0005Len := uint32(34) + var zb0005Mask uint64 /* 41 bits */ + if (*z).Block.BlockHeader.Bonus.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x40 + } if (*z).Block.BlockHeader.RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x40 + zb0005Len-- + zb0005Mask |= 0x80 + } + if (*z).Block.BlockHeader.FeesCollected.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x100 } if (*z).Block.BlockHeader.RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).Block.BlockHeader.RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).Block.BlockHeader.GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).Block.BlockHeader.GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).Block.BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).Block.BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x4000 } if (*z).Block.BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x8000 } if (*z).Block.BlockHeader.UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x10000 } if (*z).OriginalPeriod == 0 { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x20000 } if (*z).OriginalProposer.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000 + zb0005Len-- + zb0005Mask |= 0x40000 + } + if len((*z).Block.BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + zb0005Len-- + zb0005Mask |= 0x80000 } if len((*z).Block.BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { - zb0004Len-- - zb0004Mask |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x100000 + } + if (*z).Block.BlockHeader.ProposerPayout.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x200000 } if (*z).Block.BlockHeader.Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).Block.BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x800000 + } + if (*z).Block.BlockHeader.Proposer.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x1000000 } if (*z).Block.BlockHeader.RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).Block.BlockHeader.Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).Block.BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800000 + zb0005Len-- + zb0005Mask |= 0x10000000 } if (*z).Block.BlockHeader.RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if (*z).SeedProof.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).Block.BlockHeader.Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if len((*z).Block.BlockHeader.StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } if (*z).Block.BlockHeader.TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x200000000 } if (*z).Block.BlockHeader.TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x400000000 } if (*z).Block.BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000000 + zb0005Len-- + zb0005Mask |= 0x800000000 } if (*z).Block.BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000000 + zb0005Len-- + zb0005Mask |= 0x1000000000 } if (*z).Block.Payset.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000000 + zb0005Len-- + zb0005Mask |= 0x2000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000000 + zb0005Len-- + zb0005Mask |= 0x4000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400000000 + zb0005Len-- + zb0005Mask |= 0x8000000000 } if (*z).Block.BlockHeader.UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x800000000 - } - // variable map header, size zb0004Len - o = msgp.AppendMapHeader(o, zb0004Len) - if zb0004Len != 0 { - if (zb0004Mask & 0x40) == 0 { // if not empty + zb0005Len-- + zb0005Mask |= 0x10000000000 + } + // 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 (zb0004Mask & 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 & 0x200) == 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 (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 (zb0004Mask & 0x200) == 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 (zb0004Mask & 0x400) == 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 (zb0004Mask & 0x800) == 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 (zb0004Mask & 0x1000) == 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 (zb0004Mask & 0x2000) == 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 (zb0004Mask & 0x4000) == 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 (zb0004Mask & 0x8000) == 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 (zb0004Mask & 0x10000) == 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 (zb0004Mask & 0x20000) == 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 { + 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 & 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 { @@ -10704,47 +11088,57 @@ 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 (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 (zb0004Mask & 0x80000) == 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 (zb0004Mask & 0x100000) == 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 & 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 (zb0004Mask & 0x400000) == 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 (zb0004Mask & 0x800000) == 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 (zb0004Mask & 0x1000000) == 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 (zb0004Mask & 0x2000000) == 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 (zb0004Mask & 0x4000000) == 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 (zb0004Mask & 0x8000000) == 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 { @@ -10764,42 +11158,42 @@ func (z *unauthenticatedProposal) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x10000000) == 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 (zb0004Mask & 0x20000000) == 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 (zb0004Mask & 0x40000000) == 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 (zb0004Mask & 0x80000000) == 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 (zb0004Mask & 0x100000000) == 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 (zb0004Mask & 0x200000000) == 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 (zb0004Mask & 0x400000000) == 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 (zb0004Mask & 0x800000000) == 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) @@ -10822,73 +11216,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) @@ -10897,157 +11291,189 @@ 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 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 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.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) 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") @@ -11061,26 +11487,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) @@ -11090,44 +11516,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) + 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 @@ -11138,11 +11593,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) @@ -11186,14 +11641,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) @@ -11207,6 +11662,30 @@ 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 "bi": + bts, err = (*z).Block.BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + 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 { @@ -11298,27 +11777,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") @@ -11332,24 +11811,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) @@ -11358,6 +11837,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0020), uint64(config.MaxMarkAbsent)) + 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 { @@ -11372,13 +11878,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) @@ -11409,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() + 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 @@ -11421,18 +11927,22 @@ 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.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.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() + 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()) @@ -11441,6 +11951,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.MaxMarkAbsent) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.Block.Payset s += config.MaxTxnBytesPerBlock diff --git a/agreement/player_permutation_test.go b/agreement/player_permutation_test.go index e41195d1f1..216316d8bd 100644 --- a/agreement/player_permutation_test.go +++ b/agreement/player_permutation_test.go @@ -25,19 +25,22 @@ 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" ) func makeRandomProposalPayload(r round) *proposal { f := testBlockFactory{Owner: 1} - ve, _ := f.AssembleBlock(r) + ub, _ := f.AssembleBlock(r, nil) + pb := ub.FinishBlock(committee.Seed{}, basics.Address{}, false) var payload unauthenticatedProposal - payload.Block = ve.Block() + payload.Block = bookkeeping.Block(pb) payload.SeedProof = crypto.VRFProof{} - return &proposal{unauthenticatedProposal: payload, ve: ve} + return &proposal{unauthenticatedProposal: payload} } var errTestVerifyFailed = makeSerErrStr("test error") diff --git a/agreement/proposal.go b/agreement/proposal.go index ac29970b16..e696bfeb4b 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" @@ -101,14 +102,24 @@ type proposal struct { validatedAt time.Duration } -func makeProposal(ve ValidatedBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { +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 + payload.OriginalPeriod = origPer + payload.OriginalProposer = origProp + 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 { 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 { @@ -141,15 +152,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 { @@ -189,31 +195,52 @@ func deriveNewSeed(address basics.Address, vrf *crypto.VRFSecrets, rnd round, pe return } -func verifyNewSeed(p unauthenticatedProposal, ledger LedgerReader) error { +// 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 verifyProposer(p unauthenticatedProposal, ledger LedgerReader) error { value := p.value() rnd := p.Round() + + // 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, lacking the current consensus params, we confirm that + // the Proposer is *either* correct or missing. `eval` package will using + // 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) + } + cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) if err != nil { - return fmt.Errorf("failed to obtain consensus parameters in round %d: %v", ParamsRound(rnd), err) + return fmt.Errorf("failed to obtain consensus parameters in round %d: %w", ParamsRound(rnd), err) } - balanceRound := balanceRound(rnd, cparams) - proposerRecord, err := ledger.LookupAgreement(balanceRound, value.OriginalProposer) + // Similarly, we only check here that the payout is zero if + // 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 obtain balance record for address %v in round %d: %v", value.OriginalProposer, balanceRound, err) + return fmt.Errorf("failed to determine incentive eligibility %w", err) + } + if !eligible && p.ProposerPayout().Raw > 0 { + return fmt.Errorf("proposer payout (%d) for ineligible Proposer %v", + p.ProposerPayout().Raw, p.Proposer()) } 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. @@ -239,27 +266,63 @@ 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 } -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) +// 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) + balanceRecord, err := ledger.LookupAgreement(balanceRound, proposer) if err != nil { - return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could not derive new seed: %v", err) + return false, basics.OnlineAccountData{}, err } - ve = ve.WithSeed(newSeed) - proposal := makeProposal(ve, seedProof, period, address) + // When payouts begin, nobody could possible have IncentiveEligible set in + // the balanceRound, 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.Payouts.MinBalance && + balanceRecord.MicroAlgosWithRewards.Raw <= balanceParams.Payouts.MaxBalance + return eligible, balanceRecord, nil +} + +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 { + 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(rnd, address, ledger, cparams) + if err != nil { + return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could determine eligibility: %w", err) + } + + proposableBlock := blk.FinishBlock(newSeed, address, eligible) + prop := makeProposalFromProposableBlock(proposableBlock, 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. @@ -272,15 +335,15 @@ 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 := verifyProposer(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 + return makeProposalFromValidatedBlock(ve, p.SeedProof, p.OriginalPeriod, p.OriginalProposer), nil } 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..f325c507d6 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{} @@ -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) { 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/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/config/consensus.go b/config/consensus.go index fc08e2990e..f86e45e831 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -447,8 +447,8 @@ 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 // EnableAccountDataResourceSeparation enables the support for extended application and asset storage @@ -529,6 +529,101 @@ 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 + + // 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 +} + +// ProposerPayoutRules puts several related consensus parameters in one place. The same +// care for backward compatibility with old blocks must be taken. +type ProposerPayoutRules 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 + + // 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 + + // 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 avoid 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 +} + +// BonusPlan describes how the "extra" proposer payouts are to be made. It +// 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 +// +// 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 { + // 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 + // 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 + // 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`. + DecayInterval uint64 } // PaysetCommitType enumerates possible ways for the block header to commit to @@ -603,10 +698,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 +// 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]) var MaxAppTotalArgLen int @@ -680,6 +779,7 @@ func checkSetAllocBounds(p ConsensusParams) { checkSetMax(p.MaxAppProgramLen, &MaxLogCalls) checkSetMax(p.MaxInnerTransactions*p.MaxTxGroupSize, &MaxInnerTransactionsPerDelta) checkSetMax(p.MaxProposedExpiredOnlineAccounts, &MaxProposedExpiredOnlineAccounts) + 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. @@ -1412,6 +1512,20 @@ func initConsensusProtocols() { vFuture.LogicSigVersion = 11 // When moving this to a release, put a new higher LogicSigVersion here + 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 = 70_000_000_000_000 // 70M algos + vFuture.Payouts.MaxMarkAbsent = 32 + vFuture.Payouts.ChallengeInterval = 1000 + vFuture.Payouts.ChallengeGracePeriod = 200 + vFuture.Payouts.ChallengeBits = 5 + + vFuture.Bonus.BaseAmount = 10_000_000 // 10 Algos + // 2.9 sec rounds gives about 10.8M rounds per year. + vFuture.Bonus.DecayInterval = 250_000 // .99^(10.8/0.25) ~ .648. So 35% decay per year + 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..bc11070125 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 if 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..0002a7b2f7 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 if 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 000825079e..59ab489183 100644 --- a/daemon/algod/api/Makefile +++ b/daemon/algod/api/Makefile @@ -33,6 +33,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 "$(SWAGGER_CONVERTER_API)/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 e327e0e3be..b88daf40f1 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" @@ -3016,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 be9237b90e..96be615f79 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -1067,6 +1067,18 @@ }, "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" + }, + "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 addb81b432..10ec183919 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" @@ -67,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[:], @@ -123,6 +124,7 @@ func AccountDataToAccount( Status: record.Status.String(), RewardBase: &record.RewardsBase, Participation: apiParticipation, + IncentiveEligible: omitEmpty(record.IncentiveEligible), CreatedAssets: &createdAssets, TotalCreatedAssets: uint64(len(createdAssets)), CreatedApps: &createdApps, @@ -137,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 } @@ -199,12 +203,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 @@ -340,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 @@ -350,11 +368,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, @@ -362,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/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/daemon/algod/api/server/v2/generated/data/routes.go b/daemon/algod/api/server/v2/generated/data/routes.go index 4a3438b8ff..caabafbfd7 100644 --- a/daemon/algod/api/server/v2/generated/data/routes.go +++ b/daemon/algod/api/server/v2/generated/data/routes.go @@ -192,134 +192,136 @@ var swaggerSpec = []string{ "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", - "npw8mnz8/wEAAP//6aRdnSH5AAA=", + "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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 e7e56520a4..b0e581df69 100644 --- a/daemon/algod/api/server/v2/generated/experimental/routes.go +++ b/daemon/algod/api/server/v2/generated/experimental/routes.go @@ -168,134 +168,136 @@ 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..0ea0473989 100644 --- a/daemon/algod/api/server/v2/generated/model/types.go +++ b/daemon/algod/api/server/v2/generated/model/types.go @@ -185,6 +185,15 @@ 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"` + + // 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 b866cc9f42..38fa807a82 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,217 @@ 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", + "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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 713e5cd7b0..c89afa7516 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,213 @@ var swaggerSpec = []string{ "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", - "bwAAAP//bsB3VeaoAQA=", + "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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 c0e0c71fe2..ba583b859a 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,142 @@ 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 0938833520..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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", - "/G1EEgEA", + "H4sIAAAAAAAC/+x9/XPctpLgv4Kb3Srb2qEkfyT77KvUnmInedrYsctSsvvW8iUYsmcGTyTAB4DzEZ//", + "9ys0ABIkwRmOpNjJbn6yNSSBRqPR6O/+MElFUQoOXKvJsw+TkkpagAaJf9E0FRXXCcvMXxmoVLJSM8En", + "z/wzorRkfDGZTpj5taR6OZlOOC2gecd8P51I+EfFJGSTZ1pWMJ2odAkFNQPrbWnerkfaJAuRuCHO7BDn", + "LyYfdzygWSZBqT6Ur3m+JYyneZUB0ZJyRVPzSJE100uil0wR9zFhnAgORMyJXrZeJnMGeaaO/SL/UYHc", + "Bqt0kw8v6WMDYiJFDn04n4tixjh4qKAGqt4QogXJYI4vLakmZgYDq39RC6KAynRJ5kLuAdUCEcILvCom", + "z95NFPAMJO5WCmyF/51LgF8h0VQuQE/eT2OLm2uQiWZFZGnnDvsSVJVrRfBdXOOCrYAT89UxeVUpTWZA", + "KCdvv31OHj9+/NQspKBaQ+aIbHBVzezhmuznk2eTjGrwj/u0RvOFkJRnSf3+22+f4/wXboFj36JKQfyw", + "nJkn5PzF0AL8hxESYlzDAvehRf3mi8ihaH6ewVxIGLkn9uU73ZRw/s+6KynV6bIUjOvIvhB8SuzjKA8L", + "Pt/Fw2oAWu+XBlPSDPruNHn6/sPD6cPTj//07iz5L/fnF48/jlz+83rcPRiIvphWUgJPt8lCAsXTsqS8", + "j4+3jh7UUlR5RpZ0hZtPC2T17ltivrWsc0XzytAJS6U4yxdCEerIKIM5rXJN/MSk4rlhU2Y0R+2EKVJK", + "sWIZZFPDfddLli5JSpUdAt8ja5bnhgYrBdkQrcVXt+MwfQxRYuC6ET5wQb9fZDTr2oMJ2CA3SNJcKEi0", + "2HM9+RuH8oyEF0pzV6nDLityuQSCk5sH9rJF3HFD03m+JRr3NSNUEUr81TQlbE62oiJr3JycXeP3bjUG", + "awUxSMPNad2j5vAOoa+HjAjyZkLkQDkiz5+7Psr4nC0qCYqsl6CX7s6ToErBFRAx+zuk2mz7v1+8/oEI", + "SV6BUnQBb2h6TYCnIoPsmJzPCRc6IA1HS4hD8+XQOhxcsUv+70oYmijUoqTpdfxGz1nBIqt6RTesqArC", + "q2IG0mypv0K0IBJ0JfkQQHbEPaRY0E1/0ktZ8RT3v5m2JcsZamOqzOkWEVbQzVenUweOIjTPSQk8Y3xB", + "9IYPynFm7v3gJVJUPBsh5mizp8HFqkpI2ZxBRupRdkDiptkHD+OHwdMIXwE4fpBBcOpZ9oDDYROhGXO6", + "zRNS0gUEJHNMfnTMDZ9qcQ28JnQy2+KjUsKKiUrVHw3AiFPvlsC50JCUEuYsQmMXDh2Gwdh3HAcunAyU", + "Cq4p45AZ5oxACw2WWQ3CFEy4W9/p3+IzquDLJ0N3fPN05O7PRXfXd+74qN3GlxJ7JCNXp3nqDmxcsmp9", + "P0I/DOdWbJHYn3sbyRaX5raZsxxvor+b/fNoqBQygRYi/N2k2IJTXUl4dsWPzF8kIRea8ozKzPxS2J9e", + "VblmF2xhfsrtTy/FgqUXbDGAzBrWqMKFnxX2HzNenB3rTVSveCnEdVWGC0pbiutsS85fDG2yHfNQwjyr", + "td1Q8bjceGXk0C/0pt7IASAHcVdS8+I1bCUYaGk6x382c6QnOpe/mn/KMjdf63IeQ62hY3clo/nAmRXO", + "yjJnKTVIfOsem6eGCYBVJGjzxgleqM8+BCCWUpQgNbOD0rJMcpHSPFGaahzpnyXMJ88m/3TS2F9O7Ofq", + "JJj8pfnqAj8yIqsVgxJalgeM8caIPmoHszAMGh8hm7BsD4Umxu0mGlJihgXnsKJcHzcqS4sf1Af4nZup", + "wbeVdiy+OyrYIMKJfXEGykrA9sV7igSoJ4hWgmhFgXSRi1n9w/2zsmwwiM/PytLiA6VHYCiYwYYprR7g", + "8mlzksJ5zl8ck+/CsVEUFzzfmsvBihrmbpi7W8vdYrVtya2hGfGeIridQh6brfFoMGL+XVAcqhVLkRup", + "Zy+tmJf/6t4Nycz8PurjPwaJhbgdJi5UtBzmrI6DvwTKzf0O5fQJx5l7jslZ99ubkY0ZJU4wN6KVnftp", + "x92BxxqFa0lLC6B7Yu9SxlFJsy9ZWG/JTUcyuijMwRkOaA2huvFZ23seopAgKXRg+DoX6fVfqVrewZmf", + "+bH6xw+nIUugGUiypGp5PIlJGeHxakYbc8TMi6jgk1kw1XG9xLta3p6lZVTTYGkO3rhYYlGP3yHTAxnR", + "XV7jf2hOzGNztg3rt8Mek0tkYMoeZ+dkyIy2bxUEO5N5Aa0QghRWwSdG6z4IyufN5PF9GrVH31ibgtsh", + "t4h6hy43LFN3tU042NBehQLq+Qur0WkoVERrq1dFpaTb+NrtXGMQcClKksMK8i4IlmXhaBYhYnPnfOFr", + "sYnB9LXY9HiC2MCd7IQZB+Vqj9098L1wkAm5H/M49hikmwUaWV4he+ChCGRmaazVZzMhb8aOO3yWk8YG", + "T6gZNbiNph0k4atVmbizGbHj2Rc6AzVuz91ctDt8DGMtLFxo+htgQZlR7wIL7YHuGguiKFkOd0D6y+gt", + "OKMKHj8iF389++Lho58fffGlIclSioWkBZltNShy3ymrROltDg/6K0N1scp1fPQvn3jLbXvc2DhKVDKF", + "gpb9oaxF2MqE9jVi3utjrY1mXHUN4CiOCOZqs2gn1tlhQHvBlBE5i9mdbMYQwrJmlow4SDLYS0yHLq+Z", + "ZhsuUW5ldRe6PUgpZPTqKqXQIhV5sgKpmIi4l964N4h7w8v7Zfd3Cy1ZU0XM3GgLrzhKWBHK0hs+nu/b", + "oS83vMHNTs5v1xtZnZt3zL60ke9Nq4qUIBO94SSDWbVoqYZzKQpCSYYf4h39HWgrt7ACLjQtytfz+d3o", + "zgIHiuiwrABlZiL2DSM1KEgFt6Ehe9RVN+oY9HQR422WehgAh5GLLU/R8HoXx3ZYky8YRy+Q2vI0UOsN", + "jDlkixZZ3l59H0KHneqeioBj0PESH6Pl5wXkmn4r5GUj9n0nRVXeuZDXnXPscqhbjLMtZeZbb1RgfJG3", + "w5EWBvbj2Bo/y4Ke++Pr1oDQI0W+ZIulDvSsN1KI+d3DGJslBig+sFpqbr7p66o/iMwwE12pOxDBmsEa", + "DmfoNuRrdCYqTSjhIgPc/ErFhbOBABb0nKPDX4fynl5axXMGhrpSWpnVViVBd3bvvmg+TGhqT2iCqFED", + "zrzaC2vfstPZ4IhcAs22ZAbAiZg5j5nz5eEiKfritRdvnGgY4RctuEopUlAKssRZ6vaC5t+zV4fegScE", + "HAGuZyFKkDmVtwb2erUXzmvYJhg5osj9739SDz4DvFpomu9BLL4TQ29t93Bu0T7U46bfRXDdyUOyoxKI", + "v1eIFijN5qBhCIUH4WRw/7oQ9Xbx9mhZgUQH5W9K8X6S2xFQDepvTO+3hbYqB+IhnXprJDyzYZxy4QWr", + "2GA5VTrZx5bNSy0d3Kwg4IQxTowDDwheL6nS1qnOeIa2QHud4DxWCDNTDAM8qIaYkX/yGkh/7NTcg1xV", + "qlZHVFWWQmrIYmvgsNkx1w+wqecS82DsWufRglQK9o08hKVgfIcsuxKLIKpr35OLOukvDj005p7fRlHZ", + "AqJBxC5ALvxbAXbDmLABQJhqEG0Jh6kO5dSBaNOJ0qIsDbfQScXr74bQdGHfPtM/Nu/2iYvq5t7OBCgM", + "RXPvO8jXFrM2GnBJFXFwkIJeG9kDzSDW+9+H2RzGRDGeQrKL8lHFM2+FR2DvIa3KhaQZJBnkdNsf9Ef7", + "mNjHuwbAHW/UXaEhsWFd8U1vKNlH0ewYWuB4KiY8EnxCUnMEjSrQEIj7es/IGeDYMebk6OhePRTOFd0i", + "Px4u2251ZES8DVdCmx139IAgO44+BuABPNRD3xwV+HHS6J7dKf4Gyk1QyxGHT7IFNbSEZvyDFjBgQ3UR", + "88F56bD3DgeOss1BNraHjwwd2QGD7hsqNUtZibrO97C9c9WvO0HU70oy0JTlkJHggVUDy/B7YgOSumPe", + "TBUcZXvrg98zvkWWkzOFIk8b+GvYos79xka6BqaOu9BlI6Oa+4lygoD6+DkjgoevwIamOt8aQU0vYUvW", + "IIGoalYwrW0Ee1vV1aJMwgGifo0dMzqvZtSnuNPNeoFDBcvrb8V0YnWC3fBddhSDFjqcLlAKkY+wkPWQ", + "EYVgVAAMKYXZdeaC6X04taekFpCOaaNLu77+76kWmnEF5G+iIinlqHJVGmqZRkgUFFCANDMYEaye04W6", + "NBiCHAqwmiQ+OTrqLvzoyO05U2QOa5+BYl7souPoCO04b4TSrcN1B/ZQc9zOI9cHOnzMxee0kC5P2R9q", + "4UYes5NvOoPXXiJzppRyhGuWf2sG0DmZmzFrD2lkXJgJjjvKl9Ny2ffXjft+wYoqp/ouvFawonkiViAl", + "y2AvJ3cTM8G/WdH8df0ZZtdAamg0hSTFnJCRY8Gl+camkZhxGGfmANsQ0rEAwbn96sJ+tEfFbKL0WFFA", + "xqiGfEtKCSnY7AkjOap6qcfExlWmS8oXqDBIUS1cYJ8dBxl+paxpRla8N0RUqNIbnqCRO3YBuGBun0Bj", + "xCmgRqXrWsitArOm9XwuZ2rMzRzsQddjEHWSTSeDGq9B6qrReC1y2llAIy6DlrwX4KeZeKQrBVFnZJ8+", + "vsJtMYfJbO5vY7Jvho5B2Z84CDVsHg5FGxp1O9/egdBjByISSgkKr6jQTKXsUzEPM/7cHaa2SkPRt+Tb", + "T38eOH5vB/VFwXPGISkEh200yZ1xeIUPo8cJr8mBj1FgGfq2q4O04O+A1Z5nDDXeFr+4290T2vVYqW+F", + "vCuXqB1wtHg/wgO5193uprypn5TmecS16PKBugxATev6A0wSqpRIGcps55ma2oPmvJEueaiN/jd1lPMd", + "nL3uuB0fWphqijZiyEtCSZoztCALrrSsUn3FKdqogqVGgp+8Mj5stXzuX4mbSSNWTDfUFacY+FZbrqIB", + "G3OImGm+BfDGS1UtFqB0R9eZA1xx9xbjpOJM41yFOS6JPS8lSIxAOrZvFnRL5oYmtCC/ghRkVum29I/p", + "bkqzPHcOPTMNEfMrTjXJgSpNXjF+ucHhvNPfH1kOei3kdY2F+O2+AA6KqSQepPWdfYoBxW75SxdcjOUJ", + "7GMfrNnk307MMlsp9//3/r89e3eW/BdNfj1Nnv7LyfsPTz4+OOr9+OjjV1/9v/ZPjz9+9eDf/jm2Ux72", + "WDKWg/z8hdOMz1+g+tP4gHqwfzL7f8F4EiWyMJqjQ1vkPiYeOwJ60DaO6SVccb3hhpBWNGeZ4S03IYfu", + "DdM7i/Z0dKimtREdY5hf64FKxS24DIkwmQ5rvLEU1Y9rjKc9olPSZTLieZlX3G6ll75tVo+PLxPzaZ3a", + "aqvePCOY97ikPjjS/fnoiy8n0yZfsX4+mU7c0/cRSmbZJpaVmsEmpiu6A4IH454iJd0q0HHugbBHQ+ls", + "bEc4bAHFDKRasvLTcwql2SzO4XyuhLM5bfg5t4Hx5vygi3PrPCdi/unh1hIgg1IvY9UwWoIavtXsJkAn", + "7KSUYgV8StgxHHdtPpnRF11QXw50jlUZUPsUY7Sh+hxYQvNUEWA9XMgow0qMfjppAe7yV3euDrmBY3B1", + "56z9mf5vLci97765JCeOYap7NkHaDh2ktEZUaZe11QpIMtzM1gCyQt4Vv+IvYI7WB8GfXfGManoyo4ql", + "6qRSIL+mOeUpHC8EeeYTwV5QTa94T9IaLNMVpOCRsprlLCXXoULSkKctvdIf4erqHc0X4urqfS82o68+", + "uKmi/MVOkBhBWFQ6cYUjEglrKmO+L1UXDsCRbWWYXbNaIVtU1kDqC1O48eM8j5al6iYQ95dflrlZfkCG", + "yqXHmi0jSgvpZREjoFhocH9/EO5ikHTt7SqVAkV+KWj5jnH9niRX1enpYyCtjNpf3JVvaHJbwmjrymCC", + "c9eoggu3aiVstKRJSRcxF9vV1TsNtMTdR3m5QBtHnhP8rJXJ6wPzcahmAR4fwxtg4Tg4KxEXd2G/8kXC", + "4kvAR7iF+I4RNxrH/033K8jtvfF2dfKDe7tU6WViznZ0VcqQuN+ZunbQwghZPhpDsQVqq67M0gxIuoT0", + "2tW/gaLU22nrcx/w4wRNzzqYspWRbGYe1uZAB8UMSFVm1InilG+7RRIUaO3Dit/CNWwvRVPa45CqCO0k", + "fTV0UJFSA+nSEGt4bN0Y3c13UWWo2Jelz3XHpEdPFs9quvDfDB9kK/LewSGOEUUriXwIEVRGEGGJfwAF", + "N1ioGe9WpB9bHuMpcM1WkEDOFmwWK+r4H31/mIfVUKWrY+WikOsBFWFzYlT5mb1YnXovKV+AuZ7NlSoU", + "zW2NvmjQBupDS6BSz4DqnXZ+Hibje+hQpVybk2UtfFOzBNiY/WYaLXYc1karQEORfcdFLx8Px59ZwCG7", + "ITz+80ZTOB7UdR3qIvWr/K1cY7dWa11oXkhnCJd9XgAWwBNrsy8GCuFqt9kSAcH9Uim6gAHdJfTejUzE", + "b3n8cJB9EklUBhHzrqjRkwSiINuXE7Pm6BkG88QcYlQzOwGZfibrIHY+IyzJ6hA2y1GArSNX7d5T2fKi", + "2hqTQ6DFWQtI3oiCHow2RsLjuKTKH0esvue57Cjp7DcsebGr0NF5EEsYlNiryxj527DLQXt6vyt35Gsc", + "+cJGodI/okiR0b0wfSG2HYKjaJpBDgu7cPuyJ5Sm/EazQQaO1/M58pYkFpYYGKgDAcDNAUZzOSLE+kbI", + "6BFiZByAjYEPODD5QYRnky8OAZK78iHUj41XRPA3xBP7bKC+EUZFaS5XNuBvTD0HoC6WtZYsOhHVOAxh", + "fEoMm1vR3LA5p4s3g/Tq7aBC0amu40JvHgwpGjtcU/bKP2hNVki4yWpCadYDHRe1d0A8E5vEZvZGdZHZ", + "ZmboPZq7gHnGsYNpKxvdU2QmNhjOhVeLjZXfA8swHB6MwPayYQrpFb8bkrMsMLum3S3nxqhQIck4Q2tN", + "LkOC3pipB2TLIXK5HxQruhEAHTNUU/nbmSX2mg/a4kn/Mm9utWlThM+nhcWO/9ARiu7SAP769rG6vNCb", + "rsQStSC1o5LalZUC4T5G9IZN9N1nfSedghxQXUtaQlRyHfNpG60T8Ma58J8FZiWs30T59kEQ6iZhwZSG", + "xr3hI1g+h+GYYtlIIebDq9OlnJv1vRWivqasgxc/bC3zk68AY8XnTCqdoG8ougTz0rcKzR3fmlfjslI7", + "mM4WWWZZnDfgtNewTTKWV3F6dfN+/8JM+0PNElU1Q37LuA0lmmFR8GiI7Y6pbRT2zgW/tAt+Se9sveNO", + "g3nVTCwNubTn+IOciw7n3cUOIgQYI47+rg2idAeDDFKj+9wxkJuC6IvjXXbx3mHK/Nh746l8gvbQHWVH", + "iq4lMOXsXAVDB54RS5gOamr3c5YHzgAtS5ZtOlZqO+qgxkwPMkX5SoQdLODuusH2YKAdMRkNQG9VcXRx", + "mc4ad4IC8okR4WygpotCBIlajs3WzSqJ5s5WGGS/ZGgt2I1c+/c/XWgh6QKcyTqxIN1qCFzOIWgICnIq", + "opn1PWdsPofQVKtuYmZsAdczyGUjSDdCZHF7bsW4/vJJjIz2UE8D436UxSkmQgtDDrzLvknci1WB3ln3", + "lAm25gZ27Whu7/ewTX4yGgopKZOqieVzNuo2/ztg11fF97DFkfeGyBnA9uwKqqlvAWkwZhasH9mUlloF", + "CqvLYjmO1hYesFNn8V26o61x9YCHib8JmG/Vy20v5TYHo/GoGljG7MZF3JFpTg+0Ed8l5X2bwAaMcSE5", + "BiJXOBVTvntS/yqqE9f30e4l0NwTLy5n8nE6uZ3bMHabuRH34PpNfYFG8YxhadaN1IoCOBDltCylWNE8", + "cc7VoctfipW7/PF174v9xMJknLIvvzl7+caB/3E6SXOgMqmVscFV4XvlH2ZVtoLw7qsEJRZvFbHKerD5", + "ddnT0CG7XoJrcxHo+7163I2zPTiKzkE7j0fH7uV9Li7ALnFHfACUdXhA4yCx0QHtiAC6oiz3ngkP7UAk", + "Ky5uXFH3KFcIB7h1ZEEQIJLcKbvpne746Wioaw9PwrleYx27uMbBXZU7ZEUuUoDeufT0rZAt5u/SmKKR", + "Br+dWGWEbIvHgcBO3zqpK0wdEyt4/bL4xZzGo6PwqB0dTckvuXsQAIi/z9zvqF8cHUVdDVFLgmESaCjg", + "tIAHdUj24EZ8WrMTh/W4C/psVdSSpRgmw5pCbciAR/faYW8tmcNn5n7JIAfz0/6sx86mW3SHwIw5QRdD", + "aUt1RFphuzUpIng3ABMz5gxpIbMvKNajt56b/hHiVYHejkTlLI37gflMGfbKbeSVeZngywMGMzNixQYC", + "+XjFgrHMa2MKLHaADOaIIlNFazw2uJsJd7wrzv5RAWGZ0WrmDCTea52rzisHOGpPIDWqZ38uN7CNImiG", + "v40dJOzF0JUZEYjdRpAwzqsH7ovarO8XWnvNGp3p0HDRcMYe494R6unow1GzTX1ZtuO1xukxY7p2ekbn", + "mkIMzBHtwslUMpfiV4jbotGEH8ma990nGMZI/wo8FubTZSm1B6ppJtrMvm+7x+vGQxt/a13YL7pueHGT", + "yzR+qg/byJsovSpe29UheUgJC92R7TjiAdaCxyuInMNeAz5UgXJ7nmzKeCsdJX4qw8SvEzt+cyodzL1k", + "uZyuZzTWiMHoQgamYHtbQRVaEP+x3wBVJ0Tb2UkQ7lm/y2zZqRJkUzWkX8LyhnqNnXa0RtMoMEhRoeoy", + "tYFguRKRYSq+ptw2sDTfWX7lvlZgvaDmq7WQWDROxeM/MkhZETXHXl29y9K+rz9jC2Z7M1YKguZ/biDb", + "99ZSkWugWKf5O9Scz8npNOhA6nYjYyum2CwHfOOhfWNGFV6XtUey/sQsD7heKnz90YjXlxXPJGR6qSxi", + "lSC17olCXh3FNAO9BuDkFN97+JTcx/gtxVbwwGDRCUGTZw+fovfd/nEau2Vdb81dLDtDnu0jO+N0jAFs", + "dgzDJN2o8VBN21x7+HbYcZrsp2POEr7pLpT9Z6mgnC4gHsxd7IHJfou7iR7VDl649QaA0lJsCdPx+UFT", + "w58GEkQN+7NgkFQUBdOFi/JRojD01HT2s5P64WybWdeUxcPlH2KwXOljhTq2rk+sxtBiIMEDQxp/oAW0", + "0Tol1FYKzFkTxupbRZFzX4gUu9TUzWksbsxcZukoS2JU65yUknGN9o9Kz5O/GLVY0tSwv+MhcJPZl08i", + "3V7aDRH4YYB/crxLUCBXcdTLAbL3Mov7ltzngieF4SjZgyYhOziVg1F98fitoSCy3UOPlXzNKMkguVUt", + "cqMBp74V4fEdA96SFOv1HESPB6/sk1NmJePkQSuzQz++femkjELIWHXx5rg7iUOClgxWmF4T3yQz5i33", + "QuajduE20H/eEBQvcgZimT/LUUUg8Gjuyqw1UvxPr5oyyehYtWlLHRugkBFrp7PbfeKAr8Osbl3/rY3Z", + "wWcDmBuNNtuDv4eVgVBdG4tbf/OJE62j5l675y2D48NfiDQ6OMrxR0cI9NHR1InBvzxqP7bs/egoXq00", + "anIzvzZYuI1GjN/G9vBrETGA+dZgdUCRS6aOGCCHLinzwDDBmRtqStptmD69FHE3ySDxgL/4Kbi6eodP", + "PB7wjy4iPjOzxA1sQpqHD3u7DV2UZLL6eRBqTMnXYjOWcDp3kCee3wGKBlAy0jyHK+m12Yu66/fGiwQ0", + "akadQS6Mkhl2EAnt+X8cPJvFT3dgu2J59lNTCKpzkUjK02U0UHNmPvy5aYdfL9GyymhTgiXlHPLocFa3", + "/dnrwBEt/e9i7DwF4yPf7bZ5tMvtLK4BvA2mB8pPaNDLdG4mCLHarrFT53DnC5ERnKepgN8wx36/1KCJ", + "2z8qUDp2NPCBzVZCZ5dhvraHGAGeofXrmHyH1S4MLK3yxmh18oUj20XUqjIXNJtiQcvLb85eEjur/cY2", + "dbY9zBZodGmvImolH19Uru7PHK+WMH6c3enbZtVKJ3XLsVg9KvNG0xSNdUIn0BwTYueYvLCWMOXtLHYS", + "gmVRZQFZ0OHM6mJIE+Y/WtN0iSam1kU2TPLjm+95qmwM8EEn77rjBZ47A7frv2fb702J0EuQa6YAszBh", + "Be0SWHU9OGfi9CWx2suTFeeWUo4PkCnq/haHot0DZwUS7xuOQtZB/IEGBtu78tBehBf4VbQAd7exYcd5", + "6wsq1R2aXzkbcUq54CzF8tcxgQjL9YzzNo2oFB53E6mJO6GRwxVtp1jnfzksDjZY9IzQIa7vuQ2emk21", + "1GH/1LBxbXYWoJXjbJBNfVdQ59dgXIHrYGKIKOSTQkZiU6Lx7LUf/EAywkocA4aqb82zH5wZExOhrxlH", + "g4VDmxOzrechVwwdjJwwTRYClFtPuxyZeme+OcbKXBls3h+/FAuWXrAFjmGjocyybehff6gzHwjoAu/M", + "u8/Nu65ecv1zK6rHTnpWlm7S4Z6x8UbZGz6I4Fj4iY8HCJBbjx+OtoPcdkbw4n1qCA1WGHwEJd7DPcKo", + "+6d2mpUbFcFSFL5BbG5StGgi4xEwXjLuPWHxCyKNXgm4MXheB75TqaTaioCjeNol0Hwgjh1z/awr9bZD", + "datFG5TgGv0cw9vYtH4dYBz1C43gRvmW+ENhqDsQJp7TvI6AjTRyRanKCVEZ5oh0WrvGGIdh3L55dPsC", + "2NMvftp8jhXYD72JhupSzapsATqhWRYrZ/I1PiX41Of6wAbSqm48UpYkxTKs7bq0fWpzE6WCq6rYMZd/", + "4ZbTBb2SI9QQ9mv2O4zVFWZb/PeQTv517OvB+W0+0DU7rBhzP18vJvUamk4UWyTjMYF3yu3R0Ux9M0Jv", + "vr9TSs/Fog3I5zCSDnC5cI9i/O0bc3GExRp7Ycb2aqlrKWJIr8DnvshFXQWszZXwKuv1lkHndd1Bf7cZ", + "YrgX/hQvv4Gc0tDkbe9XawYeyixNBxOhqXYlWTQlO1nQYJkLG/LZMaL3PUFDYZ42yvPujM9urTsROuyC", + "+b7lcLGhPg2zGHS03MwX0mzwoc6Q71dDyca+Njs+7/bKvgZXQa+UsGKi8kE0PpTVq4T211bn6TrdO7r+", + "aID45zY+D5rKL13PQrtMp5N//5N1phHgWm5/B4bz3qb3unD3pV1rnmpeIXW7q1Htr1q34pi+BbES+U42", + "bPUB39PFvEdWL8aIA/2u5NPJeXbQhRlrszCxo8SOXbzH+HAV6qbyNB6xUijWdJ2LNR8fGTN+if3Dgyra", + "/bF8LOEKUo2tBpsYKQlwSE1tM5m33f9ZjXpYna5D610R6l2Vp/v9Bffc8b0SJEEZHdub7Xh8neWzOhLW", + "JvKsqcKuBBJt3O3U19EJePM5pFgJc2fJl/9YAg/KiUy9XQZhmQcVYFidjoK1XA+3OjYA7arIshOeoKfC", + "rcEZSke+hu09RVrUEG0WV+di3aRYJGIAuUPi64YOGZJd8A9TNWUgFnxkpyu/2RREH6zzGRQwuuFcniTN", + "xdEUNdoxZbzR7ai5zKcHlfrCzIqhqjD9PpnD+scLbEuqXJwTrYtNhlo6Oe83S1i7YpVYoKf2nfiylaD8", + "b74al50lZ9cQdsJGT9Waysy/ETW9eKtOsuM+6pVy8T0eu0DP65lZE4ff91VHym9jSkuaCyNGJEN5Qe3Q", + "9zpu7J6yAX5NHRaEaw5SWgpA+TcXChItfNz+Ljh2ocJGMd4ICWqw5YUFbrDc6dumniu2/qFY3pS64MVw", + "gURCQQ10Mqi6OjznLmQ/t899LrVv/bLXwlTT6/4ehD4Dg6keEkOqnxN3W+7P0b6JsYlxDjLxnqduCVYO", + "su0NKaXIqtRe0OHBqA1yo0ug7GAlUTtN2l9lR0cIcp2vYXtilSDfvNHvYAi0lZws6EHpvs4m36n5TcXg", + "XtwJeJ/TcjWdlELkyYCz47xfN7ZL8dcsvYaMmJvCRyoP9OUl99HGXnuz18utr5NalsAhe3BMyBm3uSHe", + "sd1uKdWZnN/Tu+bf4KxZZUs5O6Pa8RWPB9ljkWV5S27mh9nNwxQYVnfLqewge6qSbgZq1kq6jnSpPh6r", + "lfddzd3OwQ1RWShiMsmF9Vg9x4MeMxxhJntQcgEdmZQ4TxdRuYiFZN4k294MFcdUOBkCpIGPSfquoXCD", + "RxEQ7YUbOYW2gpmrXSbmRELjRL5pEbd+296YRt+duZ6lze/mQkKrAa/5WsjMizxMNZ2yqZwxLanc3qTU", + "Wq9tcM96MojlveFYdSRWs5AmGquPwzwX6wSZVVLXNo+ptuY91b6MfaOd5jtzqmcQxHVR5QS1LVnSjKRC", + "SkjDL+JpexaqQkhIcoFhXjEP9FwbubvAXB1OcrEgokxFBrZHQJyChuaqOKcoNkEQVRNFgaUdTPq03wR0", + "PHLKu+pZbYvz2EUn1pc5EHgKyhXjcRiyL/fh3dHv+aDq/OdztAgxjHVp515b6TPseg0HNr1mee4NBkN9", + "r8mPqsJwJEy8MVM8IYVQ2ml2diRVD9WEeN1PBddS5HnbCGRF4oWzbL+im7M01S+FuJ7R9PoB6pFc6Hql", + "2dSnpXaD8ZqZZKci08gG3ZfLiJ0XZ/Gn7uAu3I5zHNw8NwDz/X6Otd/GfRZrMt5eV7drPh+onalFwdI4", + "Df+xotsGY9JiLCFa6sn2r7LJ+fgaMurwcqiDGZAl9dEMnEYb8JwRx9OcUxeZh/kvSrzdcckc3CUxcDH1", + "+aSTWpJ0ULbqAICQ2oxRXUnb9CqUfGquIhY2wxxd0l1AR3JxjPy5HWxmhDsHSsOtgOpFG9YA3rfK/tSW", + "5LKRizOx8c8fNDW7bgT8x91U3mIeQyFVFw1pSRtU5et7DHCEeGXgnfFH2NLd36D7o5DqBoUjb9QAgOG4", + "pBYMo6KTDgVjTlkOWRLrb3Ve24SmgWbrMlq6bWeZcpw8pZVvL2XGriS4ehNWpO60qS+pISVRv9633PIM", + "NqCwGITttU2V9TN4fwfktq1UR/kWZZLDClrhWq4IRoWiHVuB/1bVH5MMoETvX9cmFYtDCu/yjqHCrT0J", + "IlnGYDdqubCItTtF9pglokaUDU/sMVFjj5KBaMWyirbwpw4VOdpmN3OUI6jqyeSJ19vGTvOjHeGtH+DM", + "fx8TZTwm3o/jQwezoDjqdjGgvXGJlRo69TwelhhWeKkdGjhbVjs+LYk3fEOVdM2HDYB9km/Um5H7xAQP", + "EPvNBlKUatpxd7fHCcHBiOpUbxoUwWW9wzc3JH8WGt5JwoPjxVQNBchgd1pqPF04gR1fwEaj3Ii9RmrG", + "FlKO/zv+NyWzyg9k9Grb0SrU4F6A99hhQenaWeEEWlZfaD6+cOrqCXaVchZEVhd0S4TEf4y+9o+K5my+", + "xRNqwfefEbWkhoSci9D6rl28opl4t2Ay9YB5u4DwU9l1s7FjBsNtzSgB0OYKdMYprAx0DeE2oFvecp5U", + "G5ajqlnBlMLLrrOdfSy4xfuaEAXNQh0ZK9O1m7z6WqXm6//dZG2FU/mCUmVOU9+/DIiiRccgbnsUeuLS", + "Syh2p/X11WNPAnXfw4ZopU/nzW5g3DswciMWKz/U76EFdq8fXK/Vxa2WcUjr6CYzekdC5Kil3PUujI0P", + "6QGNTmZf1WsP+LYao68A9inwHy0aObSMMeD/XvA+0EYvhNd2zPsEWG6l/EdgtXbVmdgkEuZqXyiENawa", + "RVg2xQK8cZLxVAJVNjbk/LVT2ZqaiIwbFdJGL9bet3qUDOaMN8yS8bLSEQ0ASyPybYCw0DyNaB1w9gxJ", + "CUYMW9H89QqkZNnQxpnTYdt4hTXpvUnefRtR/us7tT8AU432g5mE0GSqBa+ZC9x2vbGBhUpTnlGZha8z", + "TlKQ5t4na7pVN/d9GGhlZeSLPd4PGkgz7fz2wA+CpG0BybfOfXlLz0QNIL1DF8UI1wJGsEbcCtYoosWA", + "J6EPQ7ysAt0kuVhgftkAAbrik+j7scqK4GiwtfLQYfMo9ivsngbrbruDrwXOOmaK3efsNaIOFZ4fOdM7", + "T5q1pnUT/mxEpj0Inv75ogkLt5vTp/9YjuYlJjG08jS9cOeTGPxe2/AQOx8MeDLaFtyBXUQHuUvwDc21", + "4/sZtX3wsUxQq8MmqNuqHYHfoJogZ5q6wJ2+0aenFFukTF0e7YE2IWtJ9vfAAHi2U607W+1p62AKM84h", + "TaB2Z84mpSiTdEw0oC3NnzmDtoO0DeMAfQTm6oF114ETqm5W0Sps0upacWgfrMGuGfv8MmW6S8keMmgM", + "cNC2sVzMkZfhEbZmHMzxqI0X0272UdtgUzMJQomEtJJo0FzT7f6+QgMlYS/+evbFw0c/P/riS2JeIBlb", + "gGrKCnf68jQRY4x37SyfNkastzwd3wSfl24R5z1lPt2m3hR31iy3VU3NwF5XokMsoZELIHIcI/1gbrRX", + "OE4T9P372q7YIu98x2Io+O33TIo8j5d1r0W3iKk/tluBsd9I/CVIxZQ2jLDtq2O6iZVVSzTHYXHPla0z", + "Injqqq/XVMD0QDBObCFDoZbIzzDr1/k3CGzK3PEq65PYtS6nF1mLGAZnYPzGDEgpSidKszmJQYS5JTLI", + "uXSGRgzvDKIna2Zr4yhjhOhikuOkd8ad5inmZDe3b3dr1HFObzYxIl74Q3kD0hyypA9ntN+EkzSm9N8N", + "/4ik6N8Z16iX+1vwiqh+cLPGx6NA66drR8gDARjIw2xl0IV90ZtKo9Ja5dF+712dXfHjVeMC3ZswgJD4", + "D/aAFyZWNu/VMe4OnM9csvNVjZRgKe+HKKG1/H25mp711hdJsEXOSKE1KMuWRF8sDBJx1fM6v3VAK+ml", + "wWITdKOZ5nkkfdbaTfBMhYRjVAK5ovmn5xrYHf8M8QHZ2+GkmTCHMkSyRaW6WQW3l3TU3EG+5N1Nzd9g", + "yu5/gNmj6D3nhnLu4t5thlYvbEm98LeCzQImaxzThgM9/JLMXDX9UkLKVNcNvfbCSZ0yCJLNXeglbPSe", + "HMV96/xJ6FuQ8dzHjJAfAneSQLNdA2FzRD8zUxk4uVEqj1Ffjywi+IvxqLD75p7r4paV129WECQo7XVg", + "QZB+X9Gxy7NFL8ylUynor3P0bd3CbeSibtY2tprN6ALuV1fv9GxMEZp4sXXzOVbBuZOq6wfVXP8N6t9Y", + "HLkx3LwxivlpqCKqrfo5UHy3sx8Vy/cGiLRKKX+cThbAQTGFxYJ/ds0hPu1d6iGwOfn9o2phvU0hEYuY", + "yFpbkwdTBUWSR9RHdp9FqiFjvltaSaa32BjUG9DYz9FKPd/VVR9c1ZDad+XuPi2uoW7O3NSIqJS/Xb8T", + "NMf7yLrUuLmFRH5MvtnQosydOZh8dW/2r/D4L0+y08cP/3X2l9MvTlN48sXT01P69Al9+PTxQ3j0ly+e", + "nMLD+ZdPZ4+yR08ezZ48evLlF0/Tx08ezp58+fRf7xk+ZEC2gPra3c8m/5mc5QuRnL05Ty4NsA1OaMm+", + "B7M3qCvPBTauM0hN8SRCQVk+eeZ/+j/+hB2nomiG979OXAOWyVLrUj07OVmv18fhJycLTApPtKjS5Ymf", + "B9uJteSVN+d1NLmNe8EdbazHuKmOFM7w2dtvLi7J2Zvz44ZgJs8mp8enxw9d71pOSzZ5NnmMP+HpWeK+", + "nzhimzz78HE6OVkCzbGGivmjAC1Z6h9JoNnW/V+t6WIB8hgTBuxPq0cnXqw4+eCS4z/uenYShlScfGjV", + "EMj2fInhACcffAfL3W+3uhe6SCyz9Kgj8DvQrlyOtRBEai2gP8CNPiVKSJdTXEomzKmamisyA/SWY9CX", + "xALQWlY8tS5UOwVw/O+rs/9EN/Krs/8kX5HTqQtgV6h2xKa3GbM1OZxnFux+9J76entWV6NoXM6TZ+9i", + "piDXqaqsZjlLiZUm8DgZWgmovR6x4WZo95s0rcQb3mz47Wny9P2HL/7yMSbz9STYGklBgYYQ9Vr4BoSI", + "tIJuvhpC2cZFNJtx/1GB3DaLKOhmEgLc93NGqlb5hBPfhzWM2Ati+f794vUPREjidNw3NL2uk218dlWT", + "URYmV5kvhyB2118INPCqMDeJy9op1KJsF3Ct0fwem5YhoHjoH52eek7n9Ijg9J24Qx3M1DE+9QkNg1cC", + "c2I/lVkR2NBU51tCVRA9gLF8vsFgJyVKlEkrMHunAbM/o9uSaFT7odnUkQrjQtN8D3yXnWZsLXS4QJjS", + "XIX705d7yIhC8D522Ydb62nkz93977G7fdmBlMKcaYbRys2V46+zFpBOYsy3HtyBQhHH5G+iQgnPyO6V", + "hlgrapzBei7cnK6uTRBe1qSi4JOjo+7Cj46aYLg5rJHJUo4vdtFxdHRsdurJgaxspzW5VQZ21Nk5ZLje", + "Zr2imzqWmBIueMJhQTVbAQnUwienD/+wKzznNnrbiLRW9P44nXzxB96yc24EG5oTfNOu5vEfdjUXIFcs", + "BXIJRSkklSzfkh95HR4fdDrus78f+TUXa+4RYbTKqiio3DohmtY8p+JB35ad/KdXoaYRtJGL0oXCiBUU", + "Ua1M66vY8cXk/UevA4xULHa9djLDRnRjXwUVvDysnaD/QJ18QAv44O8nzo0Zf4ieCKvinvjaefE3W4rP", + "B70xsO75YsOyYCUp1emyKk8+4H9QIQ2AtnXVT/SGn2Aw5MmH1lrd495a2783n4dvrAqRgQdOzOe2Wf+u", + "xycf7L/BRLApQTJz42AtQ/errTl7gj1bt/2ftzyN/thfR6ve5sDPJ94eElOJ229+aP3ZJhu1rHQm1sEs", + "6EmwbrA+ZOZhpbp/n6wp00YOcmUescl7/2MNND9xPV06vzZl1HtPsDZ88GNHciqFrfPSVlrf0vVlK31Q", + "2voKXws0NAzx1E0yYxwZTcgIG/ugfdjXgnrs73IJNhDWu1gjYqYWZCYFzVKqsHe4637UU38/3lLF6paD", + "OI840BBMtCj0KwYalnG816uC446RI4N9Iecv/IRN5tVvLnv1IPqaZsQXBkrIK5qbDYeMnDkJv4WN31pu", + "+vyCzmeWTD6ZKPG1P3yKUKyS1tIBZbzOStCmbIzcYBRFwwAWwBPHgpKZyLauk9RE0rXe2LIOXeZ2Qts3", + "RtvWSCUt1NDDOzBE/r6tj/uMjn/a+v609f1pDfrT1vfn7v5p6xtp6/vTEvanJex/pCXsEPNXTMx05p9h", + "aRNbW9PWvFbvo00LgZrFtwtOMV3LZK18TuxWwPQxIZdY84SaWwJWIGlOUqqsdOUKaxUYZollqyB7dsWT", + "FiQ2mNFMfL/5r40ivapOTx8DOX3Q/UZpluchb+5/i/IuPrKJHl+Rq8nVpDeShEKsILNZqWEJa/vV3mH/", + "Vz3u617te0z/xqIyvroVUdV8zlJmUZ4LviB0IZoIaKzhyQU+AWmAsx2ECNNTlzHCXDlQ12C8XWm7Lbn3", + "JYDzZgv3Rg10yCUeMGAI78BogX8ZEyrwP1pKv2kZp9sy0p1j97jqn1zlU3CVz85X/uh+2MC0+N9SzHxy", + "+uQPu6DQEP2D0ORbjO6/nTjmakum0UZKNxW0fIUUb+5rIoTDiFu8RetY23fvzUWgQK78BdsEkD47OcGS", + "WUuh9MnEXH/t4NLw4fsa5g/+diolW2GnXrRuCskWjNM8cYGbSRMk+uj4dPLx/wcAAP//b8Lqgt4TAQA=", } // 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 69201ca848..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[:], @@ -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, @@ -538,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) @@ -1401,10 +1404,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..925d342380 --- /dev/null +++ b/data/basics/fraction.go @@ -0,0 +1,69 @@ +// 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 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 divides 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..2056717d24 --- /dev/null +++ b/data/basics/fraction_test.go @@ -0,0 +1,81 @@ +// 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 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, 2, a) + require.EqualValues(t, 4, b) + + a, b = third.Divvy(10) + require.EqualValues(t, 3, a) + require.EqualValues(t, 7, b) +} + +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, 3, a) + require.EqualValues(t, 3, b) + + a, b = half.Divvy(biggestEven) + 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, 2, a) + require.EqualValues(t, 4, b) + + a, b = uhalf.Divvy(biggestEven) + 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 + // math.MaxUint64 + + // prove 23 is relatively prime to math.MaxUint64 + require.Positive(t, math.MaxUint64%23) + + a, b = uhalf.Divvy(23) + 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/basics/msgp_gen.go b/data/basics/msgp_gen.go index 06190153d6..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(19) - var zb0009Mask uint32 /* 20 bits */ + zb0009Len := uint32(22) + var zb0009Mask uint32 /* 23 bits */ if (*z).MicroAlgos.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x2 @@ -280,54 +280,66 @@ 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).LastHeartbeat == 0 { zb0009Len-- zb0009Mask |= 0x200 } - if (*z).AuthAddr.MsgIsZero() { + if (*z).LastProposed == 0 { zb0009Len-- zb0009Mask |= 0x400 } - if (*z).StateProofID.MsgIsZero() { + if (*z).Status == 0 { zb0009Len-- zb0009Mask |= 0x800 } - if (*z).TotalBoxes == 0 { + if (*z).SelectionID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x1000 } - if (*z).TotalBoxBytes == 0 { + if (*z).AuthAddr.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x2000 } - if (*z).TotalExtraAppPages == 0 { + if (*z).StateProofID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x4000 } - if ((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0) { + if (*z).TotalBoxes == 0 { zb0009Len-- zb0009Mask |= 0x8000 } - if (*z).VoteID.MsgIsZero() { + if (*z).TotalBoxBytes == 0 { zb0009Len-- zb0009Mask |= 0x10000 } - if (*z).VoteFirstValid == 0 { + if (*z).TotalExtraAppPages == 0 { zb0009Len-- zb0009Mask |= 0x20000 } - if (*z).VoteKeyDilution == 0 { + if ((*z).TotalAppSchema.NumUint == 0) && ((*z).TotalAppSchema.NumByteSlice == 0) { zb0009Len-- zb0009Mask |= 0x40000 } - if (*z).VoteLastValid == 0 { + if (*z).VoteID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x80000 } + 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 { @@ -451,41 +463,56 @@ 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 "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 & 0x200) == 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 & 0x400) == 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 & 0x800) == 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 & 0x1000) == 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 & 0x2000) == 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 & 0x4000) == 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 & 0x8000) == 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 @@ -512,22 +539,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 & 0x80000) == 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 & 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 & 0x40000) == 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 & 0x80000) == 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)) @@ -653,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") @@ -689,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 @@ -752,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) @@ -797,27 +848,35 @@ 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) + (*z).IncentiveEligible, bts, err = msgp.ReadBoolBytes(bts) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "IncentiveEligible") + return + } + } + if zb0009 > 0 { + zb0009-- + 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") @@ -833,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") @@ -869,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 @@ -906,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") @@ -988,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) @@ -1034,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) @@ -1058,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") @@ -1093,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 @@ -1156,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) @@ -1196,28 +1275,34 @@ 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 - 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") @@ -1231,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") @@ -1265,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 @@ -1302,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") @@ -1375,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 @@ -1391,7 +1476,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,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()) && (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()) @@ -1431,7 +1516,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 +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(20) - var zb0009Mask uint32 /* 22 bits */ + zb0009Len := uint32(23) + var zb0009Mask uint32 /* 25 bits */ if (*z).Addr.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x4 @@ -3233,54 +3318,66 @@ 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.LastHeartbeat == 0 { zb0009Len-- zb0009Mask |= 0x800 } - if (*z).AccountData.AuthAddr.MsgIsZero() { + if (*z).AccountData.LastProposed == 0 { zb0009Len-- zb0009Mask |= 0x1000 } - if (*z).AccountData.StateProofID.MsgIsZero() { + if (*z).AccountData.Status == 0 { zb0009Len-- zb0009Mask |= 0x2000 } - if (*z).AccountData.TotalBoxes == 0 { + if (*z).AccountData.SelectionID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x4000 } - if (*z).AccountData.TotalBoxBytes == 0 { + if (*z).AccountData.AuthAddr.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x8000 } - if (*z).AccountData.TotalExtraAppPages == 0 { + if (*z).AccountData.StateProofID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x10000 } - if ((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0) { + if (*z).AccountData.TotalBoxes == 0 { zb0009Len-- zb0009Mask |= 0x20000 } - if (*z).AccountData.VoteID.MsgIsZero() { + if (*z).AccountData.TotalBoxBytes == 0 { zb0009Len-- zb0009Mask |= 0x40000 } - if (*z).AccountData.VoteFirstValid == 0 { + if (*z).AccountData.TotalExtraAppPages == 0 { zb0009Len-- zb0009Mask |= 0x80000 } - if (*z).AccountData.VoteKeyDilution == 0 { + if ((*z).AccountData.TotalAppSchema.NumUint == 0) && ((*z).AccountData.TotalAppSchema.NumByteSlice == 0) { zb0009Len-- zb0009Mask |= 0x100000 } - if (*z).AccountData.VoteLastValid == 0 { + if (*z).AccountData.VoteID.MsgIsZero() { zb0009Len-- zb0009Mask |= 0x200000 } + 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 { @@ -3409,41 +3506,56 @@ 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 "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 & 0x800) == 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 & 0x1000) == 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 & 0x2000) == 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 & 0x4000) == 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 & 0x8000) == 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 & 0x10000) == 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 & 0x20000) == 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 @@ -3470,22 +3582,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 & 0x200000) == 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 & 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 & 0x100000) == 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 & 0x200000) == 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)) @@ -3619,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") @@ -3655,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 @@ -3718,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) @@ -3763,27 +3899,35 @@ 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) + (*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 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") @@ -3799,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") @@ -3835,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 @@ -3872,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") @@ -3960,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) @@ -4006,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) @@ -4030,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") @@ -4065,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 @@ -4128,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) @@ -4168,28 +4332,34 @@ 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 - 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") @@ -4203,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") @@ -4237,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 @@ -4274,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") @@ -4347,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 @@ -4363,7 +4533,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,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()) && (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()) @@ -4403,7 +4573,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/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 af8743ee40..8e6ef0946c 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" @@ -122,6 +124,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 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) + } + 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 398147bb6d..d8f86aea54 100644 --- a/data/basics/userBalance.go +++ b/data/basics/userBalance.go @@ -111,12 +111,18 @@ type VotingData struct { type OnlineAccountData struct { MicroAlgosWithRewards MicroAlgos VotingData + IncentiveEligible bool } // 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 +178,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. @@ -209,6 +222,11 @@ 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 // that this account has opted in to. AppLocalStates map[AppIndex]AppLocalState `codec:"appl,allocbound=EncodedMaxAppLocalStates"` @@ -465,7 +483,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)), @@ -486,7 +504,7 @@ func MinBalance( totalAppParams uint64, totalAppLocalStates uint64, totalExtraAppPages uint64, totalBoxes uint64, totalBoxBytes uint64, -) (res MicroAlgos) { +) MicroAlgos { var min uint64 // First, base MinBalance @@ -521,8 +539,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. @@ -543,6 +560,7 @@ func (u AccountData) OnlineAccountData() OnlineAccountData { VoteLastValid: u.VoteLastValid, VoteKeyDilution: u.VoteKeyDilution, }, + IncentiveEligible: u.IncentiveEligible, } } 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/data/bookkeeping/block.go b/data/bookkeeping/block.go index d67621b343..7f2632f3f0 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -58,6 +58,26 @@ type ( // Genesis hash to which this block belongs. GenesisHash crypto.Digest `codec:"gh"` + // 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.Payouts.Enabled + Proposer basics.Address `codec:"prp"` + + // FeesCollected is the sum of all fees paid by transactions in this + // block. Populated if proto.Payouts.Enabled + 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"` + + // 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 payouts percent 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 @@ -145,6 +165,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.MaxMarkAbsent"` } // RewardsState represents the global parameters controlling the rate @@ -274,18 +298,39 @@ 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) Block { - c := block - c.BlockHeader.Seed = s - return c -} - // 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 +} + +// 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. @@ -467,6 +512,33 @@ func ProcessUpgradeParams(prev BlockHeader) (uv UpgradeVote, us UpgradeState, er return upgradeVote, upgradeState, err } +// NextBonus determines the bonus that should be paid out for proposing the next block. +func NextBonus(prev BlockHeader, params *config.ConsensusParams) basics.MicroAlgos { + current := uint64(prev.Round + 1) + prevParams := config.Consensus[prev.CurrentProtocol] // presence ensured by ProcessUpgradeParams + 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 uint64, prevBonus basics.MicroAlgos, curPlan config.BonusPlan, prevPlan config.BonusPlan) basics.MicroAlgos { + // Set the amount if it's non-zero... + 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 basics.MicroAlgos{Raw: curPlan.BaseAmount} + } + } + + if curPlan.DecayInterval != 0 && current%curPlan.DecayInterval == 0 { + // decay + keep, _ := basics.NewPercent(99).DivvyAlgos(prevBonus) + return keep + } + return prevBonus +} + // MakeBlock constructs a new valid block with an empty payset and an unset Seed. func MakeBlock(prev BlockHeader) Block { upgradeVote, upgradeState, err := ProcessUpgradeParams(prev) @@ -488,16 +560,19 @@ func MakeBlock(prev BlockHeader) Block { } } + 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, }, } blk.TxnCommitments, err = blk.PaysetCommit() @@ -613,6 +688,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..3c305b3c3b 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" @@ -54,6 +55,8 @@ func init() { params2 := config.Consensus[protocol.ConsensusCurrentVersion] params2.ApprovedUpgrades = map[protocol.ConsensusVersion]uint64{} + params2.Bonus.BaseAmount = 5_000_000 + params2.Bonus.DecayInterval = 1_000_000 config.Consensus[proto2] = params2 paramsDelay := config.Consensus[protocol.ConsensusCurrentVersion] @@ -67,6 +70,7 @@ func init() { func TestUpgradeVote(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() s := UpgradeState{ CurrentProtocol: proto1, @@ -130,6 +134,7 @@ func TestUpgradeVote(t *testing.T) { func TestUpgradeVariableDelay(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() s := UpgradeState{ CurrentProtocol: protoDelay, @@ -156,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() @@ -208,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() not parallel because it modifies config.Consensus var b Block b.CurrentProtocol = protoUnsupported @@ -218,11 +225,12 @@ 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) { partitiontest.PartitionTest(t) + t.Parallel() var prev Block prev.BlockHeader.GenesisID = t.Name() @@ -243,15 +251,49 @@ 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) + t.Parallel() + + 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) { partitiontest.PartitionTest(t) + t.Parallel() var buf bytes.Buffer log := logging.NewLogger() @@ -272,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() @@ -294,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() @@ -315,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() @@ -335,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() @@ -360,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() @@ -385,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" @@ -405,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" @@ -430,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" @@ -451,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 @@ -553,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) @@ -598,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) @@ -617,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) @@ -651,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) @@ -685,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) @@ -721,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) @@ -747,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) @@ -773,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 @@ -860,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 @@ -874,3 +934,128 @@ func TestBlockHeader_Serialization(t *testing.T) { a.Equal(crypto.Digest{}, blkHdr.TxnCommitments.Sha256Commitment) 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 := 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 + 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 := 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 = 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 + 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) +} + +// TestFirstYearsBonus shows what the bonuses look like +func TestFirstYearsBonus(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + a := require.New(t) + + yearSeconds := 365 * 24 * 60 * 60 + yearRounds := int(float64(yearSeconds) / 2.9) + + plan := config.Consensus[protocol.ConsensusFuture].Bonus + sum := uint64(0) + bonus := plan.BaseAmount + interval := int(plan.DecayInterval) + r := 0 + for i := 0; i < yearRounds; i++ { + r++ + sum += bonus + if r%interval == 0 { + bonus, _ = basics.Muldiv(bonus, 99, 100) + } + } + suma := sum / 1_000_000 // micro to Algos + + fmt.Printf("paid %d algos\n", suma) + fmt.Printf("bonus start: %d end: %d\n", plan.BaseAmount, bonus) + + // pays about 88M algos + a.InDelta(88_500_000, suma, 100_000) + + // 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/data/bookkeeping/msgp_gen.go b/data/bookkeeping/msgp_gen.go index cb3a63ad2e..fef26fd1b9 100644 --- a/data/bookkeeping/msgp_gen.go +++ b/data/bookkeeping/msgp_gen.go @@ -143,161 +143,203 @@ 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 */ + zb0005Len := uint32(31) + var zb0005Mask uint64 /* 36 bits */ + if (*z).BlockHeader.Bonus.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x20 + } if (*z).BlockHeader.RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x20 + zb0005Len-- + zb0005Mask |= 0x40 + } + if (*z).BlockHeader.FeesCollected.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x80 } if (*z).BlockHeader.RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40 + zb0005Len-- + zb0005Mask |= 0x100 } if (*z).BlockHeader.RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).BlockHeader.GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).BlockHeader.GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).BlockHeader.UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).BlockHeader.UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).BlockHeader.UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x4000 } if (*z).BlockHeader.UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x8000 + } + if len((*z).BlockHeader.ParticipationUpdates.AbsentParticipationAccounts) == 0 { + zb0005Len-- + zb0005Mask |= 0x10000 } if len((*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts) == 0 { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x20000 + } + if (*z).BlockHeader.ProposerPayout.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x40000 } if (*z).BlockHeader.Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x80000 } if (*z).BlockHeader.UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000 + zb0005Len-- + zb0005Mask |= 0x100000 + } + if (*z).BlockHeader.Proposer.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x200000 } if (*z).BlockHeader.RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).BlockHeader.Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x800000 } if (*z).BlockHeader.RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x1000000 } if (*z).BlockHeader.RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).BlockHeader.Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000 + zb0005Len-- + zb0005Mask |= 0x4000000 } if len((*z).BlockHeader.StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).BlockHeader.TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x800000 + zb0005Len-- + zb0005Mask |= 0x10000000 } if (*z).BlockHeader.TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if (*z).BlockHeader.TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).BlockHeader.TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if (*z).Payset.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } if (*z).BlockHeader.UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x200000000 } if (*z).BlockHeader.UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x400000000 } if (*z).BlockHeader.UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x40000000 + zb0005Len-- + zb0005Mask |= 0x800000000 } - // 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 "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 (zb0004Mask & 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 & 0x100) == 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 (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 (zb0004Mask & 0x100) == 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 (zb0004Mask & 0x200) == 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 (zb0004Mask & 0x400) == 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 (zb0004Mask & 0x800) == 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 (zb0004Mask & 0x1000) == 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 (zb0004Mask & 0x2000) == 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 (zb0004Mask & 0x4000) == 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 { + 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 & 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 { @@ -309,42 +351,52 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = (*z).BlockHeader.ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x8000) == 0 { // if not empty + 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 (zb0004Mask & 0x10000) == 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 (zb0004Mask & 0x20000) == 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 & 0x400000) == 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 (zb0005Mask & 0x800000) == 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 (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 (zb0004Mask & 0x100000) == 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 (zb0004Mask & 0x200000) == 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 (zb0004Mask & 0x400000) == 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 { @@ -364,42 +416,42 @@ func (z *Block) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x800000) == 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 (zb0004Mask & 0x1000000) == 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 (zb0004Mask & 0x2000000) == 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 (zb0004Mask & 0x4000000) == 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 (zb0004Mask & 0x8000000) == 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 (zb0004Mask & 0x10000000) == 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 (zb0004Mask & 0x20000000) == 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 (zb0004Mask & 0x40000000) == 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) @@ -422,73 +474,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) @@ -497,157 +549,189 @@ 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 zb0005 > 0 { + zb0005-- + bts, err = (*z).BlockHeader.FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "FeesCollected") + 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.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) 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") @@ -661,26 +745,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) @@ -690,16 +774,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) + 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 @@ -710,11 +823,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) @@ -758,14 +871,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) @@ -779,6 +892,30 @@ 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 "bi": + bts, err = (*z).BlockHeader.Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + 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 { @@ -870,27 +1007,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") @@ -904,24 +1041,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) @@ -930,6 +1067,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0019), uint64(config.MaxMarkAbsent)) + 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 { @@ -959,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() + 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 @@ -971,18 +1135,22 @@ 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.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.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() + 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()) @@ -991,6 +1159,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.MaxMarkAbsent) * (basics.AddressMaxSize())) s += 5 // Using maxtotalbytes for: z.Payset s += config.MaxTxnBytesPerBlock @@ -1037,157 +1208,199 @@ 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 */ + zb0005Len := uint32(30) + var zb0005Mask uint64 /* 35 bits */ + if (*z).Bonus.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x20 + } if (*z).RewardsState.RewardsLevel == 0 { - zb0004Len-- - zb0004Mask |= 0x20 + zb0005Len-- + zb0005Mask |= 0x40 + } + if (*z).FeesCollected.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x80 } if (*z).RewardsState.FeeSink.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40 + zb0005Len-- + zb0005Mask |= 0x100 } if (*z).RewardsState.RewardsResidue == 0 { - zb0004Len-- - zb0004Mask |= 0x80 + zb0005Len-- + zb0005Mask |= 0x200 } if (*z).GenesisID == "" { - zb0004Len-- - zb0004Mask |= 0x100 + zb0005Len-- + zb0005Mask |= 0x400 } if (*z).GenesisHash.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200 + zb0005Len-- + zb0005Mask |= 0x800 } if (*z).UpgradeState.NextProtocolVoteBefore.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x400 + zb0005Len-- + zb0005Mask |= 0x1000 } if (*z).UpgradeState.NextProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x800 + zb0005Len-- + zb0005Mask |= 0x2000 } if (*z).UpgradeState.NextProtocolSwitchOn.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x1000 + zb0005Len-- + zb0005Mask |= 0x4000 } if (*z).UpgradeState.NextProtocolApprovals == 0 { - zb0004Len-- - zb0004Mask |= 0x2000 + zb0005Len-- + zb0005Mask |= 0x8000 + } + if len((*z).ParticipationUpdates.AbsentParticipationAccounts) == 0 { + zb0005Len-- + zb0005Mask |= 0x10000 } if len((*z).ParticipationUpdates.ExpiredParticipationAccounts) == 0 { - zb0004Len-- - zb0004Mask |= 0x4000 + zb0005Len-- + zb0005Mask |= 0x20000 + } + if (*z).ProposerPayout.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x40000 } if (*z).Branch.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000 + zb0005Len-- + zb0005Mask |= 0x80000 } if (*z).UpgradeState.CurrentProtocol.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000 + zb0005Len-- + zb0005Mask |= 0x100000 + } + if (*z).Proposer.MsgIsZero() { + zb0005Len-- + zb0005Mask |= 0x200000 } if (*z).RewardsState.RewardsRate == 0 { - zb0004Len-- - zb0004Mask |= 0x20000 + zb0005Len-- + zb0005Mask |= 0x400000 } if (*z).Round.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x40000 + zb0005Len-- + zb0005Mask |= 0x800000 } if (*z).RewardsState.RewardsRecalculationRound.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x80000 + zb0005Len-- + zb0005Mask |= 0x1000000 } if (*z).RewardsState.RewardsPool.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x100000 + zb0005Len-- + zb0005Mask |= 0x2000000 } if (*z).Seed.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x200000 + zb0005Len-- + zb0005Mask |= 0x4000000 } if len((*z).StateProofTracking) == 0 { - zb0004Len-- - zb0004Mask |= 0x400000 + zb0005Len-- + zb0005Mask |= 0x8000000 } if (*z).TxnCounter == 0 { - zb0004Len-- - zb0004Mask |= 0x800000 + zb0005Len-- + zb0005Mask |= 0x10000000 } if (*z).TimeStamp == 0 { - zb0004Len-- - zb0004Mask |= 0x1000000 + zb0005Len-- + zb0005Mask |= 0x20000000 } if (*z).TxnCommitments.NativeSha512_256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x2000000 + zb0005Len-- + zb0005Mask |= 0x40000000 } if (*z).TxnCommitments.Sha256Commitment.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x4000000 + zb0005Len-- + zb0005Mask |= 0x80000000 } if (*z).UpgradeVote.UpgradeDelay.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x8000000 + zb0005Len-- + zb0005Mask |= 0x100000000 } if (*z).UpgradeVote.UpgradePropose.MsgIsZero() { - zb0004Len-- - zb0004Mask |= 0x10000000 + zb0005Len-- + zb0005Mask |= 0x200000000 } if (*z).UpgradeVote.UpgradeApprove == false { - zb0004Len-- - zb0004Mask |= 0x20000000 + zb0005Len-- + zb0005Mask |= 0x400000000 } - // 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 "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 (zb0004Mask & 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 & 0x100) == 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 (zb0005Mask & 0x200) == 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 (zb0005Mask & 0x400) == 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 (zb0005Mask & 0x800) == 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 (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 (zb0004Mask & 0x800) == 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 (zb0004Mask & 0x1000) == 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 (zb0004Mask & 0x2000) == 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 (zb0004Mask & 0x4000) == 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 { + 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 & 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 { @@ -1199,42 +1412,52 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = (*z).ParticipationUpdates.ExpiredParticipationAccounts[zb0003].MarshalMsg(o) } } - if (zb0004Mask & 0x8000) == 0 { // if not empty + 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 (zb0004Mask & 0x10000) == 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 (zb0004Mask & 0x20000) == 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 & 0x400000) == 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 (zb0005Mask & 0x800000) == 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 (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 (zb0004Mask & 0x100000) == 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 (zb0004Mask & 0x200000) == 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 (zb0004Mask & 0x400000) == 0 { // if not empty + if (zb0005Mask & 0x8000000) == 0 { // if not empty // string "spt" o = append(o, 0xa3, 0x73, 0x70, 0x74) if (*z).StateProofTracking == nil { @@ -1254,37 +1477,37 @@ func (z *BlockHeader) MarshalMsg(b []byte) (o []byte) { o = zb0002.MarshalMsg(o) } } - if (zb0004Mask & 0x800000) == 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 (zb0004Mask & 0x1000000) == 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 (zb0004Mask & 0x2000000) == 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 (zb0004Mask & 0x4000000) == 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 (zb0004Mask & 0x8000000) == 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 (zb0004Mask & 0x10000000) == 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 (zb0004Mask & 0x20000000) == 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) @@ -1307,73 +1530,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) @@ -1382,157 +1605,189 @@ 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 zb0005 > 0 { + zb0005-- + bts, err = (*z).FeesCollected.UnmarshalMsgWithState(bts, st) + if err != nil { + err = msgp.WrapError(err, "struct-from-array", "FeesCollected") + 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).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) 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") @@ -1546,26 +1801,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) @@ -1575,8 +1830,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0012), uint64(config.MaxMarkAbsent)) + 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 @@ -1587,11 +1871,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) @@ -1635,14 +1919,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) @@ -1656,6 +1940,30 @@ 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 "bi": + bts, err = (*z).Bonus.UnmarshalMsgWithState(bts, st) + if err != nil { + 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 { @@ -1747,27 +2055,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") @@ -1781,24 +2089,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) @@ -1807,6 +2115,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0019), uint64(config.MaxMarkAbsent)) + 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 { @@ -1830,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() + 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 @@ -1842,17 +2177,21 @@ 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).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).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() + 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()) @@ -1861,6 +2200,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.MaxMarkAbsent) * (basics.AddressMaxSize())) return } @@ -2875,16 +3217,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 */ + zb0003Len := uint32(2) + var zb0003Mask uint8 /* 3 bits */ + if len((*z).AbsentParticipationAccounts) == 0 { + zb0003Len-- + zb0003Mask |= 0x2 + } if len((*z).ExpiredParticipationAccounts) == 0 { - zb0002Len-- - zb0002Mask |= 0x2 + zb0003Len-- + zb0003Mask |= 0x4 } - // variable map header, size zb0002Len - o = append(o, 0x80|uint8(zb0002Len)) - if zb0002Len != 0 { - if (zb0002Mask & 0x2) == 0 { // if not empty + // 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 { @@ -2914,35 +3272,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) @@ -2952,8 +3310,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0007), uint64(config.MaxMarkAbsent)) + 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 @@ -2964,11 +3351,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) @@ -2976,24 +3363,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) @@ -3002,6 +3389,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.MaxMarkAbsent { + err = msgp.ErrOverflow(uint64(zb0011), uint64(config.MaxMarkAbsent)) + 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 { @@ -3029,12 +3443,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 @@ -3042,6 +3460,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.MaxMarkAbsent) * (basics.AddressMaxSize())) return } diff --git a/data/datatest/impls.go b/data/datatest/impls.go index 7c9462d40d..0edae496d1 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, _ []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,10 +64,15 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated return validatedBlock{blk: &b}, nil } -// WithSeed implements the agreement.ValidatedBlock interface. -func (ve validatedBlock) WithSeed(s committee.Seed) agreement.ValidatedBlock { - newblock := ve.blk.WithSeed(s) - return validatedBlock{blk: &newblock} +// FinishBlock implements the agreement.UnfinishedBlock interface. +func (ve validatedBlock) FinishBlock(s committee.Seed, proposer basics.Address, eligible bool) agreement.Block { + newblock := *ve.blk + newblock.BlockHeader.Seed = s + newblock.BlockHeader.Proposer = proposer + if !eligible { + newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } + return agreement.Block(newblock) } // Block implements the agreement.ValidatedBlock interface. @@ -75,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/data/pools/transactionPool.go b/data/pools/transactionPool.go index 2eb787eafd..31ad805d68 100644 --- a/data/pools/transactionPool.go +++ b/data/pools/transactionPool.go @@ -87,6 +87,7 @@ type TransactionPool struct { rememberedTxids map[transactions.Txid]transactions.SignedTxn log logging.Logger + vac VotingAccountSupplier // proposalAssemblyTime is the ProposalAssemblyTime configured for this node. proposalAssemblyTime time.Duration @@ -103,12 +104,17 @@ 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() } +// 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) *TransactionPool { +func MakeTransactionPool(ledger *ledger.Ledger, cfg config.Local, log logging.Logger, vac VotingAccountSupplier) *TransactionPool { if cfg.TxPoolExponentialIncreaseFactor < 1 { cfg.TxPoolExponentialIncreaseFactor = 1 } @@ -124,6 +130,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 +144,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 @@ -182,6 +189,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). @@ -602,7 +616,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 +637,7 @@ func (pool *TransactionPool) addToPendingBlockEvaluatorOnce(txgroup []transactio } blockGenerationStarts := time.Now() - lvb, gerr := pool.pendingBlockEvaluator.GenerateBlock() + 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 { @@ -773,11 +787,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.getVotingAccountsForRound(evalRnd)) if err != nil { pool.assemblyResults.err = fmt.Errorf("could not generate block for %d (end): %v", pool.assemblyResults.roundStartedEvaluating, err) } else { @@ -815,7 +829,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, deadline time.Time) (assembled *ledgercore.UnfinishedBlock, err error) { var stats telemetryspec.AssembleBlockMetrics if pool.logAssembleStats { @@ -829,7 +843,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 +878,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 +910,7 @@ func (pool *TransactionPool) AssembleBlock(round basics.Round, deadline time.Tim pool.assemblyDeadline = deadline pool.assemblyRound = round + for time.Now().Before(deadline) && (!pool.assemblyResults.ok || pool.assemblyResults.roundStartedEvaluating != round) { condvar.TimedWait(&pool.assemblyCond, time.Until(deadline)) } @@ -958,7 +973,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 +994,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.getVotingAccountsForRound(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() 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/transactions/logic/assembler_test.go b/data/transactions/logic/assembler_test.go index 3f609fd7ee..ec5537a701 100644 --- a/data/transactions/logic/assembler_test.go +++ b/data/transactions/logic/assembler_test.go @@ -1707,6 +1707,12 @@ block BlkSeed global AssetCreateMinBalance global AssetOptInMinBalance global GenesisHash +pushint 1 +block BlkProposer +pushint 1 +block BlkFeesCollected +pushint 1 +block BlkBonus `, AssemblerMaxVersion) for _, names := range [][]string{GlobalFieldNames[:], TxnFieldNames[:], blockFieldNames[:]} { for _, f := range names { diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 2bf6f2f272..3008250a54 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -5701,17 +5701,21 @@ 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 + cx.Stack[last] = stackValue{Uint: uint64(hdr.TimeStamp)} + case BlkProposer: + cx.Stack[last].Bytes = hdr.Proposer[:] + case BlkFeesCollected: + cx.Stack[last] = stackValue{Uint: hdr.FeesCollected.Raw} + case BlkBonus: + cx.Stack[last] = stackValue{Uint: hdr.Bonus.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..d0603d0ffc 100644 --- a/data/transactions/logic/fields.go +++ b/data/transactions/logic/fields.go @@ -965,6 +965,12 @@ const ( BlkSeed BlockField = iota // BlkTimestamp is the Block's timestamp, seconds from epoch BlkTimestamp + // BlkProposer is the Block's proposer, or ZeroAddress, pre Payouts.Enabled + BlkProposer + // 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 invalidBlockField // compile-time constant for number of fields ) @@ -980,6 +986,9 @@ type blockFieldSpec struct { var blockFieldSpecs = [...]blockFieldSpec{ {BlkSeed, StackBytes, randomnessVersion}, {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 37bfeb9bcc..5b92357909 100644 --- a/data/transactions/logic/fields_string.go +++ b/data/transactions/logic/fields_string.go @@ -352,12 +352,15 @@ func _() { var x [1]struct{} _ = x[BlkSeed-0] _ = x[BlkTimestamp-1] - _ = x[invalidBlockField-2] + _ = x[BlkProposer-2] + _ = x[BlkFeesCollected-3] + _ = x[BlkBonus-4] + _ = x[invalidBlockField-5] } -const _BlockField_name = "BlkSeedBlkTimestampinvalidBlockField" +const _BlockField_name = "BlkSeedBlkTimestampBlkProposerBlkFeesCollectedBlkBonusinvalidBlockField" -var _BlockField_index = [...]uint8{0, 7, 19, 36} +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/data/transactions/logic/opcodes.go b/data/transactions/logic/opcodes.go index b8ecd76cac..ae152bf919 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..d51e4ed1d6 100644 --- a/data/transactions/payment.go +++ b/data/transactions/payment.go @@ -37,17 +37,21 @@ type PaymentTxnFields struct { CloseRemainderTo basics.Address `codec:"close"` } -func (payment PaymentTxnFields) checkSpender(header Header, spec SpecialAddresses, proto config.ConsensusParams) error { +// 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) } - // the FeeSink account may only spend to the IncentivePool + // 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) + } 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/data/transactions/transaction.go b/data/transactions/transaction.go index b0bb7c413b..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 } @@ -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/transactions/transaction_test.go b/data/transactions/transaction_test.go index e3a25619bb..08dd145a8c 100644 --- a/data/transactions/transaction_test.go +++ b/data/transactions/transaction_test.go @@ -101,13 +101,15 @@ func TestGoOnlineGoNonparticipatingContradiction(t *testing.T) { tx.KeyregTxnFields = KeyregTxnFields{ 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. - 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, "tries to register keys to go online, but nonparticipatory flag is set") } func TestGoNonparticipatingWellFormed(t *testing.T) { @@ -125,19 +127,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 +253,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 +267,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 +593,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 +630,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 +673,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/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/data/txntest/txn.go b/data/txntest/txn.go index 515c9df458..aea4de005b 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 @@ -147,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: } + } } @@ -228,6 +239,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/acctdeltas.go b/ledger/acctdeltas.go index d6bb91881f..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() { - 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 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 ec00269dbe..a3e5bd49db 100644 --- a/ledger/acctdeltas_test.go +++ b/ledger/acctdeltas_test.go @@ -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/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/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/acctupdates_test.go b/ledger/acctupdates_test.go index 1cf080d15e..34ffb2f32a 100644 --- a/ledger/acctupdates_test.go +++ b/ledger/acctupdates_test.go @@ -385,11 +385,15 @@ 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) + + // 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) + require.Equal(t, od.VoteLastValid, data.VoteLastValid) + require.Equal(t, od.VoteKeyDilution, data.VoteKeyDilution) + } rewardsDelta := rewards[rnd] - d.RewardsBase switch d.Status { @@ -504,6 +508,11 @@ 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 od.VoteID.IsEmpty() { + // suspended accounts will be in `latest` (from ao.deltas), but + // `lookupOnlineAccountData` will return {}. + continue + } require.NoError(t, err) require.Equal(t, acct.VoteID, od.VoteID) require.Equal(t, acct.SelectionID, od.SelectionID) 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.go b/ledger/apply/keyreg.go index 4fe0f0a326..f5326f8240 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" ) @@ -31,7 +30,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 @@ -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 @@ -67,6 +66,8 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal record.VoteFirstValid = 0 record.VoteLastValid = 0 record.VoteKeyDilution = 0 + // 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 { @@ -77,9 +78,15 @@ func Keyreg(keyreg transactions.KeyregTxnFields, header transactions.Header, bal } } record.Status = basics.Online + 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.Payouts.GoOnlineFee && params.Payouts.Enabled { + record.IncentiveEligible = true + } } // Write the updated entry 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.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 d377211a17..6e756350fb 100644 --- a/ledger/apply/payment_test.go +++ b/ledger/apply/payment_test.go @@ -104,15 +104,16 @@ 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) } func TestCheckSpender(t *testing.T) { partitiontest.PartitionTest(t) - mockBalV0 := makeMockBalances(protocol.ConsensusCurrentVersion) - mockBalV7 := makeMockBalances(protocol.ConsensusV7) + v7 := config.Consensus[protocol.ConsensusV7] + v39 := config.Consensus[protocol.ConsensusV39] + vFuture := config.Consensus[protocol.ConsensusFuture] secretSrc := keypair() src := basics.Address(secretSrc.SignatureVerifier) @@ -134,19 +135,33 @@ func TestCheckSpender(t *testing.T) { }, } - tx.Sender = basics.Address(feeSink) - require.Error(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV0.ConsensusParams())) + tx.Sender = feeSink + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v7), + "to non incentive pool address") + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v39), + "to non incentive pool address") + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, vFuture), + "cannot spend from fee sink") - 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, 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.Error(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV0.ConsensusParams())) - require.Error(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV7.ConsensusParams())) - + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v7), + "cannot close fee sink") + require.ErrorContains(t, tx.PaymentTxnFields.CheckSpender(tx.Header, spec, v39), + "cannot close fee sink") + 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, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV7.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) { diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index 167d22f2dc..8e40d327e9 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -22,10 +22,12 @@ import ( "strconv" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "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" @@ -35,7 +37,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() @@ -58,6 +60,23 @@ func TestPayAction(t *testing.T) { itxn_submit `)) + // 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", + 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{ Type: "appl", Sender: addrs[1], @@ -65,7 +84,32 @@ func TestPayAction(t *testing.T) { Accounts: []basics.Address{addrs[1]}, // pay self } - dl.fullBlock(&payout1) + 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) + const payoutsVer = 40 + if ver >= payoutsVer { + require.True(t, dl.generator.GenesisProto().Payouts.Enabled) + require.EqualValues(t, 2000, vb.Block().FeesCollected.Raw) + } else { + require.False(t, dl.generator.GenesisProto().Payouts.Enabled) + require.Zero(t, vb.Block().FeesCollected) + } + + 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 + 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 payouts yet + } ad0 := micros(dl.t, dl.generator, addrs[0]) ad1 := micros(dl.t, dl.generator, addrs[1]) @@ -90,7 +134,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() @@ -3344,7 +3388,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 c08212e373..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" @@ -48,6 +49,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 +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} + // 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 { @@ -134,8 +139,13 @@ 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 { + require.Len(dl.t, proposer, 1, "endBlock() cannot specify multiple proposers") + 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) } @@ -168,49 +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) - cb := endBlock(t, checkLedger, check) - 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/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..23c415bbdf 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 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 { 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..6599dd5918 100644 --- a/ledger/eval/eval.go +++ b/ledger/eval/eval.go @@ -20,12 +20,15 @@ import ( "context" "errors" "fmt" + "math" + "math/bits" "sync" "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/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/data/transactions/verify" @@ -716,19 +719,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 +743,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 +754,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 { @@ -1165,12 +1168,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 and max supply is uint64 + 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,7 +1290,18 @@ func (eval *BlockEvaluator) endOfBlock() error { eval.block.TxnCounter = 0 } - eval.generateExpiredOnlineAccountsList() + 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(). + eval.block.FeesCollected = eval.state.feesCollected + eval.block.BlockHeader.ProposerPayout, err = eval.proposerPayout() + if err != nil { + return err + } + } + + eval.generateKnockOfflineAccountsList() if eval.proto.StateProofInterval > 0 { var basicStateProof bookkeeping.StateProofTrackingData @@ -1288,13 +1317,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 } @@ -1316,6 +1349,10 @@ func (eval *BlockEvaluator) endOfBlock() error { return fmt.Errorf("txn count wrong: %d != %d", eval.block.TxnCounter, expectedTxnCount) } + if err := eval.validateForPayouts(); err != nil { + return err + } + expectedVoters, expectedVotersWeight, err2 := eval.stateProofVotersAndTotal() if err2 != nil { return err2 @@ -1358,8 +1395,15 @@ func (eval *BlockEvaluator) endOfBlock() error { } } - err = eval.state.CalculateTotals() - if err != nil { + 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 } @@ -1370,41 +1414,265 @@ 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() { +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() + 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) + } + } + } + return nil +} + +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 + } + + 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 +} + +func (eval *BlockEvaluator) proposerPayout() (basics.MicroAlgos, error) { + 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) + } + + 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 + // 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 +// participation accounts by traversing over the modified accounts in the state +// 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 } - // 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. - modifiedAccounts := eval.state.modifiedAccounts() - currentRound := eval.Round() + current := eval.Round() - expectedMaxNumberOfExpiredAccounts := eval.proto.MaxProposedExpiredOnlineAccounts + maxExpirations := eval.proto.MaxProposedExpiredOnlineAccounts + maxSuspensions := eval.proto.Payouts.MaxMarkAbsent + + updates := &eval.block.ParticipationUpdates + + ch := activeChallenge(&eval.proto, uint64(eval.Round()), eval.state) - for i := 0; i < len(modifiedAccounts) && len(eval.block.ParticipationUpdates.ExpiredParticipationAccounts) < expectedMaxNumberOfExpiredAccounts; i++ { - accountAddr := modifiedAccounts[i] - acctDelta, found := eval.state.mods.Accts.GetData(accountAddr) + for _, accountAddr := range eval.state.modifiedAccounts() { + acctData, 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 + // 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 !acctData.VoteID.IsEmpty() { + expiresBeforeCurrent := acctData.VoteLastValid < current + if expiresBeforeCurrent && + len(updates.ExpiredParticipationAccounts) < maxExpirations { + 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 len(updates.AbsentParticipationAccounts) >= maxSuspensions { + continue // no more room (don't break the loop, since we may have more expiries) } + + 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, + accountAddr, + ) + } + } + } +} + +// delete me in Go 1.21 +func max(a, b basics.Round) basics.Round { + if a > b { + return a } + return b +} + +// bitsMatch checks if the first n bits of two byte slices match. Written to +// 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 entire bytes when n is bigger than 8 + for i := 0; i < n/8; i++ { + if a[i] != b[i] { + return false + } + } + remaining := n % 8 + if remaining == 0 { + 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 { + // 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 { + return false + } + // See if the account has exceeded 10x their expected observation interval. + 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 +} + +type headerSource interface { + BlockHdr(round basics.Round) (bookkeeping.BlockHeader, error) +} + +func activeChallenge(proto *config.ConsensusParams, current uint64, headers headerSource) challenge { + rules := proto.Payouts + // 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.Payouts != rules { + return challenge{} + } + return challenge{round, challengeHdr.Seed, rules.ChallengeBits} +} + +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 @@ -1427,7 +1695,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 { @@ -1444,22 +1712,66 @@ 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 { - 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 !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 suspended +func (eval *BlockEvaluator) validateAbsentOnlineAccounts() error { + if !eval.validate { + return nil + } + 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. + // This works when the expected number of accounts is zero (i.e. it is disabled) as well + if suspensionCount > maxSuspensions { + return fmt.Errorf("length of absent accounts (%d) was greater than expected (%d)", + suspensionCount, maxSuspensions) + } + + // For consistency with expired account handling, we preclude duplicates + addressSet := make(map[basics.Address]bool, suspensionCount) + + ch := activeChallenge(&eval.proto, uint64(eval.Round()), eval.state) + + 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 account %v was %v, not Online", accountAddr, acctData.Status) + } + + lastSeen := max(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 +} + // 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 @@ -1490,6 +1802,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. @@ -1497,7 +1827,7 @@ func (eval *BlockEvaluator) resetExpiredOnlineAccountsParticipationKeys() 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") } @@ -1511,7 +1841,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/eval/eval_test.go b/ledger/eval/eval_test.go index 7884a7f894..994bedd561 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -30,10 +30,10 @@ 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" + "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 +182,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 +212,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 +228,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 +646,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") @@ -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 @@ -986,11 +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) - err = ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) + // fake agreement's setting of header fields so later validates work. + seed := committee.Seed{} + crypto.RandBytes(seed[:]) + 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 @@ -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) @@ -1142,6 +1147,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 +1171,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)) @@ -1196,62 +1208,62 @@ 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(unfinishedBlock.UnfinishedBlock().WithProposer(committee.Seed{}, testPoolAddr, true), unfinishedBlock.UnfinishedDeltas()) + + 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) - - badBlock := *validatedBlock + require.Zero(t, acctData.StateProofID) + require.Zero(t, acctData.SelectionID) + require.Zero(t, acctData.VoteID) + 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 := goodBlock + badBlock.ExpiredParticipationAccounts = append(badBlock.ExpiredParticipationAccounts, basics.Address{1}) - badBlock = *validatedBlock - - 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 { @@ -1265,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) @@ -1338,22 +1351,179 @@ 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)") +} + +// 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 here so that addr[1] won't be suspended + case 2: + tmp.LastProposed = 1150 // lie here 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} + + 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 + + unfinishedBlock, err := blkEval.GenerateBlock(nil) + require.NoError(t, err) + // fake agreement's setting of header fields so later validates work + 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()) + 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 { + require.Equal(t, basics.Address{i << 3, 0xaa}, challenged) + 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) @@ -1435,22 +1605,132 @@ 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.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) { + 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)) + 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) { + 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.Payouts.ChallengeInterval + now.Payouts.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.Payouts.ChallengeGracePeriod; r++ { + ch := activeChallenge(&now, r, singleSource(nowHeader)) + a.EqualValues(ch.round, now.Payouts.ChallengeInterval) + } + + // And the next round is again challenge-less + ch = activeChallenge(&now, inChallenge+now.Payouts.ChallengeGracePeriod, singleSource(nowHeader)) + a.Zero(ch.round) + + // ignore challenge if upgrade happened + oldHeader := bookkeeping.BlockHeader{ + UpgradeState: bookkeeping.UpgradeState{ + // We need a version from before payouts got turned on + CurrentProtocol: protocol.ConsensusV39, + }, + } + ch = activeChallenge(&now, inChallenge, singleSource(oldHeader)) + a.Zero(ch.round) } diff --git a/ledger/eval/prefetcher/prefetcher_alignment_test.go b/ledger/eval/prefetcher/prefetcher_alignment_test.go index 57f4c5d92b..cb4b165c94 100644 --- a/ledger/eval/prefetcher/prefetcher_alignment_test.go +++ b/ledger/eval/prefetcher/prefetcher_alignment_test.go @@ -261,6 +261,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 +373,7 @@ func TestEvaluatorPrefetcherAlignmentPayment(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -393,7 +405,7 @@ func TestEvaluatorPrefetcherAlignmentCreateAsset(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + 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) @@ -449,7 +461,7 @@ func TestEvaluatorPrefetcherAlignmentReconfigAsset(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -504,7 +516,7 @@ func TestEvaluatorPrefetcherAlignmentAssetOptIn(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -570,7 +582,7 @@ func TestEvaluatorPrefetcherAlignmentAssetOptInCloseTo(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -641,7 +653,7 @@ func TestEvaluatorPrefetcherAlignmentAssetTransfer(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) // zero transfer of any asset @@ -660,7 +672,7 @@ func TestEvaluatorPrefetcherAlignmentAssetTransfer(t *testing.T) { requested, prefetched = run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -741,7 +753,7 @@ func TestEvaluatorPrefetcherAlignmentAssetClawback(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -811,7 +823,7 @@ func TestEvaluatorPrefetcherAlignmentAssetFreeze(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -858,7 +870,7 @@ func TestEvaluatorPrefetcherAlignmentKeyreg(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -895,7 +907,7 @@ func TestEvaluatorPrefetcherAlignmentCreateApplication(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + 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) @@ -953,7 +965,7 @@ func TestEvaluatorPrefetcherAlignmentDeleteApplication(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -1011,7 +1023,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationOptIn(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -1075,7 +1087,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCloseOut(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -1139,7 +1151,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationClearState(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + prefetched.pretend(rewardsPool()) require.Equal(t, requested, prefetched) } @@ -1203,7 +1215,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCallAccountsDeclaration(t *testi requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + 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)) @@ -1271,7 +1283,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCallForeignAppsDeclaration(t *te requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + 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}) @@ -1338,7 +1350,7 @@ func TestEvaluatorPrefetcherAlignmentApplicationCallForeignAssetsDeclaration(t * requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + 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}) @@ -1385,6 +1397,6 @@ func TestEvaluatorPrefetcherAlignmentStateProof(t *testing.T) { requested, prefetched := run(t, l, txn) - prefetched.Accounts[rewardsPool()] = struct{}{} + 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/eval_simple_test.go b/ledger/eval_simple_test.go index 7354b4a567..9ac67d2fb7 100644 --- a/ledger/eval_simple_test.go +++ b/ledger/eval_simple_test.go @@ -29,10 +29,12 @@ 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" "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" @@ -189,15 +191,17 @@ func TestBlockEvaluator(t *testing.T) { err = eval.TestTransactionGroup(txgroup) require.Error(t, err) - validatedBlock, err := eval.GenerateBlock() + unfinishedBlock, err := eval.GenerateBlock(nil) require.NoError(t, err) + 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) @@ -211,6 +215,494 @@ func TestBlockEvaluator(t *testing.T) { require.Equal(t, bal2new.MicroAlgos.Raw, bal2.MicroAlgos.Raw-minFee.Raw) } +// 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) + 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() + + proposer := basics.Address{0x01, 0x011} + const eFee = 3_000_000 + dl.txn( + &txntest.Txn{Type: "pay", Sender: addrs[1], + Receiver: proposer, Amount: eFee + 50_000_000}, + ) + + 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, + }) + + prp = lookup(dl.t, dl.generator, proposer) + require.Equal(t, ver >= payoutsBegin, prp.IncentiveEligible) + + 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) + 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) + + 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 + 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) + // 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) + } + + // 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)) + + // Rest of the tests only make sense with payout active + if ver < payoutsBegin { + return + } + + // 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) + + 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) + }) +} + +// 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() + 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() + + 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 >= payoutsBegin) + }) +} + +// 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 + }) + 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() + + // 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) + + // 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() + + // although it's not even online, we'll use addrs[7] as the proposer + proposer := addrs[7] + dl.beginBlock() + dl.txns(&txntest.Txn{ + Type: "pay", + Sender: addrs[1], + Receiver: addrs[2], + Amount: 100_000, + }) + dl.endBlock(proposer) + + prp := lookup(t, dl.validator, proposer) + 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) + + // 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 + require.Equal(t, totals.Online.Money.Raw-100_000-1000, newtotals.Online.Money.Raw) + totals = newtotals + + dl.fullBlock() + + // addrs[2] was already offline + 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) + + // an an offline keyreg transaction records no activity + require.Zero(t, regger.LastProposed) + require.Zero(t, regger.LastHeartbeat) + + // ONLINE keyreg without extra fee + dl.txns(&txntest.Txn{ + Type: "keyreg", + Sender: addrs[2], + 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) + + regger = lookup(t, dl.validator, addrs[2]) + require.Zero(t, regger.LastProposed) + require.True(t, regger.Status == basics.Online) + + if ver >= checkingBegins { + require.NotZero(t, regger.LastHeartbeat) // online keyreg caused update + } 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() + 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 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) + require.Equal(t, ver >= checkingBegins, lookup(t, dl.generator, addrs[2]).IncentiveEligible) + + // 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" + 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) + 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) + 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 stays ineligible + dl.proposer = addrs[2] + dl.fullBlock() + require.Equal(t, basics.Online, lookup(t, dl.generator, addrs[2]).Status) + require.False(t, lookup(t, dl.generator, addrs[2]).IncentiveEligible) + }) +} + +// 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() + + // 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{seedAndProp, 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().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(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 + } + 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().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 + 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) + + // 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 @@ -451,10 +943,11 @@ func TestRekeying(t *testing.T) { return err } } - validatedBlock, err := eval.GenerateBlock() + unfinishedBlock, err := eval.GenerateBlock(nil) if err != nil { return err } + 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..c70795f4d2 100644 --- a/ledger/evalbench_test.go +++ b/ledger/evalbench_test.go @@ -514,16 +514,22 @@ 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 - // 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 { - validatedBlock, err = bev.GenerateBlock() + unfinishedBlock, err = bev.GenerateBlock(nil) require.NoError(b, err) + // 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} { err = l.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) require.NoError(b, err) @@ -562,12 +568,14 @@ func benchmarkPreparePaymentTransactionsTesting(b *testing.B, numTxns int, txnSo require.NoError(b, err) } - validatedBlock, err := bev.GenerateBlock() + // as above - this might be an underestimate because we skip agreement + unfinishedBlock, err := bev.GenerateBlock(nil) require.NoError(b, err) + 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/ledger_test.go b/ledger/ledger_test.go index b18428741b..8a855ceadd 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().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 + } + // blk.ProposerPayout is allowed to be zero, so don't reproduce the calc here. + blk.BlockHeader.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) @@ -126,13 +142,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, } @@ -170,12 +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 } - blk.Payset = append(blk.Payset, txib) - blk.TxnCommitments, err = blk.PaysetCommit() - require.NoError(t, err) + require.NoError(t, endOfBlock(&blk)) return l.appendUnvalidated(blk) } @@ -241,13 +257,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, } @@ -270,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.ErrorContains(l.appendUnvalidated(badBlock), "proposed upgrade wait rounds 0") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.UpgradePropose = "invalid" - a.Error(l.appendUnvalidated(badBlock), "added block header with invalid upgrade proposal") + 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 @@ -326,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") @@ -655,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 @@ -700,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)) @@ -1248,13 +1271,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 @@ -1267,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") } diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 3685d16909..92efa394a1 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 @@ -48,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 @@ -65,6 +69,7 @@ type VotingData struct { type OnlineAccountData struct { MicroAlgosWithRewards basics.MicroAlgos VotingData + IncentiveEligible bool } // ToAccountData returns ledgercore.AccountData from basics.AccountData @@ -75,8 +80,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, @@ -86,6 +91,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, @@ -105,6 +113,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,11 +123,13 @@ 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 a.TotalBoxBytes = acct.TotalBoxBytes + + a.LastProposed = acct.LastProposed + a.LastHeartbeat = acct.LastHeartbeat } // WithUpdatedRewards calls basics account data WithUpdatedRewards @@ -134,10 +146,23 @@ func (u *AccountData) ClearOnlineState() { u.VotingData = VotingData{} } +// 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.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) +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. -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), @@ -148,6 +173,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) 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{} @@ -179,6 +213,7 @@ func (u AccountData) OnlineAccountData(proto config.ConsensusParams, rewardsLeve VoteLastValid: u.VoteLastValid, VoteKeyDilution: u.VoteKeyDilution, }, + IncentiveEligible: u.IncentiveEligible, } } 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/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 541a3a54f8..0d7704710d 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,17 +40,6 @@ 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 { - newblock := vb.blk - newblock.BlockHeader.Seed = s - - return ValidatedBlock{ - blk: newblock, - delta: vb.delta, - } -} - // MakeValidatedBlock creates a validated block. func MakeValidatedBlock(blk bookkeeping.Block, delta StateDelta) ValidatedBlock { return ValidatedBlock{ @@ -57,3 +47,54 @@ 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 +} + +// 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 { + // 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 + } + 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/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/simple_test.go b/ledger/simple_test.go index 10b87f9378..8af40eaaf3 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -17,6 +17,7 @@ package ledger import ( + "context" "fmt" "strings" "testing" @@ -26,7 +27,9 @@ 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/verify" "github.com/algorand/go-algorand/data/txntest" "github.com/algorand/go-algorand/ledger/eval" "github.com/algorand/go-algorand/ledger/ledgercore" @@ -100,6 +103,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()) } @@ -136,11 +142,43 @@ 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, 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 { + ub, err := eval.GenerateBlock(nil) + require.NoError(t, err) + + // We fake some thigns that agreement would do, like setting proposer + 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 + if len(proposer) > 0 { + prp = proposer[0] + } + + // 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 { + // To more closely mimic the agreement code, we don't + // write the proposer when !Payouts.Enabled. + *gvb = ledgercore.MakeValidatedBlock(gvb.Block().WithProposer(committee.Seed(prp), basics.Address{}, false), gvb.Delta()) + } + + vvb, err := validateWithoutSignatures(t, ledger, gvb.Block()) require.NoError(t, err) - err = ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) + + // 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, @@ -152,7 +190,14 @@ 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 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 diff --git a/ledger/simulation/simulator.go b/ledger/simulation/simulator.go index 0a33e5f12b..c7c722686d 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) if err != nil { return nil, err } - return vb, nil + // Since we skip agreement, this block is imperfect w/ respect to seed/proposer/payouts + 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..3a6bbe0edf 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) require.NoError(env.t, err) - err = env.Ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) + // 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) - return validatedBlock + return &validatedBlock } // Txn creates and executes a new block with the given transaction and returns its ApplyData diff --git a/ledger/store/trackerdb/data.go b/ledger/store/trackerdb/data.go index fc243b2b92..da2cf3eeab 100644 --- a/ledger/store/trackerdb/data.go +++ b/ledger/store/trackerdb/data.go @@ -47,6 +47,9 @@ type BaseAccountData struct { TotalAppLocalStates uint64 `codec:"l"` TotalBoxes uint64 `codec:"m"` TotalBoxBytes uint64 `codec:"n"` + IncentiveEligible bool `codec:"o"` + LastProposed basics.Round `codec:"p"` + LastHeartbeat basics.Round `codec:"q"` BaseVotingData @@ -149,8 +152,9 @@ type BaseOnlineAccountData struct { BaseVotingData - MicroAlgos basics.MicroAlgos `codec:"Y"` - RewardsBase uint64 `codec:"Z"` + IncentiveEligible bool `codec:"X"` + MicroAlgos basics.MicroAlgos `codec:"Y"` + RewardsBase uint64 `codec:"Z"` } // PersistedKVData represents the stored entry behind a application boxed key/value. @@ -286,6 +290,10 @@ func (ba *BaseAccountData) SetCoreAccountData(ad *ledgercore.AccountData) { ba.TotalAppLocalStates = ad.TotalAppLocalStates ba.TotalBoxes = ad.TotalBoxes ba.TotalBoxBytes = ad.TotalBoxBytes + ba.IncentiveEligible = ad.IncentiveEligible + + ba.LastProposed = ad.LastProposed + ba.LastHeartbeat = ad.LastHeartbeat ba.BaseVotingData.SetCoreAccountData(ad) } @@ -306,6 +314,10 @@ 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.LastProposed = ad.LastProposed + ba.LastHeartbeat = ad.LastHeartbeat ba.BaseVotingData.VoteID = ad.VoteID ba.BaseVotingData.SelectionID = ad.SelectionID @@ -342,6 +354,10 @@ func (ba *BaseAccountData) GetLedgerCoreAccountBaseData() ledgercore.AccountBase TotalAssets: ba.TotalAssets, TotalBoxes: ba.TotalBoxes, TotalBoxBytes: ba.TotalBoxBytes, + IncentiveEligible: ba.IncentiveEligible, + + LastProposed: ba.LastProposed, + LastHeartbeat: ba.LastHeartbeat, } } @@ -365,6 +381,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, @@ -379,6 +396,9 @@ func (ba *BaseAccountData) GetAccountData() basics.AccountData { VoteFirstValid: ba.VoteFirstValid, VoteLastValid: ba.VoteLastValid, VoteKeyDilution: ba.VoteKeyDilution, + + LastProposed: ba.LastProposed, + LastHeartbeat: ba.LastHeartbeat, } } @@ -389,6 +409,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 && @@ -398,6 +419,8 @@ func (ba *BaseAccountData) IsEmpty() bool { ba.TotalAppLocalStates == 0 && ba.TotalBoxes == 0 && ba.TotalBoxBytes == 0 && + ba.LastProposed == 0 && + ba.LastHeartbeat == 0 && ba.BaseVotingData.IsEmpty() } @@ -421,11 +444,11 @@ 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 && - bo.RewardsBase == 0 + bo.RewardsBase == 0 && !bo.IncentiveEligible } // GetOnlineAccount returns ledgercore.OnlineAccount for top online accounts / voters @@ -459,6 +482,7 @@ func (bo *BaseOnlineAccountData) GetOnlineAccountData(proto config.ConsensusPara VoteLastValid: bo.VoteLastValid, VoteKeyDilution: bo.VoteKeyDilution, }, + IncentiveEligible: bo.IncentiveEligible, } } @@ -471,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 e329a84e74..edc0d0dc9e 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,"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)) } @@ -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 a13469c0ab..465248e93d 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(24) + var zb0001Mask uint32 /* 26 bits */ if (*z).BaseVotingData.VoteID.MsgIsZero() { zb0001Len-- zb0001Mask |= 0x1 @@ -182,10 +182,22 @@ func (z *BaseAccountData) MarshalMsg(b []byte) (o []byte) { zb0001Len-- zb0001Mask |= 0x200000 } - if (*z).UpdateRound == 0 { + if (*z).IncentiveEligible == false { zb0001Len-- zb0001Mask |= 0x400000 } + 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 { @@ -290,6 +302,21 @@ 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 "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) @@ -433,6 +460,30 @@ 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).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) @@ -596,6 +647,24 @@ 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 "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 { @@ -661,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 + (*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).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 + 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 } @@ -680,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 @@ -706,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 { @@ -748,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 "X" + o = append(o, 0xa1, 0x58) + 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) @@ -832,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) @@ -907,6 +993,12 @@ func (z *BaseOnlineAccountData) UnmarshalMsgWithState(bts []byte, st msgp.Unmars err = msgp.WrapError(err, "StateProofID") return } + case "X": + (*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 { @@ -942,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/ledger/store/trackerdb/sqlitedriver/schema.go b/ledger/store/trackerdb/sqlitedriver/schema.go index 369ff264e0..a36dd2cd90 100644 --- a/ledger/store/trackerdb/sqlitedriver/schema.go +++ b/ledger/store/trackerdb/sqlitedriver/schema.go @@ -734,8 +734,12 @@ 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. 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 e7aee0b2a9..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 { + if acct.SelectionID.IsEmpty() { expectedAcct.StateProofID = merklesignature.Commitment{} } expectedAccts[addr] = expectedAcct - } buildDB := func(accounts map[basics.Address]basics.AccountData) (db.Pair, *sql.Tx) { @@ -211,7 +210,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,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 { - 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 { - 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 2d5f79c82a..d97eee20d4 100644 --- a/ledger/testing/randomAccounts.go +++ b/ledger/testing/randomAccounts.go @@ -18,11 +18,9 @@ package testing import ( "fmt" - "math" "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" @@ -58,47 +56,62 @@ 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 { case 0: data.Status = basics.Online - data.VoteLastValid = 10000 + data.IncentiveEligible = crypto.RandUint64()%5 == 0 case 1: data.Status = basics.Offline - data.VoteLastValid = 0 - default: + case 2: data.Status = basics.NotParticipating } - data.VoteFirstValid = 0 + // 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.RandUint63()) // int64 is the max sqlite can store + data.VoteKeyDilution = crypto.RandUint64() + } + data.RewardsBase = rewardsBase return data } // 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(), 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)) } @@ -214,6 +227,7 @@ func RandomAppParams() basics.AppParams { if len(ap.GlobalState) == 0 { ap.GlobalState = nil } + ap.ExtraProgramPages = uint32(crypto.RandUint64() % 4) return ap } @@ -268,21 +282,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/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"), diff --git a/ledger/testing/testGenesis.go b/ledger/testing/testGenesis.go index a2d469dcf0..81be9e9118 100644 --- a/ledger/testing/testGenesis.go +++ b/ledger/testing/testGenesis.go @@ -25,25 +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) -// 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 sensitive to +// "surprise" balance changes. +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) } @@ -75,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/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) } 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 53c6c492a9..88766877dc 100644 --- a/node/node.go +++ b/node/node.go @@ -228,7 +228,22 @@ func MakeFull(log logging.Logger, rootDir string, cfg config.Local, phonebookAdd return nil, err } - node.transactionPool = pools.MakeTransactionPool(node.ledger.Ledger, cfg, node.log) + 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{ node.transactionPool, @@ -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) @@ -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.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 } @@ -1290,27 +1289,23 @@ 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 } -// WithSeed satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) WithSeed(s committee.Seed) agreement.ValidatedBlock { - lvb := vb.vb.WithSeed(s) - return validatedBlock{vb: &lvb} -} +// Round satisfies the agreement.UnfinishedBlock interface. +func (ub unfinishedBlock) Round() basics.Round { return ub.blk.Round() } -// Block satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) Block() bookkeeping.Block { - blk := vb.vb.Block() - return blk +// FinishBlock satisfies the agreement.UnfinishedBlock interface. +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. -func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.ValidatedBlock, 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) + 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. @@ -1329,7 +1324,17 @@ func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.Valid } return nil, err } - return validatedBlock{vb: lvb}, 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 @@ -1349,6 +1354,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 { diff --git a/protocol/tags.go b/protocol/tags.go index e980454674..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 = 5247980 +const ProposalPayloadTagMaxSize = 5250313 // StateProofSigTagMaxSize is the maximum size of a StateProofSigTag message const StateProofSigTagMaxSize = 6378 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/payouts_test.go b/test/e2e-go/features/incentives/payouts_test.go new file mode 100644 index 0000000000..28fc91089e --- /dev/null +++ b/test/e2e-go/features/incentives/payouts_test.go @@ -0,0 +1,335 @@ +// 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/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" + "github.com/algorand/go-algorand/test/framework/fixtures" + "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) + defer fixtures.ShutdownSynchronizedTest(t) + + t.Parallel() + a := require.New(fixtures.SynchronizedTest(t)) + + var fixture fixtures.RestClientFixture + // Make the seed lookback shorter, otherwise we need to wait 320 rounds to become IncentiveEligible. + 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() + + // Overview of this test: + // rereg to become eligible (must pay extra fee) + // show payouts are paid (from fees and bonuses) + // deplete feesink to ensure it's graceful + + clientAndAccount := func(name string) (libgoal.Client, model.Account) { + c := fixture.GetLibGoalClientForNamedNode(name) + 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] + } + + 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 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) + burn, err := fixture.WaitForConfirmedTxn(uint64(txn.LastValid), txn.ID().String()) + a.NoError(err) + data01, err = c01.AccountData(account01.Address) + a.NoError(err) + + // 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+lookback-1 { + block, err := client.BookkeepingBlock(status.LastRound) + a.NoError(err) + + 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(bonus1, block.Bonus.Raw) + + // 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 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... + for i, c := range []libgoal.Client{c15, c01, relay} { + fmt.Printf("checking block %v\n", block.Round()) + bb, err := getblock(c, status.LastRound) + a.NoError(err) + a.Equal(block.Proposer(), bb.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.LessOrEqual(int(status.LastRound), int(next.LastProposed)) + // 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) + } + fixture.WaitForRoundWithTimeout(status.LastRound + 1) + status, err = client.Status() + a.NoError(err) + } + + // 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) + block, err := client.BookkeepingBlock(status.LastRound) + a.NoError(err) + a.EqualValues(bonus1, block.Bonus.Raw) + + 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: + 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) // didn't earn + data15 = next + proposed15 = true + default: + a.Fail("bad proposer", "%v proposed", block.Proposer) + } + fixture.WaitForRoundWithTimeout(status.LastRound + 1) + } + + // 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 (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) + 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+lookback) + + var feesink basics.Address + for i := 0; i < 100; i++ { + status, err := client.Status() + a.NoError(err) + block, err := client.BookkeepingBlock(status.LastRound) + a.NoError(err) + + a.EqualValues(bonus1, block.Bonus.Raw) + + data, err := client.AccountData(block.Proposer().String()) + 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 + 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) + a.Equal(fdata, data) + } + a.LessOrEqual(100000, int(data.MicroAlgos.Raw)) // won't go below minfee + if data.MicroAlgos.Raw == 100000 { + break + } + 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 + lookback) + a.NoError(err) + + // 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()) + 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 `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) + 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) +} + +// 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 + // 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.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, + 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.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 new file mode 100644 index 0000000000..3d3f0954f6 --- /dev/null +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -0,0 +1,171 @@ +// 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/daemon/algod/api/server/v2/generated/model" + "github.com/algorand/go-algorand/data/basics" + "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" +) + +// 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) + + t.Parallel() + a := require.New(fixtures.SynchronizedTest(t)) + + // Overview of this test: + // 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 + const suspend20 = 55 + + var fixture fixtures.RestClientFixture + // 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() + + clientAndAccount := func(name string) (libgoal.Client, model.Account) { + c := fixture.GetLibGoalClientForNamedNode(name) + 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] + } + + c10, account10 := clientAndAccount("Node10") + c20, account20 := clientAndAccount("Node20") + + 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) + + 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(c20.FullStop()) + + afterStop, err := c10.Status() + a.NoError(err) + + // Advance 55 rounds + err = fixture.WaitForRoundWithTimeout(afterStop.LastRound + suspend20) + a.NoError(err) + + // 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) + + // pay n10 & n20, so both could be noticed + richAccount, err := fixture.GetRichestAccount() + a.NoError(err) + 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) + a.NoError(err) + a.Equal(basics.Offline, account.Status) + a.NotZero(account.VoteID) + a.False(account.IncentiveEligible) // suspension turns off flag + + // 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) + a.NotZero(account.VoteID) + 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 (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. + stat, err := lg.Status() + a.NoError(err) + + // Get the current round, and wait for the restarted node to get there. + stat, err = fixture.AlgodClient.Status() + 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) + + // 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) + 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, restartRound, i) + + r.Equal(voteID, account.VoteID, i) + r.False(account.IncentiveEligible, i) + } +} 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..0b38fe76ff 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) @@ -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. @@ -311,7 +307,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 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/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/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/application_support_test.go b/test/e2e-go/upgrades/application_support_test.go index 49635a43ac..549a82c5ab 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 @@ -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 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/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/libgoalFixture.go b/test/framework/fixtures/libgoalFixture.go index bb96058136..1ffc6493c0 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" @@ -35,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" @@ -65,6 +67,28 @@ 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 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, timeout time.Duration, lookback basics.Round) { + if f.consensus == nil { + f.consensus = make(config.ConsensusProtocols) + } + fast := config.Consensus[ver] + // 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 = timeout + fast.AgreementFilterTimeout = timeout + } + 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/framework/fixtures/restClientFixture.go b/test/framework/fixtures/restClientFixture.go index 40e12c408b..38e89c35e5 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) @@ -228,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 @@ -274,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) @@ -363,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 } diff --git a/test/scripts/e2e_subs/absentee.py b/test/scripts/e2e_subs/absentee.py new file mode 100755 index 0000000000..7e72d8a966 --- /dev/null +++ b/test/scripts/e2e_subs/absentee.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +import os +import sys +from goal import Goal + +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 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 + +# 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}") 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 5d0dd7db0f..57d8b0acb5 100755 --- a/test/scripts/e2e_subs/goal/goal.py +++ b/test/scripts/e2e_subs/goal/goal.py @@ -240,21 +240,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.algod.suggested_params() + 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.algod.suggested_params() + 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.algod.suggested_params() + params = self.params(kwargs.pop("lifetime", 1000), kwargs.pop("fee", None)) tx = txn.AssetConfigTxn( sender, params, **kwargs, strict_empty_address_check=False ) @@ -265,7 +265,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), kwargs.pop("fee", None)) tx = txn.AssetTransferTxn( sender, params, receiver, amt, index, **kwargs ) @@ -276,7 +276,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), kwargs.pop("fee", None)) tx = txn.AssetFreezeTxn(sender, params, index, target, frozen, **kwargs) return self.finish(tx, send) @@ -287,9 +287,19 @@ def coerce_schema(self, values): return values return txn.StateSchema(num_uints=values[0], num_byte_slices=values[1]) + + def params(self, lifetime=None, fee=None): + params = self.algod.suggested_params() + 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.algod.suggested_params() + 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/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/payouts.py b/test/scripts/e2e_subs/payouts.py new file mode 100755 index 0000000000..216ecc93ef --- /dev/null +++ b/test/scripts/e2e_subs/payouts.py @@ -0,0 +1,84 @@ +#!/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 11 + 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().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") + +# 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}") 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 diff --git a/test/testdata/nettemplates/Payouts.json b/test/testdata/nettemplates/Payouts.json new file mode 100644 index 0000000000..586370921e --- /dev/null +++ b/test/testdata/nettemplates/Payouts.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 new file mode 100644 index 0000000000..439cbb888b --- /dev/null +++ b/test/testdata/nettemplates/Suspension.json @@ -0,0 +1,28 @@ +{ + "Genesis": { + "NetworkName": "tbd", + "ConsensusProtocol": "future", + "LastPartKeyRound": 500, + "Wallets": [ + { "Name": "Relay", "Stake": 70, "Online": true }, + { "Name": "Wallet20", "Stake": 20, "Online": true }, + { "Name": "Wallet10", "Stake": 10, "Online": true } + ], + "RewardsPoolBalance": 0 + }, + "Nodes": [ + { + "Name": "Relay", + "Wallets": [{ "Name": "Relay", "ParticipationOnly": false }], + "IsRelay": true + }, + { + "Name": "Node20", + "Wallets": [{ "Name": "Wallet20", "ParticipationOnly": false }] + }, + { + "Name": "Node10", + "Wallets": [{ "Name": "Wallet10", "ParticipationOnly": false }] + } + ] +} 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 { 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