diff --git a/.github/workflows/codegen_verification.yml b/.github/workflows/codegen_verification.yml index 362f0c7add..a01e3e5e9c 100644 --- a/.github/workflows/codegen_verification.yml +++ b/.github/workflows/codegen_verification.yml @@ -7,6 +7,11 @@ on: jobs: codegen_verification: runs-on: ubuntu-20.04 + services: + converter: + image: swaggerapi/swagger-converter@sha256:dcfd1c2537f5f271cb4ec942d08aa59ca41b9a24078040061a772afca7e548ae # v1.0.4 + ports: + - 8080:8080 steps: - name: Check out code uses: actions/checkout@v3.5.3 @@ -16,6 +21,8 @@ jobs: - name: Uninstall existing go installation run: sudo apt-get -y -q purge golang-go - name: Run codegen_verification.sh + env: + SWAGGER_CONVERTER_API: "http://localhost:8080" run: | export GOPATH="${GITHUB_WORKSPACE}/go" cd go-algorand diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index c2d336194f..efa0d75217 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -17,10 +17,10 @@ jobs: - name: Make libsodium.a run: sudo mv /usr/bin/go /usr/bin/go.bak && make crypto/libs/linux/amd64/lib/libsodium.a && sudo mv /usr/bin/go.bak /usr/bin/go - name: reviewdog-golangci-lint - uses: reviewdog/action-golangci-lint@v2.3.1 + uses: reviewdog/action-golangci-lint@v2.6.1 with: go_version_file: go.mod - golangci_lint_version: "v1.53.2" + golangci_lint_version: "v1.58.0" golangci_lint_flags: "-c .golangci.yml --allow-parallel-runners" reporter: "github-pr-check" tool_name: "Lint Errors" @@ -63,13 +63,13 @@ jobs: run: | cd cicdtmp/golangci-lint git clone https://github.com/golangci/golangci-lint.git . - git checkout tags/v1.53.2 + git checkout tags/v1.58.0 CGO_ENABLED=true go build -trimpath -o golangci-lint-cgo ./cmd/golangci-lint ./golangci-lint-cgo --version cd ../../ - name: Install reviewdog run: | - curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/v0.14.1/install.sh | sh -s + curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/v0.17.4/install.sh | sh -s reviewdog --version - name: Build custom linters run: | diff --git a/.golangci.yml b/.golangci.yml index 24dba2d0c4..db13e1d65a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -36,7 +36,9 @@ linters-settings: - (*github.com/spf13/pflag.FlagSet).MarkDeprecated - (*github.com/spf13/pflag.FlagSet).MarkShorthandDeprecated govet: - check-shadowing: true + # Enables these linters in addition to the default ones. + enable: + - shadow settings: shadow: # explanation of strict vs non-strict: @@ -131,6 +133,10 @@ issues: linters: - staticcheck text: "SA4006: this value" # of X is never used + - path: _test\.go + linters: + - revive + text: "dot-imports: should not use dot imports" - linters: - staticcheck text: "SA1019: rand*" diff --git a/Dockerfile b/Dockerfile index 48bc652dcd..99a30949fa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 as builder -ARG GO_VERSION="1.20.14" +ARG GO_VERSION="1.21.10" ARG CHANNEL ARG URL @@ -46,7 +46,7 @@ FROM debian:bookworm-20240311-slim as final ENV PATH="/node/bin:${PATH}" ALGOD_PORT="8080" KMD_PORT="7833" ALGORAND_DATA="/algod/data" # curl is needed to lookup the fast catchup url -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl gosu && \ +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && \ update-ca-certificates && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ 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/service_test.go b/agreement/service_test.go index 5cc05901ed..3426b4df3b 100644 --- a/agreement/service_test.go +++ b/agreement/service_test.go @@ -650,14 +650,10 @@ func createTestAccountsAndBalances(t *testing.T, numNodes int, rootSeed []byte) // add new account rootAddress to db { rootAccess, err := db.MakeAccessor(t.Name()+"root"+strconv.Itoa(i+off), false, true) - if err != nil { - panic(err) - } + require.NoError(t, err) seed = sha256.Sum256(seed[:]) // rehash every node to get different root addresses root, err := account.ImportRoot(rootAccess, seed) - if err != nil { - panic(err) - } + require.NoError(t, err) rootAddress = root.Address() } @@ -742,9 +738,7 @@ func setupAgreementWithValidator(t *testing.T, numNodes int, traceLevel traceLev for i := 0; i < numNodes; i++ { accessor, err := db.MakeAccessor(t.Name()+"_"+strconv.Itoa(i)+"_crash.db", false, true) - if err != nil { - panic(err) - } + require.NoError(t, err) dbAccessors[i] = accessor m := baseNetwork.monitors[nodeID(i)] @@ -826,23 +820,17 @@ func (m *coserviceMonitor) clearClock() { } } -func expectNewPeriod(clocks []timers.Clock[TimeoutType], zeroes uint) (newzeroes uint) { +func expectNewPeriod(t *testing.T, clocks []timers.Clock[TimeoutType], zeroes uint) (newzeroes uint) { zeroes++ for i := range clocks { - if clocks[i].(*testingClock).zeroes != zeroes { - errstr := fmt.Sprintf("unexpected number of zeroes: %v != %v", clocks[i].(*testingClock).zeroes, zeroes) - panic(errstr) - } + require.Equal(t, zeroes, clocks[i].(*testingClock).zeroes, "unexpected number of zeroes") } return zeroes } -func expectNoNewPeriod(clocks []timers.Clock[TimeoutType], zeroes uint) (newzeroes uint) { +func expectNoNewPeriod(t *testing.T, clocks []timers.Clock[TimeoutType], zeroes uint) (newzeroes uint) { for i := range clocks { - if clocks[i].(*testingClock).zeroes != zeroes { - errstr := fmt.Sprintf("unexpected number of zeroes: %v != %v", clocks[i].(*testingClock).zeroes, zeroes) - panic(errstr) - } + require.Equal(t, zeroes, clocks[i].(*testingClock).zeroes, "unexpected number of zeroes") } return zeroes } @@ -869,13 +857,13 @@ func triggerGlobalTimeoutType(timeoutType TimeoutType, clocks []timers.Clock[Tim activityMonitor.waitForQuiet() } -func runRound(clocks []timers.Clock[TimeoutType], activityMonitor *activityMonitor, zeroes uint, filterTimeout time.Duration) (newzeroes uint) { +func runRound(t *testing.T, clocks []timers.Clock[TimeoutType], activityMonitor *activityMonitor, zeroes uint, filterTimeout time.Duration) (newzeroes uint) { triggerGlobalTimeout(filterTimeout, TimeoutFilter, clocks, activityMonitor) - return expectNewPeriod(clocks, zeroes) + return expectNewPeriod(t, clocks, zeroes) } -func runRoundTriggerFilter(clocks []timers.Clock[TimeoutType], activityMonitor *activityMonitor, zeroes uint) (newzeroes uint) { +func runRoundTriggerFilter(t *testing.T, clocks []timers.Clock[TimeoutType], activityMonitor *activityMonitor, zeroes uint) (newzeroes uint) { triggerGlobalTimeoutType(TimeoutFilter, clocks, activityMonitor) - return expectNewPeriod(clocks, zeroes) + return expectNewPeriod(t, clocks, zeroes) } func sanityCheck(startRound round, numRounds round, ledgers []Ledger) { @@ -916,19 +904,19 @@ func simulateAgreementWithLedgerFactory(t *testing.T, numNodes int, numRounds in } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) filterTimeouts := make([][]time.Duration, numNodes, numNodes) // run round with round-specific consensus version first (since fix in #1896) - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) for j := 1; j < numRounds; j++ { for srvIdx, clock := range clocks { delta, err := clock.(*testingClock).when(TimeoutFilter) require.NoError(t, err) filterTimeouts[srvIdx] = append(filterTimeouts[srvIdx], delta) } - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) } for i := 0; i < numNodes; i++ { @@ -1127,21 +1115,21 @@ func TestDynamicFilterTimeoutResets(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) filterTimeouts := make([][]time.Duration, numNodes, numNodes) baseHistoryRounds := dynamicFilterCredentialArrivalHistory + int(credentialRoundLag) // run round with round-specific consensus version first (since fix in #1896) - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) for j := 1; j < baseHistoryRounds+2; j++ { for srvIdx, clock := range clocks { delta, err := clock.(*testingClock).when(TimeoutFilter) require.NoError(t, err) filterTimeouts[srvIdx] = append(filterTimeouts[srvIdx], delta) } - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) } for i := range clocks { @@ -1158,36 +1146,36 @@ func TestDynamicFilterTimeoutResets(t *testing.T) { baseNetwork.dropAllSlowNextVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeoutType(TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(firstFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 1 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } filterTimeoutsPostRecovery := make([][]time.Duration, numNodes, numNodes) // run round with round-specific consensus version first (since fix in #1896) - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) for j := 1; j < baseHistoryRounds+1; j++ { for srvIdx, clock := range clocks { delta, err := clock.(*testingClock).when(TimeoutFilter) require.NoError(t, err) filterTimeoutsPostRecovery[srvIdx] = append(filterTimeoutsPostRecovery[srvIdx], delta) } - zeroes = runRoundTriggerFilter(clocks, activityMonitor, zeroes) + zeroes = runRoundTriggerFilter(t, clocks, activityMonitor, zeroes) } for i := range clocks { @@ -1262,11 +1250,11 @@ func TestAgreementFastRecoveryDownEarly(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force fast partition recovery into bottom @@ -1275,28 +1263,28 @@ func TestAgreementFastRecoveryDownEarly(t *testing.T) { baseNetwork.dropAllSlowNextVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeoutType(TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(firstFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 1 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1320,11 +1308,11 @@ func TestAgreementFastRecoveryDownMiss(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force fast partition recovery into bottom @@ -1332,13 +1320,13 @@ func TestAgreementFastRecoveryDownMiss(t *testing.T) { // fail all steps baseNetwork.dropAllVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) firstClocks := clocks[:4] restClocks := clocks[4:] @@ -1351,7 +1339,7 @@ func TestAgreementFastRecoveryDownMiss(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() for i := range restClocks { @@ -1362,22 +1350,22 @@ func TestAgreementFastRecoveryDownMiss(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(secondFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 1 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1401,11 +1389,11 @@ func TestAgreementFastRecoveryLate(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force fast partition recovery into value @@ -1415,31 +1403,26 @@ func TestAgreementFastRecoveryLate(t *testing.T) { closeFn := baseNetwork.pocketAllCertVotes(pocket) baseNetwork.dropAllSlowNextVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } } triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.dropAllVotes() firstClocks := clocks[:4] @@ -1453,7 +1436,7 @@ func TestAgreementFastRecoveryLate(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() for i := range restClocks { @@ -1464,33 +1447,28 @@ func TestAgreementFastRecoveryLate(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(secondFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 1 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } for _, l := range ledgers { lastHash, err := l.LookupDigest(l.NextRound() - 1) - if err != nil { - panic(err) - } - if lastHash != expected.BlockDigest { - errstr := fmt.Sprintf("converged on wrong block: %v != %v", lastHash, expected.BlockDigest) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected.BlockDigest, lastHash, "converged on wrong block") } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1514,11 +1492,11 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force fast partition recovery into value @@ -1528,31 +1506,26 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { closeFn := baseNetwork.pocketAllCertVotes(pocket) baseNetwork.dropAllSlowNextVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } } triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.dropAllVotes() firstClocks := clocks[:4] @@ -1566,7 +1539,7 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() for i := range restClocks { @@ -1577,23 +1550,23 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(secondFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // fail period 1 with value again { baseNetwork.dropAllVotes() triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(0, TimeoutFastRecovery, clocks, activityMonitor) // activates fast partition recovery timer - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.dropAllVotes() firstClocks := clocks[:4] @@ -1607,7 +1580,7 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() for i := range restClocks { @@ -1618,33 +1591,28 @@ func TestAgreementFastRecoveryRedo(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(secondFPR, TimeoutFastRecovery, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 2 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } for _, l := range ledgers { lastHash, err := l.LookupDigest(l.NextRound() - 1) - if err != nil { - panic(err) - } - if lastHash != expected.BlockDigest { - errstr := fmt.Sprintf("converged on wrong block: %v != %v", lastHash, expected.BlockDigest) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected.BlockDigest, lastHash, "converged on wrong block") } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1668,42 +1636,42 @@ func TestAgreementBlockReplayBug_b29ea57(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // fail period 0 { baseNetwork.dropAllSoftVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // fail period 1 on bottom with block { triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 2 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1727,11 +1695,11 @@ func TestAgreementLateCertBug(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // delay minority cert votes to force period 1 @@ -1739,12 +1707,12 @@ func TestAgreementLateCertBug(t *testing.T) { { closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() baseNetwork.repairAll() triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // terminate on period 0 in period 1 @@ -1756,12 +1724,12 @@ func TestAgreementLateCertBug(t *testing.T) { baseNetwork.finishAllMulticast() activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { @@ -1785,11 +1753,11 @@ func TestAgreementRecoverGlobalStartingValue(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force partition recovery into value @@ -1799,28 +1767,23 @@ func TestAgreementRecoverGlobalStartingValue(t *testing.T) { closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } } triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 4, int(zeroes)) } @@ -1830,24 +1793,18 @@ func TestAgreementRecoverGlobalStartingValue(t *testing.T) { closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } - - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 5, int(zeroes)) } @@ -1856,13 +1813,13 @@ func TestAgreementRecoverGlobalStartingValue(t *testing.T) { { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 6, int(zeroes)) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -1885,11 +1842,11 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force partition recovery into value. @@ -1898,23 +1855,18 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { pocket := make(chan multicastParams, 100) closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } } // intercept all proposals for the next period; replace with unexpected @@ -1925,7 +1877,7 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { return params }) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 4, int(zeroes)) } @@ -1935,23 +1887,17 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { pocket := make(chan multicastParams, 100) closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } - - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected, uv.R.Proposal, "unexpected proposal") } triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } @@ -1959,13 +1905,13 @@ func TestAgreementRecoverGlobalStartingValueBadProposal(t *testing.T) { { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 6, int(zeroes)) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -1988,11 +1934,11 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // force partition recovery into both bottom and value. one node enters bottom, the rest enter value @@ -2001,7 +1947,7 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { pocket := make(chan multicastParams, 100) closeFn := baseNetwork.pocketAllSoftVotes(pocket) triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() pocketedSoft := make([]multicastParams, len(pocket)) i := 0 @@ -2009,16 +1955,11 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { r := bytes.NewBuffer(params.data) var uv unauthenticatedVote err := protocol.DecodeStream(r, &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if expected == (proposalValue{}) { expected = uv.R.Proposal } else { - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected soft vote: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.Equal(t, expected, uv.R.Proposal, "unexpected soft vote") } pocketedSoft[i] = params i++ @@ -2026,11 +1967,9 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { // generate a bottom quorum; let only one node see it. baseNetwork.crown(0) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - if clocks[0].(*testingClock).zeroes != zeroes+1 { - errstr := fmt.Sprintf("node 0 did not enter new period from bot quorum") - panic(errstr) - } - zeroes = expectNoNewPeriod(clocks[1:], zeroes) + require.Equal(t, zeroes+1, clocks[0].(*testingClock).zeroes, + "node 0 did not enter new period from bot quorum") + zeroes = expectNoNewPeriod(t, clocks[1:], zeroes) // enable creation of a value quorum; let everyone else see it baseNetwork.repairAll() @@ -2045,12 +1984,12 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { // actually create the value quorum _, upper := (next).nextVoteRanges(DeadlineTimeout(0, version)) triggerGlobalTimeout(upper, TimeoutDeadline, clocks[1:], activityMonitor) // activates next timers - zeroes = expectNoNewPeriod(clocks[1:], zeroes) + zeroes = expectNoNewPeriod(t, clocks[1:], zeroes) lower, upper := (next + 1).nextVoteRanges(DeadlineTimeout(0, version)) delta := time.Duration(testingRand{}.Uint64() % uint64(upper-lower)) triggerGlobalTimeout(lower+delta, TimeoutDeadline, clocks[1:], activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 4, int(zeroes)) } @@ -2060,37 +1999,31 @@ func TestAgreementRecoverBothVAndBotQuorums(t *testing.T) { pocket := make(chan multicastParams, 100) closeFn := baseNetwork.pocketAllCertVotes(pocket) triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) closeFn() for msg := range pocket { var uv unauthenticatedVote err := protocol.DecodeStream(bytes.NewBuffer(msg.data), &uv) - if err != nil { - panic(err) - } - - if uv.R.Proposal != expected { - errstr := fmt.Sprintf("got unexpected proposal: %v != %v", uv.R.Proposal, expected) - panic(errstr) - } + require.NoError(t, err) + require.Equal(t, expected, uv.R.Proposal, "got unexpected proposal") } triggerGlobalTimeout(DeadlineTimeout(1, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // Finish in period 2 { baseNetwork.repairAll() triggerGlobalTimeout(FilterTimeout(2, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 6, int(zeroes)) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2113,11 +2046,11 @@ func TestAgreementSlowPayloadsPreDeadline(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // run round and then start pocketing payloads @@ -2125,13 +2058,13 @@ func TestAgreementSlowPayloadsPreDeadline(t *testing.T) { closeFn := baseNetwork.pocketAllCompound(pocket) // (takes effect next round) { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run round with late payload { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) // release payloads; expect new round closeFn() @@ -2143,12 +2076,12 @@ func TestAgreementSlowPayloadsPreDeadline(t *testing.T) { baseNetwork.finishAllMulticast() activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2171,11 +2104,11 @@ func TestAgreementSlowPayloadsPostDeadline(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // run round and then start pocketing payloads @@ -2183,15 +2116,15 @@ func TestAgreementSlowPayloadsPostDeadline(t *testing.T) { closeFn := baseNetwork.pocketAllCompound(pocket) // (takes effect next round) { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // force network into period 1 by delaying proposals { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(DeadlineTimeout(0, version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // recover in period 1 @@ -2205,15 +2138,15 @@ func TestAgreementSlowPayloadsPostDeadline(t *testing.T) { baseNetwork.finishAllMulticast() activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) triggerGlobalTimeout(FilterTimeout(1, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2236,11 +2169,11 @@ func TestAgreementLargePeriods(t *testing.T) { activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // partition the network, run until period 60 @@ -2248,11 +2181,11 @@ func TestAgreementLargePeriods(t *testing.T) { { baseNetwork.partition(0, 1, 2) triggerGlobalTimeout(FilterTimeout(period(p), version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) baseNetwork.repairAll() triggerGlobalTimeout(DeadlineTimeout(period(p), version), TimeoutDeadline, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) require.Equal(t, 4+p, int(zeroes)) } } @@ -2260,12 +2193,12 @@ func TestAgreementLargePeriods(t *testing.T) { // terminate { triggerGlobalTimeout(FilterTimeout(60, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // run two more rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2273,9 +2206,8 @@ func TestAgreementLargePeriods(t *testing.T) { const expectNumRounds = 5 for i := 0; i < numNodes; i++ { - if ledgers[i].NextRound() != startRound+round(expectNumRounds) { - panic("did not progress 5 rounds") - } + require.Equal(t, startRound+round(expectNumRounds), ledgers[i].NextRound(), + "did not progress 5 rounds") } for j := 0; j < expectNumRounds; j++ { @@ -2283,9 +2215,7 @@ func TestAgreementLargePeriods(t *testing.T) { reference := ledger.entries[startRound+round(j)].Digest() for i := 0; i < numNodes; i++ { ledger := ledgers[i].(*testLedger) - if ledger.entries[startRound+round(j)].Digest() != reference { - panic("wrong block confirmed") - } + require.Equal(t, reference, ledger.entries[startRound+round(j)].Digest(), "wrong block confirmed") } } } @@ -2334,11 +2264,11 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds for j := 0; j < 2; j++ { - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) } // run round and then start pocketing payloads, suspending validation @@ -2347,13 +2277,13 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 closeFn := baseNetwork.pocketAllCompound(pocket0) // (takes effect next round) { triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) } // force network into period 1 by failing period 0, entering with bottom and no soft threshold (to prevent proposal value pinning) baseNetwork.dropAllSoftVotes() triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNoNewPeriod(clocks, zeroes) + zeroes = expectNoNewPeriod(t, clocks, zeroes) // resume delivery of payloads in following period baseNetwork.repairAll() @@ -2440,15 +2370,15 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 } baseNetwork.finishAllMulticast() - zeroes = expectNewPeriod(clocks, zeroes) + zeroes = expectNewPeriod(t, clocks, zeroes) activityMonitor.waitForQuiet() // run two more rounds //for j := 0; j < 2; j++ { - // zeroes = runRound(clocks, activityMonitor, zeroes, period(1-j)) + // zeroes = runRound(t, clocks, activityMonitor, zeroes, period(1-j)) //} - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(1, version)) - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(1, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) for i := 0; i < numNodes; i++ { services[i].Shutdown() @@ -2456,9 +2386,8 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 const expectNumRounds = 5 for i := 0; i < numNodes; i++ { - if ledgers[i].NextRound() != startRound+round(expectNumRounds) { - panic("did not progress 5 rounds") - } + require.Equal(t, startRound+round(expectNumRounds), ledgers[i].NextRound(), + "did not progress 5 rounds") } for j := 0; j < expectNumRounds; j++ { @@ -2466,9 +2395,7 @@ func TestAgreementRegression_WrongPeriodPayloadVerificationCancellation_8ba23942 reference := ledger.entries[startRound+round(j)].Digest() for i := 0; i < numNodes; i++ { ledger := ledgers[i].(*testLedger) - if ledger.entries[startRound+round(j)].Digest() != reference { - panic("wrong block confirmed") - } + require.Equal(t, reference, ledger.entries[startRound+round(j)].Digest(), "wrong block confirmed") } } } @@ -2491,17 +2418,15 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { } activityMonitor.waitForActivity() activityMonitor.waitForQuiet() - zeroes := expectNewPeriod(clocks, 0) + zeroes := expectNewPeriod(t, clocks, 0) // run two rounds - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) // make sure relay does not see block proposal for round 3 baseNetwork.intercept(func(params multicastParams) multicastParams { if params.tag == protocol.ProposalPayloadTag { var tp transmittedPayload err := protocol.DecodeStream(bytes.NewBuffer(params.data), &tp) - if err != nil { - panic(err) - } + require.NoError(t, err) if tp.Round() == basics.Round(startRound+2) { params.exclude = relayID } @@ -2512,9 +2437,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { if params.tag == protocol.AgreementVoteTag { var uv unauthenticatedVote err := protocol.DecodeStream(r, &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if uv.R.Step != propose { return params } @@ -2524,7 +2447,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { return params }) - zeroes = runRound(clocks, activityMonitor, zeroes, FilterTimeout(0, version)) + zeroes = runRound(t, clocks, activityMonitor, zeroes, FilterTimeout(0, version)) // Round 3: // First partition the relay to prevent it from seeing certificate or block @@ -2537,9 +2460,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { r := bytes.NewBuffer(params.data) var uv unauthenticatedVote err := protocol.DecodeStream(r, &uv) - if err != nil { - panic(err) - } + require.NoError(t, err) if uv.R.Step == cert { pocketCert <- params } @@ -2548,7 +2469,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { }) // And with some hypothetical second relay the network achieves consensus on a certificate and block. triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks, activityMonitor) - zeroes = expectNewPeriod(clocks[1:], zeroes) + zeroes = expectNewPeriod(t, clocks[1:], zeroes) require.Equal(t, uint(3), clocks[0].(*testingClock).zeroes) close(pocketCert) @@ -2567,7 +2488,7 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { // this relay must still relay initial messages. Note that payloads were already relayed with // the previous global timeout. triggerGlobalTimeout(FilterTimeout(0, version), TimeoutFilter, clocks[1:], activityMonitor) - zeroes = expectNewPeriod(clocks[1:], zeroes) + zeroes = expectNewPeriod(t, clocks[1:], zeroes) require.Equal(t, uint(3), clocks[0].(*testingClock).zeroes) for i := 0; i < numNodes; i++ { @@ -2575,18 +2496,15 @@ func TestAgreementCertificateDoesNotStallSingleRelay(t *testing.T) { } const expectNumRounds = 4 for i := 1; i < numNodes; i++ { - if ledgers[i].NextRound() != startRound+round(expectNumRounds) { - panic("did not progress 4 rounds") - } + require.Equal(t, startRound+round(expectNumRounds), ledgers[i].NextRound(), + "did not progress 4 rounds") } for j := 0; j < expectNumRounds; j++ { ledger := ledgers[1].(*testLedger) reference := ledger.entries[startRound+round(j)].Digest() for i := 1; i < numNodes; i++ { ledger := ledgers[i].(*testLedger) - if ledger.entries[startRound+round(j)].Digest() != reference { - panic("wrong block confirmed") - } + require.Equal(t, reference, ledger.entries[startRound+round(j)].Digest(), "wrong block confirmed") } } } diff --git a/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/buildnumber.dat b/buildnumber.dat index d00491fd7e..573541ac97 100644 --- a/buildnumber.dat +++ b/buildnumber.dat @@ -1 +1 @@ -1 +0 diff --git a/cmd/algokey/common.go b/cmd/algokey/common.go index 0259dc5e05..8fc5867db9 100644 --- a/cmd/algokey/common.go +++ b/cmd/algokey/common.go @@ -23,7 +23,6 @@ import ( "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/crypto/passphrase" - "github.com/muesli/termenv" ) const ( @@ -123,18 +122,3 @@ func readFile(filename string) ([]byte, error) { } return os.ReadFile(filename) } - -// printDiscreetly Print a secret string to an alternate screen, -// so the string isn't printed to the terminal. -func printDiscreetly(w io.Writer, promptMsg, secretMsg string) error { - output := termenv.NewOutput(w) - output.AltScreen() - defer output.ExitAltScreen() - if _, err := fmt.Fprintf(output, "%s\n\n%s\n\nPress 'Enter' key to continue.", promptMsg, secretMsg); err != nil { - return err - } - if _, err := fmt.Scanln(); err != nil { - return err - } - return nil -} diff --git a/cmd/algokey/common_test.go b/cmd/algokey/common_test.go deleted file mode 100644 index 2256342616..0000000000 --- a/cmd/algokey/common_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// 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 main - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/algorand/go-algorand/test/partitiontest" -) - -func Test_printDiscreetly(t *testing.T) { - partitiontest.PartitionTest(t) - t.Parallel() - buf := new(bytes.Buffer) - var ( - promptMsg = "Prompt Message" - secretMsg = "Secret Message" - ) - require.NoError(t, printDiscreetly(buf, promptMsg, secretMsg)) - require.Contains(t, buf.String(), promptMsg) - require.Contains(t, buf.String(), secretMsg) -} diff --git a/cmd/algokey/generate.go b/cmd/algokey/generate.go index 6b1c409e68..6382fa3a0c 100644 --- a/cmd/algokey/generate.go +++ b/cmd/algokey/generate.go @@ -18,7 +18,6 @@ package main import ( "fmt" - "os" "github.com/spf13/cobra" @@ -28,12 +27,10 @@ import ( var generateKeyfile string var generatePubkeyfile string -var generateDiscreet bool func init() { generateCmd.Flags().StringVarP(&generateKeyfile, "keyfile", "f", "", "Private key filename") generateCmd.Flags().StringVarP(&generatePubkeyfile, "pubkeyfile", "p", "", "Public key filename") - generateCmd.Flags().BoolVar(&generateDiscreet, "discreet", false, "Print mnemonic discreetly to an alternate screen") } var generateCmd = &cobra.Command{ @@ -49,14 +46,7 @@ var generateCmd = &cobra.Command{ key := crypto.GenerateSignatureSecrets(seed) publicKeyChecksummed := basics.Address(key.SignatureVerifier).String() - if generateDiscreet { - if err := printDiscreetly(os.Stderr, "**Important** write this private key mnemonic phrase in a safe place. Do not share it to anyone", fmt.Sprintf("Private key mnemonic: %s", mnemonic)); err != nil { - fmt.Fprintf(os.Stderr, "Fail to print mnemonic: %v", err) - os.Exit(1) - } - } else { - fmt.Printf("Private key mnemonic: %s\n", mnemonic) - } + fmt.Printf("Private key mnemonic: %s\n", mnemonic) fmt.Printf("Public key: %s\n", publicKeyChecksummed) if generateKeyfile != "" { diff --git a/cmd/goal/account.go b/cmd/goal/account.go index 9fe230f670..67d382bf45 100644 --- a/cmd/goal/account.go +++ b/cmd/goal/account.go @@ -69,8 +69,11 @@ var ( mnemonic string dumpOutFile string listAccountInfo bool - onlyShowAssetIds bool + onlyShowAssetIDs bool partKeyIDToDelete string + + next string + limit uint64 ) func init() { @@ -79,6 +82,7 @@ func init() { accountCmd.AddCommand(listCmd) accountCmd.AddCommand(renameCmd) accountCmd.AddCommand(infoCmd) + accountCmd.AddCommand(assetDetailsCmd) accountCmd.AddCommand(balanceCmd) accountCmd.AddCommand(rewardsCmd) accountCmd.AddCommand(changeOnlineCmd) @@ -134,7 +138,13 @@ func init() { // Info flags infoCmd.Flags().StringVarP(&accountAddress, "address", "a", "", "Account address to look up (required)") infoCmd.MarkFlagRequired("address") - infoCmd.Flags().BoolVar(&onlyShowAssetIds, "onlyShowAssetIds", false, "Only show ASA IDs and not pull asset metadata") + infoCmd.Flags().BoolVar(&onlyShowAssetIDs, "onlyShowAssetIDs", false, "Only show ASA IDs and not pull asset metadata") + + // Asset details flags + assetDetailsCmd.Flags().StringVarP(&accountAddress, "address", "a", "", "Account address to look up (required)") + assetDetailsCmd.MarkFlagRequired("address") + assetDetailsCmd.Flags().StringVarP(&next, "next", "n", "", "The next asset index to use for pagination") + assetDetailsCmd.Flags().Uint64VarP(&limit, "limit", "l", 0, "The maximum number of assets to return") // Balance flags balanceCmd.Flags().StringVarP(&accountAddress, "address", "a", "", "Account address to retrieve balance (required)") @@ -539,6 +549,33 @@ var listCmd = &cobra.Command{ }, } +var assetDetailsCmd = &cobra.Command{ + Use: "assetdetails", + Short: "Retrieve information about the assets belonging to the specified account inclusive of asset metadata", + Long: `Retrieve information about the assets the specified account has created or opted into, inclusive of asset metadata.`, + Args: validateNoPosArgsFn, + Run: func(cmd *cobra.Command, args []string) { + dataDir := datadir.EnsureSingleDataDir() + client := ensureAlgodClient(dataDir) + + var nextPtr *string + var limitPtr *uint64 + if next != "" { + nextPtr = &next + } + if limit != 0 { + limitPtr = &limit + } + response, err := client.AccountAssetsInformation(accountAddress, nextPtr, limitPtr) + + if err != nil { + reportErrorf(errorRequestFail, err) + } + + printAccountAssetsInformation(accountAddress, response) + + }, +} var infoCmd = &cobra.Command{ Use: "info", Short: "Retrieve information about the assets and applications belonging to the specified account", @@ -552,14 +589,14 @@ var infoCmd = &cobra.Command{ reportErrorf(errorRequestFail, err) } - hasError := printAccountInfo(client, accountAddress, onlyShowAssetIds, response) + hasError := printAccountInfo(client, accountAddress, onlyShowAssetIDs, response) if hasError { os.Exit(1) } }, } -func printAccountInfo(client libgoal.Client, address string, onlyShowAssetIds bool, account model.Account) bool { +func printAccountInfo(client libgoal.Client, address string, onlyShowAssetIDs bool, account model.Account) bool { var createdAssets []model.Asset if account.CreatedAssets != nil { createdAssets = slices.Clone(*account.CreatedAssets) @@ -627,7 +664,7 @@ func printAccountInfo(client libgoal.Client, address string, onlyShowAssetIds bo fmt.Fprintln(report, "\t") } for _, assetHolding := range heldAssets { - if onlyShowAssetIds { + if onlyShowAssetIDs { fmt.Fprintf(report, "\tID %d\n", assetHolding.AssetID) continue } @@ -731,6 +768,48 @@ func printAccountInfo(client libgoal.Client, address string, onlyShowAssetIds bo return hasError } +func printAccountAssetsInformation(address string, response model.AccountAssetsInformationResponse) { + fmt.Printf("Account: %s\n", address) + fmt.Printf("Round: %d\n", response.Round) + if response.NextToken != nil { + fmt.Printf("NextToken (to retrieve more account assets): %s\n", *response.NextToken) + } + fmt.Printf("Assets:\n") + for _, asset := range *response.AssetHoldings { + fmt.Printf(" Asset ID: %d\n", asset.AssetHolding.AssetID) + + if asset.AssetParams != nil { + amount := assetDecimalsFmt(asset.AssetHolding.Amount, asset.AssetParams.Decimals) + fmt.Printf(" Amount: %s\n", amount) + fmt.Printf(" IsFrozen: %t\n", asset.AssetHolding.IsFrozen) + fmt.Printf(" Asset Params:\n") + fmt.Printf(" Creator: %s\n", asset.AssetParams.Creator) + + name := "" + if asset.AssetParams.Name != nil { + _, name = unicodePrintable(*asset.AssetParams.Name) + } + fmt.Printf(" Name: %s\n", name) + + units := "units" + if asset.AssetParams.UnitName != nil { + _, units = unicodePrintable(*asset.AssetParams.UnitName) + } + fmt.Printf(" Units: %s\n", units) + fmt.Printf(" Total: %d\n", asset.AssetParams.Total) + fmt.Printf(" Decimals: %d\n", asset.AssetParams.Decimals) + safeURL := "" + if asset.AssetParams.Url != nil { + _, safeURL = unicodePrintable(*asset.AssetParams.Url) + } + fmt.Printf(" URL: %s\n", safeURL) + } else { + fmt.Printf(" Amount (without formatting): %d\n", asset.AssetHolding.Amount) + fmt.Printf(" IsFrozen: %t\n", asset.AssetHolding.IsFrozen) + } + } +} + var balanceCmd = &cobra.Command{ Use: "balance", Short: "Retrieve the balances for the specified account", diff --git a/cmd/goal/network.go b/cmd/goal/network.go index a0d8cf4241..41120f7db2 100644 --- a/cmd/goal/network.go +++ b/cmd/goal/network.go @@ -157,7 +157,11 @@ var networkCreateCmd = &cobra.Command{ consensus, _ = config.PreloadConfigurableConsensusProtocols(dataDir) } - network, err := netdeploy.CreateNetworkFromTemplate(networkName, networkRootDir, templateReader, binDir, !noImportKeys, nil, consensus, devModeOverride) + var overrides []netdeploy.TemplateOverride + if devModeOverride { + overrides = append(overrides, netdeploy.OverrideDevMode) + } + network, err := netdeploy.CreateNetworkFromTemplate(networkName, networkRootDir, templateReader, binDir, !noImportKeys, nil, consensus, overrides...) if err != nil { if noClean { reportInfof(" ** failed ** - Preserving network rootdir '%s'", networkRootDir) diff --git a/cmd/partitiontest_linter/go.mod b/cmd/partitiontest_linter/go.mod index 88ea431f95..871ea35935 100644 --- a/cmd/partitiontest_linter/go.mod +++ b/cmd/partitiontest_linter/go.mod @@ -1,9 +1,12 @@ module github.com/algorand/go-algorand/cmd/partitiontest_linter -go 1.20 +go 1.21 -require golang.org/x/sys v0.8.0 // indirect +toolchain go1.21.10 -require golang.org/x/mod v0.10.0 // indirect +require ( + golang.org/x/mod v0.17.0 // indirect + golang.org/x/sync v0.7.0 // indirect +) -require golang.org/x/tools v0.9.3 +require golang.org/x/tools v0.20.0 diff --git a/cmd/partitiontest_linter/go.sum b/cmd/partitiontest_linter/go.sum index a5ed5c851f..9e7e462a7c 100644 --- a/cmd/partitiontest_linter/go.sum +++ b/cmd/partitiontest_linter/go.sum @@ -1,7 +1,6 @@ -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= 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/config/localTemplate.go b/config/localTemplate.go index ce4294de01..67218f8ade 100644 --- a/config/localTemplate.go +++ b/config/localTemplate.go @@ -24,6 +24,7 @@ import ( "time" "github.com/algorand/go-algorand/protocol" + "github.com/algorand/go-algorand/util" "github.com/algorand/go-algorand/util/codecs" ) @@ -893,7 +894,7 @@ func moveDirIfExists(logger logger, srcdir, dstdir string, files ...string) erro // then, check if any files exist in srcdir, and move them to dstdir for _, file := range files { if _, err := os.Stat(filepath.Join(srcdir, file)); err == nil { - if err := os.Rename(filepath.Join(srcdir, file), filepath.Join(dstdir, file)); err != nil { + if err := util.MoveFile(filepath.Join(srcdir, file), filepath.Join(dstdir, file)); err != nil { return fmt.Errorf("failed to move file %s from %s to %s: %v", file, srcdir, dstdir, err) } logger.Infof("Moved DB file %s from ColdDataDir %s to HotDataDir %s", file, srcdir, dstdir) diff --git a/config/version.go b/config/version.go index dce2bd533e..643ac34e86 100644 --- a/config/version.go +++ b/config/version.go @@ -33,7 +33,7 @@ const VersionMajor = 3 // VersionMinor is the Minor semantic version number (x.#.z) - changed when backwards-compatible features are introduced. // Not enforced until after initial public release (x > 0). -const VersionMinor = 23 +const VersionMinor = 24 // Version is the type holding our full version information. type Version struct { diff --git a/crypto/batchverifier.go b/crypto/batchverifier.go index 0281e86e87..661e382898 100644 --- a/crypto/batchverifier.go +++ b/crypto/batchverifier.go @@ -50,14 +50,20 @@ import ( ) // BatchVerifier enqueues signatures to be validated in batch. -type BatchVerifier struct { +type BatchVerifier interface { + EnqueueSignature(sigVerifier SignatureVerifier, message Hashable, sig Signature) + GetNumberOfEnqueuedSignatures() int + Verify() error + VerifyWithFeedback() (failed []bool, err error) +} + +type cgoBatchVerifier struct { messages []Hashable // contains a slice of messages to be hashed. Each message is varible length publicKeys []SignatureVerifier // contains a slice of public keys. Each individual public key is 32 bytes. signatures []Signature // contains a slice of signatures keys. Each individual signature is 64 bytes. + useSingle bool } -const minBatchVerifierAlloc = 16 - // Batch verifications errors var ( ErrBatchHasFailedSigs = errors.New("At least one signature didn't pass verification") @@ -69,27 +75,31 @@ func ed25519_randombytes_unsafe(p unsafe.Pointer, len C.size_t) { RandBytes(randBuf) } -// MakeBatchVerifier creates a BatchVerifier instance. -func MakeBatchVerifier() *BatchVerifier { +const minBatchVerifierAlloc = 16 +const useSingleVerifierDefault = true + +// MakeBatchVerifier creates a BatchVerifier instance with the provided options. +func MakeBatchVerifier() BatchVerifier { return MakeBatchVerifierWithHint(minBatchVerifierAlloc) } -// MakeBatchVerifierWithHint creates a BatchVerifier instance. This function pre-allocates +// MakeBatchVerifierWithHint creates a cgoBatchVerifier instance. This function pre-allocates // amount of free space to enqueue signatures without expanding -func MakeBatchVerifierWithHint(hint int) *BatchVerifier { +func MakeBatchVerifierWithHint(hint int) BatchVerifier { // preallocate enough storage for the expected usage. We will reallocate as needed. if hint < minBatchVerifierAlloc { hint = minBatchVerifierAlloc } - return &BatchVerifier{ + return &cgoBatchVerifier{ messages: make([]Hashable, 0, hint), publicKeys: make([]SignatureVerifier, 0, hint), signatures: make([]Signature, 0, hint), + useSingle: useSingleVerifierDefault, } } // EnqueueSignature enqueues a signature to be enqueued -func (b *BatchVerifier) EnqueueSignature(sigVerifier SignatureVerifier, message Hashable, sig Signature) { +func (b *cgoBatchVerifier) EnqueueSignature(sigVerifier SignatureVerifier, message Hashable, sig Signature) { // do we need to reallocate ? if len(b.messages) == cap(b.messages) { b.expand() @@ -99,7 +109,7 @@ func (b *BatchVerifier) EnqueueSignature(sigVerifier SignatureVerifier, message b.signatures = append(b.signatures, sig) } -func (b *BatchVerifier) expand() { +func (b *cgoBatchVerifier) expand() { messages := make([]Hashable, len(b.messages), len(b.messages)*2) publicKeys := make([]SignatureVerifier, len(b.publicKeys), len(b.publicKeys)*2) signatures := make([]Signature, len(b.signatures), len(b.signatures)*2) @@ -112,12 +122,12 @@ func (b *BatchVerifier) expand() { } // GetNumberOfEnqueuedSignatures returns the number of signatures currently enqueued into the BatchVerifier -func (b *BatchVerifier) GetNumberOfEnqueuedSignatures() int { +func (b *cgoBatchVerifier) GetNumberOfEnqueuedSignatures() int { return len(b.messages) } // Verify verifies that all the signatures are valid. in that case nil is returned -func (b *BatchVerifier) Verify() error { +func (b *cgoBatchVerifier) Verify() error { _, err := b.VerifyWithFeedback() return err } @@ -126,11 +136,15 @@ func (b *BatchVerifier) Verify() error { // if all sigs are valid, nil will be returned for err (failed will have all false) // if some signatures are invalid, true will be set in failed at the corresponding indexes, and // ErrBatchVerificationFailed for err -func (b *BatchVerifier) VerifyWithFeedback() (failed []bool, err error) { +func (b *cgoBatchVerifier) VerifyWithFeedback() (failed []bool, err error) { if len(b.messages) == 0 { return nil, nil } + if b.useSingle { + return b.singleVerify() + } + const estimatedMessageSize = 64 msgLengths := make([]uint64, 0, len(b.messages)) var messages = make([]byte, 0, len(b.messages)*estimatedMessageSize) @@ -141,17 +155,33 @@ func (b *BatchVerifier) VerifyWithFeedback() (failed []bool, err error) { msgLengths = append(msgLengths, uint64(len(messages)-lenWas)) lenWas = len(messages) } - allValid, failed := batchVerificationImpl(messages, msgLengths, b.publicKeys, b.signatures) + allValid, failed := cgoBatchVerificationImpl(messages, msgLengths, b.publicKeys, b.signatures) if allValid { return failed, nil } return failed, ErrBatchHasFailedSigs } -// batchVerificationImpl invokes the ed25519 batch verification algorithm. +func (b *cgoBatchVerifier) singleVerify() (failed []bool, err error) { + failed = make([]bool, len(b.messages)) + var containsFailed bool + + for i := range b.messages { + failed[i] = !ed25519Verify(ed25519PublicKey(b.publicKeys[i]), HashRep(b.messages[i]), ed25519Signature(b.signatures[i])) + if failed[i] { + containsFailed = true + } + } + if containsFailed { + return failed, ErrBatchHasFailedSigs + } + return failed, nil +} + +// cgoBatchVerificationImpl invokes the ed25519 batch verification algorithm. // it returns true if all the signatures were authentically signed by the owners // otherwise, returns false, and sets the indexes of the failed sigs in failed -func batchVerificationImpl(messages []byte, msgLengths []uint64, publicKeys []SignatureVerifier, signatures []Signature) (allSigsValid bool, failed []bool) { +func cgoBatchVerificationImpl(messages []byte, msgLengths []uint64, publicKeys []SignatureVerifier, signatures []Signature) (allSigsValid bool, failed []bool) { numberOfSignatures := len(msgLengths) valid := make([]C.int, numberOfSignatures) diff --git a/crypto/batchverifier_test.go b/crypto/batchverifier_test.go index 2b8fcec0e9..3922105aa5 100644 --- a/crypto/batchverifier_test.go +++ b/crypto/batchverifier_test.go @@ -171,7 +171,7 @@ func BenchmarkBatchVerifierBigWithInvalid(b *testing.B) { failed, err := bv.VerifyWithFeedback() if err != nil { for i, f := range failed { - if bv.signatures[i] == badSig { + if bv.(*cgoBatchVerifier).signatures[i] == badSig { require.True(b, f) } else { require.False(b, f) diff --git a/crypto/multisig.go b/crypto/multisig.go index d5529c149f..f17833f1a0 100644 --- a/crypto/multisig.go +++ b/crypto/multisig.go @@ -240,7 +240,7 @@ func MultisigVerify(msg Hashable, addr Digest, sig MultisigSig) (err error) { // MultisigBatchPrep performs checks on the assembled MultisigSig and adds to the batch. // The caller must call batchVerifier.verify() to verify it. -func MultisigBatchPrep(msg Hashable, addr Digest, sig MultisigSig, batchVerifier *BatchVerifier) error { +func MultisigBatchPrep(msg Hashable, addr Digest, sig MultisigSig, batchVerifier BatchVerifier) error { // short circuit: if msig doesn't have subsigs or if Subsigs are empty // then terminate (the upper layer should now verify the unisig) if (len(sig.Subsigs) == 0 || sig.Subsigs[0] == MultisigSubsig{}) { diff --git a/crypto/onetimesig.go b/crypto/onetimesig.go index d9c94da866..d05ccaa961 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. // @@ -319,6 +324,23 @@ func (v OneTimeSignatureVerifier) Verify(id OneTimeSignatureIdentifier, message Batch: id.Batch, } + if !useSingleVerifierDefault { + return v.batchVerify(batchID, offsetID, message, sig) + } + + if !ed25519Verify(ed25519PublicKey(v), HashRep(batchID), sig.PK2Sig) { + return false + } + if !ed25519Verify(batchID.SubKeyPK, HashRep(offsetID), sig.PK1Sig) { + return false + } + if !ed25519Verify(offsetID.SubKeyPK, HashRep(message), sig.Sig) { + return false + } + return true +} + +func (v OneTimeSignatureVerifier) batchVerify(batchID OneTimeSignatureSubkeyBatchID, offsetID OneTimeSignatureSubkeyOffsetID, message Hashable, sig OneTimeSignature) bool { // serialize encoded batchID, offsetID, message into a continuous memory buffer with the layout // hashRep(batchID)... hashRep(offsetID)... hashRep(message)... const estimatedSize = 256 @@ -331,7 +353,7 @@ func (v OneTimeSignatureVerifier) Verify(id OneTimeSignatureIdentifier, message messageBuffer = HashRepToBuff(message, messageBuffer) messageLen := uint64(len(messageBuffer)) - offsetIDLen - batchIDLen msgLengths := []uint64{batchIDLen, offsetIDLen, messageLen} - allValid, _ := batchVerificationImpl( + allValid, _ := cgoBatchVerificationImpl( messageBuffer, msgLengths, []PublicKey{PublicKey(v), PublicKey(batchID.SubKeyPK), PublicKey(offsetID.SubKeyPK)}, diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go index 5da4e593ca..2ce95634bf 100644 --- a/crypto/secp256k1/secp256_test.go +++ b/crypto/secp256k1/secp256_test.go @@ -7,7 +7,6 @@ package secp256k1 import ( "bytes" "crypto/ecdsa" - "crypto/elliptic" "crypto/rand" "encoding/hex" "io" @@ -23,7 +22,7 @@ func generateKeyPair() (pubkey, privkey []byte) { if err != nil { panic(err) } - pubkey = elliptic.Marshal(S256(), key.X, key.Y) + pubkey = S256().Marshal(key.X, key.Y) privkey = make([]byte, 32) blob := key.D.Bytes() 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 c4104d9e80..59ab489183 100644 --- a/daemon/algod/api/Makefile +++ b/daemon/algod/api/Makefile @@ -1,6 +1,9 @@ GOPATH := $(shell go env GOPATH) GOPATH1 := $(firstword $(subst :, ,$(GOPATH))) +# Allow overriding swagger-converter API, e.g. for use with local container +SWAGGER_CONVERTER_API ?= https://converter.swagger.io + # `make all` or just `make` should be appropriate for dev work all: server/v2/generated/model/types.go server/v2/generated/nonparticipating/public/routes.go server/v2/generated/nonparticipating/private/routes.go server/v2/generated/participating/public/routes.go server/v2/generated/participating/private/routes.go server/v2/generated/data/routes.go server/v2/generated/experimental/routes.go @@ -30,7 +33,8 @@ 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 - curl -s -X POST "https://converter.swagger.io/api/convert" -H "accept: application/json" -H "Content-Type: application/json" -d @./algod.oas2.json -o .3tmp.json + 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/README.md b/daemon/algod/api/README.md index 482ba23004..f8b9cc48c4 100644 --- a/daemon/algod/api/README.md +++ b/daemon/algod/api/README.md @@ -49,4 +49,9 @@ Specifically, `uint64` types aren't strictly supported by OpenAPI. So we added a ## Why do we have algod.oas2.json and algod.oas3.yml? -We chose to maintain V2 and V3 versions of the spec because OpenAPI v3 doesn't seem to be widely supported. Some tools worked better with V3 and others with V2, so having both available has been useful. To reduce developer burdon, the v2 specfile is automatically converted v3 using [converter.swagger.io](http://converter.swagger.io/). +We chose to maintain V2 and V3 versions of the spec because OpenAPI v3 doesn't seem to be widely supported. Some tools worked better with V3 and others with V2, so having both available has been useful. To reduce developer burden, the v2 specfile is automatically converted v3 using [converter.swagger.io](http://converter.swagger.io/). + +If you want to run the converter locally, you can build the [swagger-converter](https://github.com/swagger-api/swagger-converter) project or run its [docker image](https://hub.docker.com/r/swaggerapi/swagger-converter) and specify the `SWAGGER_CONVERTER_API` environment variable when using this Makefile, for example by running: +``` +SWAGGER_CONVERTER_API=http://localhost:8080 make +``` diff --git a/daemon/algod/api/algod.oas2.json b/daemon/algod/api/algod.oas2.json index e327e0e3be..704b8a4638 100644 --- a/daemon/algod/api/algod.oas2.json +++ b/daemon/algod/api/algod.oas2.json @@ -344,6 +344,73 @@ } ] }, + "/v2/accounts/{address}/assets": { + "get": { + "description": "Lookup an account's asset holdings.", + "tags": [ + "public", + "experimental" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http" + ], + "summary": "Get a list of assets held by an account, inclusive of asset params.", + "operationId": "AccountAssetsInformation", + "parameters": [ + { + "pattern": "[A-Z0-9]{58}", + "type": "string", + "description": "An account public key", + "name": "address", + "in": "path", + "required": true + }, + { + "$ref": "#/parameters/limit" + }, + { + "$ref": "#/parameters/next" + } + ], + "responses": { + "200": { + "$ref": "#/responses/AccountAssetsInformationResponse" + }, + "400": { + "description": "Malformed address", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "401": { + "description": "Invalid API Token", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "default": { + "description": "Unknown Error" + } + } + }, + "parameters": [ + { + "type": "string", + "name": "address", + "in": "path", + "required": true + } + ] + }, "/v2/accounts/{address}/applications/{application-id}": { "get": { "description": "Given a specific account public key and application ID, this call returns the account's application local state and global state (AppLocalState and AppParams, if either exists). Global state will only be returned if the provided address is the application's creator.", @@ -787,6 +854,62 @@ } ] }, + "/v2/blocks/{round}/logs": { + "get": { + "tags": [ + "public", + "nonparticipating" + ], + "description": "Get all of the logs from outer and inner app calls in the given round", + "produces": [ + "application/json" + ], + "schemes": [ + "http" + ], + "summary": "Get all of the logs from outer and inner app calls in the given round", + "operationId": "GetBlockLogs", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "The round from which to fetch block log information.", + "name": "round", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/BlockLogsResponse" + }, + "400": { + "description": "Bad Request - Non integer number", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "401": { + "description": "Invalid API Token", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "404": { + "description": "Nonexistent block ", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + }, + "500": { + "description": "Internal Error", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + } + } + }, "/v2/ledger/supply": { "get": { "tags": [ @@ -2983,6 +3106,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 +3143,31 @@ "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" + } + } + }, + "AccountAssetHolding": { + "description": "AccountAssetHolding describes the account's asset holding and asset parameters (if either exist) for a specific asset ID.", + "type": "object", + "required": [ + "asset-holding" + ], + "properties": { + "asset-holding": { + "description": "\\[asset\\] Details about the asset held by this account.\n\nThe raw account uses `AssetHolding` for this type.", + "$ref": "#/definitions/AssetHolding" + }, + "asset-params": { + "description": "\\[apar\\] parameters of the asset held by this account.\n\nThe raw account uses `AssetParams` for this type.", + "$ref": "#/definitions/AssetParams" } } }, @@ -4449,6 +4601,33 @@ } } } + }, + "AppCallLogs": { + "description": "The logged messages from an app call along with the app ID and outer transaction ID. Logs appear in the same order that they were emitted.", + "type": "object", + "required": [ + "logs", + "application-index", + "txId" + ], + "properties": { + "logs": { + "description": "An array of logs", + "type": "array", + "items": { + "type": "string", + "format": "byte" + } + }, + "application-index": { + "description": "The application from which the logs were generated", + "type": "integer" + }, + "txId": { + "description": "The transaction ID of the outer app call that lead to these logs", + "type": "string" + } + } } }, "parameters": { @@ -4723,6 +4902,31 @@ } } }, + "AccountAssetsInformationResponse": { + "description": "AccountAssetsInformationResponse contains a list of assets held by an account.", + "schema": { + "type": "object", + "required": [ + "round" + ], + "properties": { + "round": { + "description": "The round for which this information is relevant.", + "type": "integer" + }, + "next-token": { + "description": "Used for pagination, when making another request provide this token with the next parameter.", + "type": "string" + }, + "asset-holdings": { + "type": "array", + "items": { + "$ref": "#/definitions/AccountAssetHolding" + } + } + } + } + }, "AccountApplicationResponse": { "description": "AccountApplicationResponse describes the account's application local state and global state (AppLocalState and AppParams, if either exists) for a specific application ID. Global state will only be returned if the provided address is the application's creator.", "schema": { @@ -5106,6 +5310,23 @@ } } }, + "BlockLogsResponse": { + "description": "All logs emitted in the given round. Each app call, whether top-level or inner, that contains logs results in a separate AppCallLogs object. Therefore there may be multiple AppCallLogs with the same application ID and outer transaction ID in the event of multiple inner app calls to the same app. App calls with no logs are not included in the response. AppCallLogs are returned in the same order that their corresponding app call appeared in the block (pre-order traversal of inner app calls)", + "schema": { + "type": "object", + "required": [ + "logs" + ], + "properties": { + "logs": { + "type": "array", + "items": { + "$ref": "#/definitions/AppCallLogs" + } + } + } + } + }, "SupplyResponse": { "description": "Supply represents the current supply of MicroAlgos in the system.", "schema": { diff --git a/daemon/algod/api/algod.oas3.yml b/daemon/algod/api/algod.oas3.yml index be9237b90e..734dc4667a 100644 --- a/daemon/algod/api/algod.oas3.yml +++ b/daemon/algod/api/algod.oas3.yml @@ -274,6 +274,35 @@ }, "description": "AccountAssetResponse describes the account's asset holding and asset parameters (if either exist) for a specific asset ID. Asset parameters will only be returned if the provided address is the asset's creator." }, + "AccountAssetsInformationResponse": { + "content": { + "application/json": { + "schema": { + "properties": { + "asset-holdings": { + "items": { + "$ref": "#/components/schemas/AccountAssetHolding" + }, + "type": "array" + }, + "next-token": { + "description": "Used for pagination, when making another request provide this token with the next parameter.", + "type": "string" + }, + "round": { + "description": "The round for which this information is relevant.", + "type": "integer" + } + }, + "required": [ + "round" + ], + "type": "object" + } + } + }, + "description": "AccountAssetsInformationResponse contains a list of assets held by an account." + }, "AccountResponse": { "content": { "application/json": { @@ -323,6 +352,27 @@ }, "description": "Hash of a block header." }, + "BlockLogsResponse": { + "content": { + "application/json": { + "schema": { + "properties": { + "logs": { + "items": { + "$ref": "#/components/schemas/AppCallLogs" + }, + "type": "array" + } + }, + "required": [ + "logs" + ], + "type": "object" + } + } + }, + "description": "All logs emitted in the given round. Each app call, whether top-level or inner, that contains logs results in a separate AppCallLogs object. Therefore there may be multiple AppCallLogs with the same application ID and outer transaction ID in the event of multiple inner app calls to the same app. App calls with no logs are not included in the response. AppCallLogs are returned in the same order that their corresponding app call appeared in the block (pre-order traversal of inner app calls)" + }, "BlockResponse": { "content": { "application/json": { @@ -1067,6 +1117,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" @@ -1144,6 +1206,21 @@ ], "type": "object" }, + "AccountAssetHolding": { + "description": "AccountAssetHolding describes the account's asset holding and asset parameters (if either exist) for a specific asset ID.", + "properties": { + "asset-holding": { + "$ref": "#/components/schemas/AssetHolding" + }, + "asset-params": { + "$ref": "#/components/schemas/AssetParams" + } + }, + "required": [ + "asset-holding" + ], + "type": "object" + }, "AccountParticipation": { "description": "AccountParticipation describes the parameters used by this account in consensus protocol.", "properties": { @@ -1203,6 +1280,34 @@ ], "type": "object" }, + "AppCallLogs": { + "description": "The logged messages from an app call along with the app ID and outer transaction ID. Logs appear in the same order that they were emitted.", + "properties": { + "application-index": { + "description": "The application from which the logs were generated", + "type": "integer" + }, + "logs": { + "description": "An array of logs", + "items": { + "format": "byte", + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", + "type": "string" + }, + "type": "array" + }, + "txId": { + "description": "The transaction ID of the outer app call that lead to these logs", + "type": "string" + } + }, + "required": [ + "application-index", + "logs", + "txId" + ], + "type": "object" + }, "Application": { "description": "Application index and its parameters", "properties": { @@ -3018,6 +3123,110 @@ ] } }, + "/v2/accounts/{address}/assets": { + "get": { + "description": "Lookup an account's asset holdings.", + "operationId": "AccountAssetsInformation", + "parameters": [ + { + "description": "An account public key", + "in": "path", + "name": "address", + "required": true, + "schema": { + "pattern": "[A-Z0-9]{58}", + "type": "string" + } + }, + { + "description": "Maximum number of results to return.", + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "description": "The next page of results. Use the next token provided by the previous results.", + "in": "query", + "name": "next", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "asset-holdings": { + "items": { + "$ref": "#/components/schemas/AccountAssetHolding" + }, + "type": "array" + }, + "next-token": { + "description": "Used for pagination, when making another request provide this token with the next parameter.", + "type": "string" + }, + "round": { + "description": "The round for which this information is relevant.", + "type": "integer" + } + }, + "required": [ + "round" + ], + "type": "object" + } + } + }, + "description": "AccountAssetsInformationResponse contains a list of assets held by an account." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Malformed address" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Invalid API Token" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Internal Error" + }, + "default": { + "content": {}, + "description": "Unknown Error" + } + }, + "summary": "Get a list of assets held by an account, inclusive of asset params.", + "tags": [ + "public", + "experimental" + ] + } + }, "/v2/accounts/{address}/assets/{asset-id}": { "get": { "description": "Given a specific account public key and asset ID, this call returns the account's asset holding and asset parameters (if either exist). Asset parameters will only be returned if the provided address is the asset's creator.", @@ -3976,6 +4185,92 @@ ] } }, + "/v2/blocks/{round}/logs": { + "get": { + "description": "Get all of the logs from outer and inner app calls in the given round", + "operationId": "GetBlockLogs", + "parameters": [ + { + "description": "The round from which to fetch block log information.", + "in": "path", + "name": "round", + "required": true, + "schema": { + "minimum": 0, + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "logs": { + "items": { + "$ref": "#/components/schemas/AppCallLogs" + }, + "type": "array" + } + }, + "required": [ + "logs" + ], + "type": "object" + } + } + }, + "description": "All logs emitted in the given round. Each app call, whether top-level or inner, that contains logs results in a separate AppCallLogs object. Therefore there may be multiple AppCallLogs with the same application ID and outer transaction ID in the event of multiple inner app calls to the same app. App calls with no logs are not included in the response. AppCallLogs are returned in the same order that their corresponding app call appeared in the block (pre-order traversal of inner app calls)" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Bad Request - Non integer number" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Invalid API Token" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Nonexistent block " + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Internal Error" + } + }, + "summary": "Get all of the logs from outer and inner app calls in the given round", + "tags": [ + "public", + "nonparticipating" + ] + } + }, "/v2/blocks/{round}/transactions/{txid}/proof": { "get": { "operationId": "GetTransactionProof", diff --git a/daemon/algod/api/client/restClient.go b/daemon/algod/api/client/restClient.go index f6494fa380..33b8da8ee1 100644 --- a/daemon/algod/api/client/restClient.go +++ b/daemon/algod/api/client/restClient.go @@ -377,6 +377,11 @@ type accountInformationParams struct { Exclude string `url:"exclude"` } +type pageParams struct { + Next *string `url:"next,omitempty"` + Limit *uint64 `url:"limit,omitempty"` +} + type catchupParams struct { Min uint64 `url:"min"` } @@ -508,6 +513,20 @@ func (client RestClient) RawAccountAssetInformation(accountAddress string, asset return } +// AccountAssetsInformation gets account information about a particular account's assets, subject to pagination. +func (client RestClient) AccountAssetsInformation(accountAddress string, next *string, limit *uint64) (response model.AccountAssetsInformationResponse, err error) { + err = client.get(&response, fmt.Sprintf("/v2/accounts/%s/assets", accountAddress), pageParams{next, limit}) + return +} + +// RawAccountAssetsInformation gets account information about a particular account's assets, subject to pagination. +func (client RestClient) RawAccountAssetsInformation(accountAddress string, next *string, limit *uint64) (response []byte, err error) { + var blob Blob + err = client.getRaw(&blob, fmt.Sprintf("/v2/accounts/%s/assets", accountAddress), pageParams{next, limit}) + response = blob + return +} + // SuggestedParams gets the suggested transaction parameters func (client RestClient) SuggestedParams() (response model.TransactionParametersResponse, err error) { err = client.get(&response, "/v2/transactions/params", nil) @@ -776,3 +795,9 @@ func (client RestClient) GetBlockTimestampOffset() (response model.GetBlockTimeS err = client.get(&response, "/v2/devmode/blocks/offset", nil) return } + +// BlockLogs returns all the logs in a block for a given round +func (client RestClient) BlockLogs(round uint64) (response model.BlockLogsResponse, err error) { + err = client.get(&response, fmt.Sprintf("/v2/blocks/%d/logs", round), nil) + return +} 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..7f5cfbeb7a 100644 --- a/daemon/algod/api/server/v2/generated/data/routes.go +++ b/daemon/algod/api/server/v2/generated/data/routes.go @@ -114,212 +114,221 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "npw8mnz8/wEAAP//6aRdnSH5AAA=", + "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", + "y8xbNdpOULPHJw9nH/93AAAA//8Oc6QNuAMBAA==", } // 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..e6472ca97a 100644 --- a/daemon/algod/api/server/v2/generated/experimental/routes.go +++ b/daemon/algod/api/server/v2/generated/experimental/routes.go @@ -8,17 +8,22 @@ import ( "compress/gzip" "encoding/base64" "fmt" + "net/http" "net/url" "path" "strings" . "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model" + "github.com/algorand/oapi-codegen/pkg/runtime" "github.com/getkin/kin-openapi/openapi3" "github.com/labstack/echo/v4" ) // ServerInterface represents all server handlers. type ServerInterface interface { + // Get a list of assets held by an account, inclusive of asset params. + // (GET /v2/accounts/{address}/assets) + AccountAssetsInformation(ctx echo.Context, address string, params AccountAssetsInformationParams) error // Returns OK if experimental API is enabled. // (GET /v2/experimental) ExperimentalCheck(ctx echo.Context) error @@ -32,6 +37,40 @@ type ServerInterfaceWrapper struct { Handler ServerInterface } +// AccountAssetsInformation converts echo context to params. +func (w *ServerInterfaceWrapper) AccountAssetsInformation(ctx echo.Context) error { + var err error + // ------------- Path parameter "address" ------------- + var address string + + err = runtime.BindStyledParameterWithLocation("simple", false, "address", runtime.ParamLocationPath, ctx.Param("address"), &address) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter address: %s", err)) + } + + ctx.Set(Api_keyScopes, []string{""}) + + // Parameter object where we will unmarshal all parameters from the context + var params AccountAssetsInformationParams + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", ctx.QueryParams(), ¶ms.Limit) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter limit: %s", err)) + } + + // ------------- Optional query parameter "next" ------------- + + err = runtime.BindQueryParameter("form", true, false, "next", ctx.QueryParams(), ¶ms.Next) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter next: %s", err)) + } + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.AccountAssetsInformation(ctx, address, params) + return err +} + // ExperimentalCheck converts echo context to params. func (w *ServerInterfaceWrapper) ExperimentalCheck(ctx echo.Context) error { var err error @@ -82,6 +121,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL Handler: si, } + router.GET(baseURL+"/v2/accounts/:address/assets", wrapper.AccountAssetsInformation, m...) router.GET(baseURL+"/v2/experimental", wrapper.ExperimentalCheck, m...) router.POST(baseURL+"/v2/transactions/async", wrapper.RawTransactionAsync, m...) @@ -90,212 +130,226 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "AwAA///pfgaenwQBAA==", } // 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..1755e868ee 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. @@ -242,6 +251,23 @@ type Account struct { // * lsig type AccountSigType string +// AccountAssetHolding AccountAssetHolding describes the account's asset holding and asset parameters (if either exist) for a specific asset ID. +type AccountAssetHolding struct { + // AssetHolding Describes an asset held by an account. + // + // Definition: + // data/basics/userBalance.go : AssetHolding + AssetHolding AssetHolding `json:"asset-holding"` + + // AssetParams AssetParams specifies the parameters for an asset. + // + // \[apar\] when part of an AssetConfig transaction. + // + // Definition: + // data/transactions/asset.go : AssetParams + AssetParams *AssetParams `json:"asset-params,omitempty"` +} + // AccountParticipation AccountParticipation describes the parameters used by this account in consensus protocol. type AccountParticipation struct { // SelectionParticipationKey \[sel\] Selection public key (if any) currently registered for this round. @@ -271,6 +297,18 @@ type AccountStateDelta struct { Delta StateDelta `json:"delta"` } +// AppCallLogs The logged messages from an app call along with the app ID and outer transaction ID. Logs appear in the same order that they were emitted. +type AppCallLogs struct { + // ApplicationIndex The application from which the logs were generated + ApplicationIndex uint64 `json:"application-index"` + + // Logs An array of logs + Logs [][]byte `json:"logs"` + + // TxId The transaction ID of the outer app call that lead to these logs + TxId string `json:"txId"` +} + // Application Application index and its parameters type Application struct { // Id \[appidx\] application index. @@ -1070,6 +1108,17 @@ type AccountAssetResponse struct { Round uint64 `json:"round"` } +// AccountAssetsInformationResponse defines model for AccountAssetsInformationResponse. +type AccountAssetsInformationResponse struct { + AssetHoldings *[]AccountAssetHolding `json:"asset-holdings,omitempty"` + + // NextToken Used for pagination, when making another request provide this token with the next parameter. + NextToken *string `json:"next-token,omitempty"` + + // Round The round for which this information is relevant. + Round uint64 `json:"round"` +} + // AccountResponse Account information at a given round. // // Definition: @@ -1088,6 +1137,11 @@ type BlockHashResponse struct { BlockHash string `json:"blockHash"` } +// BlockLogsResponse defines model for BlockLogsResponse. +type BlockLogsResponse struct { + Logs []AppCallLogs `json:"logs"` +} + // BlockResponse defines model for BlockResponse. type BlockResponse struct { // Block Block header data. @@ -1399,6 +1453,15 @@ type AccountApplicationInformationParams struct { // AccountApplicationInformationParamsFormat defines parameters for AccountApplicationInformation. type AccountApplicationInformationParamsFormat string +// AccountAssetsInformationParams defines parameters for AccountAssetsInformation. +type AccountAssetsInformationParams struct { + // Limit Maximum number of results to return. + Limit *uint64 `form:"limit,omitempty" json:"limit,omitempty"` + + // Next The next page of results. Use the next token provided by the previous results. + Next *string `form:"next,omitempty" json:"next,omitempty"` +} + // AccountAssetInformationParams defines parameters for AccountAssetInformation. type AccountAssetInformationParams struct { // Format Configures whether the response object is JSON or MessagePack encoded. If not provided, defaults to JSON. 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..568ebbb8ae 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,224 @@ 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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", + "NYXFJk9OHk0+/t8AAAD//5YZK+EHBgEA", } // 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..815a346434 100644 --- a/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go +++ b/daemon/algod/api/server/v2/generated/nonparticipating/public/routes.go @@ -51,6 +51,9 @@ type ServerInterface interface { // Gets a proof for a given light block header inside a state proof commitment // (GET /v2/blocks/{round}/lightheader/proof) GetLightBlockHeaderProof(ctx echo.Context, round uint64) error + // Get all of the logs from outer and inner app calls in the given round + // (GET /v2/blocks/{round}/logs) + GetBlockLogs(ctx echo.Context, round uint64) error // Get a proof for a transaction in a block. // (GET /v2/blocks/{round}/transactions/{txid}/proof) GetTransactionProof(ctx echo.Context, round uint64, txid string, params GetTransactionProofParams) error @@ -363,6 +366,24 @@ func (w *ServerInterfaceWrapper) GetLightBlockHeaderProof(ctx echo.Context) erro return err } +// GetBlockLogs converts echo context to params. +func (w *ServerInterfaceWrapper) GetBlockLogs(ctx echo.Context) error { + var err error + // ------------- Path parameter "round" ------------- + var round uint64 + + err = runtime.BindStyledParameterWithLocation("simple", false, "round", runtime.ParamLocationPath, ctx.Param("round"), &round) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter round: %s", err)) + } + + ctx.Set(Api_keyScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetBlockLogs(ctx, round) + return err +} + // GetTransactionProof converts echo context to params. func (w *ServerInterfaceWrapper) GetTransactionProof(ctx echo.Context) error { var err error @@ -702,6 +723,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/v2/blocks/:round", wrapper.GetBlock, m...) router.GET(baseURL+"/v2/blocks/:round/hash", wrapper.GetBlockHash, m...) router.GET(baseURL+"/v2/blocks/:round/lightheader/proof", wrapper.GetLightBlockHeaderProof, m...) + router.GET(baseURL+"/v2/blocks/:round/logs", wrapper.GetBlockLogs, m...) router.GET(baseURL+"/v2/blocks/:round/transactions/:txid/proof", wrapper.GetTransactionProof, m...) router.GET(baseURL+"/v2/blocks/:round/txids", wrapper.GetBlockTxids, m...) router.GET(baseURL+"/v2/deltas/txn/group/:id", wrapper.GetLedgerStateDeltaForTransactionGroup, m...) @@ -724,289 +746,302 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "bwAAAP//bsB3VeaoAQA=", + "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", + "IGsT4ujB5N7ow/8LAAD//5pnmKfruQEA", } // 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..d64047fe99 100644 --- a/daemon/algod/api/server/v2/generated/participating/private/routes.go +++ b/daemon/algod/api/server/v2/generated/participating/private/routes.go @@ -203,218 +203,227 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/daemon/algod/api/server/v2/generated/participating/public/routes.go b/daemon/algod/api/server/v2/generated/participating/public/routes.go index 0938833520..409fd4ca65 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,233 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/G1EEgEA", + "H4sIAAAAAAAC/+x9f3fbtpLoV8HT7jlJvKLt/Gj3Nu/07HOSttfbpMmJ3e7ejfNaiBxJuCYBXgCUpebl", + "u7+DAUCCJChRtpq0u/0rsUgCg8FgML/nwyQVRSk4cK0mTz9MSippARok/kXTVFRcJywzf2WgUslKzQSf", + "PPXPiNKS8cVkOmHm15Lq5WQ64bSA5h3z/XQi4R8Vk5BNnmpZwXSi0iUU1AysN6V5ux5pnSxE4oY4s0Oc", + "v5h83PKAZpkEpfpQvub5hjCe5lUGREvKFU3NI0VumF4SvWSKuI8J40RwIGJO9LL1MpkzyDN17Bf5jwrk", + "Jlilm3x4SR8bEBMpcujD+VwUM8bBQwU1UPWGEC1IBnN8aUk1MTMYWP2LWhAFVKZLMhdyB6gWiBBe4FUx", + "efpuooBnIHG3UmAr/O9cAvwKiaZyAXryfhpb3FyDTDQrIks7d9iXoKpcK4Lv4hoXbAWcmK+OyatKaTID", + "Qjl5++1z8vjx46/MQgqqNWSOyAZX1cwersl+Pnk6yagG/7hPazRfCEl5ltTvv/32Oc5/4RY49i2qFMQP", + "y5l5Qs5fDC3AfxghIcY1LHAfWtRvvogciubnGcyFhJF7Yl8+6KaE83/WXUmpTpelYFxH9oXgU2IfR3lY", + "8Pk2HlYD0Hq/NJiSZtB3p8lX7z88nD48/fhP786S/3J/fvH448jlP6/H3YGB6ItpJSXwdJMsJFA8LUvK", + "+/h46+hBLUWVZ2RJV7j5tEBW774l5lvLOlc0rwydsFSKs3whFKGOjDKY0yrXxE9MKp4bNmVGc9ROmCKl", + "FCuWQTY13PdmydIlSamyQ+B75IbluaHBSkE2RGvx1W05TB9DlBi4boUPXNDvFxnNunZgAtbIDZI0FwoS", + "LXZcT/7GoTwj4YXS3FVqv8uKXC6B4OTmgb1sEXfc0HSeb4jGfc0IVYQSfzVNCZuTjajIDW5Ozq7xe7ca", + "g7WCGKTh5rTuUXN4h9DXQ0YEeTMhcqAckefPXR9lfM4WlQRFbpagl+7Ok6BKwRUQMfs7pNps+79fvP6B", + "CElegVJ0AW9oek2ApyKD7JiczwkXOiANR0uIQ/Pl0DocXLFL/u9KGJoo1KKk6XX8Rs9ZwSKrekXXrKgK", + "wqtiBtJsqb9CtCASdCX5EEB2xB2kWNB1f9JLWfEU97+ZtiXLGWpjqszpBhFW0PXXp1MHjiI0z0kJPGN8", + "QfSaD8pxZu7d4CVSVDwbIeZos6fBxapKSNmcQUbqUbZA4qbZBQ/j+8HTCF8BOH6QQXDqWXaAw2EdoRlz", + "us0TUtIFBCRzTH50zA2fanENvCZ0Mtvgo1LCiolK1R8NwIhTb5fAudCQlBLmLEJjFw4dhsHYdxwHLpwM", + "lAquKeOQGeaMQAsNllkNwhRMuF3f6d/iM6rgyydDd3zzdOTuz0V317fu+KjdxpcSeyQjV6d56g5sXLJq", + "fT9CPwznVmyR2J97G8kWl+a2mbMcb6K/m/3zaKgUMoEWIvzdpNiCU11JeHrFj8xfJCEXmvKMysz8Utif", + "XlW5ZhdsYX7K7U8vxYKlF2wxgMwa1qjChZ8V9h8zXpwd63VUr3gpxHVVhgtKW4rrbEPOXwxtsh1zX8I8", + "q7XdUPG4XHtlZN8v9LreyAEgB3FXUvPiNWwkGGhpOsd/1nOkJzqXv5p/yjI3X+tyHkOtoWN3JaP5wJkV", + "zsoyZyk1SHzrHpunhgmAVSRo88YJXqhPPwQgllKUIDWzg9KyTHKR0jxRmmoc6Z8lzCdPJ/900thfTuzn", + "6iSY/KX56gI/MiKrFYMSWpZ7jPHGiD5qC7MwDBofIZuwbA+FJsbtJhpSYoYF57CiXB83KkuLH9QH+J2b", + "qcG3lXYsvjsq2CDCiX1xBspKwPbFe4oEqCeIVoJoRYF0kYtZ/cP9s7JsMIjPz8rS4gOlR2AomMGaKa0e", + "4PJpc5LCec5fHJPvwrFRFBc835jLwYoa5m6Yu1vL3WK1bcmtoRnxniK4nUIem63xaDBi/iEoDtWKpciN", + "1LOTVszLf3XvhmRmfh/18R+DxELcDhMXKloOc1bHwV8C5eZ+h3L6hOPMPcfkrPvt7cjGjLKFYNR5g8VD", + "Ew/+wjQUaiclBBAF1OS2h0pJNxMnJCYo7PXJ5EcFlkJKumAcoZ0a9YmTgl7b/RCId0MIoGq9yNKSlSBr", + "E6qTOR3qj3t2lj8AtcY21kuiRlLNmdKoV+PLZAk5Cs6Ue4IOSeVWlDFiw7csoob5RtLS0rJ7YsUuxlGf", + "ty9ZWO948Y68E6MwB+w+2GiE6tZseSfrjEKCXKMDw7NcpNd/pWp5gBM+82P1aR+nIUugGUiypGoZOTgd", + "2m5GG0Pf5kWkWTILpjqul/hSLNQBlpiLfVhXWT6neW6m7rOszmpx4FEHOc+JeZlAwdBg7hRHa2G3+hf5", + "hqZLIxaQlOb5tDEViTLJYQW5UdoZ5yCnRC+pbg4/juz1GjxHCgyz00CC1TgzE5rYZG2LkEAKijdQYbSZ", + "Mm9/U3NQRQvoSEF4I4oKrQiBonH+wq8OVsCRJ9VDI/j1GtFaEw5+bOZ2j3BmLuzirAVQe/ddjb+aX7SA", + "Nm839ylvphAyszZrbX5jkqRC2iHsDe8mN/8BKpuPLXXeLyUkbghJVyAVzc3qOot6UJPvoU7njpOZUU2D", + "k+moMK6AWc6B36F4BzJipXmN/6E5MY+NFGMoqaEehsKICNypmb2YDarsTOYFtLcKUlhTJilper0XlM+b", + "yeNsZtTJ+8ZaT90WukXUO3S5Zpk61DbhYEN71T4h1nbl2VFPFtnKdIK5xiDgUpTEso8OCJZT4GgWIWJ9", + "8GvtmVjHYHom1r0rTazhIDthxhnN7J+J9QsHmZC7MY9jj0G6WSCnBSi83XjIOM0sjV/ubCbk7aSJzgXD", + "SeNtJNSMGghT0w6S8NWqTNzZjHgs7AudgZoAj+1CQHf4GMZaWLjQ9DfAgjKjHgIL7YEOjQVRlCyHA5D+", + "MirEzaiCx4/IxV/Pvnj46OdHX3xpSLKUYiFpQWYbDYrcd2Y5ovQmhwdR7Qili/joXz7xPqr2uLFxlKhk", + "CgUt+0NZ35fVfu1rxLzXx1obzbjqGsBRHBHM1WbRTqxb14D2gimjOxWzg2zGEMKyZpaMOEgy2ElM+y6v", + "mWYTLlFuZHUIswBIKWT06iql0CIVeWLkIyYiiv0b9wZxb3jLRtn93UJLbqgiZm70+lU8G9Df9ZqP5/t2", + "6Ms1b3CzlfPb9UZW5+Ydsy9t5DfSewky0WtOMphVi5ZZYS5FQSjJ8EO8o78DbeUWVsCFpkX5ej4/jJVQ", + "4EAR+wcrQJmZiH3DSA0KUsFtENwOU4cbdQx6uojx3hk9DIDDyMWGp+hiOsSxHbYCFYyjv1tteBqYhAyM", + "OWSLFlne3fQzhA471T0VAceg4yU+Rhv3C8g1/VbIy0bs+06Kqjy4kNedc+xyqFuMs6Jn5ltvPmV8kbcD", + "LxcG9uPYGj/Lgp7XyrddA0KPFPmSLZY60LPeSCHmh4cxNksMUHxgjSy5+aZvavlBZIaZ6EodQARrBms4", + "nKHbkK/Rmag0oYSLDHDzKxUXzgZC9TBGCEObdCjvoV7PFJmBoa6UVma1VUkwcKd3XzQfJjS1JzRB1KiB", + "sIU63sS+ZaezYWC5BJptyAyAEzFzsQEuagEXSTHqSHvxxomGEX7RgquUIgWlIEucCXcnaP49e3XoLXhC", + "wBHgehaiBJlTeWdgr1c74byGTYIxcorc//4n9eAzwKuFpvkOxOI7MfR27VB9qMdNv43gupOHZGctXJZq", + "iRYozeagYQiFe+FkcP+6EPV28e5oWYHEUIzflOL9JHcjoBrU35je7wptVQ5Efjv11kh4ZsM45cILVrHB", + "cqp0sostm5daOrhZQcAJY5wYBx4QvF5SpW34EOMZ2gLtdYLzWCHMTDEM8KAaYkb+yWsg/bFTcw9yVala", + "HVFVWQqpIYutAT2Zg3P9AOt6LjEPxq51Hi1IpWDXyENYCsZ3yLIrsQiiuvZbOk9of3Hoizb3/CaKyhYQ", + "DSK2AXLh3wqwG0a/DgDCVINoSzhMdSinDrmdTpQWZWm4hU4qXn83hKYL+/aZ/rF5t09c1jlg7+1MgELH", + "g3vfQX5jMWvjnpdUEQeHd02jGcTGOfVhNocxUYynkGyjfFTxzFvhEdh5SKtyIWkGSQY53USc6vYxsY+3", + "DYA73qi7QkNiA1jjm95Qso8X3DK0wPFUTHgk+ISk5ggaVaAhEPf1jpEzwLFjzMnR0b16KJwrukV+PFy2", + "3erIiHgbroQ2O+7oAUF2HH0MwAN4qIe+PSrw46TRPbtT/A2Um6CWI/afZANqaAnN+HstYMCG6nKDgvPS", + "Ye8dDhxlm4NsbAcfGTqyAwbdN1RqlrISdZ3vYXNw1a87QdThTDLQlOWQkeCBVQPL8HtiQy+7Y95OFRxl", + "e+uD3zO+RZbjw1vawF/DBnXuNzamPzB1HEKXjYxq7ifKCQLqI4WNCB6+Amua6nxjBDW9hA25AQlEVTPr", + "+u/7IbQok3CAqF9jy4zOqxn1KW51s17gUMHyYjFaVifYDt9lRzFoocPpAqUQ+QgLWQ8ZUQhGxVyQUphd", + "Zy5tyCeOeEpqAemYNrq06+v/nmqhGVdA/iYqklKOKleloZZphERBAQVIM4MRweo5XVBfgyHIoQCrSeKT", + "o6Puwo+O3J4zReZw43PtzItddBwdoR3njVC6dbgOYA81x+08cn2gw8dcfE4L6fKU3ZFCbuQxO/mmM3jt", + "JTJnSilHuGb5d2YAnZO5HrP2kEbGRUnhuKN8Oe24mt66cd8vWFHlVB/CawUrmidiBVKyDHZycjcxE/yb", + "Fc1f159hHiGkhkZTSFLMfhs5Flyab2zCnBmHcWYOsA2WHwsQnNuvLuxHO1TMJsKTFQVkjGrIN6SUkILN", + "EzOSo6qXekxsBHm6pHyBCoMU1cIFhdpxkOFXyppmZMV7Q0SFKr3mCRq5YxeAC+/yqYJGnAJqVLquhdwq", + "MDe0ns9lh465mYM96HoMok6y6WRQ4zVIXTUar0VOO99xxGXQkvcC/DQTj3SlIOqM7NPHV7gt5jCZzf1t", + "TPbN0DEo+xMHkbLNw6FgWaNu55sDCD12ICKhlKDwigrNVMo+FfMwt9mH2G2UhqJvybef/jxw/N4O6ouC", + "54xDUggOm2g5D8bhFT6MHie8Jgc+RoFl6NuuDtKCvwNWe54x1HhX/OJud09o12OlvhXyUC5RO+Bo8X6E", + "B3Knu91NeVs/Kc3ziGvRZT52GYCa1kGuTBKqlEgZymznmZq6aFrrjXRpkm30v6nzOQ5w9rrjdnxoYVI9", + "2oghLwklac7Qgiy40rJK9RWnaKMKlhoJfvLK+LDV8rl/JW4mjVgx3VBXnGLgW225igZszCFipvkWwBsv", + "VbVYgNIdXWcOcMXdW4yTijONcxXmuCT2vJQgMQLp2L5Z0A2ZG5rQgvwKUpBZpdvSPyb2Ks3y3Dn0zDRE", + "zK841SQHqjR5xfjlGofzTn9/ZDnoGyGvayzEb/cFcFBMJfEgre/sU4yHd8tfuth4DBO3j32wZlNpYGKW", + "2Sou8n/v/9vTd2fJf9Hk19Pkq385ef/hyccHR70fH338+uv/1/7p8cevH/zbP8d2ysMeSzt1kJ+/cJrx", + "+QtUf4IQ9y7sn8z+XzCeRIksjObo0Ba5jyUWHAE9aBvH9BKuuF5zQ0grmrPM8JbbkEP3humdRXs6OlTT", + "2oiOMcyvdU+l4g5chkSYTIc13lqK6sc1xhO80SnpcrbxvMwrbrfSS982f9HHl4n5tE7it/W9nhLM8F5S", + "Hxzp/nz0xZeTaZOZXT+fTCfu6fsIJbNsHcu/z2Ad0xXD5IJ7ipR0o0DHuQfCHg2ls7Ed4bAFFDOQasnK", + "T88plGazOIfzqT7O5rTm59wGxpvzgy7OjfOciPmnh1tLgAxKvYzV/WkJavhWs5sAnbCTUooV8Clhx3Dc", + "tflkRl90QX050LlPf5FCjNGG6nNgCc1TRYD1cCGjDCsx+umkBbjLXx1cHXIDx+Dqzln7M/3fWpB7331z", + "SU4cw1T3bCkIO3SQvB9RpV3SYSsgyXCzMBfril/xFzBH64PgT694RjU9mVHFUnVSKZDPaE55CscLQZ76", + "PMYXVNMr3pO0BgsSBsnGpKxmOUvJdaiQNORpi0z1R7i6ekfzhbi6et+LzeirD26qKH+xEyRGEBaVTlyJ", + "nETCDZUx35eqS6TgyLYG1rZZrZAtKmsg9SV43PhxnkfLUnVLJfSXX5a5WX5AhsoVAjBbRpQWdR6XEVBc", + "KqzZ3x+EuxgkvfF2lUqBIr8UtHzHuH5Pkqvq9PQxZsQ1tQN+cVe+oclNCaOtK4OlHLpGFVy4VSthrSVN", + "SrqIudiurt5poCXuPsrLBdo48pzgZ61sPR+Yj0M1C6hTgwc3wMKxd1ItLu7CfuXLIcaXgI9wC9uJy3fa", + "ryDv/NbbtSN3nVZ6mZizHV2VMiTud6aukrYwQpaPxlBsgdqqKyg3A5IuIb12lb6gKPVm2vrcB/w4QdOz", + "DqZsDTibmYdViNBBMQNSlRl1ojjlm245GAVa+7Dit3ANm0vRFDHap/5LuxyJGjqoSKmBdGmINTy2bozu", + "5ruoMp+g6ap6YNKjJ4unNV34b4YPshV5D3CIY0TRKpcxhAgqI4iwxD+Aglss1Ix3J9KPLY/xFLhmK0gg", + "Zws2i5Wv/Y++P8zDaqjSVexzUcj1gIqwOTGq/MxerE69l5QvwFzP5koViua2Gmk0aAP1oSVQqWdA9VY7", + "Pw8LOXjoUKW8wYxltPBNzRJgbfababTYcbgxWgUaiuw7Lnr5eDj+zAIO2S3h8Z83msLxoK7rUBep1Odv", + "5Rq7tVrrQvNCOkO47PMCsNSnuDH7YqAQrkqlLYYS3C+VogsY0F1C793IOhItjx8OsksiicogYt4VNXqS", + "QBRk+3Ji1hw9w2CemEOMamYnINPPZB3EzmeExacdwmY5CrB15KrdeypbXlRbTXcItDhrAckbUdCD0cZI", + "eByXVPnjiHVGPZcdJZ39huVStpV0Ow9iCYNionXBNn8bdjloT+93hd18NTdfwi1U+keUYzO6F6YvxLZD", + "cBRNM8hhYRduX/aE0hQaajbIwPF6PkfeksTCEgMDdSAAuDnAaC5HhFjfCBk9QoyMA7Ax8AEHJj+I8Gzy", + "xT5Aclcoifqx8YoI/oZ4Yp8N1DfCqCjN5coG/I2p5wCuhEMjWXQiqnEYwviUGDa3orlhc04XbwbpVRZD", + "haJTR8yF3jwYUjS2uKbslb/XmqyQcJvVhNKsBzouam+BeCbWic3sjeois/XM0Hs0dwHzjGMH09Zwu6fI", + "TKwxnAuvFhsrvwOWYTg8GIHtZc0U0it+NyRnWWC2Tbtdzo1RoUKScYbWmlyGBL0xUw/IlkPkcj8oy3Yr", + "ADpmqKbHgTNL7DQftMWT/mXe3GrTptyoTwuLHf+hIxTdpQH89e1j7UJqf20K5g0X5fIn6pNUkOtblu5S", + "2c9+XNpqffsU9uuSQwuILVh905UDo2htx3q18RpgLcZKDPPtOyX7aFOQAyrBSUs0Ta5jkQJGlwe8xy/8", + "Z4GxDneP8s2DIIBQwoIpDY3TyMcFfQ5zPMWyw0LMh1enSzk363srRH35W7c5ftha5idfAUbgz5lUOkGP", + "W3QJ5qVvFRqRvjWvxiXQdoiiLdLPsjjHxWmvYZNkLK/i9Orm/f6FmfaH+qJR1QxvMcZtgNYMm0pEA5e3", + "TG1j27cu+KVd8Et6sPWOOw3mVTOxNOTSnuMPci46DGwbO4gQYIw4+rs2iNItDDJIOO9zx0AaDWJajrd5", + "G3qHKfNj74xS82nvQze/HSm6lqB8XjxDUCwWkPmyYN4fxoPia7ngi6D7UVluqzV3TGzJN6zYtqXYmwvD", + "h6Eg/EDcTxjPYB2HPtQKEPImsw4L1eEkC+C2XEncLBRFTRjij28EtrpP7AvtJgBEg6AvO87sJjrZ7lK9", + "nbgBOdDM6SQK/Pq2H8v+hjjUTYfCp1sVQ7cfIRwQaYrpoCFIvwzBAAOmZcmydcfxZEcdNILRvazLA9IW", + "shY32A4MtIOgowTXKkHtQq2dgf0Edd4To5XZ2GsXWGzom6YuAT+rJHowWpHN/Xrnta42cu3f/3ShhaQL", + "cF6oxIJ0pyFwOfugIagmrohmNpwkY/M5hN4XdRvPQQu4no09G0G6ESKLu2gqxvWXT2JktIN6Ghh3oyxO", + "MRFaGPLJX/a9XF6mD0xJ9ZUQbM0tXFXRdP3vYZP8RPPKKBlMqiY817md2pfvHru+Kr6HDY68M+rVALZj", + "V9Dy9BaQBmOW/vqRCgo/31Ot0vioXra2cI+dOovv0oG2xjUzGCb+5pZpFftvL+UuB6MJkjCwjNmNi3hs", + "gjk90EZ8l5R3bQLLdssggbwfTsWUb/3Yv4rqWhS7aPcSaO6JF5cz+Tid3C0SIHabuRF34PpNfYFG8YyR", + "ptYz3Ars2RPltCylWNE8cfESQ5e/FCt3+ePrPrziE2syccq+/Obs5RsH/sfpJM2ByqS2BAyuCt8r/zCr", + "su0Ptl8ltkq2M3RaS1Gw+XUl4zDG4gYrYneMTb1mIk38THAUXczFPB7wvpP3uVAfu8QtIT9Q1hE/jc/T", + "Bvy0g3zoirLcOxs9tAPB6bi4cR1polwhHODOwUJBzFdyUHbTO93x09FQ1w6ehHO9xtKUcY2Du8KVyIpc", + "8A89uPT0rZAt5u8yE6PBQ7+dWGWEbIvHgVht3/exK0wdEyt4/bL4xZzGo6PwqB0dTckvuXsQAIi/z9zv", + "qF8cHUW9h1EzlmESaKXitIAHdZbF4EZ8WgWcw824C/psVdSSpRgmw5pCbRSQR/eNw96NZA6fmfslgxzM", + "T8djlPRw0y26Q2DGnKCLoUzEOsi0sK0mFRG8G1ONSbCGtJDZu1YG1hnbP0K8KtCBmaicpfHQDj5Thr1y", + "G0xpXib48oC11oxYsYHYXF6xYCzz2piaqR0ggzmiyFTRsq0N7mbCHe+Ks39UQFhmtJo5A4n3Wueq88oB", + "jtoTSON2MTew9VM1w9/FDrLF3+RtQduMIFv9dy9qn5JfaKxZzp4R4OGMPca9JXrb0YejZpvNtmyHYI7T", + "Y8a0HPeMzjnrBuaIthBnKplL8SvEHSHoP4oUwvCOT4Zm3l+BxyL3uiyldio3ndCb2Xdt93jdeGjj76wL", + "+0XX3bpuc5nGT/V+G3kbpVfFyzU7JA8pYWGEQTs1YIC14PEKgmGxfYiPPqLcnidbBaKVYRY/lWEu54kd", + "vzmVDuZe/mtOb2Y01lvF6EIGpmB7W3FSWhD/sd8AVdc4sLOTIIK7fpfZSnIlyMYH0a9Ke0u9xk47WqNp", + "FBikqFB1mdowhVyJyDAVv6Hcdt8231l+5b5WYF3w5qsbIbEOpIqHdGWQsiJqjr26epel/fCdjC2YbSxd", + "KQg6F7uBbNN+S0Wu+3NducOh5nxOTqdB+3S3GxlbMcVmOeAbD+0bM6rwuqzd4fUnZnnA9VLh649GvL6s", + "eCYh00tlEasEqXVPFPLqwMQZ6BsATk7xvYdfkfsYkqnYCh4YLDohaPL04VcYUGP/OI3dsq4x+DaWnSHP", + "9sHacTrGmFQ7hmGSbtR49PVcAvwKw7fDltNkPx1zlvBNd6HsPksF5XQB8fyMYgdM9lvcTXTnd/DCrTcA", + "lJZiQ5iOzw+aGv40kPNt2J8Fg6SiKJguXOCeEoWhp6YtsZ3UD2d75Ls+Sx4u/xDjX0sf/texdX1iNYYW", + "AzlbGKX8A/poQ7ROCbXFP3PWRKb7Ppfk3NcWxsZTdb8pixszl1k6ypIYqD4npWRco/2j0vPkL0YtljQ1", + "7O94CNxk9uWTSAOndo8Tvh/gnxzvEhTIVRz1coDsvcziviX3ueBJYThK9qCpsRCcysFA3XhI5lBc6Pah", + "x0q+ZpRkkNyqFrnRgFPfifD4lgHvSIr1evaix71X9skps5Jx8qCV2aEf3750UkYhZKxhQHPcncQhQUsG", + "K8yYi2+SGfOOeyHzUbtwF+g/b/yTFzkDscyf5agiEHg0tyXLGyn+p1dN5XN0rNpMxI4NUMiItdPZ7T5x", + "tOF+Vreu/9YGjOGzAcyNRhuO0sfKQPS9Da+vv/kc8UJdkOyetwyOD38h0ujgKMcfHSHQR0dTJwb/8qj9", + "2LL3o6N4AeKoyc382mDhLhoxfhvbw2ciYgDz3f7qgCJXHyFigBy6pMwDwwRnbqgpaXdW+/RSxGHyu+LR", + "pvFTcHX1Dp94POAfXUR8ZmaJG9hkKQwf9nZnySjJZPXzIM6dkmdiPZZwOneQJ57fAYoGUDLSPIcr6XXO", + "jLrrd8aLBDRqRp1BLoySGTYFCu35fxw8m8VPt2C7Ynn2U1PbrXORSMrTZTRKeGY+/NnK6K0r2LLKaJ+R", + "JeUc8uhwVrf92evAES3972LsPAXjI9/tdm61y+0srgG8DaYHyk9o0Mt0biYIsdoum1WXZcgXIiM4T9PU", + "omGO/RbIQV/Gf1SgdOxo4AObgIjOLsN8bVtAAjxD69cx+Q4L2BhYWhXL0erka8G26yJWZS5oNsUatZff", + "nL0kdlb7je2gbdsSLtDo0l5F1Eq+R591Z3QeKICyT7/2bRUZzKqVTuougrESc+aNps8h64ROoDkmxM4x", + "eWEtYXX/cjsJwUrHsoAsaFpodTGkCfMfrWm6RBNT6yIbJvnx/TQ9VTYG+CCJrG5ig+fOwO1aatqOmlMi", + "9BLkDVOAidWwgnZVu7rEozNx+ip37eXJinNLKcd7yBR1y5p90e6BswKJ9w1HIesgfk8Dg21Hu2970Qv8", + "Kh5S3+lV2nHe+hppddP1V85GnFIuOEuxon1MIMIKXOO8TSOK/8fdRGriTmjkcEU7pNYpnQ6Lgz1TPSN0", + "iOt7boOnZlMtddg/Naxd56wFaOU4G2RT3+jX+TUYV+CaEhkiCvmkkJHYlGg8e+0H35OMsLjOgKHqW/Ps", + "B2fGxNoG14yjwcKhzYnZ1vOQK4YORk6YJgsByq2nnZSh3plvjrHYXgbr98cvxYKlF2yBY9hoKLNsG/rX", + "H+rMBwK6wDvz7nPzriuBXv/ciuqxk56VpZt0uA10vPf9mg8iOBZ+4uMBAuTW44ejbSG3rRG8eJ8aQoMV", + "Bh9BifdwjzDqlsjtUb4xKoKlKHyD2MS4aB1UxiNgvGTce8LiF0QavRJwY/C8DnynUkm1FQFH8bRLoPlA", + "HDsmmlpX6l2H6haANyjBNfo5hrex6eY8wDjqFxrBjfIN8YfCUHcgTDyneR0BG+nNjFKVE6IyzBHpdGuO", + "MQ7DuH0/+PYFsDMLq/4cmyrsexMNlZqbVdkCdEKzLFah6Bk+JfjU5/rAGtKq7iVUJ3m1S033qc1NlAqu", + "qmLLXP6FO04XtD+PUEPYgt3vMBZMmW3w31gjneGdcbGveydX+kDXbL/66v1k0ZjUa2g6UWyRjMcE3il3", + "R0cz9e0Ivfn+oJTusy5/F0mVHS4X7lGMv31jLo6w/movzNheLXV5VAzpFfjc162pC/u1uRJeZb12Uei8", + "xs2LbFkHeP9iFPAVzQcSmkOTt71frRl4KK05HczCp9pVWdKUbGVBg5VrbMhnx4je9wQNhXnaKM/DGZ/d", + "WrcidNgF833L4WJDfRpmMehouZ0vpNngfZ0h36+GMt19uwV83m1/fw2uKGYpYcVE5YNofCirVwntr61m", + "8nWtgej6owHin9v4PGgqv3RtSO0ynU7+/U/WmUaAa7n5HRjOe5vea6zfl3ateap5hdQd7EZ1tGvdimNa", + "kcS6XjjZsNXav01LvS4iPbJ6MUYc6OHj43Rynu11YcY6p0zsKLFj95ItlhoLr/8VaAbyzY7C8k0xeTxi", + "pVCsaSSZm8FcJc8lDnc8NmbcEDALC+P3x/KxhCtINXYPbWKkJMA+ZfLNZN52/2eB+WF1ug6td3XltxWT", + "77cM3XHH9+rfBDWcbLvF4/Gl08/qSFibyHNDVVN1o5P6OjoBbz6HFIvbbq039B9L4EEtm6m3yyAs86D8", + "EKvTUbA88/5WxwagbeWAtsITtEm5MzhD6cjXsLmnSIsaov0f61ys29R/RQwgd0h8KeAhQ7IL/mGqpgzE", + "go/sdBV1mx4Hg6V7g+pZt5zLk6S5OJqKWlumjPeuHjWX+XSv6n2YWTFUkqjf+nZY/3iBnYaVi3Oidf3Y", + "UEsn5/3+Jzeu/ixWh6p9J74SLSj/my8FZ2fJ2TWEze3RU3VDZebfOEhtH3s3sTjQ83pm1sTh933VkYr6", + "mNKS5sKIEclQXlA79L2OG7unbIBfU4cF4ZqDlJDVLpFcKEi08HH72+DYhgobxXgrJKjBLjYWuMEKxm+b", + "Es3YzYtixWLqghfDBRIJBTXQyaCQ8vCc25D93D73udS+m9NOC1NNr7vbivoMDKZ6SAypfk7cbbk7R/s2", + "xibGOcjEe566VZV5u7AWlk/MqtRe0OHBqA1yo0ugbGElUTtN2l9lR0cIcp2vYXNilSDfj9XvYAi0lZws", + "6EHdyM4mH9T8pmJwLw4C3uctB1YKkScDzo7zfinoLsVfs/QasJRbHak80Gqb3Ecbe+3NvllufOnjsgQO", + "2YNjQs64zQ3xju12l7jO5Pye3jb/GmfNKlud3RnVjq94PMge66bLO3IzP8x2HqbAsLo7TmUH2VFoeD1Q", + "hlrSm0jj+eOxWnnf1dxtBt4QlYUiJpNcWI/VczzoMcMRZrIHJRfQkUmJ83QRlYtYSOZtsu3NUHFMhZMh", + "QBr4mKTvGgo3eBQB0fbWkVNoK5i52mViTiQ0TuTbFnHrd+KOafTdmetZ2vxuLiS0emqbr23Bxjp/wTe/", + "p3LGtKRyc5tSa71O4D3rySCWd4Zj1ZFYzUKaaKw+DvNc3CTIrJK6XUFMtTXvqfZl7HtnNd+ZUz2DIK6L", + "KieobciSZiQVUkIafhFP27NQFUJCkgsM84p5oOfayN0F5upwkosFEWUqMrBtP+IUNDRXxTlFsQmCqJoo", + "CiztYNKn/Sag45FTHqoNvS3OYxedWF/mQOApKFeMx2HIvtyHd0sL970abpzP0SLEMNalnXttpc+wkT3s", + "2cee5bk3GAy1sic/qgrDkTDxxkzxhBRCaafZ2ZFUPVQT4nU/FVxLkedtI5AViRfOsv2Krs/SVL8U4npG", + "0+sHqEdyoeuVZlOfltoNxmtmkp2KTCN77ncrnNr3MDTNEcnejfUd59i7H3YA5vvdHGu3jfss0ge/s642", + "84qrDWecUC0KlsZp+I8V3TYYkxZjCdFST7YlnU3Ox9eQUYeXQx3MgCypj2bgNNpT64w4nuacusg8zH9R", + "4u2OS+bgLomBi6nPJ53UkqSDslUHAITUZozqSto+dqHkU3MVsbAZ5uiS7gI6kotj5M/dYDMjHBwoDXcC", + "qhdtWAN43yr7U1uSy0YuzsTaP3/Q1Oy6FfAft1N5i3kMhVRdNKQlbVCVr+8xwBHilYG3xh9dYrbwbGwU", + "Ut1zdOSNGgAwHJfUgmFUdNK+YMwpyyFLYi3rzmub0DTQbF1GS7eTNFOOk6e08h3jzNiVBFdvworUsu1v", + "KqkhJVG/3rfc8gzWoLAYhG2fT5X1M3h/B+S2U1xH+RZlksMKWuFarghGhaIdW4H/VtUfkwygRO9f1yYV", + "i0MK7/KOocKtPQkiWcZgN2q5sIi1O0V2mCWiRpQ1T+wxUWOPkoFoxbKKtvCn9hU52mY3c5QjqOrJ5InX", + "28ZO86Md4a0f4Mx/HxNlPCbej+NDe7OgOOq2MaCdcYmVGjr1PB6WGFZ4qR0aOFtWOz4tiTd8Q5X0hg8b", + "APsk36g3I/eJCR4g9ps1pCjVtOPu7o4TgoMR1aneNCiCy3qHb29I/iw0vJWEB8eLqRoKkMFutdR4unAC", + "O76AvYO5EXuN1Ixd4Rz/d/xvSmaVH8jo1bZJXajBvQDvscOC0rWzwgm0rL7QfHzh1NUT7CrlLIisLuiG", + "CIn/GH3tHxXN2XyDJ9SC7z8jakkNCTkXofVdu3hFM/F2wWTqAfN2AeGnsutmY8cMhtuYUQKgzRXou4kI", + "UtBrCLcB3fKW86TasBxVzQqmFF52ne3sY8Et3teEKGgW6shYma7dt9nXKjVf/+8mayucyheUKnOa+paE", + "ridKyyBu24564tJLKLan9fXVY08CdSvThmilT+fNbmHc2zNyIxYrP9TvoQV2r8Vjr9XFnZaxTzf4JjN6", + "S0LkqKUcehfGxof0gA4bw+0CP+yT92nwHy0aObSMMeD/XvA+0BkzhNc2wfwEWG6l/EdgtXbVmVgnEuZq", + "VyiENawaRVg2xQK8cZLxVAJVNjbk/LVT2ZqaiIwbFdJGL9bet3qUDOaMN8yS8bLSEQ0ASyPyTYCw0DyN", + "aB1w9gxJCUYMW9H89QqkZNnQxpnTYXvIhTXpvUnefRtR/us7tT8AU432g5mE0GSqBa+ZC9x2vbGBhUpT", + "nlGZha8zTlKQ5t4nN3Sjbu/7MNDKysgXO7wfNJBm2vntgR8ESdsCkm+c+/KOnokaQHpAF8UI1wJGsEbc", + "CtYoosWAJ6EPQ7ysAl0nuVhgftkAAbrik+j7scqK4GiwtfLQfvMo9itsnwbrbruDrwXOOmaK7efsNaIO", + "FZ4fOdNbT5q1pnUT/mxEpj0Inv75ogkLt5vTp/9YjuYlJjG08jS9cOeTGPxe2/AQOx8MeDLaFtyBXUQH", + "uUvwDc214/sZtX3wsUxQq8MmqNuqLYHfoJogZ5q6wJ2+0aenFFukTF0e7Z42IWtJ9vfAAHi2+bQ7W+1p", + "62AKM84+TaC2Z84mpSiTdEw0oC3NnzmDtoO0DeMAfQTm6oF114ETqm5W0Sps0upasW8frMGuGbv8MmW6", + "TckeMmgMcNC2sVzMkZfZ1sxoh8Ecj9p4Me1mH7UNNjWTIJRISCuJBs0butndV2igJOzFX8++ePjo50df", + "fEnMCyRjC1BNWeFOX54mYozxrp3l08aI9Zan45vg89It4rynzKfb1JvizprltqqpGdjrSrSPJTRyAUSO", + "Y6QfzK32Csdpgr5/X9sVW+TBdyyGgt9+z6TI83hZ91p0i5j6Y7sVGPuNxF+CVExpwwjbvjqmm1hZtURz", + "HBb3XNk6I4Knrvp6TQVMDwTjxBYyFGqJ/Ayzfp1/g8C6zB2vsj6JbetyepG1iGFwBsZvzICUonSiNJuT", + "GESYWyKDnEtnaMTwziB6sma2No4yRoguJjlOemFH3O3cvt2tUcc5vdnEiHjhD+UtSHPIkj6c0X4bTtKY", + "0n83/COSon8wrlEv97fgFVH94HZdt0eB1k/XjpAHAjCQh9nKoAub8jeVRqW1yqP93rs6u+LHq8YFujNh", + "ACHxH+wAL0ysbN6rY9wdOJ+5ZOerGinBUt4PUUJr+btyNT3rrS+SYIuckUJrUJYtib5YGCTiqud1fuuA", + "VtJLg8UO/EYzzfNI+qy1m+CZCgnHqARyRfNPzzW+ZVLpM8QHZG+Hk2bCHMoQyRaV6nYV3F7SUXMH+ZKH", + "m5q/wZTd/wCzR9F7zg3l3MW92wytXtiSeuFvBZsFTG5wTBsO9PBLMnPV9EsJKVNdN/SNF07qlEGQbO5C", + "L2Gtd+Qo7lrnT0LfgYznPmaE/BC4kwSa7RoImyP6mZnKwMmNUnmM+npkEcFfjEeF3Td3XBd3rLx+u4Ig", + "QWmvPQuC9PuKjl2eLXphLp1KQX+do2/rFm4jF3WztrHVbEYXcL+6eqdnY4rQxIutm8+xCs5Bqq7vVXP9", + "N6h/Y3HkxnDzxijmp6GKqLbq50Dx3c5+VCzfGSDSKqX8cTpZAAfFFBYL/tk1h/i0d6mHwObk94+qhfUu", + "hUQsYiJrbU0eTBUUSR5RH9l9FqmGjPluaSWZ3mBjUG9AYz9HK/V8V1d9cFVDat+Vu/u0uIa6OXNTI6JS", + "/nb9TtAc7yPrUuPmFhL5MflmTYsyd+Zg8vW92b/C4788yU4fP/zX2V9OvzhN4ckXX52e0q+e0IdfPX4I", + "j/7yxZNTeDj/8qvZo+zRk0ezJ4+efPnFV+njJw9nT7786l/vGT5kQLaA+trdTyf/mZzlC5GcvTlPLg2w", + "DU5oyb4HszeoK88FNq4zSE3xJEJBWT556n/6P/6EHaeiaIb3v05cA5bJUutSPT05ubm5OQ4/OVlgUnii", + "RZUuT/w82E6sJa+8Oa+jyW3cC+5oYz3GTXWkcIbP3n5zcUnO3pwfNwQzeTo5PT49fuh613JassnTyWP8", + "CU/PEvf9xBHb5OmHj9PJyRJojjVUzB8FaMlS/0gCzTbu/+qGLhYgjzFhwP60enTixYqTDy45/uO2Zydh", + "SMXJh1YNgWzHlz5kYNcrJx98k8vtA7YaHLpgLYOdqK/wO9Cuoo41IkTKMaDLwI0+JUpIl3ZcSibMwZua", + "WzQDdKhjXJjEGtFaVjy1XlY7BXD876uz/0RP86uz/yRfk9Opi3FXqJnEprdJtTXFnGcW7H6An3q2OasL", + "VjRe6cnTdzFrkWtmVVaznKXEChx44gw5BQeiHrFheGganDTdxhv2bVjyafLV+w9f/OVjTCzsCbk1koIa", + "DiHqtfA9ChFpBV1/PYSytQt6NuP+owK5aRZR0PUkBLjvCo0UtvI5Kb5VaxjUF4T7/fvF6x+IkMSpwW9o", + "el3n4/gErCbpLMy/Ml8OQexuyBBo4FVhLhuX2FOoRdmu8Vqj+T32NUNAkS88Oj31zNCpGsEBPXHnPpip", + "Y5/qExrGtwQWx362syKwpqnON4SqIMAAw/18D8JO1pQok1bs9lYbZ39GtyXRwPd9E64jRciFpvkO+C47", + "/dpa6HCxMqW5LXdnOPeQEYXgfUweCLfW08ifu/vfY3f74gUphTnTDAOamyvHX2ctIJ1QmW88uAO1JI7J", + "30SFQqAR7ysNsW7VOIN1brg5XembIAKtyVbBJ0dH3YUfHTXxcnO4QSZLOb7YRcfR0bHZqSd7srKtBudW", + "pdhRZ2ef4Xqb9Yqu63BjSrjgCYcF1WwFJNAcn5w+/MOu8JzbAG8j9Vrp/ON08sUfeMvOuRFsaE7wTbua", + "x3/Y1VyAXLEUyCUUpZBUsnxDfuR1BH3QDLnP/n7k11zccI8Io3hWRUHlxgnRtOY5FQ9au2zlP70iNo2g", + "jVyULhQGtaCIamVaX+iOLybvP3odYKTuse21kxn2qhv7KoQKy7B2gi4GdfIBjeSDv584T2f8ITorrBZ8", + "4svrDbxpCynFH7a0og96bRayfTjzTjBeSnW6rMqTD/gfVGiDFdm67Cd6zU8wmPLkQwsR7nEPEe3fm8/D", + "N1aFyMADJ+Zz2+x/2+OTD/bfYCJYlyCZuY6wFqL71dasPcGer5v+zxueRn/sr6NVr3Pg5xNvT4mp1O03", + "P7T+bNOUWlY6EzfBLOiJsG60PmTmYaW6f5/cUKaNkOTKRGKT+P7HGmh+4nrCdH5tyrD3nmBt+eDHjlhV", + "Clsnpq3RvqU3l630Q2nrMzwTaKgYYrjrZMY4cqGQSzb2RfuwryL1eOPlEmwgrXfRRmRQLchMCpqlVGHv", + "cdc9qacbf7yj/tUtJ3EeccAhmGhu6FccNPzkeKdXBscdI2QG+0LOX/gJm8yt31ww60H0jGbEFxZKyCua", + "mw2HjJw58b+Fjd9aqPr8UtBnFls+mZzxzB8+RShWWWspiDJepyVoczZGqDBapGEAC+CJY0HJTGQb14lq", + "IumNXtuyEF3mdkLbN0bbEEklLdTQwwNYKX/fpsldFsk/DYF/GgL/NBX9aQj8c3f/NASONAT+aSb700z2", + "P9JMto9tLCZmOvPPsLSJrbFpa16r99GmBUHN4tsFq5iuZbJWPih2O2D6mJBLrJlCzS0BK5A0JylVVrpy", + "hbkKDNPEsleQPb3iSQsSGwxpJr7f/NdGoV5Vp6ePgZw+6H6jNMvzkDf3v0V5Fx/ZRJGvydXkatIbSUIh", + "VpDZrNawBLb9auew/6se93Wvdj6mj2NRGl8di6hqPmcpsyjPBV8QuhBNBDXWAOUCn4A0wNkORITpqcs4", + "Ya6cqGtQ3q7U3Zbc+xLAebOFO0MKOuQSjyYwhLdnKMG/jIkj+B8tpd+2DNRdGenWsXtc9U+u8im4ymfn", + "K390J21gWvxvKWY+OX3yh11QaIj+QWjyLWYH3E0cc7Up02gjptsKWr7Cijf3NRHGYcQu3qJ1rO679+Yi", + "UCBX/oJtAlCfnpxgya2lUPpkYq6/dnBq+PB9DfMHfzuVkq2w0y9aN4VkC8ZpnrjAz6QJMn10fDr5+P8D", + "AAD//23D8FrbHAEA", } // 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..3d581dbd78 100644 --- a/daemon/algod/api/server/v2/handlers.go +++ b/daemon/algod/api/server/v2/handlers.go @@ -26,6 +26,7 @@ import ( "math" "net/http" "os" + "strconv" "strings" "time" @@ -69,6 +70,19 @@ const MaxTealSourceBytes = 200_000 // become quite large, so we allow up to 1MB const MaxTealDryrunBytes = 1_000_000 +// MaxAssetResults sets a size limit for the number of assets returned in a single request to the +// /v2/accounts/{address}/assets endpoint +const MaxAssetResults = 1000 + +// DefaultAssetResults sets a default size limit for the number of assets returned in a single request to the +// /v2/accounts/{address}/assets endpoint +const DefaultAssetResults = uint64(1000) + +const ( + errInvalidLimit = "limit parameter must be a positive integer" + errUnableToParseNext = "unable to parse next token" +) + // WaitForBlockTimeout is the timeout for the WaitForBlock endpoint. var WaitForBlockTimeout = 1 * time.Minute @@ -91,6 +105,7 @@ type LedgerForAPI interface { ConsensusParams(r basics.Round) (config.ConsensusParams, error) Latest() basics.Round LookupAsset(rnd basics.Round, addr basics.Address, aidx basics.AssetIndex) (ledgercore.AssetResource, error) + LookupAssets(addr basics.Address, assetIDGT basics.AssetIndex, limit uint64) ([]ledgercore.AssetResourceWithIDs, basics.Round, error) LookupApplication(rnd basics.Round, addr basics.Address, aidx basics.AppIndex) (ledgercore.AppResource, error) BlockCert(rnd basics.Round) (blk bookkeeping.Block, cert agreement.Certificate, err error) LatestTotals() (basics.Round, ledgercore.AccountTotals, error) @@ -495,7 +510,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 +540,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 +554,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) @@ -732,6 +750,70 @@ func (v2 *Handlers) GetBlockTxids(ctx echo.Context, round uint64) error { return ctx.JSON(http.StatusOK, response) } +// NewAppCallLogs generates a new model.AppCallLogs struct. +func NewAppCallLogs(txid string, logs []string, appIndex uint64) model.AppCallLogs { + return model.AppCallLogs{ + TxId: txid, + Logs: convertSlice(logs, func(s string) []byte { return []byte(s) }), + ApplicationIndex: appIndex, + } +} + +func getAppIndexFromTxn(txn transactions.SignedTxnWithAD) uint64 { + appIndex := uint64(txn.SignedTxn.Txn.ApplicationID) + if appIndex == 0 { + appIndex = uint64(txn.ApplyData.ApplicationID) + } + + return appIndex +} + +func appendLogsFromTxns(blockLogs []model.AppCallLogs, txns []transactions.SignedTxnWithAD, outerTxnID string) []model.AppCallLogs { + + for _, txn := range txns { + if len(txn.EvalDelta.Logs) > 0 { + blockLogs = append( + blockLogs, + NewAppCallLogs(outerTxnID, txn.EvalDelta.Logs, getAppIndexFromTxn(txn)), + ) + } + + blockLogs = appendLogsFromTxns(blockLogs, txn.EvalDelta.InnerTxns, outerTxnID) + } + + return blockLogs +} + +// GetBlockLogs gets all of the logs (inner and outer app calls) for a given block +// (GET /v2/blocks/{round}/logs) +func (v2 *Handlers) GetBlockLogs(ctx echo.Context, round uint64) error { + ledger := v2.Node.LedgerForAPI() + block, err := ledger.Block(basics.Round(round)) + if err != nil { + switch err.(type) { + case ledgercore.ErrNoEntry: + return notFound(ctx, err, errFailedLookingUpLedger, v2.Log) + default: + return internalError(ctx, err, errFailedLookingUpLedger, v2.Log) + } + } + + txns, err := block.DecodePaysetFlat() + if err != nil { + return internalError(ctx, err, "decoding transactions", v2.Log) + } + + blockLogs := []model.AppCallLogs{} + + for _, txn := range txns { + blockLogs = appendLogsFromTxns(blockLogs, []transactions.SignedTxnWithAD{txn}, txn.ID().String()) + } + + response := model.BlockLogsResponse{Logs: blockLogs} + + return ctx.JSON(http.StatusOK, response) +} + // GetBlockHash gets the block hash for the given round. // (GET /v2/blocks/{round}/hash) func (v2 *Handlers) GetBlockHash(ctx echo.Context, round uint64) error { @@ -1029,6 +1111,96 @@ func (v2 *Handlers) RawTransactionAsync(ctx echo.Context) error { return ctx.NoContent(http.StatusOK) } +// AccountAssetsInformation looks up an account's asset holdings. +// (GET /v2/accounts/{address}/assets) +func (v2 *Handlers) AccountAssetsInformation(ctx echo.Context, address string, params model.AccountAssetsInformationParams) error { + if !v2.Node.Config().EnableExperimentalAPI { + return ctx.String(http.StatusNotFound, "/v2/accounts/{address}/assets was not enabled in the configuration file by setting the EnableExperimentalAPI to true") + } + + addr, err := basics.UnmarshalChecksumAddress(address) + if err != nil { + return badRequest(ctx, err, errFailedToParseAddress, v2.Log) + } + + var assetGreaterThan uint64 = 0 + if params.Next != nil { + agt, err0 := strconv.ParseUint(*params.Next, 10, 64) + if err0 != nil { + return badRequest(ctx, err0, fmt.Sprintf("%s: %v", errUnableToParseNext, err0), v2.Log) + } + assetGreaterThan = agt + } + + if params.Limit != nil { + if *params.Limit <= 0 { + return badRequest(ctx, errors.New(errInvalidLimit), errInvalidLimit, v2.Log) + } + + if *params.Limit > MaxAssetResults { + limitErrMsg := fmt.Sprintf("limit %d exceeds max assets single batch limit %d", *params.Limit, MaxAssetResults) + return badRequest(ctx, errors.New(limitErrMsg), limitErrMsg, v2.Log) + } + } else { + // default limit + l := DefaultAssetResults + params.Limit = &l + } + + ledger := v2.Node.LedgerForAPI() + + // Logic + // 1. Get the account's asset holdings subject to limits + // 2. Handle empty response + // 3. Prepare JSON response + + // We intentionally request one more than the limit to determine if there are more assets. + records, lookupRound, err := ledger.LookupAssets(addr, basics.AssetIndex(assetGreaterThan), *params.Limit+1) + + if err != nil { + return internalError(ctx, err, errFailedLookingUpLedger, v2.Log) + } + + // prepare JSON response + response := model.AccountAssetsInformationResponse{Round: uint64(lookupRound)} + + // If the total count is greater than the limit, we set the next token to the last asset ID being returned + if uint64(len(records)) > *params.Limit { + // we do not include the last record in the response + records = records[:*params.Limit] + nextTk := strconv.FormatUint(uint64(records[len(records)-1].AssetID), 10) + response.NextToken = &nextTk + } + + assetHoldings := make([]model.AccountAssetHolding, 0, len(records)) + + for _, record := range records { + if record.AssetHolding == nil { + v2.Log.Warnf("AccountAssetsInformation: asset %d has no holding - should not be possible", record.AssetID) + continue + } + + aah := model.AccountAssetHolding{ + AssetHolding: model.AssetHolding{ + Amount: record.AssetHolding.Amount, + AssetID: uint64(record.AssetID), + IsFrozen: record.AssetHolding.Frozen, + }, + } + + if !record.Creator.IsZero() { + asset := AssetParamsToAsset(record.Creator.String(), record.AssetID, record.AssetParams) + aah.AssetParams = &asset.Params + } + + assetHoldings = append(assetHoldings, aah) + } + + response.AssetHoldings = &assetHoldings + + return ctx.JSON(http.StatusOK, response) +} + // PreEncodedSimulateTxnResult mirrors model.SimulateTransactionResult type PreEncodedSimulateTxnResult struct { Txn PreEncodedTxInfo `codec:"txn-result"` @@ -1401,10 +1573,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/daemon/algod/api/server/v2/test/handlers_resources_test.go b/daemon/algod/api/server/v2/test/handlers_resources_test.go index 8260e54ffb..f028a9b2a1 100644 --- a/daemon/algod/api/server/v2/test/handlers_resources_test.go +++ b/daemon/algod/api/server/v2/test/handlers_resources_test.go @@ -21,6 +21,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "strconv" "testing" "github.com/algorand/go-algorand/data/transactions/logic" @@ -107,6 +108,33 @@ func (l *mockLedger) LookupAsset(rnd basics.Round, addr basics.Address, aidx bas } return ar, nil } + +func (l *mockLedger) LookupAssets(addr basics.Address, assetIDGT basics.AssetIndex, limit uint64) ([]ledgercore.AssetResourceWithIDs, basics.Round, error) { + ad, ok := l.accounts[addr] + if !ok { + return nil, basics.Round(0), nil + } + + var res []ledgercore.AssetResourceWithIDs + for i := assetIDGT + 1; i < assetIDGT+1+basics.AssetIndex(limit); i++ { + apr := ledgercore.AssetResourceWithIDs{} + if ap, ok := ad.AssetParams[i]; ok { + apr.AssetParams = &ap + apr.Creator = basics.Address{} + } + + if ah, ok := ad.Assets[i]; ok { + apr.AssetHolding = &ah + } + + if apr.AssetParams != nil || apr.AssetHolding != nil { + apr.AssetID = i + res = append(res, apr) + } + } + return res, basics.Round(0), nil +} + func (l *mockLedger) LookupApplication(rnd basics.Round, addr basics.Address, aidx basics.AppIndex) (ar ledgercore.AppResource, err error) { ad, ok := l.accounts[addr] if !ok { @@ -211,6 +239,23 @@ func randomAccountWithAssetParams(N int) basics.AccountData { return a } +func randomAccountWithSomeAssetHoldingsAndOverlappingAssetParams(overlapN int, nonOverlapAssetHoldingsN int) basics.AccountData { + a := ledgertesting.RandomAccountData(0) + a.AssetParams = make(map[basics.AssetIndex]basics.AssetParams) + a.Assets = make(map[basics.AssetIndex]basics.AssetHolding) + // overlapN assets have both asset params and asset holdings + for i := 1; i <= overlapN; i++ { + a.AssetParams[basics.AssetIndex(i)] = ledgertesting.RandomAssetParams() + a.Assets[basics.AssetIndex(i)] = ledgertesting.RandomAssetHolding(false) + } + + // nonOverlapAssetHoldingsN assets have only asset holdings + for i := overlapN + 1; i <= (overlapN + nonOverlapAssetHoldingsN); i++ { + a.Assets[basics.AssetIndex(i)] = ledgertesting.RandomAssetHolding(false) + } + return a +} + func randomAccountWithAppLocalState(N int) basics.AccountData { a := ledgertesting.RandomAccountData(0) a.AppLocalStates = make(map[basics.AppIndex]basics.AppLocalState) @@ -241,6 +286,7 @@ func setupTestForLargeResources(t *testing.T, acctSize, maxResults int, accountM mockNode := makeMockNode(&ml, t.Name(), nil, cannedStatusReportGolden, false) mockNode.config.MaxAPIResourcesPerAccount = uint64(maxResults) + mockNode.config.EnableExperimentalAPI = true dummyShutdownChan := make(chan struct{}) handlers = v2.Handlers{ Node: mockNode, @@ -380,6 +426,120 @@ func accountInformationResourceLimitsTest(t *testing.T, accountMaker func(int) b } } +func accountAssetInformationResourceLimitsTest(t *testing.T, handlers v2.Handlers, addr basics.Address, + acctData basics.AccountData, params model.AccountAssetsInformationParams, inputNextToken int, maxResults int, expectToken bool) { + + ctx, rec := newReq(t) + err := handlers.AccountAssetsInformation(ctx, addr.String(), params) + require.NoError(t, err) + require.Equal(t, 200, rec.Code) + var ret model.AccountAssetsInformationResponse + err = json.Unmarshal(rec.Body.Bytes(), &ret) + require.NoError(t, err) + + if expectToken { + nextRaw, err0 := strconv.ParseUint(*ret.NextToken, 10, 64) + require.NoError(t, err0) + // The next token decoded is actually the last asset id returned + assert.Equal(t, (*ret.AssetHoldings)[maxResults-1].AssetHolding.AssetID, nextRaw) + } + assert.Equal(t, maxResults, len(*ret.AssetHoldings)) + + // Asset holdings should match the first limit assets from the account data + minForResults := 0 + if inputNextToken > 0 { + minForResults = inputNextToken + } + for i := minForResults; i < minForResults+maxResults; i++ { + expectedIndex := i + 1 + + assert.Equal(t, acctData.Assets[basics.AssetIndex(expectedIndex)].Amount, (*ret.AssetHoldings)[i-minForResults].AssetHolding.Amount) + assert.Equal(t, acctData.Assets[basics.AssetIndex(expectedIndex)].Frozen, (*ret.AssetHoldings)[i-minForResults].AssetHolding.IsFrozen) + assert.Equal(t, uint64(expectedIndex), (*ret.AssetHoldings)[i-minForResults].AssetHolding.AssetID) + } +} + +// TestAccountAssetsInformation tests the account asset information endpoint +func TestAccountAssetsInformation(t *testing.T) { + partitiontest.PartitionTest(t) + + accountOverlappingAssetParamsHoldingsCount := 1000 + accountNonOverlappingAssetHoldingsCount := 25 + totalAssetHoldings := accountOverlappingAssetParamsHoldingsCount + accountNonOverlappingAssetHoldingsCount + + handlers, addr, acctData := setupTestForLargeResources(t, accountOverlappingAssetParamsHoldingsCount, 50, func(N int) basics.AccountData { + return randomAccountWithSomeAssetHoldingsAndOverlappingAssetParams(N, accountNonOverlappingAssetHoldingsCount) + }) + + // 1. Query with no limit/pagination - should get DefaultAssetResults back + accountAssetInformationResourceLimitsTest(t, handlers, addr, acctData, model.AccountAssetsInformationParams{}, + 0, int(v2.DefaultAssetResults), false) + + rawLimit := 100 + limit := uint64(rawLimit) + // 2. Query with limit= totalAssetHoldings { + expectToken = false + expectedResultsCount = totalAssetHoldings - rawNext + } + accountAssetInformationResourceLimitsTest(t, handlers, addr, acctData, + model.AccountAssetsInformationParams{Limit: &limit, Next: &nextTk}, rawNext, expectedResultsCount, expectToken) + } + + // 4. Query with limit, next to provide batch, but no data in that range + rawNext := 1025 + nextTk := strconv.FormatUint(uint64(rawNext), 10) + accountAssetInformationResourceLimitsTest(t, handlers, addr, acctData, + model.AccountAssetsInformationParams{Limit: &limit, Next: &nextTk}, rawNext, totalAssetHoldings-rawNext, false) + + // 5. Malformed address + ctx, rec := newReq(t) + err := handlers.AccountAssetsInformation(ctx, "", model.AccountAssetsInformationParams{}) + require.NoError(t, err) + require.Equal(t, 400, rec.Code) + require.Equal(t, "{\"message\":\"failed to parse the address\"}\n", rec.Body.String()) + + // 6. Unknown address (200 returned, just no asset data) + unknownAddress := basics.Address{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + accountAssetInformationResourceLimitsTest(t, handlers, unknownAddress, basics.AccountData{}, model.AccountAssetsInformationParams{}, + 0, 0, false) + + // 7a. Invalid limits - larger than configured max + ctx, rec = newReq(t) + err = handlers.AccountAssetsInformation(ctx, addr.String(), model.AccountAssetsInformationParams{ + Limit: func() *uint64 { + l := uint64(v2.MaxAssetResults + 1) + return &l + }(), + }) + require.NoError(t, err) + require.Equal(t, 400, rec.Code) + require.Equal(t, "{\"message\":\"limit 1001 exceeds max assets single batch limit 1000\"}\n", rec.Body.String()) + + // 7b. Invalid limits - zero + ctx, rec = newReq(t) + err = handlers.AccountAssetsInformation(ctx, addr.String(), model.AccountAssetsInformationParams{ + Limit: func() *uint64 { + l := uint64(0) + return &l + }(), + }) + require.NoError(t, err) + require.Equal(t, 400, rec.Code) + require.Equal(t, "{\"message\":\"limit parameter must be a positive integer\"}\n", rec.Body.String()) + +} + func TestAccountInformationResourceLimits(t *testing.T) { partitiontest.PartitionTest(t) diff --git a/daemon/algod/api/server/v2/test/helpers.go b/daemon/algod/api/server/v2/test/helpers.go index 400906d299..ad028fc8ae 100644 --- a/daemon/algod/api/server/v2/test/helpers.go +++ b/daemon/algod/api/server/v2/test/helpers.go @@ -24,7 +24,6 @@ import ( "github.com/stretchr/testify/mock" - "github.com/algorand/go-algorand/agreement" "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" v2 "github.com/algorand/go-algorand/daemon/algod/api/server/v2" @@ -36,7 +35,6 @@ import ( "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/transactions" "github.com/algorand/go-algorand/data/transactions/logic" - "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/simulation" "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/node" @@ -95,7 +93,6 @@ type mockNode struct { err error id account.ParticipationID keys account.StateProofKeys - usertxns map[basics.Address][]node.TxnWithStatus status node.StatusReport devmode bool timestampOffset *int64 @@ -148,7 +145,6 @@ func makeMockNodeWithConfig(ledger v2.LedgerForAPI, genesisID string, nodeError genesisID: genesisID, config: cfg, err: nodeError, - usertxns: map[basics.Address][]node.TxnWithStatus{}, status: status, devmode: devMode, } @@ -199,44 +195,6 @@ func (m *mockNode) SuggestedFee() basics.MicroAlgos { func (m *mockNode) Config() config.Local { return m.config } -func (m *mockNode) Start() {} - -func (m *mockNode) ListeningAddress() (string, bool) { - return "mock listening addresses not implemented", false -} - -func (m *mockNode) Stop() {} - -func (m *mockNode) ListTxns(addr basics.Address, minRound basics.Round, maxRound basics.Round) ([]node.TxnWithStatus, error) { - txns, ok := m.usertxns[addr] - if !ok { - return nil, fmt.Errorf("no txns for %s", addr) - } - - return txns, nil -} - -func (m *mockNode) GetTransaction(addr basics.Address, txID transactions.Txid, minRound basics.Round, maxRound basics.Round) (node.TxnWithStatus, bool) { - return node.TxnWithStatus{}, false -} - -func (m *mockNode) IsArchival() bool { - return false -} - -func (m *mockNode) OnNewBlock(block bookkeeping.Block, delta ledgercore.StateDelta) {} - -func (m *mockNode) Uint64() uint64 { - return 1 -} - -func (m *mockNode) GetTransactionByID(txid transactions.Txid, rnd basics.Round) (node.TxnWithStatus, error) { - return node.TxnWithStatus{}, fmt.Errorf("get transaction by id not implemented") -} - -func (m *mockNode) AssembleBlock(round basics.Round) (agreement.ValidatedBlock, error) { - return nil, fmt.Errorf("assemble block not implemented") -} func (m *mockNode) StartCatchup(catchpoint string) error { return m.err 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..687a3db80c 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 { @@ -654,7 +668,7 @@ func (pool *TransactionPool) addToPendingBlockEvaluator(txgroup []transactions.S // recomputeBlockEvaluator constructs a new BlockEvaluator and feeds all // in-pool transactions to it (removing any transactions that are rejected // by the BlockEvaluator). Expects that the pool.mu mutex would be already taken. -func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIds map[transactions.Txid]ledgercore.IncludedTransactions, knownCommitted uint) (stats telemetryspec.ProcessBlockMetrics) { +func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIDs map[transactions.Txid]ledgercore.IncludedTransactions, knownCommitted uint) (stats telemetryspec.ProcessBlockMetrics) { pool.pendingBlockEvaluator = nil latest := pool.ledger.Latest() @@ -727,7 +741,7 @@ func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIds map[transact asmStats.InvalidCount++ continue } - if _, alreadyCommitted := committedTxIds[txgroup[0].ID()]; alreadyCommitted { + if _, alreadyCommitted := committedTxIDs[txgroup[0].ID()]; alreadyCommitted { asmStats.EarlyCommittedCount++ continue } @@ -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..01b851f86d 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 @@ -1163,24 +1167,24 @@ func BenchmarkTransactionPoolRecompute(b *testing.B) { // make args for recomputeBlockEvaluator() like OnNewBlock() would var knownCommitted uint - committedTxIds := make(map[transactions.Txid]ledgercore.IncludedTransactions) + committedTxIDs := make(map[transactions.Txid]ledgercore.IncludedTransactions) for i := 0; i < blockTxnCount; i++ { knownCommitted++ // OK to use empty IncludedTransactions: recomputeBlockEvaluator is only checking map membership - committedTxIds[signedTransactions[i].ID()] = ledgercore.IncludedTransactions{} + committedTxIDs[signedTransactions[i].ID()] = ledgercore.IncludedTransactions{} } - b.Logf("Made transactionPool with %d signedTransactions, %d committedTxIds, %d knownCommitted", - len(signedTransactions), len(committedTxIds), knownCommitted) + b.Logf("Made transactionPool with %d signedTransactions, %d committedTxIDs, %d knownCommitted", + len(signedTransactions), len(committedTxIDs), knownCommitted) b.Logf("transactionPool pendingTxGroups %d rememberedTxGroups %d", len(transactionPool.pendingTxGroups), len(transactionPool.rememberedTxGroups)) - return transactionPool, committedTxIds, knownCommitted + return transactionPool, committedTxIDs, knownCommitted } transactionPool := make([]*TransactionPool, b.N) - committedTxIds := make([]map[transactions.Txid]ledgercore.IncludedTransactions, b.N) + committedTxIDs := make([]map[transactions.Txid]ledgercore.IncludedTransactions, b.N) knownCommitted := make([]uint, b.N) for i := 0; i < b.N; i++ { - transactionPool[i], committedTxIds[i], knownCommitted[i] = setupPool() + transactionPool[i], committedTxIDs[i], knownCommitted[i] = setupPool() } time.Sleep(time.Second) runtime.GC() @@ -1198,7 +1202,7 @@ func BenchmarkTransactionPoolRecompute(b *testing.B) { } b.ResetTimer() for i := 0; i < b.N; i++ { - transactionPool[i].recomputeBlockEvaluator(committedTxIds[i], knownCommitted[i]) + transactionPool[i].recomputeBlockEvaluator(committedTxIDs[i], knownCommitted[i]) } b.StopTimer() if profF != nil { @@ -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..bb5992ae44 100644 --- a/data/transactions/logic/opcodes.go +++ b/data/transactions/logic/opcodes.go @@ -17,6 +17,7 @@ package logic import ( + "cmp" "fmt" "strconv" "strings" @@ -71,11 +72,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 @@ -834,8 +837,8 @@ func OpcodesByVersion(version uint64) []OpSpec { } } result := maps.Values(subv) - slices.SortFunc(result, func(a, b OpSpec) bool { - return a.Opcode < b.Opcode + slices.SortFunc(result, func(a, b OpSpec) int { + return cmp.Compare(a.Opcode, b.Opcode) }) return result } diff --git a/data/transactions/payment.go b/data/transactions/payment.go index 62eafdb1e9..9a11730fc3 100644 --- a/data/transactions/payment.go +++ b/data/transactions/payment.go @@ -37,17 +37,21 @@ type PaymentTxnFields struct { CloseRemainderTo basics.Address `codec:"close"` } +// checkSpender performs some stateless checks on the Sender of a pay transaction func (payment PaymentTxnFields) checkSpender(header Header, spec SpecialAddresses, proto config.ConsensusParams) error { if header.Sender == payment.CloseRemainderTo { return fmt.Errorf("transaction cannot close account to its sender %v", header.Sender) } - // 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/payment_test.go b/data/transactions/payment_test.go index e2583c429c..ae9ecfab64 100644 --- a/data/transactions/payment_test.go +++ b/data/transactions/payment_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/algorand/go-algorand/config" "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/protocol" @@ -68,3 +69,66 @@ func TestAlgosEncoding(t *testing.T) { panic("decode of bool into MicroAlgos succeeded") } } + +func TestCheckSpender(t *testing.T) { + partitiontest.PartitionTest(t) + + v7 := config.Consensus[protocol.ConsensusV7] + v39 := config.Consensus[protocol.ConsensusV39] + vFuture := config.Consensus[protocol.ConsensusFuture] + + secretSrc := keypair() + src := basics.Address(secretSrc.SignatureVerifier) + + secretDst := keypair() + dst := basics.Address(secretDst.SignatureVerifier) + + tx := Transaction{ + Type: protocol.PaymentTx, + Header: Header{ + Sender: src, + Fee: basics.MicroAlgos{Raw: 1}, + FirstValid: basics.Round(100), + LastValid: basics.Round(1000), + }, + PaymentTxnFields: PaymentTxnFields{ + Receiver: dst, + Amount: basics.MicroAlgos{Raw: uint64(50)}, + }, + } + + feeSink := basics.Address{0x01} + poolAddr := basics.Address{0x02} + var spec = SpecialAddresses{ + FeeSink: feeSink, + RewardsPool: poolAddr, + } + + 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") + + tx.Receiver = poolAddr + require.NoError(t, tx.PaymentTxnFields.checkSpender(tx.Header, spec, v7)) + require.NoError(t, tx.PaymentTxnFields.checkSpender(tx.Header, spec, v39)) + require.ErrorContains(t, tx.PaymentTxnFields.checkSpender(tx.Header, spec, vFuture), + "cannot spend from fee sink") + + tx.CloseRemainderTo = poolAddr + require.ErrorContains(t, tx.PaymentTxnFields.checkSpender(tx.Header, spec, 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, 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)) +} diff --git a/data/transactions/transaction.go b/data/transactions/transaction.go index b0bb7c413b..06ae38c0d6 100644 --- a/data/transactions/transaction.go +++ b/data/transactions/transaction.go @@ -365,8 +365,8 @@ func (tx Transaction) WellFormed(spec SpecialAddresses, proto config.ConsensusPa } // The trio of [VotePK, SelectionPK, VoteKeyDilution] needs to be all zeros or all non-zero for the transaction to be valid. - if !((tx.KeyregTxnFields.VotePK == crypto.OneTimeSignatureVerifier{} && tx.KeyregTxnFields.SelectionPK == crypto.VRFVerifier{} && tx.KeyregTxnFields.VoteKeyDilution == 0) || - (tx.KeyregTxnFields.VotePK != crypto.OneTimeSignatureVerifier{} && tx.KeyregTxnFields.SelectionPK != crypto.VRFVerifier{} && tx.KeyregTxnFields.VoteKeyDilution != 0)) { + if !((tx.KeyregTxnFields.VotePK.IsEmpty() && tx.KeyregTxnFields.SelectionPK.IsEmpty() && tx.KeyregTxnFields.VoteKeyDilution == 0) || + (!tx.KeyregTxnFields.VotePK.IsEmpty() && !tx.KeyregTxnFields.SelectionPK.IsEmpty() && tx.KeyregTxnFields.VoteKeyDilution != 0)) { return errKeyregTxnNonCoherentVotingKeys } @@ -395,7 +395,7 @@ func (tx Transaction) WellFormed(spec SpecialAddresses, proto config.ConsensusPa // that type of transaction, it is invalid. return errKeyregTxnUnsupportedSwitchToNonParticipating } - suppliesNullKeys := tx.KeyregTxnFields.VotePK == crypto.OneTimeSignatureVerifier{} || tx.KeyregTxnFields.SelectionPK == crypto.VRFVerifier{} + suppliesNullKeys := tx.KeyregTxnFields.VotePK.IsEmpty() || tx.KeyregTxnFields.SelectionPK.IsEmpty() if !suppliesNullKeys { return errKeyregTxnGoingOnlineWithNonParticipating } @@ -673,7 +673,7 @@ func (tx Transaction) stateProofPKWellFormed(proto config.ConsensusParams) error return nil } - if tx.VotePK == (crypto.OneTimeSignatureVerifier{}) || tx.SelectionPK == (crypto.VRFVerifier{}) { + if tx.VotePK.IsEmpty() || tx.SelectionPK.IsEmpty() { if !isEmpty { return errKeyregTxnOfflineShouldBeEmptyStateProofPK } diff --git a/data/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/transactions/verify/txn.go b/data/transactions/verify/txn.go index 640189cc57..a345c679af 100644 --- a/data/transactions/verify/txn.go +++ b/data/transactions/verify/txn.go @@ -163,7 +163,7 @@ func (g *GroupContext) Equal(other *GroupContext) bool { // txnBatchPrep verifies a SignedTxn having no obviously inconsistent data. // Block-assembly time checks of LogicSig and accounting rules may still block the txn. // It is the caller responsibility to call batchVerifier.Verify(). -func txnBatchPrep(gi int, groupCtx *GroupContext, verifier *crypto.BatchVerifier) *TxGroupError { +func txnBatchPrep(gi int, groupCtx *GroupContext, verifier crypto.BatchVerifier) *TxGroupError { s := &groupCtx.signedGroupTxns[gi] if !groupCtx.consensusParams.SupportRekeying && (s.AuthAddr != basics.Address{}) { return &TxGroupError{err: errRekeyingNotSupported, GroupIndex: gi, Reason: TxGroupErrorReasonGeneric} @@ -206,7 +206,7 @@ func txnGroup(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader // txnGroupBatchPrep verifies a []SignedTxn having no obviously inconsistent data. // it is the caller responsibility to call batchVerifier.Verify() -func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader, ledger logic.LedgerForSignature, verifier *crypto.BatchVerifier, evalTracer logic.EvalTracer) (*GroupContext, error) { +func txnGroupBatchPrep(stxs []transactions.SignedTxn, contextHdr *bookkeeping.BlockHeader, ledger logic.LedgerForSignature, verifier crypto.BatchVerifier, evalTracer logic.EvalTracer) (*GroupContext, error) { groupCtx, err := PrepareGroupContext(stxs, contextHdr, ledger, evalTracer) if err != nil { return nil, err @@ -287,7 +287,7 @@ func checkTxnSigTypeCounts(s *transactions.SignedTxn, groupIndex int) (sigType s } // stxnCoreChecks runs signatures validity checks and enqueues signature into batchVerifier for verification. -func stxnCoreChecks(gi int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier) *TxGroupError { +func stxnCoreChecks(gi int, groupCtx *GroupContext, batchVerifier crypto.BatchVerifier) *TxGroupError { s := &groupCtx.signedGroupTxns[gi] sigType, err := checkTxnSigTypeCounts(s, gi) if err != nil { @@ -340,7 +340,7 @@ func LogicSigSanityCheck(gi int, groupCtx *GroupContext) error { // logicSigSanityCheckBatchPrep checks that the signature is valid and that the program is basically well formed. // It does not evaluate the logic. // it is the caller responsibility to call batchVerifier.Verify() -func logicSigSanityCheckBatchPrep(gi int, groupCtx *GroupContext, batchVerifier *crypto.BatchVerifier) error { +func logicSigSanityCheckBatchPrep(gi int, groupCtx *GroupContext, batchVerifier crypto.BatchVerifier) error { if groupCtx.consensusParams.LogicSigVersion == 0 { return errors.New("LogicSig not enabled") } diff --git a/data/transactions/verify/txnBatch.go b/data/transactions/verify/txnBatch.go index 4b309f6946..8619208da8 100644 --- a/data/transactions/verify/txnBatch.go +++ b/data/transactions/verify/txnBatch.go @@ -170,7 +170,7 @@ func (tbp *txnSigBatchProcessor) ProcessBatch(txns []execpool.InputJob) { tbp.postProcessVerifiedJobs(ctx, failed, err) } -func (tbp *txnSigBatchProcessor) preProcessUnverifiedTxns(uTxns []execpool.InputJob) (batchVerifier *crypto.BatchVerifier, ctx interface{}) { +func (tbp *txnSigBatchProcessor) preProcessUnverifiedTxns(uTxns []execpool.InputJob) (batchVerifier crypto.BatchVerifier, ctx interface{}) { batchVerifier = crypto.MakeBatchVerifier() bl := makeBatchLoad(len(uTxns)) // TODO: separate operations here, and get the sig verification inside the LogicSig to the batch here 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/docker/files/run/run.sh b/docker/files/run/run.sh index 29b69423dc..d92e7e1d1f 100755 --- a/docker/files/run/run.sh +++ b/docker/files/run/run.sh @@ -17,7 +17,7 @@ fi # as the algorand user. if [ "$(id -u)" = '0' ]; then chown -R algorand:algorand $ALGORAND_DATA - exec gosu algorand "$0" "$@" + exec su -p -c "$(readlink -f $0) $@" algorand fi # Script to configure or resume a network. Based on environment settings the diff --git a/go.mod b/go.mod index 0d85043883..c15195b54b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/algorand/go-algorand -go 1.20 +go 1.21 + +toolchain go1.21.10 require ( github.com/DataDog/zstd v1.5.2 @@ -23,36 +25,34 @@ require ( github.com/getkin/kin-openapi v0.107.0 github.com/gofrs/flock v0.7.0 github.com/golang/snappy v0.0.4 - github.com/google/go-cmp v0.5.9 + github.com/google/go-cmp v0.6.0 github.com/google/go-querystring v1.0.0 github.com/gorilla/mux v1.8.0 github.com/jmoiron/sqlx v1.2.0 github.com/karalabe/usb v0.0.2 github.com/labstack/echo/v4 v4.9.1 - github.com/libp2p/go-libp2p v0.29.1 - github.com/libp2p/go-libp2p-pubsub v0.9.3 + github.com/libp2p/go-libp2p v0.33.2 + github.com/libp2p/go-libp2p-pubsub v0.10.0 github.com/libp2p/go-yamux/v4 v4.0.1 github.com/mattn/go-sqlite3 v1.14.16 - github.com/miekg/dns v1.1.55 - github.com/muesli/termenv v0.15.2 - github.com/multiformats/go-multiaddr v0.10.1 + github.com/miekg/dns v1.1.58 + github.com/multiformats/go-multiaddr v0.12.3 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/olivere/elastic v6.2.14+incompatible github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.14.0 - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 - golang.org/x/sync v0.3.0 - golang.org/x/sys v0.13.0 - golang.org/x/text v0.13.0 + golang.org/x/crypto v0.19.0 + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a + golang.org/x/sync v0.6.0 + golang.org/x/sys v0.17.0 + golang.org/x/text v0.14.0 gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 pgregory.net/rapid v0.6.2 ) require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect @@ -70,7 +70,7 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect - github.com/flynn/noise v1.0.0 // indirect + github.com/flynn/noise v1.1.0 // indirect github.com/fortytw2/leaktest v1.3.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect @@ -79,14 +79,12 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect - github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect - github.com/huin/goupnp v1.2.0 // indirect + github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect + github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/invopop/yaml v0.1.0 // indirect github.com/ipfs/go-cid v0.4.1 // indirect @@ -95,27 +93,23 @@ require ( github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jmespath/go-jmespath v0.3.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/compress v1.17.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/koron/go-ssdp v0.0.4 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect - github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-nat v0.2.0 // indirect github.com/libp2p/go-netroute v0.2.1 // indirect - github.com/libp2p/go-reuseport v0.3.0 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -128,43 +122,40 @@ require ( github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multicodec v0.9.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect - github.com/multiformats/go-multistream v0.4.1 // indirect + github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect - github.com/onsi/ginkgo/v2 v2.11.0 // indirect - github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/onsi/ginkgo/v2 v2.15.0 // indirect + github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.47.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-19 v0.3.3 // indirect - github.com/quic-go/qtls-go1-20 v0.2.3 // indirect - github.com/quic-go/quic-go v0.36.3 // indirect - github.com/quic-go/webtransport-go v0.5.3 // indirect + github.com/quic-go/quic-go v0.42.0 // indirect + github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect - go.uber.org/atomic v1.11.0 // indirect - go.uber.org/dig v1.17.0 // indirect - go.uber.org/fx v1.20.0 // indirect + go.uber.org/dig v1.17.1 // indirect + go.uber.org/fx v1.20.1 // indirect + go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.24.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect - golang.org/x/tools v0.11.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/term v0.17.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.18.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect diff --git a/go.sum b/go.sum index f8d0ac3a04..badf55816d 100644 --- a/go.sum +++ b/go.sum @@ -2,45 +2,13 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= @@ -50,11 +18,6 @@ github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4K github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/algorand/avm-abi v0.2.0 h1:bkjsG+BOEcxUcnGSALLosmltE0JZdg+ZisXKx0UDX2k= github.com/algorand/avm-abi v0.2.0/go.mod h1:+CgwM46dithy850bpTeHh9MC99zpn2Snirb3QTl2O/g= github.com/algorand/falcon v0.1.0 h1:xl832kfZ7hHG6B4p90DQynjfKFGbIUgUOnsRiMZXfAo= @@ -81,15 +44,12 @@ github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.34.0 h1:brux2dRrlwCF5JhTL7MUT3WUwo9zfDHZZp3+g3Mvlmo= github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= @@ -98,19 +58,16 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e h1:CHPYEbz71w8DqJ7DRIq+MXyCQsdibK08vdcQTY4ufas= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e/go.mod h1:6Xhs0ZlsRjXLIiSMLKafbZxML/j30pg9Z1priLuha5s= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= @@ -153,6 +110,7 @@ github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6Uh github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -167,7 +125,6 @@ github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0 github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= @@ -177,8 +134,8 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= -github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= +github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= @@ -195,18 +152,8 @@ github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -215,7 +162,6 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -237,98 +183,62 @@ github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo= +github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= -github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= +github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= -github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -336,8 +246,6 @@ github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= -github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= -github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= @@ -355,20 +263,13 @@ github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/karalabe/usb v0.0.2 h1:M6QQBNxF+CQ8OFvxrT90BA0qBOXymndZnk5q235mFc4= github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= @@ -381,16 +282,13 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -407,33 +305,31 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= -github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.29.1 h1:yNeg6XgP8gbdc4YSrwiIt5T1TGOrVjH8dzl8h0GIOfQ= -github.com/libp2p/go-libp2p v0.29.1/go.mod h1:20El+LLy3/YhdUYIvGbLnvVJN32nMdqY6KXBENRAfLY= -github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= -github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= -github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo= -github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc= +github.com/libp2p/go-libp2p v0.33.2 h1:vCdwnFxoGOXMKmaGHlDSnL4bM3fQeW8pgIa9DECnb40= +github.com/libp2p/go-libp2p v0.33.2/go.mod h1:zTeppLuCvUIkT118pFVzA8xzP/p2dJYOMApCkFh0Yww= +github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= +github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= +github.com/libp2p/go-libp2p-pubsub v0.10.0 h1:wS0S5FlISavMaAbxyQn3dxMOe2eegMfswM471RuHJwA= +github.com/libp2p/go-libp2p-pubsub v0.10.0/go.mod h1:1OxbaT/pFRO5h+Dpze8hdHQ63R0ke55XTs6b6NwLLkw= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= -github.com/libp2p/go-reuseport v0.3.0 h1:iiZslO5byUYZEg9iCwJGf5h+sf1Agmqx2V2FDjPyvUw= -github.com/libp2p/go-reuseport v0.3.0/go.mod h1:laea40AimhtfEqysZ71UpYj4S+R9VpH8PgqLo7L+SwI= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -453,24 +349,20 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -486,27 +378,22 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= -github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.10.1 h1:HghtFrWyZEPrpTvgAMFJi6gFdgHfs2cb0pyfDsk+lqU= -github.com/multiformats/go-multiaddr v0.10.1/go.mod h1:jLEZsA61rwWNZQTHHnqq2HNa+4os/Hz54eqiRnsRqYQ= +github.com/multiformats/go-multiaddr v0.12.3 h1:hVBXvPRcKG0w80VinQ23P5t7czWgg65BmIvQKjDydU8= +github.com/multiformats/go-multiaddr v0.12.3/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= @@ -518,13 +405,11 @@ github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI1 github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= -github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= -github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= +github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE= +github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= @@ -536,13 +421,15 @@ github.com/olivere/elastic v6.2.14+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGe github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= +github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= @@ -552,58 +439,35 @@ github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCr github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k= +github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE= -github.com/quic-go/qtls-go1-19 v0.3.3/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.2.3 h1:m575dovXn1y2ATOb1XrRFcrv0F+EQmlowTkoraNkDPI= -github.com/quic-go/qtls-go1-20 v0.2.3/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/quic-go/quic-go v0.36.3 h1:f+yOqeGhMoRX7/M3wmEw/djhzKWr15FtQysox85/834= -github.com/quic-go/quic-go v0.36.3/go.mod h1:qxQumdeKw5GmWs1OsTZZnOxzSI+RJWuhf1O8FN35L2o= -github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= -github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM= +github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= +github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFDD3NxaZLY= +github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -635,9 +499,6 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -659,7 +520,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -695,42 +555,35 @@ github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmv github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= -go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= -go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= -go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= +go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= +go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -738,51 +591,29 @@ golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -790,48 +621,23 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -839,98 +645,61 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -939,51 +708,17 @@ golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= -golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -991,101 +726,38 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= @@ -1097,7 +769,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -1111,18 +782,11 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJd honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= pgregory.net/rapid v0.6.2 h1:ErW5sL+UKtfBfUTsWHDCoeB+eZKLKMxrSd1VJY6W4bw= pgregory.net/rapid v0.6.2/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= 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..b16657dc64 100644 --- a/ledger/acctdeltas_test.go +++ b/ledger/acctdeltas_test.go @@ -508,10 +508,10 @@ func randomCreatables(numElementsPerSegement int) ([]basics.CreatableIndex, map[basics.CreatableIndex]ledgercore.ModifiedCreatable) { creatables := make(map[basics.CreatableIndex]ledgercore.ModifiedCreatable) creatablesList := make([]basics.CreatableIndex, numElementsPerSegement*10) - uniqueAssetIds := make(map[basics.CreatableIndex]bool) + uniqueAssetIDs := make(map[basics.CreatableIndex]bool) for i := 0; i < numElementsPerSegement*10; i++ { - assetIndex, mc := randomCreatable(uniqueAssetIds) + assetIndex, mc := randomCreatable(uniqueAssetIDs) creatables[assetIndex] = mc creatablesList[i] = assetIndex } @@ -519,7 +519,7 @@ func randomCreatables(numElementsPerSegement int) ([]basics.CreatableIndex, } // randomCreatable generates a random creatable. -func randomCreatable(uniqueAssetIds map[basics.CreatableIndex]bool) ( +func randomCreatable(uniqueAssetIDs map[basics.CreatableIndex]bool) ( assetIndex basics.CreatableIndex, mc ledgercore.ModifiedCreatable) { var ctype basics.CreatableType @@ -541,9 +541,9 @@ func randomCreatable(uniqueAssetIds map[basics.CreatableIndex]bool) ( var assetIdx basics.CreatableIndex for { assetIdx = basics.CreatableIndex(crypto.RandUint64() % (uint64(2) << 50)) - _, found := uniqueAssetIds[assetIdx] + _, found := uniqueAssetIDs[assetIdx] if !found { - uniqueAssetIds[assetIdx] = true + uniqueAssetIDs[assetIdx] = true break } } @@ -2233,7 +2233,7 @@ func TestAccountOnlineQueries(t *testing.T) { MicroAlgos: basics.MicroAlgos{Raw: 100_000_000}, Status: basics.Online, }, - VotingData: ledgercore.VotingData{ + VotingData: basics.VotingData{ VoteID: voteIDA, }, } @@ -2243,7 +2243,7 @@ func TestAccountOnlineQueries(t *testing.T) { MicroAlgos: basics.MicroAlgos{Raw: 200_000_000}, Status: basics.Online, }, - VotingData: ledgercore.VotingData{ + VotingData: basics.VotingData{ VoteID: voteIDB, }, } @@ -2253,7 +2253,7 @@ func TestAccountOnlineQueries(t *testing.T) { MicroAlgos: basics.MicroAlgos{Raw: 300_000_000}, Status: basics.Online, }, - VotingData: ledgercore.VotingData{ + VotingData: basics.VotingData{ VoteID: voteIDC, }, } @@ -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..667255fb51 100644 --- a/ledger/acctonline.go +++ b/ledger/acctonline.go @@ -618,20 +618,7 @@ func (ao *onlineAccounts) onlineTotals(rnd basics.Round) (basics.MicroAlgos, pro // LookupOnlineAccountData returns the online account data for a given address at a given round. func (ao *onlineAccounts) LookupOnlineAccountData(rnd basics.Round, addr basics.Address) (data basics.OnlineAccountData, err error) { - oad, err := ao.lookupOnlineAccountData(rnd, addr) - if err != nil { - return - } - - data.MicroAlgosWithRewards = oad.MicroAlgosWithRewards - data.VotingData.VoteID = oad.VotingData.VoteID - data.VotingData.SelectionID = oad.VotingData.SelectionID - data.VotingData.StateProofID = oad.VotingData.StateProofID - data.VotingData.VoteFirstValid = oad.VotingData.VoteFirstValid - data.VotingData.VoteLastValid = oad.VotingData.VoteLastValid - data.VotingData.VoteKeyDilution = oad.VotingData.VoteKeyDilution - - return + return ao.lookupOnlineAccountData(rnd, addr) } // roundOffset calculates the offset of the given round compared to the current dbRound. Requires that the lock would be taken. @@ -675,7 +662,7 @@ func (ao *onlineAccounts) roundParamsOffset(rnd basics.Round) (offset uint64, er } // lookupOnlineAccountData returns the online account data for a given address at a given round. -func (ao *onlineAccounts) lookupOnlineAccountData(rnd basics.Round, addr basics.Address) (ledgercore.OnlineAccountData, error) { +func (ao *onlineAccounts) lookupOnlineAccountData(rnd basics.Round, addr basics.Address) (basics.OnlineAccountData, error) { needUnlock := false defer func() { if needUnlock { @@ -703,14 +690,14 @@ func (ao *onlineAccounts) lookupOnlineAccountData(rnd basics.Round, addr basics. if err != nil { var roundOffsetError *RoundOffsetError if !errors.As(err, &roundOffsetError) { - return ledgercore.OnlineAccountData{}, err + return basics.OnlineAccountData{}, err } // the round number cannot be found in deltas, it is in history inHistory = true } paramsOffset, err = ao.roundParamsOffset(rnd) if err != nil { - return ledgercore.OnlineAccountData{}, err + return basics.OnlineAccountData{}, err } rewardsProto = config.Consensus[ao.onlineRoundParamsData[paramsOffset].CurrentProtocol] @@ -754,7 +741,7 @@ func (ao *onlineAccounts) lookupOnlineAccountData(rnd basics.Round, addr basics. persistedData, err = ao.accountsq.LookupOnline(addr, rnd) if err != nil || persistedData.Ref == nil { // no such online account, return empty - return ledgercore.OnlineAccountData{}, err + return basics.OnlineAccountData{}, err } // Now we load the entire history of this account to fill the onlineAccountsCache, so that the // next lookup for this online account will not hit the on-disk DB. @@ -769,7 +756,7 @@ func (ao *onlineAccounts) lookupOnlineAccountData(rnd basics.Round, addr basics. // 3. after postCommit => OK, postCommit does not add new entry with writeFrontIfExist, but here all the full history is loaded persistedDataHistory, validThrough, err := ao.accountsq.LookupOnlineHistory(addr) if err != nil || len(persistedDataHistory) == 0 { - return ledgercore.OnlineAccountData{}, err + return basics.OnlineAccountData{}, err } // 3. After we finished reading the history (lookupOnlineHistory), either // 1. The DB round has not advanced (validThrough == currentDbRound) => OK @@ -796,7 +783,7 @@ func (ao *onlineAccounts) lookupOnlineAccountData(rnd basics.Round, addr basics. if !written { ao.accountsMu.Unlock() err = fmt.Errorf("failed to write history of acct %s for round %d into online accounts cache", data.Addr.String(), data.UpdRound) - return ledgercore.OnlineAccountData{}, err + return basics.OnlineAccountData{}, err } } ao.log.Info("inserted new item to onlineAccountsCache") @@ -809,7 +796,7 @@ func (ao *onlineAccounts) lookupOnlineAccountData(rnd basics.Round, addr basics. if validThrough < currentDbRound { ao.log.Errorf("onlineAccounts.lookupOnlineAccountData: database round %d is behind in-memory round %d", validThrough, currentDbRound) - return ledgercore.OnlineAccountData{}, &StaleDatabaseRoundError{databaseRound: validThrough, memoryRound: currentDbRound} + return basics.OnlineAccountData{}, &StaleDatabaseRoundError{databaseRound: validThrough, memoryRound: currentDbRound} } } } @@ -1009,7 +996,7 @@ func (ao *onlineAccounts) TopOnlineAccounts(rnd basics.Round, voteRnd basics.Rou } } -func (ao *onlineAccounts) onlineAcctsExpiredByRound(rnd, voteRnd basics.Round) (map[basics.Address]*ledgercore.OnlineAccountData, error) { +func (ao *onlineAccounts) onlineAcctsExpiredByRound(rnd, voteRnd basics.Round) (map[basics.Address]*basics.OnlineAccountData, error) { needUnlock := false defer func() { if needUnlock { @@ -1017,7 +1004,7 @@ func (ao *onlineAccounts) onlineAcctsExpiredByRound(rnd, voteRnd basics.Round) ( } }() - var expiredAccounts map[basics.Address]*ledgercore.OnlineAccountData + var expiredAccounts map[basics.Address]*basics.OnlineAccountData ao.accountsMu.RLock() needUnlock = true for { 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/acctonline_test.go b/ledger/acctonline_test.go index 5a2b2ca1ac..d60353e2ae 100644 --- a/ledger/acctonline_test.go +++ b/ledger/acctonline_test.go @@ -207,7 +207,7 @@ func TestAcctOnline(t *testing.T) { var updates ledgercore.AccountDeltas acctIdx := int(i) - 1 - updates.Upsert(allAccts[acctIdx].Addr, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}) + updates.Upsert(allAccts[acctIdx].Addr, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}) base := genesisAccts[i-1] newAccts := applyPartialDeltas(base, updates) @@ -489,13 +489,13 @@ func TestAcctOnlineCache(t *testing.T) { // put all accts online, then all offline, one each round if (int(i)-1)%(numAccts*2) >= numAccts { - updates.Upsert(allAccts[acctIdx].Addr, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}) + updates.Upsert(allAccts[acctIdx].Addr, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}) } else { updates.Upsert(allAccts[acctIdx].Addr, ledgercore.ToAccountData(allAccts[acctIdx].AccountData)) } // set acctA online for each round - updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: ledgercore.VotingData{VoteLastValid: basics.Round(100 * i)}}) + updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: basics.VotingData{VoteLastValid: basics.Round(100 * i)}}) base := genesisAccts[i-1] newAccts := applyPartialDeltas(base, updates) @@ -887,7 +887,7 @@ func TestAcctOnlineCacheDBSync(t *testing.T) { require.NoError(t, err) var updates ledgercore.AccountDeltas - updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}) + updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}) // copy genesisAccts for the test accounts := copyGenesisAccts() @@ -967,7 +967,7 @@ func TestAcctOnlineCacheDBSync(t *testing.T) { require.NoError(t, err) var updates ledgercore.AccountDeltas - updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}) + updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}) // copy genesisAccts for the test accounts := copyGenesisAccts() @@ -1016,8 +1016,8 @@ func TestAcctOnlineCacheDBSync(t *testing.T) { addrB := ledgertesting.RandomAddress() var updates ledgercore.AccountDeltas - updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}) - updates.Upsert(addrB, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: ledgercore.VotingData{VoteLastValid: 10000}}) + updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}) + updates.Upsert(addrB, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: basics.VotingData{VoteLastValid: 10000}}) // copy genesisAccts for the test accounts := copyGenesisAccts() @@ -1174,9 +1174,9 @@ func TestAcctOnlineBaseAccountCache(t *testing.T) { accounts := genesisAccts acctDatas := [3]ledgercore.AccountData{ - {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: ledgercore.VotingData{VoteLastValid: basics.Round(1000 + maxBalLookback)}}, - {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}, - {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: ledgercore.VotingData{VoteLastValid: basics.Round(1000 + maxBalLookback)}}, + {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: basics.VotingData{VoteLastValid: basics.Round(1000 + maxBalLookback)}}, + {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}, + {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: basics.VotingData{VoteLastValid: basics.Round(1000 + maxBalLookback)}}, } // set online, offline, online for i := 1; i <= 3; i++ { @@ -1276,7 +1276,7 @@ func TestAcctOnlineVotersLongerHistory(t *testing.T) { maxBlocks := maxBalLookback * 5 for i := 1; i <= maxBlocks; i++ { var updates ledgercore.AccountDeltas - updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: ledgercore.VotingData{VoteLastValid: basics.Round(100 * i)}}) + updates.Upsert(addrA, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online}, VotingData: basics.VotingData{VoteLastValid: basics.Round(100 * i)}}) base := genesisAccts[i-1] newAccts := applyPartialDeltas(base, updates) totals = newBlock(t, ml, testProtocolVersion, protoParams, basics.Round(i), base, updates, totals) @@ -1438,7 +1438,7 @@ func TestAcctOnlineTop(t *testing.T) { var updates ledgercore.AccountDeltas ac := allAccts[numAccts-3] updates.Upsert(ac.Addr, ledgercore.AccountData{ - AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline, MicroAlgos: ac.MicroAlgos}, VotingData: ledgercore.VotingData{}}) + AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline, MicroAlgos: ac.MicroAlgos}, VotingData: basics.VotingData{}}) totals = newBlockWithUpdates(genesisAccts, updates, totals, t, ml, 1, oa) accountToBeUpdated := ac accountToBeUpdated.Status = basics.Offline @@ -1452,7 +1452,7 @@ func TestAcctOnlineTop(t *testing.T) { updates = ledgercore.AccountDeltas{} updates.Upsert(allAccts[numAccts-2].Addr, ledgercore.AccountData{ AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: allAccts[numAccts-2].MicroAlgos}, - VotingData: ledgercore.VotingData{ + VotingData: basics.VotingData{ VoteFirstValid: 0, VoteLastValid: 1, }}) @@ -1471,7 +1471,7 @@ func TestAcctOnlineTop(t *testing.T) { // mark an account with high stake as online - it should be pushed to the top of the list updates.Upsert(allAccts[numAccts-1].Addr, ledgercore.AccountData{ AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: allAccts[numAccts-1].MicroAlgos}, - VotingData: ledgercore.VotingData{VoteLastValid: basics.Round(1000)}}) + VotingData: basics.VotingData{VoteLastValid: basics.Round(1000)}}) totals = newBlockWithUpdates(genesisAccts, updates, totals, t, ml, 3, oa) accountToBeUpdated = allAccts[numAccts-1] accountToBeUpdated.Status = basics.Online @@ -1549,7 +1549,7 @@ func TestAcctOnlineTopInBatches(t *testing.T) { if i == 300 { updates.Upsert(acct299.Addr, ledgercore.AccountData{ AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline, MicroAlgos: acct299.MicroAlgos}, - VotingData: ledgercore.VotingData{}, + VotingData: basics.VotingData{}, }) } newBlockWithUpdates(genesisAccts, updates, totals, t, ml, i, oa) @@ -1626,7 +1626,7 @@ func TestAcctOnlineTopBetweenCommitAndPostCommit(t *testing.T) { for ; i < 10; i++ { var updates ledgercore.AccountDeltas updates.Upsert(allAccts[numAccts-1].Addr, ledgercore.AccountData{ - AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}) + AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}) newBlockWithUpdates(genesisAccts, updates, totals, t, ml, i, oa) } @@ -1635,7 +1635,7 @@ func TestAcctOnlineTopBetweenCommitAndPostCommit(t *testing.T) { updateAccountsRoutine := func() { var updates ledgercore.AccountDeltas updates.Upsert(allAccts[numAccts-1].Addr, ledgercore.AccountData{ - AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}) + AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}) newBlockWithUpdates(genesisAccts, updates, totals, t, ml, i, oa) } @@ -1717,7 +1717,7 @@ func TestAcctOnlineTopDBBehindMemRound(t *testing.T) { for ; i < 10; i++ { var updates ledgercore.AccountDeltas updates.Upsert(allAccts[numAccts-1].Addr, ledgercore.AccountData{ - AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}) + AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}) newBlockWithUpdates(genesisAccts, updates, totals, t, ml, i, oa) } @@ -1726,7 +1726,7 @@ func TestAcctOnlineTopDBBehindMemRound(t *testing.T) { updateAccountsRoutine := func() { var updates ledgercore.AccountDeltas updates.Upsert(allAccts[numAccts-1].Addr, ledgercore.AccountData{ - AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: ledgercore.VotingData{}}) + AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline}, VotingData: basics.VotingData{}}) newBlockWithUpdates(genesisAccts, updates, totals, t, ml, i, oa) } @@ -1807,10 +1807,10 @@ func TestAcctOnlineTop_ChangeOnlineStake(t *testing.T) { var updates ledgercore.AccountDeltas if i == 15 { // round 15 should be in deltas (memory) // turn account `i` offline - updates.Upsert(allAccts[i].Addr, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline, MicroAlgos: allAccts[i].MicroAlgos}, VotingData: ledgercore.VotingData{}}) + updates.Upsert(allAccts[i].Addr, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline, MicroAlgos: allAccts[i].MicroAlgos}, VotingData: basics.VotingData{}}) } if i == 18 { - updates.Upsert(allAccts[i].Addr, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: allAccts[i].MicroAlgos}, VotingData: ledgercore.VotingData{VoteLastValid: basics.Round(18)}}) + updates.Upsert(allAccts[i].Addr, ledgercore.AccountData{AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: allAccts[i].MicroAlgos}, VotingData: basics.VotingData{VoteLastValid: basics.Round(18)}}) } // else: insert empty block totals = newBlockWithUpdates(genesisAccts, updates, totals, t, ml, i, oa) } @@ -1969,19 +1969,19 @@ func TestAcctOnline_ExpiredOnlineCirculation(t *testing.T) { addrA := allAccts[0].Addr stakeA := allAccts[0].MicroAlgos statesA := map[acctState]ledgercore.AccountData{ - acctStateOffline: {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline, MicroAlgos: stakeA}, VotingData: ledgercore.VotingData{}}, - acctStateOnline: {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: stakeA}, VotingData: ledgercore.VotingData(allAccts[0].OnlineAccountData().VotingData)}, + acctStateOffline: {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline, MicroAlgos: stakeA}, VotingData: basics.VotingData{}}, + acctStateOnline: {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: stakeA}, VotingData: basics.VotingData(allAccts[0].OnlineAccountData().VotingData)}, } addrB := allAccts[1].Addr stakeB := allAccts[1].MicroAlgos votingDataB := allAccts[1].OnlineAccountData().VotingData statesB := map[acctState]ledgercore.AccountData{ - acctStateOffline: {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline, MicroAlgos: stakeB}, VotingData: ledgercore.VotingData{}}, - acctStateOnline: {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: stakeB}, VotingData: ledgercore.VotingData(votingDataB)}, + acctStateOffline: {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Offline, MicroAlgos: stakeB}, VotingData: basics.VotingData{}}, + acctStateOnline: {AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: stakeB}, VotingData: basics.VotingData(votingDataB)}, } expStatesB := func(state acctState, voteRnd basics.Round) ledgercore.AccountData { - vd := ledgercore.VotingData(votingDataB) + vd := basics.VotingData(votingDataB) switch state { case acctStateExpired: vd.VoteLastValid = voteRnd - 1 @@ -2100,7 +2100,7 @@ func TestAcctOnline_ExpiredOnlineCirculation(t *testing.T) { commitSync(t, oa, ml, basics.Round(rnd-1)) a.Equal(int(conf.MaxAcctLookback), len(oa.deltas)) // ensure the only expected deltas are not flushed - var expiredAccts map[basics.Address]*ledgercore.OnlineAccountData + var expiredAccts map[basics.Address]*basics.OnlineAccountData err = ml.trackers.dbs.Snapshot(func(ctx context.Context, tx trackerdb.SnapshotScope) error { reader, err := tx.MakeAccountsReader() if err != nil { @@ -2158,7 +2158,7 @@ func TestAcctOnline_ExpiredOnlineCirculation(t *testing.T) { base := accounts[rnd-1] updates.Upsert(addrA, statesA[acctStateOnline]) updates.Upsert(addrB, ledgercore.AccountData{ - AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: stakeB}, VotingData: ledgercore.VotingData(votingDataB), + AccountBaseData: ledgercore.AccountBaseData{Status: basics.Online, MicroAlgos: stakeB}, VotingData: basics.VotingData(votingDataB), }) accounts = append(accounts, applyPartialDeltas(base, updates)) totals = newBlock(t, ml, proto, params, rnd, base, updates, totals) diff --git a/ledger/acctupdates.go b/ledger/acctupdates.go index e9c9696711..3f12955666 100644 --- a/ledger/acctupdates.go +++ b/ledger/acctupdates.go @@ -341,6 +341,10 @@ func (au *accountUpdates) LookupResource(rnd basics.Round, addr basics.Address, return au.lookupResource(rnd, addr, aidx, ctype, true /* take lock */) } +func (au *accountUpdates) LookupAssetResources(addr basics.Address, assetIDGT basics.AssetIndex, limit uint64) ([]ledgercore.AssetResourceWithIDs, basics.Round, error) { + return au.lookupAssetResources(addr, assetIDGT, limit) +} + func (au *accountUpdates) LookupKv(rnd basics.Round, key string) ([]byte, error) { return au.lookupKv(rnd, key, true /* take lock */) } @@ -1208,6 +1212,55 @@ func (au *accountUpdates) lookupResource(rnd basics.Round, addr basics.Address, } } +// lookupAllResources returns all the resources for a given address, solely based on what is persisted to disk. It does not +// take into account any in-memory deltas; the round number returned is the latest round number that is known to the database. +func (au *accountUpdates) lookupAssetResources(addr basics.Address, assetIDGT basics.AssetIndex, limit uint64) (data []ledgercore.AssetResourceWithIDs, validThrough basics.Round, err error) { + // Look for resources on disk + persistedResources, resourceDbRound, err0 := au.accountsq.LookupLimitedResources(addr, basics.CreatableIndex(assetIDGT), limit, basics.AssetCreatable) + if err0 != nil { + return nil, basics.Round(0), err0 + } + + data = make([]ledgercore.AssetResourceWithIDs, 0, len(persistedResources)) + for _, pd := range persistedResources { + ah := pd.Data.GetAssetHolding() + + var arwi ledgercore.AssetResourceWithIDs + if !pd.Creator.IsZero() { + ap := pd.Data.GetAssetParams() + + arwi = ledgercore.AssetResourceWithIDs{ + AssetID: basics.AssetIndex(pd.Aidx), + Creator: pd.Creator, + + AssetResource: ledgercore.AssetResource{ + AssetHolding: &ah, + AssetParams: &ap, + }, + } + } else { + arwi = ledgercore.AssetResourceWithIDs{ + AssetID: basics.AssetIndex(pd.Aidx), + + AssetResource: ledgercore.AssetResource{ + AssetHolding: &ah, + }, + } + } + + data = append(data, arwi) + } + // We've found all the resources we could find for this address. + currentDbRound := resourceDbRound + // The resourceDbRound will not be set if there are no persisted resources + if len(data) == 0 { + au.accountsMu.RLock() + currentDbRound = au.cachedDBRound + au.accountsMu.RUnlock() + } + return data, currentDbRound, nil +} + func (au *accountUpdates) lookupStateDelta(rnd basics.Round) (ledgercore.StateDelta, error) { au.accountsMu.RLock() defer au.accountsMu.RUnlock() diff --git a/ledger/acctupdates_test.go b/ledger/acctupdates_test.go index 1cf080d15e..283fbcdc2e 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 { @@ -425,7 +429,7 @@ func checkAcctUpdates(t *testing.T, au *accountUpdates, ao *onlineAccounts, base require.Equal(t, d, ledgercore.AccountData{}) od, err := ao.lookupOnlineAccountData(rnd, ledgertesting.RandomAddress()) require.NoError(t, err) - require.Equal(t, od, ledgercore.OnlineAccountData{}) + require.Equal(t, od, basics.OnlineAccountData{}) } } checkAcctUpdatesConsistency(t, au, latestRnd) @@ -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..e7d23721b6 100644 --- a/ledger/apply/payment_test.go +++ b/ledger/apply/payment_test.go @@ -104,51 +104,10 @@ 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) - - secretSrc := keypair() - src := basics.Address(secretSrc.SignatureVerifier) - - secretDst := keypair() - dst := basics.Address(secretDst.SignatureVerifier) - - tx := transactions.Transaction{ - Type: protocol.PaymentTx, - Header: transactions.Header{ - Sender: src, - Fee: basics.MicroAlgos{Raw: 1}, - FirstValid: basics.Round(100), - LastValid: basics.Round(1000), - }, - PaymentTxnFields: transactions.PaymentTxnFields{ - Receiver: dst, - Amount: basics.MicroAlgos{Raw: uint64(50)}, - }, - } - - tx.Sender = basics.Address(feeSink) - require.Error(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV0.ConsensusParams())) - - poolAddr := basics.Address(poolAddr) - tx.Receiver = poolAddr - require.NoError(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV0.ConsensusParams())) - - tx.CloseRemainderTo = poolAddr - require.Error(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV0.ConsensusParams())) - require.Error(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV7.ConsensusParams())) - - tx.Sender = src - require.NoError(t, checkSpender(tx.PaymentTxnFields, tx.Header, spec, mockBalV7.ConsensusParams())) -} - func TestPaymentValidation(t *testing.T) { partitiontest.PartitionTest(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/catchpointfilewriter_test.go b/ledger/catchpointfilewriter_test.go index 71a0737fb6..499adeedc8 100644 --- a/ledger/catchpointfilewriter_test.go +++ b/ledger/catchpointfilewriter_test.go @@ -29,6 +29,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/stretchr/testify/require" @@ -685,7 +686,9 @@ func testNewLedgerFromCatchpoint(t *testing.T, catchpointWriterReadAccess tracke var initState ledgercore.InitState initState.Block.CurrentProtocol = protocol.ConsensusCurrentVersion conf := config.GetDefaultLocal() - l, err := OpenLedger(logging.TestingLog(t), t.Name()+"FromCatchpoint", true, initState, conf) + dbName := fmt.Sprintf("%s.%d", t.Name()+"FromCatchpoint", crypto.RandUint64()) + dbName = strings.Replace(dbName, "/", "_", -1) + l, err := OpenLedger(logging.TestingLog(t), dbName, true, initState, conf) require.NoError(t, err) accessor := MakeCatchpointCatchupAccessor(l, l.log) @@ -702,6 +705,8 @@ func testNewLedgerFromCatchpoint(t *testing.T, catchpointWriterReadAccess tracke err = accessor.BuildMerkleTrie(context.Background(), nil) require.NoError(t, err) + resetAccountDBToV6(t, l) + err = l.trackerDBs.Transaction(func(ctx context.Context, tx trackerdb.TransactionScope) error { cw, err := tx.MakeCatchpointWriter() if err != nil { @@ -816,6 +821,33 @@ func TestExactAccountChunk(t *testing.T) { dl.fullBlock(&selfpay) } + // ensure both committed all pending changes before taking a catchpoint + // another approach is to modify the test and craft round numbers, + // and make the ledger to generate catchpoint itself when it is time + flushRound := func(l *Ledger) { + // Clear the timer to ensure a flush + l.trackers.mu.Lock() + l.trackers.lastFlushTime = time.Time{} + l.trackers.mu.Unlock() + + r, _ := l.LatestCommitted() + l.trackers.committedUpTo(r) + l.trackers.waitAccountsWriting() + } + flushRound(dl.generator) + flushRound(dl.validator) + + require.Eventually(t, func() bool { + dl.generator.accts.accountsMu.RLock() + dlg := len(dl.generator.accts.deltas) + dl.generator.accts.accountsMu.RUnlock() + + dl.validator.accts.accountsMu.RLock() + dlv := len(dl.validator.accts.deltas) + dl.validator.accts.accountsMu.RUnlock() + return dlg == dlv && dl.generator.Latest() == dl.validator.Latest() + }, 10*time.Second, 100*time.Millisecond) + tempDir := t.TempDir() catchpointDataFilePath := filepath.Join(tempDir, t.Name()+".data") diff --git a/ledger/catchpointtracker.go b/ledger/catchpointtracker.go index b50a0d2d1e..036f5490b3 100644 --- a/ledger/catchpointtracker.go +++ b/ledger/catchpointtracker.go @@ -818,7 +818,6 @@ func (ct *catchpointTracker) createCatchpoint(ctx context.Context, accountsRound } relCatchpointFilePath := filepath.Join(trackerdb.CatchpointDirName, trackerdb.MakeCatchpointFilePath(round)) - absCatchpointFilePath := filepath.Join(ct.dbDirectory, relCatchpointFilePath) err = os.MkdirAll(filepath.Dir(absCatchpointFilePath), 0700) @@ -929,7 +928,7 @@ func (ct *catchpointTracker) pruneFirstStageRecordsData(ctx context.Context, max for _, round := range rounds { relCatchpointDataFilePath := filepath.Join(trackerdb.CatchpointDirName, makeCatchpointDataFilePath(round)) - err = trackerdb.RemoveSingleCatchpointFileFromDisk(ct.dbDirectory, relCatchpointDataFilePath) + err = trackerdb.RemoveSingleCatchpointFileFromDisk(ct.tmpDir, relCatchpointDataFilePath) if err != nil { return err } diff --git a/ledger/catchpointtracker_test.go b/ledger/catchpointtracker_test.go index 60f7366c74..6790344889 100644 --- a/ledger/catchpointtracker_test.go +++ b/ledger/catchpointtracker_test.go @@ -63,19 +63,23 @@ func TestCatchpointIsWritingCatchpointFile(t *testing.T) { } func newCatchpointTracker(tb testing.TB, l *mockLedgerForTracker, conf config.Local, dbPathPrefix string) *catchpointTracker { + return newCatchpointTrackerWithPaths(tb, l, conf, dbPathPrefix, dbPathPrefix) +} + +func newCatchpointTrackerWithPaths(tb testing.TB, l *mockLedgerForTracker, conf config.Local, hotPath string, coldPath string) *catchpointTracker { au := &accountUpdates{} ct := &catchpointTracker{} ao := &onlineAccounts{} au.initialize(conf) paths := DirsAndPrefix{ ResolvedGenesisDirs: config.ResolvedGenesisDirs{ - CatchpointGenesisDir: dbPathPrefix, - HotGenesisDir: dbPathPrefix, + CatchpointGenesisDir: coldPath, + HotGenesisDir: hotPath, }, } ct.initialize(conf, paths) ao.initialize(conf) - _, err := trackerDBInitialize(l, ct.catchpointEnabled(), dbPathPrefix) + _, err := trackerDBInitialize(l, ct.catchpointEnabled(), hotPath) require.NoError(tb, err) err = l.trackers.initialize(l, []ledgerTracker{au, ct, ao, &txTail{}}, conf) @@ -1180,109 +1184,139 @@ func TestCalculateCatchpointRounds(t *testing.T) { } } -// Test that pruning first stage catchpoint database records and catchpoint data files -// works. +// TestCatchpointFirstStageInfoPruning checks pruning first stage catchpoint database records and catchpoint data files. +// The test makes a catchpoint tracker and adds blocks into a mock ledger +// until it reaches expected number of catchpoint. Then checks if database records match to data files existence. +// Additional effect is that there are much more data files written during the process than catchpoints at the very end of the test +// because of automatic pruning so check that most data files are removed confirms pruning works. func TestCatchpointFirstStageInfoPruning(t *testing.T) { partitiontest.PartitionTest(t) - // create new protocol version, which has lower lookback - testProtocolVersion := - protocol.ConsensusVersion("test-protocol-TestFirstStageInfoPruning") - protoParams := config.Consensus[protocol.ConsensusCurrentVersion] - protoParams.CatchpointLookback = 32 - protoParams.EnableCatchpointsWithSPContexts = true - config.Consensus[testProtocolVersion] = protoParams - defer func() { - delete(config.Consensus, testProtocolVersion) - }() - - accts := []map[basics.Address]basics.AccountData{ledgertesting.RandomAccounts(20, true)} - - ml := makeMockLedgerForTracker(t, false, 1, testProtocolVersion, accts) - defer ml.Close() - - cfg := config.GetDefaultLocal() - cfg.CatchpointInterval = 4 - cfg.CatchpointTracking = 2 - ct := newCatchpointTracker(t, ml, cfg, ".") - defer ct.close() - - temporaryDirectory := t.TempDir() - catchpointsDirectory := filepath.Join(temporaryDirectory, trackerdb.CatchpointDirName) - err := os.Mkdir(catchpointsDirectory, 0777) - require.NoError(t, err) - - ct.dbDirectory = temporaryDirectory - ct.tmpDir = temporaryDirectory + tempDir := t.TempDir() + var tests = []struct { + hotPath string + coldPath string + }{ + {"", ""}, + {"hot", "cold"}, + } - expectedNumEntries := protoParams.CatchpointLookback / cfg.CatchpointInterval + for _, test := range tests { + var testName string + if test.hotPath == test.coldPath { + testName = "dirs=same" + } else { + testName = "dirs=different" + } + t.Run(testName, func(t *testing.T) { + test.hotPath = filepath.Join(tempDir, test.hotPath) + test.coldPath = filepath.Join(tempDir, test.coldPath) + for _, path := range []string{test.hotPath, test.coldPath} { + _, err := os.Stat(path) + if errors.Is(err, os.ErrNotExist) { + err := os.MkdirAll(path, 0777) + require.NoError(t, err) + } + } - isCatchpointRound := func(rnd basics.Round) bool { - return (uint64(rnd) >= cfg.MaxAcctLookback) && - (uint64(rnd)-cfg.MaxAcctLookback > protoParams.CatchpointLookback) && - ((uint64(rnd)-cfg.MaxAcctLookback)%cfg.CatchpointInterval == 0) - } - isDataFileRound := func(rnd basics.Round) bool { - return ((uint64(rnd)-cfg.MaxAcctLookback+protoParams.CatchpointLookback)%cfg.CatchpointInterval == 0) - } + dataFileDirectory := filepath.Join(test.hotPath, trackerdb.CatchpointDirName) + err := os.Mkdir(dataFileDirectory, 0777) + require.NoError(t, err) - numCatchpointsCreated := uint64(0) - i := basics.Round(0) - lastCatchpointLabel := "" + // create new protocol version, which has lower lookback + testProtocolVersion := + protocol.ConsensusVersion("test-protocol-TestFirstStageInfoPruning") + protoParams := config.Consensus[protocol.ConsensusCurrentVersion] + protoParams.CatchpointLookback = 32 + protoParams.EnableCatchpointsWithSPContexts = true + config.Consensus[testProtocolVersion] = protoParams + defer func() { + delete(config.Consensus, testProtocolVersion) + }() + + accts := []map[basics.Address]basics.AccountData{ledgertesting.RandomAccounts(20, true)} + + ml := makeMockLedgerForTracker(t, false, 1, testProtocolVersion, accts) + defer ml.Close() + + cfg := config.GetDefaultLocal() + cfg.CatchpointInterval = 4 + cfg.CatchpointTracking = 2 + ct := newCatchpointTrackerWithPaths(t, ml, cfg, test.hotPath, test.coldPath) + defer ct.close() + + expectedNumEntries := protoParams.CatchpointLookback / cfg.CatchpointInterval + + isCatchpointRound := func(rnd basics.Round) bool { + return (uint64(rnd) >= cfg.MaxAcctLookback) && + (uint64(rnd)-cfg.MaxAcctLookback > protoParams.CatchpointLookback) && + ((uint64(rnd)-cfg.MaxAcctLookback)%cfg.CatchpointInterval == 0) + } + isDataFileRound := func(rnd basics.Round) bool { + return ((uint64(rnd)-cfg.MaxAcctLookback+protoParams.CatchpointLookback)%cfg.CatchpointInterval == 0) + } - for numCatchpointsCreated < expectedNumEntries { - i++ + numCatchpointsCreated := uint64(0) + numDataFilesWritten := uint64(0) + i := basics.Round(0) + lastCatchpointLabel := "" - blk := bookkeeping.Block{ - BlockHeader: bookkeeping.BlockHeader{ - Round: basics.Round(i), - UpgradeState: bookkeeping.UpgradeState{ - CurrentProtocol: testProtocolVersion, - }, - }, - } - delta := ledgercore.MakeStateDelta(&blk.BlockHeader, 0, 0, 0) + for numCatchpointsCreated < expectedNumEntries { + i++ - ml.addBlock(blockEntry{block: blk}, delta) + blk := bookkeeping.Block{ + BlockHeader: bookkeeping.BlockHeader{ + Round: basics.Round(i), + UpgradeState: bookkeeping.UpgradeState{ + CurrentProtocol: testProtocolVersion, + }, + }, + } + delta := ledgercore.MakeStateDelta(&blk.BlockHeader, 0, 0, 0) + + ml.addBlock(blockEntry{block: blk}, delta) + + if isDataFileRound(i) || isCatchpointRound(i) { + ml.trackers.committedUpTo(i) + ml.trackers.waitAccountsWriting() + // Let catchpoint data generation finish so that nothing gets skipped. + for ct.isWritingCatchpointDataFile() { + time.Sleep(time.Millisecond) + } + numDataFilesWritten++ + } - if isDataFileRound(i) || isCatchpointRound(i) { - ml.trackers.committedUpTo(i) - ml.trackers.waitAccountsWriting() - // Let catchpoint data generation finish so that nothing gets skipped. - for ct.isWritingCatchpointDataFile() { - time.Sleep(time.Millisecond) + if isCatchpointRound(i) { + catchpointLabel := ct.GetLastCatchpointLabel() + require.NotEqual(t, lastCatchpointLabel, catchpointLabel) + lastCatchpointLabel = catchpointLabel + numCatchpointsCreated++ + } } - } - if isCatchpointRound(i) { - catchpointLabel := ct.GetLastCatchpointLabel() - require.NotEqual(t, lastCatchpointLabel, catchpointLabel) - lastCatchpointLabel = catchpointLabel - numCatchpointsCreated++ - } - } + numEntries := uint64(0) + i -= basics.Round(cfg.MaxAcctLookback) + for i > 0 { + _, recordExists, err := ct.catchpointStore.SelectCatchpointFirstStageInfo(context.Background(), i) + require.NoError(t, err) - numEntries := uint64(0) - i -= basics.Round(cfg.MaxAcctLookback) - for i > 0 { - _, recordExists, err := ct.catchpointStore.SelectCatchpointFirstStageInfo(context.Background(), i) - require.NoError(t, err) + catchpointDataFilePath := filepath.Join(dataFileDirectory, makeCatchpointDataFilePath(i)) + _, err = os.Stat(catchpointDataFilePath) + if errors.Is(err, os.ErrNotExist) { + require.False(t, recordExists, i) + } else { + require.NoError(t, err) + require.True(t, recordExists, i) + numEntries++ + } - catchpointDataFilePath := - filepath.Join(catchpointsDirectory, makeCatchpointDataFilePath(i)) - _, err = os.Stat(catchpointDataFilePath) - if errors.Is(err, os.ErrNotExist) { - require.False(t, recordExists, i) - } else { - require.NoError(t, err) - require.True(t, recordExists, i) - numEntries++ - } + i-- + } - i-- + require.Equal(t, expectedNumEntries, numEntries) + require.Greater(t, numDataFilesWritten, numEntries) + }) } - - require.Equal(t, expectedNumEntries, numEntries) } // Test that on startup the catchpoint tracker restarts catchpoint's first stage if 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/txntracer.go b/ledger/eval/txntracer.go index 8736a1fdb9..96d307390a 100644 --- a/ledger/eval/txntracer.go +++ b/ledger/eval/txntracer.go @@ -32,6 +32,8 @@ import ( ) // TxnGroupDeltaWithIds associates all the Ids (group and Txn) with a single state delta object +// +//revive:disable:var-naming type TxnGroupDeltaWithIds struct { _struct struct{} `codec:",omitempty,omitemptyarray"` Ids []string 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.go b/ledger/ledger.go index 110a72fc18..2cc1b36ee1 100644 --- a/ledger/ledger.go +++ b/ledger/ledger.go @@ -583,6 +583,12 @@ func (l *Ledger) LookupAsset(rnd basics.Round, addr basics.Address, aidx basics. return ledgercore.AssetResource{AssetParams: r.AssetParams, AssetHolding: r.AssetHolding}, err } +// LookupAssets loads asset resources that match the request parameters from the ledger. +func (l *Ledger) LookupAssets(addr basics.Address, assetIDGT basics.AssetIndex, limit uint64) ([]ledgercore.AssetResourceWithIDs, basics.Round, error) { + resources, lookupRound, err := l.accts.LookupAssetResources(addr, assetIDGT, limit) + return resources, lookupRound, err +} + // lookupResource loads a resource that matches the request parameters from the accounts update func (l *Ledger) lookupResource(rnd basics.Round, addr basics.Address, aidx basics.CreatableIndex, ctype basics.CreatableType) (ledgercore.AccountResource, error) { l.trackerMu.RLock() @@ -621,11 +627,7 @@ func (l *Ledger) LookupAgreement(rnd basics.Round, addr basics.Address) (basics. // Intentionally apply (pending) rewards up to rnd. data, err := l.acctsOnline.LookupOnlineAccountData(rnd, addr) - if err != nil { - return basics.OnlineAccountData{}, err - } - - return data, nil + return data, err } // LookupWithoutRewards is like Lookup but does not apply pending rewards up 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..127ecf85cb 100644 --- a/ledger/ledger_test.go +++ b/ledger/ledger_test.go @@ -53,6 +53,8 @@ import ( "github.com/algorand/go-deadlock" ) +const preReleaseDBVersion = 6 + func sign(secrets map[basics.Address]*crypto.SignatureSecrets, t transactions.Transaction) transactions.SignedTxn { var sig crypto.Signature _, ok := secrets[t.Sender] @@ -71,7 +73,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 +102,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 +144,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 +189,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 +259,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 +288,72 @@ func TestLedgerBlockHeaders(t *testing.T) { badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.Round++ - a.Error(l.appendUnvalidated(badBlock), "added block header with round that was too high") + a.ErrorContains(l.appendUnvalidated(badBlock), "ledger does not have entry") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.Round-- - a.Error(l.appendUnvalidated(badBlock), "added block header with round that was too low") + a.ErrorIs(l.appendUnvalidated(badBlock), eval.ErrRoundZero) badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.Round = 0 - a.Error(l.appendUnvalidated(badBlock), "added block header with round 0") + a.ErrorIs(l.appendUnvalidated(badBlock), eval.ErrRoundZero) badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.GenesisID = "" - a.Error(l.appendUnvalidated(badBlock), "added block header with empty genesis ID") + a.ErrorContains(l.appendUnvalidated(badBlock), "genesis ID missing") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.GenesisID = "incorrect" - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect genesis ID") + a.ErrorContains(l.appendUnvalidated(badBlock), "genesis ID mismatch") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.UpgradePropose = "invalid" - a.Error(l.appendUnvalidated(badBlock), "added block header with invalid upgrade proposal") + a.ErrorContains(l.appendUnvalidated(badBlock), "proposed upgrade wait rounds 0") + + badBlock = bookkeeping.Block{BlockHeader: correctHeader} + badBlock.BlockHeader.UpgradePropose = "invalid" + badBlock.BlockHeader.UpgradeDelay = 20000 + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.UpgradeApprove = true - a.Error(l.appendUnvalidated(badBlock), "added block header with upgrade approve set but no open upgrade") + a.ErrorContains(l.appendUnvalidated(badBlock), "approval without an active proposal") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.CurrentProtocol = "incorrect" - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect current protocol") + a.ErrorContains(l.appendUnvalidated(badBlock), "protocol not supported") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.CurrentProtocol = "" - a.Error(l.appendUnvalidated(badBlock), "added block header with empty current protocol") + a.ErrorContains(l.appendUnvalidated(badBlock), "protocol not supported", "header with empty current protocol") + + badBlock = bookkeeping.Block{BlockHeader: correctHeader} + var wrongVersion protocol.ConsensusVersion + for ver := range config.Consensus { + if ver != correctHeader.CurrentProtocol { + wrongVersion = ver + break + } + } + a.NotEmpty(wrongVersion) + badBlock.BlockHeader.CurrentProtocol = wrongVersion + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.NextProtocol = "incorrect" - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect next protocol") + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch", "added block header with incorrect next protocol") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.NextProtocolApprovals++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect number of upgrade approvals") + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch", "added block header with incorrect number of upgrade approvals") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.NextProtocolVoteBefore++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect next protocol vote deadline") + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch", "added block header with incorrect next protocol vote deadline") badBlock = bookkeeping.Block{BlockHeader: correctHeader} badBlock.BlockHeader.NextProtocolSwitchOn++ - a.Error(l.appendUnvalidated(badBlock), "added block header with incorrect next protocol switch round") + a.ErrorContains(l.appendUnvalidated(badBlock), "UpgradeState mismatch", "added block header with incorrect next protocol switch round") // TODO test upgrade cases with a valid upgrade in progress @@ -326,33 +361,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 +690,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 +729,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 +1273,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 +1293,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") } @@ -2155,6 +2180,42 @@ func TestLedgerReloadShrinkDeltas(t *testing.T) { } } +func resetAccountDBToV6(t *testing.T, l *Ledger) { + // reset tables and re-init again, similary to the catchpount apply code + // since the ledger has only genesis accounts, this recreates them + err := l.trackerDBs.Transaction(func(ctx context.Context, tx trackerdb.TransactionScope) error { + arw, err := tx.MakeAccountsWriter() + if err != nil { + return err + } + + err0 := arw.AccountsReset(ctx) + if err0 != nil { + return err0 + } + tp := trackerdb.Params{ + InitAccounts: l.GenesisAccounts(), + InitProto: l.GenesisProtoVersion(), + GenesisHash: l.GenesisHash(), + FromCatchpoint: true, + CatchpointEnabled: l.catchpoint.catchpointEnabled(), + DbPathPrefix: l.catchpoint.dbDirectory, + BlockDb: l.blockDBs, + } + _, err0 = tx.RunMigrations(ctx, tp, l.log, preReleaseDBVersion /*target database version*/) + if err0 != nil { + return err0 + } + + if err0 := tx.Testing().AccountsUpdateSchemaTest(ctx); err != nil { + return err0 + } + + return nil + }) + require.NoError(t, err) +} + // TestLedgerReloadTxTailHistoryAccess checks txtail has MaxTxnLife + DeeperBlockHeaderHistory block headers // for TEAL after applying catchpoint. // Simulate catchpoints by the following: @@ -2166,8 +2227,6 @@ func TestLedgerReloadShrinkDeltas(t *testing.T) { func TestLedgerReloadTxTailHistoryAccess(t *testing.T) { partitiontest.PartitionTest(t) - const preReleaseDBVersion = 6 - dbName := fmt.Sprintf("%s.%d", t.Name(), crypto.RandUint64()) genesisInitState, initKeys := ledgertesting.GenerateInitState(t, protocol.ConsensusCurrentVersion, 10_000_000_000) proto := config.Consensus[protocol.ConsensusCurrentVersion] @@ -2208,11 +2267,7 @@ func TestLedgerReloadTxTailHistoryAccess(t *testing.T) { return err0 } - if err0 := tx.Testing().AccountsUpdateSchemaTest(ctx); err != nil { - return err0 - } - - return nil + return tx.Testing().AccountsUpdateSchemaTest(ctx) }) require.NoError(t, err) diff --git a/ledger/ledgercore/accountdata.go b/ledger/ledgercore/accountdata.go index 3685d16909..081fbffde6 100644 --- a/ledger/ledgercore/accountdata.go +++ b/ledger/ledgercore/accountdata.go @@ -18,8 +18,6 @@ package ledgercore import ( "github.com/algorand/go-algorand/config" - "github.com/algorand/go-algorand/crypto" - "github.com/algorand/go-algorand/crypto/merklesignature" "github.com/algorand/go-algorand/data/basics" ) @@ -29,7 +27,7 @@ import ( // separately, to better support on-disk and in-memory schemas that do not store them together. type AccountData struct { AccountBaseData - VotingData + basics.VotingData } // AccountBaseData contains base account info like balance, status and total number of resources @@ -39,6 +37,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,23 +47,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 -} - -// VotingData holds participation information -type VotingData struct { - VoteID crypto.OneTimeSignatureVerifier - SelectionID crypto.VRFVerifier - StateProofID merklesignature.Commitment - VoteFirstValid basics.Round - VoteLastValid basics.Round - VoteKeyDilution uint64 -} - -// OnlineAccountData holds MicroAlgosWithRewards and VotingData as needed for agreement -type OnlineAccountData struct { - MicroAlgosWithRewards basics.MicroAlgos - VotingData + 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. } // ToAccountData returns ledgercore.AccountData from basics.AccountData @@ -75,8 +60,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,8 +71,11 @@ 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{ + VotingData: basics.VotingData{ VoteID: acct.VoteID, SelectionID: acct.SelectionID, StateProofID: acct.StateProofID, @@ -105,6 +93,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 +103,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 @@ -131,13 +123,26 @@ func (u AccountData) WithUpdatedRewards(proto config.ConsensusParams, rewardsLev // ClearOnlineState resets the account's fields to indicate that the account is an offline account func (u *AccountData) ClearOnlineState() { u.Status = basics.Offline - u.VotingData = VotingData{} + u.VotingData = basics.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 +153,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{} @@ -160,25 +174,19 @@ func (u AccountData) Money(proto config.ConsensusParams, rewardsLevel uint64) (m } // OnlineAccountData calculates the online account data given an AccountData, by adding the rewards. -func (u AccountData) OnlineAccountData(proto config.ConsensusParams, rewardsLevel uint64) OnlineAccountData { +func (u AccountData) OnlineAccountData(proto config.ConsensusParams, rewardsLevel uint64) basics.OnlineAccountData { if u.Status != basics.Online { // if the account is not Online and agreement requests it for some reason, clear it out - return OnlineAccountData{} + return basics.OnlineAccountData{} } microAlgos, _, _ := basics.WithUpdatedRewards( proto, u.Status, u.MicroAlgos, u.RewardedMicroAlgos, u.RewardsBase, rewardsLevel, ) - return OnlineAccountData{ + return basics.OnlineAccountData{ MicroAlgosWithRewards: microAlgos, - VotingData: VotingData{ - VoteID: u.VoteID, - SelectionID: u.SelectionID, - StateProofID: u.StateProofID, - VoteFirstValid: u.VoteFirstValid, - VoteLastValid: u.VoteLastValid, - VoteKeyDilution: u.VoteKeyDilution, - }, + VotingData: u.VotingData, + IncentiveEligible: u.IncentiveEligible, } } diff --git a/ledger/ledgercore/accountresource.go b/ledger/ledgercore/accountresource.go index e1b13e71d7..e8f03bd60e 100644 --- a/ledger/ledgercore/accountresource.go +++ b/ledger/ledgercore/accountresource.go @@ -34,6 +34,14 @@ type AssetResource struct { AssetHolding *basics.AssetHolding } +// AssetResourceWithIDs is used to retrieve a asset resource information from the data tier, +// inclusive of the asset ID and creator address +type AssetResourceWithIDs struct { + AssetResource + AssetID basics.AssetIndex + Creator basics.Address +} + // AppResource used to retrieve a generic app resource information from the data tier type AppResource struct { AppLocalState *basics.AppLocalState 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..8e69f2fc69 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. @@ -202,6 +206,14 @@ type PersistedResourcesData struct { Round basics.Round } +// PersistedResourcesDataWithCreator is exported view of persistedResourcesData inclusive of creator +type PersistedResourcesDataWithCreator struct { + PersistedResourcesData + + // the address of the account that created this resource + Creator basics.Address +} + // PersistedOnlineAccountData is exported view of persistedOnlineAccountData type PersistedOnlineAccountData struct { Addr basics.Address @@ -286,6 +298,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 +322,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,12 +362,16 @@ func (ba *BaseAccountData) GetLedgerCoreAccountBaseData() ledgercore.AccountBase TotalAssets: ba.TotalAssets, TotalBoxes: ba.TotalBoxes, TotalBoxBytes: ba.TotalBoxBytes, + IncentiveEligible: ba.IncentiveEligible, + + LastProposed: ba.LastProposed, + LastHeartbeat: ba.LastHeartbeat, } } // GetLedgerCoreVotingData getter for voting data. -func (ba *BaseAccountData) GetLedgerCoreVotingData() ledgercore.VotingData { - return ledgercore.VotingData{ +func (ba *BaseAccountData) GetLedgerCoreVotingData() basics.VotingData { + return basics.VotingData{ VoteID: ba.VoteID, SelectionID: ba.SelectionID, StateProofID: ba.StateProofID, @@ -365,6 +389,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 +404,9 @@ func (ba *BaseAccountData) GetAccountData() basics.AccountData { VoteFirstValid: ba.VoteFirstValid, VoteLastValid: ba.VoteLastValid, VoteKeyDilution: ba.VoteKeyDilution, + + LastProposed: ba.LastProposed, + LastHeartbeat: ba.LastHeartbeat, } } @@ -389,6 +417,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 +427,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 +452,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 @@ -444,14 +475,14 @@ func (bo *BaseOnlineAccountData) GetOnlineAccount(addr basics.Address, normBalan // GetOnlineAccountData returns basics.OnlineAccountData for lookup agreement // TODO: unify with GetOnlineAccount/ledgercore.OnlineAccount -func (bo *BaseOnlineAccountData) GetOnlineAccountData(proto config.ConsensusParams, rewardsLevel uint64) ledgercore.OnlineAccountData { +func (bo *BaseOnlineAccountData) GetOnlineAccountData(proto config.ConsensusParams, rewardsLevel uint64) basics.OnlineAccountData { microAlgos, _, _ := basics.WithUpdatedRewards( proto, basics.Online, bo.MicroAlgos, basics.MicroAlgos{}, bo.RewardsBase, rewardsLevel, ) - return ledgercore.OnlineAccountData{ + return basics.OnlineAccountData{ MicroAlgosWithRewards: microAlgos, - VotingData: ledgercore.VotingData{ + VotingData: basics.VotingData{ VoteID: bo.VoteID, SelectionID: bo.SelectionID, StateProofID: bo.StateProofID, @@ -459,6 +490,7 @@ func (bo *BaseOnlineAccountData) GetOnlineAccountData(proto config.ConsensusPara VoteLastValid: bo.VoteLastValid, VoteKeyDilution: bo.VoteKeyDilution, }, + IncentiveEligible: bo.IncentiveEligible, } } @@ -471,9 +503,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/dualdriver/accounts_reader.go b/ledger/store/trackerdb/dualdriver/accounts_reader.go index b39aadd7cb..5692994636 100644 --- a/ledger/store/trackerdb/dualdriver/accounts_reader.go +++ b/ledger/store/trackerdb/dualdriver/accounts_reader.go @@ -185,3 +185,39 @@ func (ar *accountsReader) LookupResources(addr basics.Address, aidx basics.Creat // return primary results return dataP, nil } + +func (ar *accountsReader) LookupLimitedResources(addr basics.Address, minIdx basics.CreatableIndex, maxCreatables uint64, ctype basics.CreatableType) (data []trackerdb.PersistedResourcesDataWithCreator, rnd basics.Round, err error) { + dataP, rndP, errP := ar.primary.LookupLimitedResources(addr, minIdx, maxCreatables, ctype) + dataS, rndS, errS := ar.secondary.LookupLimitedResources(addr, minIdx, maxCreatables, ctype) + // coalesce errors + err = coalesceErrors(errP, errS) + if err != nil { + return + } + // coalesce refs + if len(dataP) != len(dataS) { + err = ErrInconsistentResult + return + } + var ref trackerdb.AccountRef + for i := range dataP { + ref, err = coalesceAccountRefs(dataP[i].AcctRef, dataS[i].AcctRef) + if err != nil { + return data, rnd, err + } + // update ref in results + dataP[i].AcctRef = ref + dataS[i].AcctRef = ref + } + // check results match + if !cmp.Equal(dataP, dataS, allowAllUnexported) { + err = ErrInconsistentResult + return + } + if rndP != rndS { + err = ErrInconsistentResult + return + } + // return primary results + return dataP, rndP, nil +} diff --git a/ledger/store/trackerdb/dualdriver/accounts_reader_ext.go b/ledger/store/trackerdb/dualdriver/accounts_reader_ext.go index 6f241d3e5e..6d6b527f48 100644 --- a/ledger/store/trackerdb/dualdriver/accounts_reader_ext.go +++ b/ledger/store/trackerdb/dualdriver/accounts_reader_ext.go @@ -284,7 +284,7 @@ func (ar *accountsReaderExt) OnlineAccountsAll(maxAccounts uint64) (accounts []t } // ExpiredOnlineAccountsForRound implements trackerdb.AccountsReaderExt -func (ar *accountsReaderExt) ExpiredOnlineAccountsForRound(rnd basics.Round, voteRnd basics.Round, proto config.ConsensusParams, rewardsLevel uint64) (expAccounts map[basics.Address]*ledgercore.OnlineAccountData, err error) { +func (ar *accountsReaderExt) ExpiredOnlineAccountsForRound(rnd basics.Round, voteRnd basics.Round, proto config.ConsensusParams, rewardsLevel uint64) (expAccounts map[basics.Address]*basics.OnlineAccountData, err error) { expAccountsP, errP := ar.primary.ExpiredOnlineAccountsForRound(rnd, voteRnd, proto, rewardsLevel) expAccountsS, errS := ar.secondary.ExpiredOnlineAccountsForRound(rnd, voteRnd, proto, rewardsLevel) // coalesce errors diff --git a/ledger/store/trackerdb/dualdriver/dualdriver.go b/ledger/store/trackerdb/dualdriver/dualdriver.go index c7763dacef..cbcba9c480 100644 --- a/ledger/store/trackerdb/dualdriver/dualdriver.go +++ b/ledger/store/trackerdb/dualdriver/dualdriver.go @@ -458,10 +458,9 @@ func coalesceAccountRefs(primary, secondary trackerdb.AccountRef) (trackerdb.Acc } else if primary == nil && secondary == nil { // all good, ref is nil return nil, nil - } else { - // ref mismatch - return nil, ErrInconsistentResult } + // ref mismatch + return nil, ErrInconsistentResult } type onlineAccountRef struct { @@ -478,10 +477,9 @@ func coalesceOnlineAccountRefs(primary, secondary trackerdb.OnlineAccountRef) (t } else if primary == nil && secondary == nil { // all good, ref is nil return nil, nil - } else { - // ref mismatch - return nil, ErrInconsistentResult } + // ref mismatch + return nil, ErrInconsistentResult } type resourceRef struct { diff --git a/ledger/store/trackerdb/generickv/accounts_ext_reader.go b/ledger/store/trackerdb/generickv/accounts_ext_reader.go index af2730a514..79460b51e7 100644 --- a/ledger/store/trackerdb/generickv/accounts_ext_reader.go +++ b/ledger/store/trackerdb/generickv/accounts_ext_reader.go @@ -353,7 +353,7 @@ func (r *accountsReader) OnlineAccountsAll(maxAccounts uint64) ([]trackerdb.Pers } // ExpiredOnlineAccountsForRound implements trackerdb.AccountsReaderExt -func (r *accountsReader) ExpiredOnlineAccountsForRound(rnd basics.Round, voteRnd basics.Round, proto config.ConsensusParams, rewardsLevel uint64) (data map[basics.Address]*ledgercore.OnlineAccountData, err error) { +func (r *accountsReader) ExpiredOnlineAccountsForRound(rnd basics.Round, voteRnd basics.Round, proto config.ConsensusParams, rewardsLevel uint64) (data map[basics.Address]*basics.OnlineAccountData, err error) { // The SQL at the time of writing: // // SELECT address, data, max(updround) @@ -364,7 +364,7 @@ func (r *accountsReader) ExpiredOnlineAccountsForRound(rnd basics.Round, voteRnd // ORDER BY address // initialize return map - data = make(map[basics.Address]*ledgercore.OnlineAccountData) + data = make(map[basics.Address]*basics.OnlineAccountData) expired := make(map[basics.Address]struct{}) // prepare iter over online accounts (by balance) diff --git a/ledger/store/trackerdb/generickv/accounts_reader.go b/ledger/store/trackerdb/generickv/accounts_reader.go index 063b5a35f5..0aa9d55a21 100644 --- a/ledger/store/trackerdb/generickv/accounts_reader.go +++ b/ledger/store/trackerdb/generickv/accounts_reader.go @@ -17,6 +17,7 @@ package generickv import ( + "errors" "fmt" "io" @@ -178,6 +179,10 @@ func (r *accountsReader) LookupAllResources(addr basics.Address) (data []tracker return } +func (r *accountsReader) LookupLimitedResources(_ basics.Address, _ basics.CreatableIndex, _ uint64, _ basics.CreatableType) ([]trackerdb.PersistedResourcesDataWithCreator, basics.Round, error) { + return nil, 0, errors.New("not supported") +} + func (r *accountsReader) LookupKeyValue(key string) (pv trackerdb.PersistedKVData, err error) { // read the current db round pv.Round, err = r.AccountsRound() diff --git a/ledger/store/trackerdb/generickv/migrations.go b/ledger/store/trackerdb/generickv/migrations.go index c2a0d0f208..a329d84bf7 100644 --- a/ledger/store/trackerdb/generickv/migrations.go +++ b/ledger/store/trackerdb/generickv/migrations.go @@ -222,6 +222,6 @@ func (m *migrator) initialVersion(ctx context.Context) error { return err } - // KV store starts at version 10 - return m.setVersion(ctx, 10) + // KV store starts at version 11 + return m.setVersion(ctx, 11) } diff --git a/ledger/store/trackerdb/interface.go b/ledger/store/trackerdb/interface.go index 8d88f0d42a..25d7b79e3c 100644 --- a/ledger/store/trackerdb/interface.go +++ b/ledger/store/trackerdb/interface.go @@ -105,6 +105,7 @@ type AccountsReader interface { LookupResources(addr basics.Address, aidx basics.CreatableIndex, ctype basics.CreatableType) (data PersistedResourcesData, err error) LookupAllResources(addr basics.Address) (data []PersistedResourcesData, rnd basics.Round, err error) + LookupLimitedResources(addr basics.Address, minIdx basics.CreatableIndex, maxCreatables uint64, ctype basics.CreatableType) (data []PersistedResourcesDataWithCreator, rnd basics.Round, err error) LookupKeyValue(key string) (pv PersistedKVData, err error) LookupKeysByPrefix(prefix string, maxKeyNum uint64, results map[string]bool, resultCount uint64) (round basics.Round, err error) @@ -129,7 +130,7 @@ type AccountsReaderExt interface { LookupOnlineAccountDataByAddress(addr basics.Address) (ref OnlineAccountRef, data []byte, err error) AccountsOnlineTop(rnd basics.Round, offset uint64, n uint64, proto config.ConsensusParams) (map[basics.Address]*ledgercore.OnlineAccount, error) AccountsOnlineRoundParams() (onlineRoundParamsData []ledgercore.OnlineRoundParamsData, endRound basics.Round, err error) - ExpiredOnlineAccountsForRound(rnd, voteRnd basics.Round, proto config.ConsensusParams, rewardsLevel uint64) (map[basics.Address]*ledgercore.OnlineAccountData, error) + ExpiredOnlineAccountsForRound(rnd, voteRnd basics.Round, proto config.ConsensusParams, rewardsLevel uint64) (map[basics.Address]*basics.OnlineAccountData, error) OnlineAccountsAll(maxAccounts uint64) ([]PersistedOnlineAccountData, error) LoadTxTail(ctx context.Context, dbRound basics.Round) (roundData []*TxTailRound, roundHash []crypto.Digest, baseRound basics.Round, err error) LoadAllFullAccounts(ctx context.Context, balancesTable string, resourcesTable string, acctCb func(basics.Address, basics.AccountData)) (count int, err error) 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/accountsV2.go b/ledger/store/trackerdb/sqlitedriver/accountsV2.go index bb20285d9d..0ba84c84bd 100644 --- a/ledger/store/trackerdb/sqlitedriver/accountsV2.go +++ b/ledger/store/trackerdb/sqlitedriver/accountsV2.go @@ -303,7 +303,7 @@ func (r *accountsV2Reader) OnlineAccountsAll(maxAccounts uint64) ([]trackerdb.Pe } // ExpiredOnlineAccountsForRound returns all online accounts known at `rnd` that will be expired by `voteRnd`. -func (r *accountsV2Reader) ExpiredOnlineAccountsForRound(rnd, voteRnd basics.Round, proto config.ConsensusParams, rewardsLevel uint64) (map[basics.Address]*ledgercore.OnlineAccountData, error) { +func (r *accountsV2Reader) ExpiredOnlineAccountsForRound(rnd, voteRnd basics.Round, proto config.ConsensusParams, rewardsLevel uint64) (map[basics.Address]*basics.OnlineAccountData, error) { // This relies on SQLite's handling of max(updround) and bare columns not in the GROUP BY. // The values of votelastvalid, votefirstvalid, and data will all be from the same row as max(updround) rows, err := r.q.Query(`SELECT address, data, max(updround) @@ -317,7 +317,7 @@ ORDER BY address`, rnd, voteRnd) } defer rows.Close() - ret := make(map[basics.Address]*ledgercore.OnlineAccountData) + ret := make(map[basics.Address]*basics.OnlineAccountData) for rows.Next() { var addrbuf []byte var buf []byte diff --git a/ledger/store/trackerdb/sqlitedriver/schema.go b/ledger/store/trackerdb/sqlitedriver/schema.go index 369ff264e0..d938ba180f 100644 --- a/ledger/store/trackerdb/sqlitedriver/schema.go +++ b/ledger/store/trackerdb/sqlitedriver/schema.go @@ -21,6 +21,7 @@ import ( "context" "database/sql" "encoding/hex" + "errors" "fmt" "time" @@ -734,8 +735,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{} @@ -937,3 +942,98 @@ func convertOnlineRoundParamsTail(ctx context.Context, e db.Executable) error { _, err := e.ExecContext(ctx, createVoteLastValidIndex) return err } + +func accountsAddCreatableTypeColumn(ctx context.Context, e db.Executable, populateColumn bool) error { + // Run ctype resources migration if it hasn't run yet + var creatableTypeOnResourcesRun bool + err := e.QueryRow("SELECT 1 FROM pragma_table_info('resources') WHERE name='ctype'").Scan(&creatableTypeOnResourcesRun) + if err == nil { + // Check if any ctypes are invalid + var count uint64 + err0 := e.QueryRow("SELECT COUNT(*) FROM resources WHERE ctype NOT IN (0, 1)").Scan(&count) + if err0 != nil { + return err0 + } + if count > 0 { + // Invalid ctypes found, return an error + return fmt.Errorf("invalid ctypes found in resources table; database is corrupted and needs to be rebuilt") + } + // Column exists, no ctypes are invalid, no migration needed so return clean + return nil + } else if !errors.Is(err, sql.ErrNoRows) { + return err + } // A sql.ErrNoRows error means the column does not exist, so we need to create it/run the migration + + // If we reached here, a sql.ErrNoRows error was returned, so we need to create the column + + // Add ctype column + createStmt := `ALTER TABLE resources ADD COLUMN ctype INTEGER NOT NULL DEFAULT -1` + + _, err = e.ExecContext(ctx, createStmt) + if err != nil { + return err + } + + if populateColumn { + // Populate the new ctype column with the corresponding creatable type from assetcreators where available + updateStmt := `UPDATE resources SET ctype = ( + SELECT COALESCE((SELECT ac.ctype FROM assetcreators ac WHERE ac.asset = resources.aidx),-1) + ) WHERE ctype = -1` + + _, err0 := e.ExecContext(ctx, updateStmt) + if err0 != nil { + return err0 + } + + updatePrepStmt, err0 := e.PrepareContext(ctx, "UPDATE resources SET ctype = ? WHERE addrid = ? AND aidx = ?") + if err0 != nil { + return err0 + } + defer updatePrepStmt.Close() + + // Pull resource entries into memory where ctype is not set + rows, err0 := e.QueryContext(ctx, "SELECT addrid, aidx, data FROM resources r WHERE ctype = -1") + if err0 != nil { + return err0 + } + defer rows.Close() + + // Update the ctype column for subset of resources where ctype was not resolved from assetcreators + for rows.Next() { + var addrid int64 + var aidx int64 + var encodedData []byte + err0 = rows.Scan(&addrid, &aidx, &encodedData) + if err0 != nil { + return err0 + } + + var rd trackerdb.ResourcesData + err0 = protocol.Decode(encodedData, &rd) + if err0 != nil { + return err0 + } + + var ct basics.CreatableType + if rd.IsAsset() && rd.IsApp() { + // This should never happen! + return fmt.Errorf("unable to discern creatable type for addrid %d, resource %d", addrid, aidx) + } else if rd.IsAsset() { + ct = basics.AssetCreatable + } else if rd.IsApp() { + ct = basics.AppCreatable + } else { // This should never happen! + return fmt.Errorf("unable to discern creatable type for addrid %d, resource %d", addrid, aidx) + } + + _, err0 = updatePrepStmt.ExecContext(ctx, ct, addrid, aidx) + if err0 != nil { + return err0 + } + } + + return rows.Err() + } + + return nil +} 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/store/trackerdb/sqlitedriver/sql.go b/ledger/store/trackerdb/sqlitedriver/sql.go index 634c7f54ef..1befd06298 100644 --- a/ledger/store/trackerdb/sqlitedriver/sql.go +++ b/ledger/store/trackerdb/sqlitedriver/sql.go @@ -19,7 +19,6 @@ package sqlitedriver import ( "database/sql" "fmt" - "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/ledgercore" "github.com/algorand/go-algorand/ledger/store/trackerdb" @@ -30,12 +29,13 @@ import ( // accountsDbQueries is used to cache a prepared SQL statement to look up // the state of a single account. type accountsDbQueries struct { - lookupAccountStmt *sql.Stmt - lookupResourcesStmt *sql.Stmt - lookupAllResourcesStmt *sql.Stmt - lookupKvPairStmt *sql.Stmt - lookupKeysByRangeStmt *sql.Stmt - lookupCreatorStmt *sql.Stmt + lookupAccountStmt *sql.Stmt + lookupResourcesStmt *sql.Stmt + lookupAllResourcesStmt *sql.Stmt + lookupLimitedResourcesStmt *sql.Stmt + lookupKvPairStmt *sql.Stmt + lookupKeysByRangeStmt *sql.Stmt + lookupCreatorStmt *sql.Stmt } type onlineAccountsDbQueries struct { @@ -87,6 +87,29 @@ func AccountsInitDbQueries(q db.Queryable) (*accountsDbQueries, error) { return nil, err } + qs.lookupLimitedResourcesStmt, err = q.Prepare( + `SELECT ab.rowid, + ar.rnd, + r.aidx, + ac.creator, + r.data, + cr.data + FROM acctrounds ar + JOIN accountbase ab ON ab.address = ? + JOIN resources r ON r.addrid = ab.addrid + LEFT JOIN assetcreators ac ON r.aidx = ac.asset + LEFT JOIN accountbase cab ON ac.creator = cab.address + LEFT JOIN resources cr ON cr.addrid = cab.addrid + AND cr.aidx = r.aidx + WHERE ar.id = 'acctbase' + AND r.ctype = ? + AND r.aidx > ? + ORDER BY r.aidx ASC + LIMIT ?`) + if err != nil { + return nil, err + } + qs.lookupKvPairStmt, err = q.Prepare("SELECT acctrounds.rnd, kvstore.key, kvstore.value FROM acctrounds LEFT JOIN kvstore ON key = ? WHERE id='acctbase';") if err != nil { return nil, err @@ -168,7 +191,7 @@ func MakeAccountsSQLWriter(e db.Executable, hasAccounts, hasResources, hasKvPair return } - w.insertResourceStmt, err = e.Prepare("INSERT INTO resources(addrid, aidx, data) VALUES(?, ?, ?)") + w.insertResourceStmt, err = e.Prepare("INSERT INTO resources(addrid, aidx, data, ctype) VALUES(?, ?, ?, ?)") if err != nil { return } @@ -379,9 +402,9 @@ func (qs *accountsDbQueries) LookupResources(addr basics.Address, aidx basics.Cr func (qs *accountsDbQueries) LookupAllResources(addr basics.Address) (data []trackerdb.PersistedResourcesData, rnd basics.Round, err error) { err = db.Retry(func() error { // Query for all resources - rows, err := qs.lookupAllResourcesStmt.Query(addr[:]) - if err != nil { - return err + rows, err0 := qs.lookupAllResourcesStmt.Query(addr[:]) + if err0 != nil { + return err0 } defer rows.Close() @@ -390,7 +413,7 @@ func (qs *accountsDbQueries) LookupAllResources(addr basics.Address) (data []tra data = nil var buf []byte for rows.Next() { - err := rows.Scan(&addrid, &dbRound, &aidx, &buf) + err = rows.Scan(&addrid, &dbRound, &aidx, &buf) if err != nil { return err } @@ -421,6 +444,89 @@ func (qs *accountsDbQueries) LookupAllResources(addr basics.Address) (data []tra return } +func (qs *accountsDbQueries) LookupLimitedResources(addr basics.Address, minIdx basics.CreatableIndex, maxCreatables uint64, ctype basics.CreatableType) (data []trackerdb.PersistedResourcesDataWithCreator, rnd basics.Round, err error) { + err = db.Retry(func() error { + rows, err0 := qs.lookupLimitedResourcesStmt.Query(addr[:], ctype, minIdx, maxCreatables) + if err0 != nil { + return err0 + } + defer rows.Close() + + var addrid, aidx sql.NullInt64 + var dbRound basics.Round + data = nil + var actAssetBuf []byte + var crtAssetBuf []byte + var creatorAddrBuf []byte + for rows.Next() { + err = rows.Scan(&addrid, &dbRound, &aidx, &creatorAddrBuf, &actAssetBuf, &crtAssetBuf) + if err != nil { + return err + } + if !addrid.Valid || !aidx.Valid { + // we received an entry without any index. This would happen only on the first entry when there are no resources for this address. + // ensure this is the first entry, set the round and return + if len(data) != 0 { + return fmt.Errorf("LookupLimitedResources: unexpected invalid result on non-first resource record: (%v, %v)", addrid.Valid, aidx.Valid) + } + rnd = dbRound + break + } + var actResData trackerdb.ResourcesData + var crtResData trackerdb.ResourcesData + err = protocol.Decode(actAssetBuf, &actResData) + if err != nil { + return err + } + + var prdwc trackerdb.PersistedResourcesDataWithCreator + if len(crtAssetBuf) > 0 { + err = protocol.Decode(crtAssetBuf, &crtResData) + if err != nil { + return err + } + + // Since there is a creator, we want to return all of the asset params along with the asset holdings. + // The most simple way to do this is to set the necessary asset holding data on the creator resource data + // retrieved from the database. Note that this is unique way of setting resource flags, making this structure + // not suitable for use in other contexts (where the params would only be present colocated with the asset holding + // of the creator). + crtResData.Amount = actResData.Amount + crtResData.Frozen = actResData.Frozen + crtResData.ResourceFlags = actResData.ResourceFlags + + creatorAddr := basics.Address{} + copy(creatorAddr[:], creatorAddrBuf) + + prdwc = trackerdb.PersistedResourcesDataWithCreator{ + PersistedResourcesData: trackerdb.PersistedResourcesData{ + AcctRef: sqlRowRef{addrid.Int64}, + Aidx: basics.CreatableIndex(aidx.Int64), + Data: crtResData, + Round: dbRound, + }, + Creator: creatorAddr, + } + } else { // no creator found, asset was likely deleted, will not have asset params + prdwc = trackerdb.PersistedResourcesDataWithCreator{ + PersistedResourcesData: trackerdb.PersistedResourcesData{ + AcctRef: sqlRowRef{addrid.Int64}, + Aidx: basics.CreatableIndex(aidx.Int64), + Data: actResData, + Round: dbRound, + }, + } + } + + data = append(data, prdwc) + + rnd = dbRound + } + return nil + }) + return +} + // LookupAccount looks up for a the account data given it's address. It returns the persistedAccountData, which includes the current database round and the matching // account data, if such was found. If no matching account data could be found for the given address, an empty account data would // be retrieved. @@ -541,6 +647,7 @@ func (qs *accountsDbQueries) Close() { &qs.lookupAccountStmt, &qs.lookupResourcesStmt, &qs.lookupAllResourcesStmt, + &qs.lookupLimitedResourcesStmt, &qs.lookupKvPairStmt, &qs.lookupKeysByRangeStmt, &qs.lookupCreatorStmt, @@ -633,7 +740,17 @@ func (w accountsSQLWriter) InsertResource(accountRef trackerdb.AccountRef, aidx return nil, fmt.Errorf("no account could be found for rowid = nil: %w", err) } addrid := accountRef.(sqlRowRef).rowid - result, err := w.insertResourceStmt.Exec(addrid, aidx, protocol.Encode(&data)) + var ctype basics.CreatableType + if data.IsAsset() && data.IsApp() { + return nil, fmt.Errorf("unable to resolve single creatable type for account ref %d, creatable idx %d", addrid, aidx) + } else if data.IsAsset() { + ctype = basics.AssetCreatable + } else if data.IsApp() { + ctype = basics.AppCreatable + } else { + return nil, fmt.Errorf("unable to resolve creatable type for account ref %d, creatable idx %d", addrid, aidx) + } + result, err := w.insertResourceStmt.Exec(addrid, aidx, protocol.Encode(&data), ctype) if err != nil { return } diff --git a/ledger/store/trackerdb/sqlitedriver/testing.go b/ledger/store/trackerdb/sqlitedriver/testing.go index 49736a681a..fd4076799b 100644 --- a/ledger/store/trackerdb/sqlitedriver/testing.go +++ b/ledger/store/trackerdb/sqlitedriver/testing.go @@ -101,26 +101,37 @@ func AccountsInitTest(tb testing.TB, e db.Executable, initAccounts map[basics.Ad err = performKVStoreNullBlobConversion(context.Background(), e) require.NoError(tb, err) + err = accountsAddCreatableTypeColumn(context.Background(), e, false) + require.NoError(tb, err) + return newDB } // AccountsUpdateSchemaTest adds some empty tables for tests to work with a "v6" store. func AccountsUpdateSchemaTest(ctx context.Context, e db.Executable) (err error) { - if err := accountsCreateOnlineAccountsTable(ctx, e); err != nil { + if err = accountsCreateOnlineAccountsTable(ctx, e); err != nil { return err } - if err := accountsCreateTxTailTable(ctx, e); err != nil { + if err = accountsCreateTxTailTable(ctx, e); err != nil { return err } - if err := accountsCreateOnlineRoundParamsTable(ctx, e); err != nil { + if err = accountsCreateOnlineRoundParamsTable(ctx, e); err != nil { return err } - if err := accountsCreateCatchpointFirstStageInfoTable(ctx, e); err != nil { + if err = accountsCreateCatchpointFirstStageInfoTable(ctx, e); err != nil { return err } // this line creates kvstore table, even if it is not required in accountDBVersion 6 -> 7 // or in later version where we need kvstore table, some tests will fail - if err := accountsCreateBoxTable(ctx, e); err != nil { + if err = accountsCreateBoxTable(ctx, e); err != nil { + return err + } + // this adds the resources table and ctype column, even if it is not required in accountDBVersion 6 -> 7 + // this prevents some tests from failing. + if err = accountsCreateResourceTable(ctx, e); err != nil { + return err + } + if err = accountsAddCreatableTypeColumn(ctx, e, false); err != nil { return err } return createStateProofVerificationTable(ctx, e) diff --git a/ledger/store/trackerdb/sqlitedriver/trackerdbV2.go b/ledger/store/trackerdb/sqlitedriver/trackerdbV2.go index db1416b521..9987ef8b03 100644 --- a/ledger/store/trackerdb/sqlitedriver/trackerdbV2.go +++ b/ledger/store/trackerdb/sqlitedriver/trackerdbV2.go @@ -136,6 +136,12 @@ func RunMigrations(ctx context.Context, e db.Executable, params trackerdb.Params tu.log.Warnf("trackerDBInitialize failed to upgrade accounts database (ledger.tracker.sqlite) from schema 9 : %v", err) return } + case 10: + err = tu.upgradeDatabaseSchema10(ctx, e) + if err != nil { + tu.log.Warnf("trackerDBInitialize failed to upgrade accounts database (ledger.tracker.sqlite) from schema 10 : %v", err) + return + } default: return trackerdb.InitParams{}, fmt.Errorf("trackerDBInitialize unable to upgrade database from schema version %d", tu.schemaVersion) } @@ -507,6 +513,17 @@ func (tu *trackerDBSchemaInitializer) upgradeDatabaseSchema9(ctx context.Context return tu.setVersion(ctx, e, 10) } +// upgradeDatabaseSchema10 upgrades the database schema from version 10 to version 11, +// altering the resources table to add a new column, resources.ctype. +func (tu *trackerDBSchemaInitializer) upgradeDatabaseSchema10(ctx context.Context, e db.Executable) (err error) { + err = accountsAddCreatableTypeColumn(ctx, e, true) + if err != nil { + return err + } + // update version + return tu.setVersion(ctx, e, 11) +} + func removeEmptyDirsOnSchemaUpgrade(dbDirectory string) (err error) { catchpointRootDir := filepath.Join(dbDirectory, trackerdb.CatchpointDirName) if _, err := os.Stat(catchpointRootDir); os.IsNotExist(err) { diff --git a/ledger/store/trackerdb/testsuite/accounts_kv_test.go b/ledger/store/trackerdb/testsuite/accounts_kv_test.go index 115e958901..b804ec8cb9 100644 --- a/ledger/store/trackerdb/testsuite/accounts_kv_test.go +++ b/ledger/store/trackerdb/testsuite/accounts_kv_test.go @@ -19,6 +19,7 @@ package testsuite import ( "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/ledger/store/trackerdb" + ledgertesting "github.com/algorand/go-algorand/ledger/testing" "github.com/stretchr/testify/require" ) @@ -27,6 +28,9 @@ func init() { registerTest("accounts-crud", CustomTestAccountsCrud) registerTest("resources-crud", CustomTestResourcesCrud) registerTest("resources-query-all", CustomTestResourcesQueryAll) + // NOTE: this test is disabled because it is not supported by the kv implementation, + // it is only supported by the sqlite implementation and is enabled there (see sqlitedb_test.go) + // registerTest("resources-query-all-limited", CustomTestResourcesQueryAllLimited) registerTest("kv-crud", CustomTestAppKVCrud) registerTest("creatables-crud", CustomTestCreatablesCrud) } @@ -210,12 +214,14 @@ func CustomTestResourcesQueryAll(t *customT) { // resource A-0 resDataA0 := trackerdb.ResourcesData{} + resDataA0.SetAssetHolding(basics.AssetHolding{Amount: 0}) aidxResA0 := basics.CreatableIndex(0) _, err = aow.InsertResource(refAccA, aidxResA0, resDataA0) require.NoError(t, err) // resource A-1 resDataA1 := trackerdb.ResourcesData{} + resDataA1.SetAssetHolding(basics.AssetHolding{Amount: 0}) aidxResA1 := basics.CreatableIndex(1) _, err = aow.InsertResource(refAccA, aidxResA1, resDataA1) require.NoError(t, err) @@ -232,6 +238,195 @@ func CustomTestResourcesQueryAll(t *customT) { require.Equal(t, expectedRound, rnd) // db round (from the return) } +func CustomTestResourcesQueryAllLimited(t *customT) { + aow, err := t.db.MakeAccountsOptimizedWriter(true, true, false, true) + require.NoError(t, err) + + aor, err := t.db.MakeAccountsOptimizedReader() + require.NoError(t, err) + + aw, err := t.db.MakeAccountsWriter() + require.NoError(t, err) + + // set round to 3 + // Note: this will be used to check that we read the round + expectedRound := basics.Round(3) + err = aw.UpdateAccountsRound(expectedRound) + require.NoError(t, err) + + // + // pre-fill the db with two accounts for testing - one owning creatables, the other opting into them + // + + // account A - will own creatables + addrA := RandomAddress() + accDataA := trackerdb.BaseAccountData{RewardsBase: 1000} + refAccA, err := aow.InsertAccount(addrA, accDataA.NormalizedOnlineBalance(t.proto), accDataA) + require.NoError(t, err) + + // account B - will opt into creatables + addrB := RandomAddress() + accDataB := trackerdb.BaseAccountData{RewardsBase: 1000} + refAccB, err := aow.InsertAccount(addrB, accDataB.NormalizedOnlineBalance(t.proto), accDataB) + require.NoError(t, err) + + // asset A-0 for accounts A and B + resDataA0AcctA := trackerdb.ResourcesData{} + resDataA0AcctA.SetAssetHolding(basics.AssetHolding{Amount: 10}) + resDataA0AcctA.SetAssetParams(basics.AssetParams{ + Total: 100, + }, true) + // Non-creators will inherit asset params from the creator + resDataA0AcctB := trackerdb.ResourcesData{} + resDataA0AcctB.SetAssetHolding(basics.AssetHolding{Amount: 0}) + aidxResA0 := basics.CreatableIndex(1) + + _, err = aow.InsertResource(refAccA, aidxResA0, resDataA0AcctA) + require.NoError(t, err) + _, err = aow.InsertResource(refAccB, aidxResA0, resDataA0AcctB) + require.NoError(t, err) + + // App A-1 for accounts A and B - this should be completely ignored + resDataA1AcctA := trackerdb.ResourcesData{} + appParams := ledgertesting.RandomAppParams() + resDataA1AcctA.SetAppParams(appParams, true) + resDataA1AcctA.SetAppLocalState(basics.AppLocalState{}) + resDataA1AcctB := trackerdb.ResourcesData{} + resDataA1AcctB.SetAppLocalState(basics.AppLocalState{}) + aidxResA1 := basics.CreatableIndex(2) + _, err = aow.InsertResource(refAccA, aidxResA1, resDataA1AcctA) + require.NoError(t, err) + _, err = aow.InsertResource(refAccB, aidxResA1, resDataA1AcctB) + require.NoError(t, err) + + // asset A-2 for accounts A and B + resDataA2AcctA := trackerdb.ResourcesData{} + resDataA2AcctA.SetAssetHolding(basics.AssetHolding{Amount: 100}) + resDataA2AcctA.SetAssetParams(basics.AssetParams{ + Total: 10000, + }, true) + resDataA2AcctB := trackerdb.ResourcesData{} + resDataA2AcctB.SetAssetHolding(basics.AssetHolding{Amount: 200}) + + aidxResA2 := basics.CreatableIndex(3) + _, err = aow.InsertResource(refAccA, aidxResA2, resDataA2AcctA) + require.NoError(t, err) + _, err = aow.InsertResource(refAccB, aidxResA2, resDataA2AcctB) + require.NoError(t, err) + + // Results for account B (opted in, not creator) we expect back will have asset params but the resource flags + // are explicitly set to be + resDataWithParamsA0AcctB := trackerdb.ResourcesData{} + resDataWithParamsA0AcctB.SetAssetHolding(resDataA0AcctB.GetAssetHolding()) + resDataWithParamsA0AcctB.SetAssetParams(resDataA0AcctA.GetAssetParams(), true) + resDataWithParamsA0AcctB.ResourceFlags = resDataA0AcctB.ResourceFlags + + resDataWithParamsA1AcctB := trackerdb.ResourcesData{} + resDataWithParamsA1AcctB.SetAssetHolding(resDataA2AcctB.GetAssetHolding()) + resDataWithParamsA1AcctB.SetAssetParams(resDataA2AcctA.GetAssetParams(), true) + resDataWithParamsA1AcctB.ResourceFlags = resDataA2AcctB.ResourceFlags + + // insert creator account A for A-0 + resA0ctype := basics.AssetCreatable + cRefA0, err := aow.InsertCreatable(aidxResA0, resA0ctype, addrA[:]) + require.NoError(t, err) + require.NotNil(t, cRefA0) + + // insert creator account A for A-1 + resA1ctype := basics.AppCreatable + cRefA1, err := aow.InsertCreatable(aidxResA1, resA1ctype, addrA[:]) + require.NoError(t, err) + require.NotNil(t, cRefA1) + + // insert creator account A for A-2 + resA2ctype := basics.AssetCreatable + cRefA2, err := aow.InsertCreatable(aidxResA2, resA2ctype, addrA[:]) + require.NoError(t, err) + require.NotNil(t, cRefA2) + + // Lookup with limited resources for account A + prs, rnd, err := aor.LookupLimitedResources(addrA, 0, 2, basics.AssetCreatable) + require.NoError(t, err) + require.Equal(t, 2, len(prs)) + require.Equal(t, aidxResA0, prs[0].Aidx) + require.Equal(t, aidxResA2, prs[1].Aidx) + require.Equal(t, addrA, prs[0].Creator) + require.Equal(t, addrA, prs[1].Creator) + require.Equal(t, expectedRound, prs[0].Round) // db round (inside resources) + require.Equal(t, expectedRound, prs[1].Round) + require.Equal(t, resDataA0AcctA, prs[0].Data) + require.Equal(t, resDataA2AcctA, prs[1].Data) + require.Equal(t, expectedRound, rnd) // db round (from the return) + + // Lookup with limited resources for account B + prs, rnd, err = aor.LookupLimitedResources(addrB, 0, 2, basics.AssetCreatable) + require.NoError(t, err) + require.Equal(t, 2, len(prs)) + require.Equal(t, aidxResA0, prs[0].Aidx) + require.Equal(t, aidxResA2, prs[1].Aidx) + // Creator should be present and set to address A + require.Equal(t, addrA, prs[0].Creator) + require.Equal(t, addrA, prs[1].Creator) + require.Equal(t, expectedRound, prs[0].Round) // db round (inside resources) + require.Equal(t, expectedRound, prs[1].Round) + require.Equal(t, resDataWithParamsA0AcctB, prs[0].Data) + require.Equal(t, resDataWithParamsA1AcctB, prs[1].Data) + require.Equal(t, expectedRound, rnd) // db round (from the return) + + // Set limit to 1, should return only 1 resource + prs, rnd, err = aor.LookupLimitedResources(addrB, 0, 1, basics.AssetCreatable) + require.NoError(t, err) + require.Equal(t, 1, len(prs)) + require.Equal(t, aidxResA0, prs[0].Aidx) + require.Equal(t, addrA, prs[0].Creator) + require.Equal(t, expectedRound, prs[0].Round) // db round (inside resources) + require.Equal(t, resDataWithParamsA0AcctB, prs[0].Data) + require.Equal(t, expectedRound, rnd) // db round (from the return) + + // Delete app owner for A-1 + _, err = aow.DeleteCreatable(aidxResA1, basics.AppCreatable) + require.NoError(t, err) + + // Set min to 1, should return only 1 resource (index 1) + prs, rnd, err = aor.LookupLimitedResources(addrB, 1, 1, basics.AssetCreatable) + require.NoError(t, err) + require.Equal(t, 1, len(prs)) + require.Equal(t, aidxResA2, prs[0].Aidx) + require.Equal(t, addrA, prs[0].Creator) + require.Equal(t, expectedRound, prs[0].Round) // db round (inside resources) + require.Equal(t, resDataWithParamsA1AcctB, prs[0].Data) + require.Equal(t, expectedRound, rnd) // db round (from the return) + + // Delete both resource creatables + rowsAffected, err := aow.DeleteCreatable(aidxResA0, basics.AssetCreatable) + require.NoError(t, err) + require.Equal(t, int64(1), rowsAffected) + rowsAffected, err = aow.DeleteCreatable(aidxResA2, basics.AssetCreatable) + require.NoError(t, err) + require.Equal(t, int64(1), rowsAffected) + _, err = aow.DeleteResource(refAccA, aidxResA0) + require.NoError(t, err) + _, err = aow.DeleteResource(refAccA, aidxResA2) + require.NoError(t, err) + + // Account A should have no resources, account B should have 2 resources without a creator/params + prs, rnd, err = aor.LookupLimitedResources(addrA, 0, 2, basics.AssetCreatable) + require.NoError(t, err) + require.Equal(t, 0, len(prs)) + prs, rnd, err = aor.LookupLimitedResources(addrB, 0, 2, basics.AssetCreatable) + require.NoError(t, err) + require.Equal(t, 2, len(prs)) + require.Equal(t, aidxResA0, prs[0].Aidx) + require.Equal(t, aidxResA2, prs[1].Aidx) + require.True(t, prs[0].Creator.IsZero()) + require.True(t, prs[1].Creator.IsZero()) + require.Equal(t, expectedRound, prs[0].Round) // db round (inside resources) + require.Equal(t, expectedRound, prs[1].Round) + // Note these directly reflect what was inserted into resources table (no creator/params) + require.Equal(t, resDataA0AcctB, prs[0].Data) + require.Equal(t, resDataA2AcctB, prs[1].Data) +} + func CustomTestAppKVCrud(t *customT) { aow, err := t.db.MakeAccountsOptimizedWriter(true, true, true, false) require.NoError(t, err) @@ -259,6 +454,7 @@ func CustomTestAppKVCrud(t *customT) { require.NoError(t, err) // resource resDataA0 := trackerdb.ResourcesData{} + resDataA0.SetAssetHolding(basics.AssetHolding{Amount: 0}) aidxResA0 := basics.CreatableIndex(0) refResA0, err := aow.InsertResource(refAccA, aidxResA0, resDataA0) require.NoError(t, err) @@ -333,12 +529,14 @@ func CustomTestCreatablesCrud(t *customT) { // resource A-0 resDataA0 := trackerdb.ResourcesData{} + resDataA0.SetAssetHolding(basics.AssetHolding{Amount: 0}) aidxResA0 := basics.CreatableIndex(0) _, err = aow.InsertResource(refAccA, aidxResA0, resDataA0) require.NoError(t, err) // resource A-1 resDataA1 := trackerdb.ResourcesData{} + resDataA1.SetAssetHolding(basics.AssetHolding{Amount: 0}) aidxResA1 := basics.CreatableIndex(1) _, err = aow.InsertResource(refAccA, aidxResA1, resDataA1) require.NoError(t, err) diff --git a/ledger/store/trackerdb/testsuite/sqlitedb_test.go b/ledger/store/trackerdb/testsuite/sqlitedb_test.go index 5a0d942ab8..3b76e8d04d 100644 --- a/ledger/store/trackerdb/testsuite/sqlitedb_test.go +++ b/ledger/store/trackerdb/testsuite/sqlitedb_test.go @@ -38,6 +38,9 @@ func TestSqliteDB(t *testing.T) { return db } + // Run CustomTestResourcesQueryAllLimited, which is not supported by the k-v store implementation + registerTest("resources-query-all-limited", CustomTestResourcesQueryAllLimited) + // run the suite runGenericTestsWithDB(t, dbFactory) } diff --git a/ledger/store/trackerdb/version.go b/ledger/store/trackerdb/version.go index bf81dce82d..4cb343f335 100644 --- a/ledger/store/trackerdb/version.go +++ b/ledger/store/trackerdb/version.go @@ -19,4 +19,4 @@ package trackerdb // AccountDBVersion is the database version that this binary would know how to support and how to upgrade to. // details about the content of each of the versions can be found in the upgrade functions upgradeDatabaseSchemaXXXX // and their descriptions. -var AccountDBVersion = int32(10) +var AccountDBVersion = int32(11) 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/libgoal/libgoal.go b/libgoal/libgoal.go index 75ddea8ab6..e72b0588d6 100644 --- a/libgoal/libgoal.go +++ b/libgoal/libgoal.go @@ -664,6 +664,15 @@ func (c *Client) AccountInformation(account string, includeCreatables bool) (res return } +// AccountAssetsInformation returns the assets held by an account, including asset params for non-deleted assets. +func (c *Client) AccountAssetsInformation(account string, next *string, limit *uint64) (resp model.AccountAssetsInformationResponse, err error) { + algod, err := c.ensureAlgodClient() + if err == nil { + resp, err = algod.AccountAssetsInformation(account, next, limit) + } + return +} + // AccountApplicationInformation gets account information about a given app. func (c *Client) AccountApplicationInformation(accountAddress string, applicationID uint64) (resp model.AccountApplicationResponse, err error) { algod, err := c.ensureAlgodClient() @@ -1339,3 +1348,12 @@ func (c *Client) GetLedgerStateDelta(round uint64) (rep model.LedgerStateDeltaRe } return } + +// BlockLogs returns all the logs in a block for a given round +func (c *Client) BlockLogs(round uint64) (resp model.BlockLogsResponse, err error) { + algod, err := c.ensureAlgodClient() + if err == nil { + return algod.BlockLogs(round) + } + return +} diff --git a/logging/cyclicWriter.go b/logging/cyclicWriter.go index d15782c93b..579fb4f881 100644 --- a/logging/cyclicWriter.go +++ b/logging/cyclicWriter.go @@ -26,6 +26,7 @@ import ( "text/template" "time" + "github.com/algorand/go-algorand/util" "github.com/algorand/go-deadlock" ) @@ -173,7 +174,7 @@ func (cyclic *CyclicFileWriter) Write(p []byte) (n int, err error) { shouldBz2 = true archivePath = archivePath[:len(archivePath)-4] } - if err = os.Rename(cyclic.liveLog, archivePath); err != nil { + if err = util.MoveFile(cyclic.liveLog, archivePath); err != nil { panic(fmt.Sprintf("CyclicFileWriter: cannot archive full log %v", err)) } if shouldGz { diff --git a/logging/cyclicWriter_test.go b/logging/cyclicWriter_test.go index 1d5de4bb1a..1149ae2098 100644 --- a/logging/cyclicWriter_test.go +++ b/logging/cyclicWriter_test.go @@ -18,16 +18,19 @@ package logging import ( "os" + "os/exec" + "path/filepath" + "runtime" + "strings" "testing" "github.com/algorand/go-algorand/test/partitiontest" "github.com/stretchr/testify/require" ) -func TestCyclicWrite(t *testing.T) { - partitiontest.PartitionTest(t) - liveFileName := "live.test" - archiveFileName := "archive.test" +func testCyclicWrite(t *testing.T, liveFileName, archiveFileName string) { + t.Helper() + defer os.Remove(liveFileName) defer os.Remove(archiveFileName) @@ -60,3 +63,46 @@ func TestCyclicWrite(t *testing.T) { require.Equal(t, byte('A'), oldData[i]) } } + +func TestCyclicWrite(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + tmpDir := t.TempDir() + + liveFileName := filepath.Join(tmpDir, "live.test") + archiveFileName := filepath.Join(tmpDir, "archive.test") + + testCyclicWrite(t, liveFileName, archiveFileName) +} + +func execCommand(t *testing.T, cmdAndArsg ...string) { + t.Helper() + + cmd := exec.Command(cmdAndArsg[0], cmdAndArsg[1:]...) + var errOutput strings.Builder + cmd.Stderr = &errOutput + err := cmd.Run() + require.NoError(t, err, errOutput.String()) +} + +func TestCyclicWriteAcrossFilesystems(t *testing.T) { + partitiontest.PartitionTest(t) + + isLinux := strings.HasPrefix(runtime.GOOS, "linux") + + // Skip unless CIRCLECI or TEST_MOUNT_TMPFS is set, and we are on a linux system + if !isLinux || (os.Getenv("CIRCLECI") == "" && os.Getenv("TEST_MOUNT_TMPFS") == "") { + t.Skip("This test must be run on a linux system with administrator privileges") + } + + mountDir := t.TempDir() + execCommand(t, "sudo", "mount", "-t", "tmpfs", "-o", "size=2K", "tmpfs", mountDir) + + defer execCommand(t, "sudo", "umount", mountDir) + + liveFileName := filepath.Join(t.TempDir(), "live.test") + archiveFileName := filepath.Join(mountDir, "archive.test") + + testCyclicWrite(t, liveFileName, archiveFileName) +} diff --git a/netdeploy/network.go b/netdeploy/network.go index d5e4f522ee..b26c8ef5bc 100644 --- a/netdeploy/network.go +++ b/netdeploy/network.go @@ -32,6 +32,7 @@ import ( "github.com/algorand/go-algorand/libgoal" "github.com/algorand/go-algorand/netdeploy/remote" "github.com/algorand/go-algorand/nodecontrol" + "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/util" "golang.org/x/exp/maps" ) @@ -58,9 +59,27 @@ type Network struct { nodeExitCallback nodecontrol.AlgodExitErrorCallback } +// TemplateOverride is a function that modifies the NetworkTemplate after it is read in. +type TemplateOverride func(*NetworkTemplate) + +// OverrideDevMode turns on dev mode, regardless of whether the json says so. +func OverrideDevMode(template *NetworkTemplate) { + template.Genesis.DevMode = true + if len(template.Nodes) > 0 { + template.Nodes[0].IsRelay = false + } +} + +// OverrideConsensusVersion changes the protocol version of a template. +func OverrideConsensusVersion(ver protocol.ConsensusVersion) TemplateOverride { + return func(template *NetworkTemplate) { + template.Genesis.ConsensusProtocol = ver + } +} + // CreateNetworkFromTemplate uses the specified template to deploy a new private network // under the specified root directory. -func CreateNetworkFromTemplate(name, rootDir string, templateReader io.Reader, binDir string, importKeys bool, nodeExitCallback nodecontrol.AlgodExitErrorCallback, consensus config.ConsensusProtocols, overrideDevMode bool) (Network, error) { +func CreateNetworkFromTemplate(name, rootDir string, templateReader io.Reader, binDir string, importKeys bool, nodeExitCallback nodecontrol.AlgodExitErrorCallback, consensus config.ConsensusProtocols, overrides ...TemplateOverride) (Network, error) { n := Network{ rootDir: rootDir, nodeExitCallback: nodeExitCallback, @@ -70,19 +89,14 @@ func CreateNetworkFromTemplate(name, rootDir string, templateReader io.Reader, b var err error template := defaultNetworkTemplate - err = LoadTemplateFromReader(templateReader, &template) - - if err == nil { - if overrideDevMode { - template.Genesis.DevMode = true - if len(template.Nodes) > 0 { - template.Nodes[0].IsRelay = false - } - } - } else { + if err = LoadTemplateFromReader(templateReader, &template); err != nil { return n, err } + for _, overide := range overrides { + overide(&template) + } + if err = template.Validate(); err != nil { return n, err } diff --git a/network/p2p/peerstore/peerstore_test.go b/network/p2p/peerstore/peerstore_test.go index b4aa241ba4..9bbf2b87c6 100644 --- a/network/p2p/peerstore/peerstore_test.go +++ b/network/p2p/peerstore/peerstore_test.go @@ -59,13 +59,13 @@ func TestPeerstore(t *testing.T) { // add peer addresses var addrs []string - var peerIDS []peer.ID + var peerIDs []peer.ID for i := 0; i < 4; i++ { privKey, _, err := libp2p_crypto.GenerateEd25519Key(rand.Reader) require.NoError(t, err) peerID, err := peer.IDFromPrivateKey(privKey) require.NoError(t, err) - peerIDS = append(peerIDS, peerID) + peerIDs = append(peerIDs, peerID) maddrStr := fmt.Sprintf("/ip4/1.2.3.4/tcp/%d/p2p/%s", 4000+i, peerID.String()) addrs = append(addrs, maddrStr) } @@ -81,7 +81,7 @@ func TestPeerstore(t *testing.T) { require.Equal(t, 8, len(peers)) // remove a peer addr - ps.ClearAddrs(peerIDS[0]) + ps.ClearAddrs(peerIDs[0]) peers = ps.PeersWithAddrs() require.Equal(t, 7, len(peers)) 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..acf204facf 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 } @@ -577,58 +576,6 @@ func (node *AlgorandFullNode) Simulate(request simulation.Request) (result simul return simulator.Simulate(request) } -// ListTxns returns SignedTxns associated with a specific account in a range of Rounds (inclusive). -// TxnWithStatus returns the round in which a particular transaction appeared, -// since that information is not part of the SignedTxn itself. -func (node *AlgorandFullNode) ListTxns(addr basics.Address, minRound basics.Round, maxRound basics.Round) ([]TxnWithStatus, error) { - result := make([]TxnWithStatus, 0) - for r := minRound; r <= maxRound; r++ { - h, err := node.ledger.AddressTxns(addr, r) - if err != nil { - return nil, err - } - for _, tx := range h { - result = append(result, TxnWithStatus{ - Txn: tx.SignedTxn, - ConfirmedRound: r, - ApplyData: tx.ApplyData, - }) - } - } - return result, nil -} - -// GetTransaction looks for the required txID within with a specific account within a range of rounds (inclusive) and -// returns the SignedTxn and true iff it finds the transaction. -func (node *AlgorandFullNode) GetTransaction(addr basics.Address, txID transactions.Txid, minRound basics.Round, maxRound basics.Round) (TxnWithStatus, bool) { - // start with the most recent round, and work backwards: - // this will abort early if it hits pruned rounds - if maxRound < minRound { - return TxnWithStatus{}, false - } - r := maxRound - for { - h, err := node.ledger.AddressTxns(addr, r) - if err != nil { - return TxnWithStatus{}, false - } - for _, tx := range h { - if tx.ID() == txID { - return TxnWithStatus{ - Txn: tx.SignedTxn, - ConfirmedRound: r, - ApplyData: tx.ApplyData, - }, true - } - } - if r == minRound { - break - } - r-- - } - return TxnWithStatus{}, false -} - // GetPendingTransaction looks for the required txID in the recent ledger // blocks, in the txpool, and in the txpool's status cache. It returns // the SignedTxn (with status information), and a bool to indicate if the @@ -1080,11 +1027,6 @@ func (node *AlgorandFullNode) txPoolGaugeThread(done <-chan struct{}) { } } -// IsArchival returns true the node is an archival node, false otherwise -func (node *AlgorandFullNode) IsArchival() bool { - return node.config.Archival -} - // OnNewBlock implements the BlockListener interface so we're notified after each block is written to the ledger func (node *AlgorandFullNode) OnNewBlock(block bookkeeping.Block, delta ledgercore.StateDelta) { if node.ledger.Latest() > block.Round() { @@ -1160,20 +1102,6 @@ func (node *AlgorandFullNode) Uint64() uint64 { return crypto.RandUint64() } -// GetTransactionByID gets transaction by ID -// this function is intended to be called externally via the REST api interface. -func (node *AlgorandFullNode) GetTransactionByID(txid transactions.Txid, rnd basics.Round) (TxnWithStatus, error) { - stx, _, err := node.ledger.LookupTxid(txid, rnd) - if err != nil { - return TxnWithStatus{}, err - } - return TxnWithStatus{ - Txn: stx.SignedTxn, - ConfirmedRound: rnd, - ApplyData: stx.ApplyData, - }, nil -} - // StartCatchup starts the catchpoint mode and attempt to get to the provided catchpoint // this function is intended to be called externally via the REST api interface. func (node *AlgorandFullNode) StartCatchup(catchpoint string) error { @@ -1290,27 +1218,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 +1253,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 +1283,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/scripts/buildnumber.py b/scripts/buildnumber.py index 59de8a076b..7488572e0f 100755 --- a/scripts/buildnumber.py +++ b/scripts/buildnumber.py @@ -7,9 +7,9 @@ # e.g. if NOW is 5/28/2018 5:30am # => 305 -from datetime import datetime +from datetime import datetime, timezone -epoch = datetime(2018, 5, 25, 0, 0, 0) -d1 = datetime.utcnow() +epoch = datetime(2018, 5, 25, 0, 0, 0, tzinfo=timezone.utc) +d1 = datetime.now(timezone.utc) delta = d1 - epoch print("%d%02d" % (delta.days, d1.hour)) diff --git a/scripts/buildtools/versions b/scripts/buildtools/versions index ba43b37f60..61dd519985 100644 --- a/scripts/buildtools/versions +++ b/scripts/buildtools/versions @@ -1,7 +1,7 @@ golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 -golang.org/x/tools v0.9.3 +golang.org/x/tools v0.20.0 github.com/algorand/msgp v1.1.60 github.com/algorand/oapi-codegen v1.12.0-algorand.0 github.com/go-swagger/go-swagger v0.30.4 gotest.tools/gotestsum v1.10.0 -github.com/golangci/golangci-lint/cmd/golangci-lint v1.53.2 +github.com/golangci/golangci-lint/cmd/golangci-lint v1.58.0 diff --git a/scripts/get_golang_version.sh b/scripts/get_golang_version.sh index 13dfca5bf1..256683831c 100755 --- a/scripts/get_golang_version.sh +++ b/scripts/get_golang_version.sh @@ -11,9 +11,9 @@ # Our build task-runner `mule` will refer to this script and will automatically # build a new image whenever the version number has been changed. -BUILD=1.20.14 - MIN=1.20 - GO_MOD_SUPPORT=1.20 +BUILD=1.21.10 + MIN=1.21 + GO_MOD_SUPPORT=1.21 if [ "$1" = all ] then 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/devmode/devmode_test.go b/test/e2e-go/features/devmode/devmode_test.go index c2303085c1..525a6cacbd 100644 --- a/test/e2e-go/features/devmode/devmode_test.go +++ b/test/e2e-go/features/devmode/devmode_test.go @@ -26,20 +26,21 @@ import ( "github.com/algorand/go-algorand/crypto" "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/netdeploy" + "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/test/framework/fixtures" "github.com/algorand/go-algorand/test/partitiontest" ) func TestDevMode(t *testing.T) { partitiontest.PartitionTest(t) + fixtures.MultiProtocolTest(t, testDevMode, protocol.ConsensusFuture, protocol.ConsensusCurrentVersion) +} - if testing.Short() { - t.Skip() - } - +func testDevMode(t *testing.T, version protocol.ConsensusVersion) { // Start devmode network, and make sure everything is primed by sending a transaction. var fixture fixtures.RestClientFixture - fixture.SetupNoStart(t, filepath.Join("nettemplates", "DevModeNetwork.json")) + fixture.SetupNoStart(t, filepath.Join("nettemplates", "DevModeNetwork.json"), netdeploy.OverrideConsensusVersion(version)) fixture.Start() defer fixture.Shutdown() sender, err := fixture.GetRichestAccount() @@ -76,17 +77,16 @@ func TestDevMode(t *testing.T) { } } -// Starts up a devmode network, sends a txn, and fetches the txn group delta for that txn func TestTxnGroupDeltasDevMode(t *testing.T) { partitiontest.PartitionTest(t) + fixtures.MultiProtocolTest(t, testTxnGroupDeltasDevMode, protocol.ConsensusFuture, protocol.ConsensusCurrentVersion) +} - if testing.Short() { - t.Skip() - } - +// Starts up a devmode network, sends a txn, and fetches the txn group delta for that txn +func testTxnGroupDeltasDevMode(t *testing.T, version protocol.ConsensusVersion) { // Start devmode network, and send a transaction. var fixture fixtures.RestClientFixture - fixture.SetupNoStart(t, filepath.Join("nettemplates", "DevModeTxnTracerNetwork.json")) + fixture.SetupNoStart(t, filepath.Join("nettemplates", "DevModeTxnTracerNetwork.json"), netdeploy.OverrideConsensusVersion(version)) fixture.Start() defer fixture.Shutdown() sender, err := fixture.GetRichestAccount() 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..1b9f4d0ec3 --- /dev/null +++ b/test/e2e-go/features/incentives/payouts_test.go @@ -0,0 +1,360 @@ +// 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, 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) + // sync up with the network + _, err = c01.WaitForRound(*burn.ConfirmedRound) + 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.NoError(err) + 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) + } + + // all nodes are in sync + for _, c := range []libgoal.Client{c15, c01, relay} { + _, err := c.WaitForRound(status.LastRound) + 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()) + a.NoError(err) + 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} { + _, err := c.WaitForRound(status.LastRound) + a.NoError(err) + data, err := c.AccountData(block.Proposer().String()) + a.NoError(err) + // <= in case one node is behind, and the others have already advanced + a.LessOrEqual(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} { + _, err := c.WaitForRound(status.LastRound) + a.NoError(err) + data, err = c.AccountData(block.Proposer().String()) + a.NoError(err) + // <= in case one node is behind, and the others have already advanced + a.LessOrEqual(block.Round(), data.LastProposed) + // <= in case one node is behind, and the others have already advanced + a.LessOrEqual(pdata.MicroAlgos.Raw, data.MicroAlgos.Raw) + a.Equal(pdata.Status, data.Status) + a.True(data.IncentiveEligible) + + data, err = c.AccountData(feesink.String()) + a.NoError(err) + // >= in case one node is behind, and the others have already advanced + a.GreaterOrEqual(fdata.MicroAlgos.Raw, data.MicroAlgos.Raw) + } + 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.NoError(err) + 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) + // sync up with the network + _, err = client.WaitForRound(*txn.ConfirmedRound) + 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..6768f7926e --- /dev/null +++ b/test/e2e-go/features/incentives/suspension_test.go @@ -0,0 +1,179 @@ +// 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, 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, "") + + // make sure c10 node is in-sync with the network + status, err := fixture.LibGoalClient.Status() + a.NoError(err) + _, err = c10.WaitForRound(status.LastRound) + a.NoError(err) + + // 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, 2*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} { + _, err := c.WaitForRound(stat.LastRound) + r.NoError(err) + 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/restAPI/other/appsRestAPI_test.go b/test/e2e-go/restAPI/other/appsRestAPI_test.go index c4a4986ed9..f270eb48a6 100644 --- a/test/e2e-go/restAPI/other/appsRestAPI_test.go +++ b/test/e2e-go/restAPI/other/appsRestAPI_test.go @@ -17,6 +17,7 @@ package other import ( + "encoding/base64" "encoding/binary" "encoding/hex" "fmt" @@ -549,3 +550,168 @@ end: assertBoxCount(numberOfBoxesRemaining) } + +func TestBlockLogs(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + a := require.New(fixtures.SynchronizedTest(t)) + var localFixture fixtures.RestClientFixture + localFixture.Setup(t, filepath.Join("nettemplates", "TwoNodes50EachFuture.json")) + defer localFixture.Shutdown() + + testClient := localFixture.LibGoalClient + + testClient.WaitForRound(1) + + wh, err := testClient.GetUnencryptedWalletHandle() + a.NoError(err) + addresses, err := testClient.ListAddresses(wh) + a.NoError(err) + _, someAddress := helper.GetMaxBalAddr(t, testClient, addresses) + if someAddress == "" { + t.Error("no addr with funds") + } + a.NoError(err) + + innerTEAL := "#pragma version 10\nbyte 0xdeadbeef\nlog\nint 1" + + innerOps, err := logic.AssembleString(innerTEAL) + a.NoError(err) + innerApproval := innerOps.Program + a.NoError(err) + clearState := innerOps.Program + + b64InnerApproval := base64.StdEncoding.EncodeToString(innerApproval) + + outerTEAL := fmt.Sprintf(`#pragma version 10 + byte 0xDD0000DD + log + byte 0x + log + byte 0xDEADD00D + log + txn ApplicationID + bz ret + + itxn_begin + int appl + itxn_field TypeEnum + byte b64 %s + itxn_field ApprovalProgram + byte b64 %s + itxn_field ClearStateProgram + itxn_submit + + ret: + int 1 + return + `, b64InnerApproval, b64InnerApproval) + + outerOps, err := logic.AssembleString(outerTEAL) + a.NoError(err) + outerApproval := outerOps.Program + + gl := basics.StateSchema{} + lc := basics.StateSchema{} + + // create app + appCreateTxn, err := testClient.MakeUnsignedApplicationCallTx( + 0, nil, nil, nil, + nil, nil, transactions.NoOpOC, + outerApproval, clearState, gl, lc, 0, + ) + a.NoError(err) + appCreateTxn, err = testClient.FillUnsignedTxTemplate(someAddress, 0, 0, 0, appCreateTxn) + a.NoError(err) + appCreateTxID, err := testClient.SignAndBroadcastTransaction(wh, nil, appCreateTxn) + a.NoError(err) + createConf, err := helper.WaitForTransaction(t, testClient, appCreateTxID, 30*time.Second) + a.NoError(err) + + createdAppID := basics.AppIndex(*createConf.ApplicationIndex) + + // fund app account + appFundTxn, err := testClient.SendPaymentFromWallet(wh, nil, someAddress, createdAppID.Address().String(), 0, 1_000_000, nil, "", 0, 0) + a.NoError(err) + appFundTxID := appFundTxn.ID() + payConf, err := helper.WaitForTransaction(t, testClient, appFundTxID.String(), 30*time.Second) + a.NoError(err) + + // get response when block has no app calls + resp, err := testClient.BlockLogs(*payConf.ConfirmedRound) + a.NoError(err) + expected := model.BlockLogsResponse{ + Logs: []model.AppCallLogs{}, + } + a.Equal(expected, resp) + + // call app twice + appCallTxn, err := testClient.MakeUnsignedAppNoOpTx( + uint64(createdAppID), nil, nil, nil, + nil, nil, + ) + a.NoError(err) + appCallTxn0, err := testClient.FillUnsignedTxTemplate(someAddress, 0, 0, 0, appCallTxn) + a.NoError(err) + appCallTxn0.Note = []byte("0") + + appCallTxn1, err := testClient.FillUnsignedTxTemplate(someAddress, 0, 0, 0, appCallTxn) + appCallTxn1.Note = []byte("1") + a.NoError(err) + + gid, err := testClient.GroupID([]transactions.Transaction{appCallTxn0, appCallTxn1}) + a.NoError(err) + appCallTxn0.Group = gid + appCallTxn1.Group = gid + + stxn0, err := testClient.SignTransactionWithWallet(wh, nil, appCallTxn0) + a.NoError(err) + stxn1, err := testClient.SignTransactionWithWallet(wh, nil, appCallTxn1) + a.NoError(err) + + err = testClient.BroadcastTransactionGroup([]transactions.SignedTxn{stxn0, stxn1}) + a.NoError(err) + + callConf, err := helper.WaitForTransaction(t, testClient, stxn0.ID().String(), 30*time.Second) + a.NoError(err) + + round := callConf.ConfirmedRound + + deadDood, err := hex.DecodeString("deadd00d") + a.NoError(err) + deadBeef, err := hex.DecodeString("deadbeef") + a.NoError(err) + dd0000dd, err := hex.DecodeString("dd0000dd") + a.NoError(err) + + // get block logs + resp, err = testClient.BlockLogs(*round) + a.NoError(err) + + expected = model.BlockLogsResponse{ + Logs: []model.AppCallLogs{ + { + ApplicationIndex: uint64(createdAppID), + TxId: stxn0.ID().String(), + Logs: [][]byte{dd0000dd, {}, deadDood}, + }, + { + ApplicationIndex: uint64(createdAppID + 3), + TxId: stxn0.ID().String(), + Logs: [][]byte{deadBeef}, + }, + { + ApplicationIndex: uint64(createdAppID), + TxId: stxn1.ID().String(), + Logs: [][]byte{dd0000dd, {}, deadDood}, + }, + { + ApplicationIndex: uint64(createdAppID + 5), + TxId: stxn1.ID().String(), + Logs: [][]byte{deadBeef}, + }, + }, + } + a.Equal(expected, resp) +} 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/fixture.go b/test/framework/fixtures/fixture.go index a307534d3f..7e58b8eb33 100644 --- a/test/framework/fixtures/fixture.go +++ b/test/framework/fixtures/fixture.go @@ -19,6 +19,7 @@ package fixtures import ( "testing" + "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-deadlock" ) @@ -206,3 +207,16 @@ func (st *synchTest) Skipped() bool { defer st.Unlock() return st.t.Skipped() } + +// MultiProtocolTest runs a test for multiple consensus versions. It only runs +// against the *first* version when doing Short tests. +func MultiProtocolTest(t *testing.T, test func(t *testing.T, version protocol.ConsensusVersion), versions ...protocol.ConsensusVersion) { + for _, version := range versions { + t.Run(string(version), func(t *testing.T) { + test(t, version) + }) + if testing.Short() { + break // supply most important version first, probably future + } + } +} diff --git a/test/framework/fixtures/libgoalFixture.go b/test/framework/fixtures/libgoalFixture.go index 474c902186..f1a13111a0 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,20 +67,42 @@ 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) +func (f *LibGoalFixture) Setup(t TestingTB, templateFile string, overrides ...netdeploy.TemplateOverride) { + f.setup(t, t.Name(), templateFile, true, overrides...) } // SetupNoStart is called to initialize the test fixture for the test(s) // but does not start the network before returning. Call NC.Start() to start later. -func (f *LibGoalFixture) SetupNoStart(t TestingTB, templateFile string) { - f.setup(t, t.Name(), templateFile, false) +func (f *LibGoalFixture) SetupNoStart(t TestingTB, templateFile string, overrides ...netdeploy.TemplateOverride) { + f.setup(t, t.Name(), templateFile, false, overrides...) } // SetupShared is called to initialize the test fixture that will be used for multiple tests -func (f *LibGoalFixture) SetupShared(testName string, templateFile string) { - f.setup(nil, testName, templateFile, true) +func (f *LibGoalFixture) SetupShared(testName string, templateFile string, overrides ...netdeploy.TemplateOverride) { + f.setup(nil, testName, templateFile, true, overrides...) } // Genesis returns the genesis data for this fixture @@ -86,7 +110,7 @@ func (f *LibGoalFixture) Genesis() gen.GenesisData { return f.network.Genesis() } -func (f *LibGoalFixture) setup(test TestingTB, testName string, templateFile string, startNetwork bool) { +func (f *LibGoalFixture) setup(test TestingTB, testName string, templateFile string, startNetwork bool, overrides ...netdeploy.TemplateOverride) { // Call initialize for our base implementation f.initialize(f) f.t = SynchronizedTest(test) @@ -98,7 +122,7 @@ func (f *LibGoalFixture) setup(test TestingTB, testName string, templateFile str importKeys := false // Don't automatically import root keys when creating folders, we'll import on-demand file, err := os.Open(templateFile) f.failOnError(err, "Template file could not be opened: %v") - network, err := netdeploy.CreateNetworkFromTemplate("test", f.rootDir, file, f.binDir, importKeys, f.nodeExitWithError, f.consensus, false) + network, err := netdeploy.CreateNetworkFromTemplate("test", f.rootDir, file, f.binDir, importKeys, f.nodeExitWithError, f.consensus, overrides...) f.failOnError(err, "CreateNetworkFromTemplate failed: %v") f.network = network @@ -120,8 +144,10 @@ func (f *LibGoalFixture) nodeExitWithError(nc *nodecontrol.NodeController, err e return } - f.t.Logf("Node at %s has terminated with an error: %v. Dumping logs...", nc.GetDataDir(), err) - f.dumpLogs(filepath.Join(nc.GetDataDir(), "node.log")) + defer func() { + f.t.Logf("Node at %s has terminated with an error: %v. Dumping logs...", nc.GetDataDir(), err) + f.dumpLogs(filepath.Join(nc.GetDataDir(), "node.log")) + }() exitError, ok := err.(*exec.ExitError) if !ok { diff --git a/test/framework/fixtures/restClientFixture.go b/test/framework/fixtures/restClientFixture.go index 40e12c408b..473df25d38 100644 --- a/test/framework/fixtures/restClientFixture.go +++ b/test/framework/fixtures/restClientFixture.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/data/basics" + "github.com/algorand/go-algorand/netdeploy" "github.com/algorand/go-algorand/protocol" "github.com/algorand/go-algorand/daemon/algod/api/client" @@ -44,20 +45,20 @@ type RestClientFixture struct { } // Setup is called to initialize the test fixture for the test(s) -func (f *RestClientFixture) Setup(t TestingTB, templateFile string) { - f.LibGoalFixture.Setup(t, templateFile) +func (f *RestClientFixture) Setup(t TestingTB, templateFile string, overrides ...netdeploy.TemplateOverride) { + f.LibGoalFixture.Setup(t, templateFile, overrides...) f.AlgodClient = f.GetAlgodClientForController(f.NC) } // SetupNoStart is called to initialize the test fixture for the test(s) // but does not start the network before returning. Call NC.Start() to start later. -func (f *RestClientFixture) SetupNoStart(t TestingTB, templateFile string) { - f.LibGoalFixture.SetupNoStart(t, templateFile) +func (f *RestClientFixture) SetupNoStart(t TestingTB, templateFile string, overrides ...netdeploy.TemplateOverride) { + f.LibGoalFixture.SetupNoStart(t, templateFile, overrides...) } // SetupShared is called to initialize the test fixture that will be used for multiple tests -func (f *RestClientFixture) SetupShared(testName string, templateFile string) { - f.LibGoalFixture.SetupShared(testName, templateFile) +func (f *RestClientFixture) SetupShared(testName string, templateFile string, overrides ...netdeploy.TemplateOverride) { + f.LibGoalFixture.SetupShared(testName, templateFile, overrides...) f.AlgodClient = f.GetAlgodClientForController(f.NC) } @@ -196,16 +197,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 +225,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 +271,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 +360,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/heapwatch/requirements.txt b/test/heapwatch/requirements.txt index d8ee41d7e6..9743ce85c1 100644 --- a/test/heapwatch/requirements.txt +++ b/test/heapwatch/requirements.txt @@ -1,4 +1,4 @@ -dash==2.11.1 +dash==2.15.0 dash-table==5.0.0 Jinja2==3.1.3 matplotlib==3.7.2 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-account-asset.sh b/test/scripts/e2e_subs/goal-account-asset.sh index 9df881ca59..3366838d9e 100755 --- a/test/scripts/e2e_subs/goal-account-asset.sh +++ b/test/scripts/e2e_subs/goal-account-asset.sh @@ -41,9 +41,90 @@ ${gcmd} asset optin --account ${ACCOUNTB} --assetid ${ASSET_D_ID} --signer ${ACC # displays held assets ${gcmd} account info -a ${ACCOUNTB} +# wait a few rounds for the asset optins to get into the db, since the account asset info endpoint +# does not look at in memory deltas +${gcmd} clerk send -a 0 -f ${ACCOUNTA} -t ${ACCOUNTB} +${gcmd} clerk send -a 0 -f ${ACCOUNTA} -t ${ACCOUNTB} +${gcmd} clerk send -a 0 -f ${ACCOUNTA} -t ${ACCOUNTB} + +# query account assets w/ details, (1) +RES=$(${gcmd} account assetdetails -a ${ACCOUNTB}) +if [[ ${RES} != *"Account: ${ACCOUNTB}"* ]]; then + date '+goal-account-asset-test assetdetails (1) should be for correct account %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} != *"Asset ID: ${ASSET_A_ID}"$'\n'" Amount: 0"* ]]; then + date '+goal-account-asset-test assetdetails (1) should contain asset A %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} != *"Asset ID: ${ASSET_B_ID}"$'\n'" Amount: 0"* ]]; then + date '+goal-account-asset-test assetdetails (1) should contain asset B %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} != *"Asset ID: ${ASSET_C_ID}"$'\n'" Amount: 0"* ]]; then + date '+goal-account-asset-test assetdetails (1) should contain asset C %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} != *"Asset ID: ${ASSET_D_ID}"$'\n'" Amount: 0"* ]]; then + date '+goal-account-asset-test assetdetails (1) should contain asset D %Y%m%d_%H%M%S' + false +fi + +# query account assets w/ details, limit 2, next set to asset B, (2) +RES=$(${gcmd} account assetdetails -a ${ACCOUNTB} -l 2 -n ${ASSET_B_ID}) +if [[ ${RES} != *"Account: ${ACCOUNTB}"* ]]; then + date '+goal-account-asset-test assetdetails (2) should be for correct account %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} == *"Asset ID: ${ASSET_A_ID}"* ]]; then + date '+goal-account-asset-test assetdetails (2) should not contain asset A %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} == *"Asset ID: ${ASSET_B_ID}"* ]]; then + date '+goal-account-asset-test assetdetails (2) should not contain asset B %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} != *"Asset ID: ${ASSET_C_ID}"$'\n'" Amount: 0"* ]]; then + date '+goal-account-asset-test assetdetails (2) should contain asset C %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} != *"Asset ID: ${ASSET_D_ID}"$'\n'" Amount: 0"* ]]; then + date '+goal-account-asset-test assetdetails (2) should contain asset D %Y%m%d_%H%M%S' + false +fi + # delete one of the asset ${gcmd} asset destroy --assetid ${ASSET_B_ID} --creator ${ACCOUNTA} --signer ${ACCOUNTA} +# wait a few rounds for the deletion to get into the db +${gcmd} clerk send -a 0 -f ${ACCOUNTA} -t ${ACCOUNTB} +${gcmd} clerk send -a 0 -f ${ACCOUNTA} -t ${ACCOUNTB} +${gcmd} clerk send -a 0 -f ${ACCOUNTA} -t ${ACCOUNTB} + +# query account assets w/ details after deletion, (3) +RES=$(${gcmd} account assetdetails -a ${ACCOUNTB}) +if [[ ${RES} != *"Account: ${ACCOUNTB}"* ]]; then + date '+goal-account-asset-test assetdetails (3) should be for correct account %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} != *"Asset ID: ${ASSET_A_ID}"$'\n'" Amount: 0"* ]]; then + date '+goal-account-asset-test assetdetails (3) should contain asset A %Y%m%d_%H%M%S' + false +fi +# ensure asset B is still present, but its params are unavailable +if [[ ${RES} != *"Asset ID: ${ASSET_B_ID}"$'\n'" Amount (without formatting): 0"* ]]; then + date '+goal-account-asset-test assetdetails (3) should contain asset B without asset params %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} != *"Asset ID: ${ASSET_C_ID}"$'\n'" Amount: 0"* ]]; then + date '+goal-account-asset-test assetdetails (3) should contain asset C %Y%m%d_%H%M%S' + false +fi +if [[ ${RES} != *"Asset ID: ${ASSET_D_ID}"$'\n'" Amount: 0"* ]]; then + date '+goal-account-asset-test assetdetails (3) should contain asset D %Y%m%d_%H%M%S' + false +fi + # check account info display RES=$(${gcmd} account info -a ${ACCOUNTB}) 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/block-generator/go.mod b/tools/block-generator/go.mod index 965cbd9a09..29bdbdb4e8 100644 --- a/tools/block-generator/go.mod +++ b/tools/block-generator/go.mod @@ -2,7 +2,9 @@ module github.com/algorand/go-algorand/tools/block-generator replace github.com/algorand/go-algorand => ../.. -go 1.20 +go 1.21 + +toolchain go1.21.10 require ( github.com/algorand/avm-abi v0.2.0 @@ -44,21 +46,19 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect - github.com/flynn/noise v1.0.0 // indirect + github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/gorilla/mux v1.8.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect - github.com/huin/goupnp v1.2.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect + github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect @@ -66,28 +66,26 @@ require ( github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jmespath/go-jmespath v0.3.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/compress v1.17.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/koron/go-ssdp v0.0.4 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect - github.com/libp2p/go-libp2p v0.29.1 // indirect - github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect - github.com/libp2p/go-libp2p-pubsub v0.9.3 // indirect + github.com/libp2p/go-libp2p v0.33.2 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-libp2p-pubsub v0.10.0 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-nat v0.2.0 // indirect github.com/libp2p/go-netroute v0.2.1 // indirect - github.com/libp2p/go-reuseport v0.3.0 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.55 // indirect + github.com/miekg/dns v1.1.58 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -95,49 +93,47 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr v0.10.1 // indirect + github.com/multiformats/go-multiaddr v0.12.3 // indirect github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multicodec v0.9.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect - github.com/multiformats/go-multistream v0.4.1 // indirect + github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/olivere/elastic v6.2.14+incompatible // indirect - github.com/onsi/ginkgo/v2 v2.11.0 // indirect - github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/onsi/ginkgo/v2 v2.15.0 // indirect + github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.6.0 // indirect + github.com/prometheus/common v0.47.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-19 v0.3.3 // indirect - github.com/quic-go/qtls-go1-20 v0.2.3 // indirect - github.com/quic-go/quic-go v0.36.3 // indirect - github.com/quic-go/webtransport-go v0.5.3 // indirect + github.com/quic-go/quic-go v0.42.0 // indirect + github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - go.uber.org/atomic v1.11.0 // indirect - go.uber.org/dig v1.17.0 // indirect - go.uber.org/fx v1.20.0 // indirect + go.uber.org/dig v1.17.1 // indirect + go.uber.org/fx v1.20.1 // indirect + go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.11.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect + golang.org/x/mod v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.18.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/sohlich/elogrus.v3 v3.0.0-20180410122755-1fa29e2f2009 // indirect lukechampine.com/blake3 v1.2.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/tools/block-generator/go.sum b/tools/block-generator/go.sum index dbba8cc727..e34dae4485 100644 --- a/tools/block-generator/go.sum +++ b/tools/block-generator/go.sum @@ -2,45 +2,13 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= @@ -49,11 +17,6 @@ github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKz github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/algorand/avm-abi v0.2.0 h1:bkjsG+BOEcxUcnGSALLosmltE0JZdg+ZisXKx0UDX2k= github.com/algorand/avm-abi v0.2.0/go.mod h1:+CgwM46dithy850bpTeHh9MC99zpn2Snirb3QTl2O/g= github.com/algorand/falcon v0.1.0 h1:xl832kfZ7hHG6B4p90DQynjfKFGbIUgUOnsRiMZXfAo= @@ -82,7 +45,6 @@ github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= @@ -90,19 +52,16 @@ github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edY github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e h1:CHPYEbz71w8DqJ7DRIq+MXyCQsdibK08vdcQTY4ufas= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chrismcguire/gobberish v0.0.0-20150821175641-1d8adb509a0e/go.mod h1:6Xhs0ZlsRjXLIiSMLKafbZxML/j30pg9Z1priLuha5s= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= @@ -144,6 +103,7 @@ github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6Uh github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -158,7 +118,6 @@ github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0 github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= @@ -166,9 +125,10 @@ github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= -github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= +github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -181,21 +141,10 @@ github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -213,104 +162,67 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo= +github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= -github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= +github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= -github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= -github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= -github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= @@ -326,19 +238,12 @@ github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2 github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= @@ -349,16 +254,13 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -371,29 +273,29 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= -github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.29.1 h1:yNeg6XgP8gbdc4YSrwiIt5T1TGOrVjH8dzl8h0GIOfQ= -github.com/libp2p/go-libp2p v0.29.1/go.mod h1:20El+LLy3/YhdUYIvGbLnvVJN32nMdqY6KXBENRAfLY= -github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= -github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= -github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo= -github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc= +github.com/libp2p/go-libp2p v0.33.2 h1:vCdwnFxoGOXMKmaGHlDSnL4bM3fQeW8pgIa9DECnb40= +github.com/libp2p/go-libp2p v0.33.2/go.mod h1:zTeppLuCvUIkT118pFVzA8xzP/p2dJYOMApCkFh0Yww= +github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= +github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= +github.com/libp2p/go-libp2p-pubsub v0.10.0 h1:wS0S5FlISavMaAbxyQn3dxMOe2eegMfswM471RuHJwA= +github.com/libp2p/go-libp2p-pubsub v0.10.0/go.mod h1:1OxbaT/pFRO5h+Dpze8hdHQ63R0ke55XTs6b6NwLLkw= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= -github.com/libp2p/go-reuseport v0.3.0 h1:iiZslO5byUYZEg9iCwJGf5h+sf1Agmqx2V2FDjPyvUw= -github.com/libp2p/go-reuseport v0.3.0/go.mod h1:laea40AimhtfEqysZ71UpYj4S+R9VpH8PgqLo7L+SwI= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= @@ -408,21 +310,19 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -438,11 +338,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= @@ -453,8 +350,8 @@ github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9 github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.10.1 h1:HghtFrWyZEPrpTvgAMFJi6gFdgHfs2cb0pyfDsk+lqU= -github.com/multiformats/go-multiaddr v0.10.1/go.mod h1:jLEZsA61rwWNZQTHHnqq2HNa+4os/Hz54eqiRnsRqYQ= +github.com/multiformats/go-multiaddr v0.12.3 h1:hVBXvPRcKG0w80VinQ23P5t7czWgg65BmIvQKjDydU8= +github.com/multiformats/go-multiaddr v0.12.3/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= @@ -466,13 +363,11 @@ github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI1 github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= -github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= -github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= +github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE= +github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= @@ -484,13 +379,15 @@ github.com/olivere/elastic v6.2.14+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGe github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= +github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= @@ -500,56 +397,35 @@ github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCr github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= +github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k= +github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE= -github.com/quic-go/qtls-go1-19 v0.3.3/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.2.3 h1:m575dovXn1y2ATOb1XrRFcrv0F+EQmlowTkoraNkDPI= -github.com/quic-go/qtls-go1-20 v0.2.3/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/quic-go/quic-go v0.36.3 h1:f+yOqeGhMoRX7/M3wmEw/djhzKWr15FtQysox85/834= -github.com/quic-go/quic-go v0.36.3/go.mod h1:qxQumdeKw5GmWs1OsTZZnOxzSI+RJWuhf1O8FN35L2o= -github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= -github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM= +github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= +github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFDD3NxaZLY= +github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -580,9 +456,6 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= @@ -603,8 +476,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -632,42 +505,35 @@ github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmv github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= -go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= -go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= -go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= +go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= +go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -675,51 +541,29 @@ golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= +golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -727,48 +571,23 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -776,91 +595,55 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -869,51 +652,17 @@ golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= -golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -921,101 +670,38 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= @@ -1027,7 +713,6 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -1040,17 +725,11 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJd honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= pgregory.net/rapid v0.6.2 h1:ErW5sL+UKtfBfUTsWHDCoeB+eZKLKMxrSd1VJY6W4bw= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +pgregory.net/rapid v0.6.2/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= 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 diff --git a/tools/x-repo-types/go.mod b/tools/x-repo-types/go.mod index df9ddf0114..34160acb0e 100644 --- a/tools/x-repo-types/go.mod +++ b/tools/x-repo-types/go.mod @@ -1,6 +1,8 @@ module github.com/algorand/go-algorand/tools/x-repo-types -go 1.20 +go 1.21 + +toolchain go1.21.10 replace github.com/algorand/go-algorand => ../.. diff --git a/util/io.go b/util/io.go index f8290aec40..43e47d3f49 100644 --- a/util/io.go +++ b/util/io.go @@ -24,6 +24,71 @@ import ( "strings" ) +// MoveFile moves a file from src to dst. The advantages of using this over +// os.Rename() is that it can move files across different filesystems. +func MoveFile(src, dst string) error { + err := os.Rename(src, dst) + if err != nil { + // os.Rename() may have failed because src and dst are on different + // filesystems. Let's try to move the file by copying and deleting the + // source file. + return moveFileByCopying(src, dst) + } + return err +} + +func moveFileByCopying(src, dst string) error { + // Lstat is specifically used to detect if src is a symlink. We could + // support moving symlinks by deleting src and creating a new symlink at + // dst, but we don't currently expect to encounter that case, so it has not + // been implemented. + srcInfo, srcErr := os.Lstat(src) + if srcErr != nil { + return srcErr + } + if !srcInfo.Mode().IsRegular() { + return fmt.Errorf("cannot move source file '%s': it is not a regular file (%v)", src, srcInfo.Mode()) + } + + if dstInfo, dstErr := os.Lstat(dst); dstErr == nil { + if dstInfo.Mode().IsDir() { + return fmt.Errorf("cannot move source file '%s' to destination '%s': destination is a directory", src, dst) + } + if os.SameFile(dstInfo, srcInfo) { + return fmt.Errorf("cannot move source file '%s' to destination '%s': source and destination are the same file", src, dst) + } + } + + dstDir := filepath.Dir(dst) + dstBase := filepath.Base(dst) + + tmpDstFile, errTmp := os.CreateTemp(dstDir, dstBase+".tmp-") + if errTmp != nil { + return errTmp + } + tmpDst := tmpDstFile.Name() + if errClose := tmpDstFile.Close(); errClose != nil { + return errClose + } + + if _, err := CopyFile(src, tmpDst); err != nil { + // If the copy fails, try to clean up the temporary file + _ = os.Remove(tmpDst) + return err + } + if err := os.Rename(tmpDst, dst); err != nil { + // If the rename fails, try to clean up the temporary file + _ = os.Remove(tmpDst) + return err + } + if err := os.Remove(src); err != nil { + // Don't try to clean up the destination file here. Duplicate data is + // better than lost/incomplete data. + return fmt.Errorf("failed to remove source file '%s' after moving it to '%s': %w", src, dst, err) + } + return nil +} + // CopyFile uses io.Copy() to copy a file to another location // This was copied from https://opensource.com/article/18/6/copying-files-go func CopyFile(src, dst string) (int64, error) { diff --git a/util/io_test.go b/util/io_test.go index 6f9f6dcfdd..3c587286f7 100644 --- a/util/io_test.go +++ b/util/io_test.go @@ -17,25 +17,185 @@ package util import ( + "fmt" "os" - "path" + "os/exec" + "path/filepath" + "runtime" + "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/algorand/go-algorand/test/partitiontest" ) func TestIsEmpty(t *testing.T) { partitiontest.PartitionTest(t) + t.Parallel() - testPath := path.Join(os.TempDir(), "this", "is", "a", "long", "path") + testPath := filepath.Join(t.TempDir(), "this", "is", "a", "long", "path") err := os.MkdirAll(testPath, os.ModePerm) assert.NoError(t, err) defer os.RemoveAll(testPath) assert.True(t, IsEmpty(testPath)) - _, err = os.Create(path.Join(testPath, "file.txt")) + _, err = os.Create(filepath.Join(testPath, "file.txt")) assert.NoError(t, err) assert.False(t, IsEmpty(testPath)) } + +func testMoveFileSimple(t *testing.T, src, dst string) { + t.Helper() + + require.NoFileExists(t, src) + require.NoFileExists(t, dst) + + defer os.Remove(src) + defer os.Remove(dst) + + f, err := os.Create(src) + require.NoError(t, err) + + _, err = f.WriteString("test file contents") + require.NoError(t, err) + require.NoError(t, f.Close()) + + err = MoveFile(src, dst) + require.NoError(t, err) + + require.FileExists(t, dst) + require.NoFileExists(t, src) + + dstContents, err := os.ReadFile(dst) + require.NoError(t, err) + assert.Equal(t, "test file contents", string(dstContents)) +} + +func TestMoveFile(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + tmpDir := t.TempDir() + + src := filepath.Join(tmpDir, "src.txt") + dst := filepath.Join(tmpDir, "dst.txt") + testMoveFileSimple(t, src, dst) +} + +func execCommand(t *testing.T, cmdAndArsg ...string) { + t.Helper() + + cmd := exec.Command(cmdAndArsg[0], cmdAndArsg[1:]...) + var errOutput strings.Builder + cmd.Stderr = &errOutput + err := cmd.Run() + require.NoError(t, err, errOutput.String()) +} + +func TestMoveFileAcrossFilesystems(t *testing.T) { + partitiontest.PartitionTest(t) + + isLinux := strings.HasPrefix(runtime.GOOS, "linux") + + // Skip unless CIRCLECI or TEST_MOUNT_TMPFS is set, and we are on a linux system + if !isLinux || (os.Getenv("CIRCLECI") == "" && os.Getenv("TEST_MOUNT_TMPFS") == "") { + t.Skip("This test must be run on a linux system with administrator privileges") + } + + mountDir := t.TempDir() + execCommand(t, "sudo", "mount", "-t", "tmpfs", "-o", "size=1K", "tmpfs", mountDir) + + defer execCommand(t, "sudo", "umount", mountDir) + + src := filepath.Join(t.TempDir(), "src.txt") + dst := filepath.Join(mountDir, "dst.txt") + + testMoveFileSimple(t, src, dst) +} + +func TestMoveFileSourceDoesNotExist(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + tmpDir := t.TempDir() + + src := filepath.Join(tmpDir, "src.txt") + dst := filepath.Join(tmpDir, "dst.txt") + + err := MoveFile(src, dst) + var pathError *os.PathError + require.ErrorAs(t, err, &pathError) + require.Equal(t, "lstat", pathError.Op) + require.Equal(t, src, pathError.Path) +} + +func TestMoveFileSourceIsASymlink(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + tmpDir := t.TempDir() + + root := filepath.Join(tmpDir, "root.txt") + src := filepath.Join(tmpDir, "src.txt") + dst := filepath.Join(tmpDir, "dst.txt") + + _, err := os.Create(root) + require.NoError(t, err) + + err = os.Symlink(root, src) + require.NoError(t, err) + + // os.Rename should work in this case + err = MoveFile(src, dst) + require.NoError(t, err) + + // Undo the move + require.NoError(t, MoveFile(dst, src)) + + // But our moveFileByCopying should fail, since we haven't implemented this case + err = moveFileByCopying(src, dst) + require.ErrorContains(t, err, fmt.Sprintf("cannot move source file '%s': it is not a regular file", src)) +} + +func TestMoveFileSourceAndDestinationAreSame(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + tmpDir := t.TempDir() + require.NoError(t, os.Mkdir(filepath.Join(tmpDir, "folder"), os.ModePerm)) + + src := filepath.Join(tmpDir, "src.txt") + dst := src[:len(src)-len("src.txt")] + "folder/../src.txt" + + // dst refers to the same file as src, but with a different path + require.NotEqual(t, src, dst) + require.Equal(t, src, filepath.Clean(dst)) + + _, err := os.Create(src) + require.NoError(t, err) + + // os.Rename can handle this case, but our moveFileByCopying should fail + err = moveFileByCopying(src, dst) + require.ErrorContains(t, err, fmt.Sprintf("cannot move source file '%s' to destination '%s': source and destination are the same file", src, dst)) +} + +func TestMoveFileDestinationIsADirectory(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + tmpDir := t.TempDir() + + src := filepath.Join(tmpDir, "src.txt") + dst := filepath.Join(tmpDir, "dst.txt") + + _, err := os.Create(src) + require.NoError(t, err) + + err = os.Mkdir(dst, os.ModePerm) + require.NoError(t, err) + + err = MoveFile(src, dst) + require.ErrorContains(t, err, fmt.Sprintf("cannot move source file '%s' to destination '%s': destination is a directory", src, dst)) +} diff --git a/util/metrics/couge.go b/util/metrics/couge.go new file mode 100644 index 0000000000..503265efa9 --- /dev/null +++ b/util/metrics/couge.go @@ -0,0 +1,211 @@ +// 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 . + +// Common code for COUnters and gaUGEs. + +package metrics + +import ( + "math" + "strconv" + "strings" + "sync/atomic" + + "github.com/algorand/go-deadlock" +) + +type couge struct { + // Collects value for special fast-path with no labels through Inc(nil) AddUint64(x, nil) + intValue atomic.Uint64 + + deadlock.Mutex + name string + description string + values []*cougeValues + labels map[string]int // map each label ( i.e. httpErrorCode ) to an index. + valuesIndices map[int]int +} + +type cougeValues struct { + value uint64 + labels map[string]string + formattedLabels string +} + +func (cv *cougeValues) createFormattedLabel() { + var buf strings.Builder + if len(cv.labels) < 1 { + return + } + for k, v := range cv.labels { + buf.WriteString("," + k + "=\"" + v + "\"") + } + + cv.formattedLabels = buf.String()[1:] +} + +func (cg *couge) findLabelIndex(labels map[string]string) int { + accumulatedIndex := 0 + for k, v := range labels { + t := k + ":" + v + // do we already have this key ( label ) in our map ? + if i, has := cg.labels[t]; has { + // yes, we do. use this index. + accumulatedIndex += i + } else { + // no, we don't have it. + cg.labels[t] = int(math.Exp2(float64(len(cg.labels)))) + accumulatedIndex += cg.labels[t] + } + } + return accumulatedIndex +} + +func (cg *couge) fastAddUint64(x uint64) { + if cg.intValue.Add(x) == x { + // What we just added is the whole value, this + // is the first Add. Create a dummy + // counterValue for the no-labels value. + // Dummy counterValue simplifies display in WriteMetric. + cg.addLabels(0, nil) + } +} + +// addLabels increases counter by x +func (cg *couge) addLabels(x uint64, labels map[string]string) { + cg.Lock() + defer cg.Unlock() + + labelIndex := cg.findLabelIndex(labels) + + // find where we have the same labels. + if counterIdx, has := cg.valuesIndices[labelIndex]; !has { + // we need to add a new counter. + val := &cougeValues{ + value: x, + labels: labels, + } + val.createFormattedLabel() + cg.values = append(cg.values, val) + cg.valuesIndices[labelIndex] = len(cg.values) - 1 + } else { + // update existing value. + cg.values[counterIdx].value += x + } +} + +// setLabels sets value to x +func (cg *couge) setLabels(x uint64, labels map[string]string) { + cg.Lock() + defer cg.Unlock() + + labelIndex := cg.findLabelIndex(labels) + + // find where we have the same labels. + if counterIdx, has := cg.valuesIndices[labelIndex]; !has { + // we need to set a new value. + val := &cougeValues{ + value: x, + labels: labels, + } + val.createFormattedLabel() + cg.values = append(cg.values, val) + cg.valuesIndices[labelIndex] = len(cg.values) - 1 + } else { + // update existing value. + cg.values[counterIdx].value = x + } +} + +// getUint64ValueForLabels returns the value of the counter for the given labels or 0 if it's not found. +func (cg *couge) getUint64ValueForLabels(labels map[string]string) uint64 { + cg.Lock() + defer cg.Unlock() + + labelIndex := cg.findLabelIndex(labels) + counterIdx, has := cg.valuesIndices[labelIndex] + if !has { + return 0 + } + return cg.values[counterIdx].value +} + +// writeMetric writes the metric into the output stream +func (cg *couge) writeMetric(buf *strings.Builder, metricType string, parentLabels string) { + cg.Lock() + defer cg.Unlock() + + buf.WriteString("# HELP ") + buf.WriteString(cg.name) + buf.WriteString(" ") + buf.WriteString(cg.description) + buf.WriteString("\n# TYPE ") + buf.WriteString(cg.name) + buf.WriteString(" " + metricType + "\n") + // if counter is zero, report 0 using parentLabels and no tags + if len(cg.values) == 0 { + buf.WriteString(cg.name) + if len(parentLabels) > 0 { + buf.WriteString("{" + parentLabels + "}") + } + buf.WriteString(" " + strconv.FormatUint(cg.intValue.Load(), 10)) + buf.WriteString("\n") + return + } + // otherwise iterate through values and write one line per label + for _, l := range cg.values { + buf.WriteString(cg.name) + if len(parentLabels) > 0 || len(l.formattedLabels) > 0 { + buf.WriteString("{") + if len(parentLabels) > 0 { + buf.WriteString(parentLabels) + if len(l.formattedLabels) > 0 { + buf.WriteString(",") + } + } + buf.WriteString(l.formattedLabels) + buf.WriteString("}") + } + value := l.value + if len(l.labels) == 0 { + value += cg.intValue.Load() + } + buf.WriteString(" " + strconv.FormatUint(value, 10)) + buf.WriteString("\n") + } +} + +// addMetric adds the metric into the map +func (cg *couge) addMetric(values map[string]float64) { + cg.Lock() + defer cg.Unlock() + + if len(cg.values) < 1 { + return + } + + for _, l := range cg.values { + sum := l.value + if len(l.labels) == 0 { + sum += cg.intValue.Load() + } + var suffix string + if len(l.formattedLabels) > 0 { + suffix = ":" + l.formattedLabels + } + values[sanitizeTelemetryName(cg.name+suffix)] = float64(sum) + } +} diff --git a/util/metrics/counter.go b/util/metrics/counter.go index 59b1bb2747..38852386d8 100644 --- a/util/metrics/counter.go +++ b/util/metrics/counter.go @@ -17,21 +17,24 @@ package metrics import ( - "math" - "strconv" "strings" "time" ) +// Counter represent a single counter variable. +type Counter struct { + c couge +} + // MakeCounter create a new counter with the provided name and description. func MakeCounter(metric MetricName) *Counter { - c := &Counter{ - values: make([]*counterValues, 0), + c := &Counter{c: couge{ + values: make([]*cougeValues, 0), description: metric.Description, name: metric.Name, labels: make(map[string]int), valuesIndices: make(map[int]int), - } + }} c.Register(nil) return c } @@ -63,32 +66,9 @@ func (counter *Counter) Deregister(reg *Registry) { // Much faster if labels is nil or empty. func (counter *Counter) Inc(labels map[string]string) { if len(labels) == 0 { - counter.fastAddUint64(1) + counter.c.fastAddUint64(1) } else { - counter.addLabels(1.0, labels) - } -} - -// addLabels increases counter by x -func (counter *Counter) addLabels(x uint64, labels map[string]string) { - counter.Lock() - defer counter.Unlock() - - labelIndex := counter.findLabelIndex(labels) - - // find where we have the same labels. - if counterIdx, has := counter.valuesIndices[labelIndex]; !has { - // we need to add a new counter. - val := &counterValues{ - counter: x, - labels: labels, - } - val.createFormattedLabel() - counter.values = append(counter.values, val) - counter.valuesIndices[labelIndex] = len(counter.values) - 1 - } else { - // update existing value. - counter.values[counterIdx].counter += x + counter.c.addLabels(1.0, labels) } } @@ -96,9 +76,9 @@ func (counter *Counter) addLabels(x uint64, labels map[string]string) { // If labels is nil this is much faster than if labels is not nil. func (counter *Counter) AddUint64(x uint64, labels map[string]string) { if len(labels) == 0 { - counter.fastAddUint64(x) + counter.c.fastAddUint64(x) } else { - counter.addLabels(x, labels) + counter.c.addLabels(x, labels) } } @@ -110,122 +90,20 @@ func (counter *Counter) AddMicrosecondsSince(t time.Time, labels map[string]stri // GetUint64Value returns the value of the counter. func (counter *Counter) GetUint64Value() (x uint64) { - return counter.intValue.Load() + return counter.c.intValue.Load() } // GetUint64ValueForLabels returns the value of the counter for the given labels or 0 if it's not found. func (counter *Counter) GetUint64ValueForLabels(labels map[string]string) uint64 { - counter.Lock() - defer counter.Unlock() - - labelIndex := counter.findLabelIndex(labels) - counterIdx, has := counter.valuesIndices[labelIndex] - if !has { - return 0 - } - return counter.values[counterIdx].counter -} - -func (counter *Counter) fastAddUint64(x uint64) { - if counter.intValue.Add(x) == x { - // What we just added is the whole value, this - // is the first Add. Create a dummy - // counterValue for the no-labels value. - // Dummy counterValue simplifies display in WriteMetric. - counter.addLabels(0, nil) - } -} - -func (counter *Counter) findLabelIndex(labels map[string]string) int { - accumulatedIndex := 0 - for k, v := range labels { - t := k + ":" + v - // do we already have this key ( label ) in our map ? - if i, has := counter.labels[t]; has { - // yes, we do. use this index. - accumulatedIndex += i - } else { - // no, we don't have it. - counter.labels[t] = int(math.Exp2(float64(len(counter.labels)))) - accumulatedIndex += counter.labels[t] - } - } - return accumulatedIndex -} - -func (cv *counterValues) createFormattedLabel() { - var buf strings.Builder - if len(cv.labels) < 1 { - return - } - for k, v := range cv.labels { - buf.WriteString("," + k + "=\"" + v + "\"") - } - - cv.formattedLabels = buf.String()[1:] + return counter.c.getUint64ValueForLabels(labels) } // WriteMetric writes the metric into the output stream func (counter *Counter) WriteMetric(buf *strings.Builder, parentLabels string) { - counter.Lock() - defer counter.Unlock() - - buf.WriteString("# HELP ") - buf.WriteString(counter.name) - buf.WriteString(" ") - buf.WriteString(counter.description) - buf.WriteString("\n# TYPE ") - buf.WriteString(counter.name) - buf.WriteString(" counter\n") - // if counter is zero, report 0 using parentLabels and no tags - if len(counter.values) == 0 { - buf.WriteString(counter.name) - if len(parentLabels) > 0 { - buf.WriteString("{" + parentLabels + "}") - } - buf.WriteString(" 0") - buf.WriteString("\n") - return - } - // otherwise iterate through values and write one line per label - for _, l := range counter.values { - buf.WriteString(counter.name) - buf.WriteString("{") - if len(parentLabels) > 0 { - buf.WriteString(parentLabels) - if len(l.formattedLabels) > 0 { - buf.WriteString(",") - } - } - buf.WriteString(l.formattedLabels) - buf.WriteString("} ") - value := l.counter - if len(l.labels) == 0 { - value += counter.intValue.Load() - } - buf.WriteString(strconv.FormatUint(value, 10)) - buf.WriteString("\n") - } + counter.c.writeMetric(buf, "counter", parentLabels) } // AddMetric adds the metric into the map func (counter *Counter) AddMetric(values map[string]float64) { - counter.Lock() - defer counter.Unlock() - - if len(counter.values) < 1 { - return - } - - for _, l := range counter.values { - sum := l.counter - if len(l.labels) == 0 { - sum += counter.intValue.Load() - } - var suffix string - if len(l.formattedLabels) > 0 { - suffix = ":" + l.formattedLabels - } - values[sanitizeTelemetryName(counter.name+suffix)] = float64(sum) - } + counter.c.addMetric(values) } diff --git a/util/metrics/counterCommon.go b/util/metrics/counterCommon.go deleted file mode 100644 index 2ce5468672..0000000000 --- a/util/metrics/counterCommon.go +++ /dev/null @@ -1,42 +0,0 @@ -// 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 metrics - -import ( - "sync/atomic" - - "github.com/algorand/go-deadlock" -) - -// Counter represent a single counter variable. -type Counter struct { - // Collects value for special fast-path with no labels through Inc(nil) AddUint64(x, nil) - intValue atomic.Uint64 - - deadlock.Mutex - name string - description string - values []*counterValues - labels map[string]int // map each label ( i.e. httpErrorCode ) to an index. - valuesIndices map[int]int -} - -type counterValues struct { - counter uint64 - labels map[string]string - formattedLabels string -} diff --git a/util/metrics/counter_test.go b/util/metrics/counter_test.go index 1e1fa2e16b..c987624cb7 100644 --- a/util/metrics/counter_test.go +++ b/util/metrics/counter_test.go @@ -231,3 +231,42 @@ func TestGetValueForLabels(t *testing.T) { c.Inc(labels2) require.Equal(t, uint64(1), c.GetUint64ValueForLabels(labels2)) } + +func TestCounterLabels(t *testing.T) { + partitiontest.PartitionTest(t) + + m := MakeCounter(MetricName{Name: "testname", Description: "testhelp"}) + m.Deregister(nil) + + m.AddUint64(1, map[string]string{"a": "b"}) + m.AddUint64(10, map[string]string{"c": "d"}) + m.AddUint64(1, map[string]string{"a": "b"}) + m.AddUint64(5, nil) + + require.Equal(t, uint64(2), m.GetUint64ValueForLabels(map[string]string{"a": "b"})) + require.Equal(t, uint64(10), m.GetUint64ValueForLabels(map[string]string{"c": "d"})) + + buf := strings.Builder{} + m.WriteMetric(&buf, "") + res := buf.String() + require.Contains(t, res, `testname{a="b"} 2`) + require.Contains(t, res, `testname{c="d"} 10`) + require.Contains(t, res, `testname 5`) + require.Equal(t, 1, strings.Count(res, "# HELP testname testhelp")) + require.Equal(t, 1, strings.Count(res, "# TYPE testname counter")) + + buf = strings.Builder{} + m.WriteMetric(&buf, `p1=v1,p2="v2"`) + res = buf.String() + require.Contains(t, res, `testname{p1=v1,p2="v2",a="b"} 2`) + require.Contains(t, res, `testname{p1=v1,p2="v2",c="d"} 10`) + + m = MakeCounter(MetricName{Name: "testname2", Description: "testhelp2"}) + m.Deregister(nil) + + m.AddUint64(101, nil) + buf = strings.Builder{} + m.WriteMetric(&buf, "") + res = buf.String() + require.Contains(t, res, `testname2 101`) +} diff --git a/util/metrics/gauge.go b/util/metrics/gauge.go index 6b43075ef7..bbc143a14f 100644 --- a/util/metrics/gauge.go +++ b/util/metrics/gauge.go @@ -17,24 +17,23 @@ package metrics import ( - "strconv" "strings" - "sync/atomic" ) // Gauge represent a single gauge variable. type Gauge struct { - value atomic.Uint64 - name string - description string + g couge } // MakeGauge create a new gauge with the provided name and description. func MakeGauge(metric MetricName) *Gauge { - c := &Gauge{ - description: metric.Description, - name: metric.Name, - } + c := &Gauge{g: couge{ + values: make([]*cougeValues, 0), + description: metric.Description, + name: metric.Name, + labels: make(map[string]int), + valuesIndices: make(map[int]int), + }} c.Register(nil) return c } @@ -57,39 +56,32 @@ func (gauge *Gauge) Deregister(reg *Registry) { } } -// Add increases gauge by x -func (gauge *Gauge) Add(x uint64) { - gauge.value.Add(x) -} - // Set sets gauge to x func (gauge *Gauge) Set(x uint64) { - gauge.value.Store(x) + if gauge.g.intValue.Swap(x) == 0 { + // This is the first Set. Create a dummy + // counterValue for the no-labels value. + // Dummy counterValue simplifies display in WriteMetric. + gauge.g.setLabels(0, nil) + } +} + +// SetLabels sets gauge to x with labels +func (gauge *Gauge) SetLabels(x uint64, labels map[string]string) { + gauge.g.setLabels(x, labels) } // WriteMetric writes the metric into the output stream func (gauge *Gauge) WriteMetric(buf *strings.Builder, parentLabels string) { - buf.WriteString("# HELP ") - buf.WriteString(gauge.name) - buf.WriteString(" ") - buf.WriteString(gauge.description) - buf.WriteString("\n# TYPE ") - buf.WriteString(gauge.name) - buf.WriteString(" gauge\n") - buf.WriteString(gauge.name) - buf.WriteString("{") - if len(parentLabels) > 0 { - buf.WriteString(parentLabels) - } - buf.WriteString("} ") - value := gauge.value.Load() - buf.WriteString(strconv.FormatUint(value, 10)) - buf.WriteString("\n") + gauge.g.writeMetric(buf, "gauge", parentLabels) } // AddMetric adds the metric into the map func (gauge *Gauge) AddMetric(values map[string]float64) { - value := gauge.value.Load() + gauge.g.addMetric(values) +} - values[sanitizeTelemetryName(gauge.name)] = float64(value) +// GetUint64ValueForLabels returns the value of the counter for the given labels or 0 if it's not found. +func (gauge *Gauge) GetUint64ValueForLabels(labels map[string]string) uint64 { + return gauge.g.getUint64ValueForLabels(labels) } diff --git a/util/metrics/gauge_test.go b/util/metrics/gauge_test.go index 098d20193e..8470362e82 100644 --- a/util/metrics/gauge_test.go +++ b/util/metrics/gauge_test.go @@ -52,8 +52,7 @@ func TestMetricGauge(t *testing.T) { gauges[i] = MakeGauge(MetricName{Name: fmt.Sprintf("gauge_%d", i), Description: "this is the metric test for gauge object"}) } for i := 0; i < 9; i++ { - gauges[i%3].Set(uint64(i * 100)) - gauges[i%3].Add(uint64(i)) + gauges[i%3].Set(uint64(i*100 + i)) // wait half-a cycle time.Sleep(test.sampleRate / 2) } @@ -86,3 +85,42 @@ func TestMetricGauge(t *testing.T) { } } } + +func TestGaugeLabels(t *testing.T) { + partitiontest.PartitionTest(t) + + m := MakeGauge(MetricName{Name: "testname", Description: "testhelp"}) + m.Deregister(nil) + + m.SetLabels(1, map[string]string{"a": "b"}) + m.SetLabels(10, map[string]string{"c": "d"}) + m.SetLabels(2, map[string]string{"a": "b"}) + m.SetLabels(5, nil) + + require.Equal(t, uint64(2), m.GetUint64ValueForLabels(map[string]string{"a": "b"})) + require.Equal(t, uint64(10), m.GetUint64ValueForLabels(map[string]string{"c": "d"})) + + buf := strings.Builder{} + m.WriteMetric(&buf, "") + res := buf.String() + require.Contains(t, res, `testname{a="b"} 2`) + require.Contains(t, res, `testname{c="d"} 10`) + require.Contains(t, res, `testname 5`) + require.Equal(t, 1, strings.Count(res, "# HELP testname testhelp")) + require.Equal(t, 1, strings.Count(res, "# TYPE testname gauge")) + + buf = strings.Builder{} + m.WriteMetric(&buf, `p1=v1,p2="v2"`) + res = buf.String() + require.Contains(t, res, `testname{p1=v1,p2="v2",a="b"} 2`) + require.Contains(t, res, `testname{p1=v1,p2="v2",c="d"} 10`) + + m = MakeGauge(MetricName{Name: "testname2", Description: "testhelp2"}) + m.Deregister(nil) + + m.Set(101) + buf = strings.Builder{} + m.WriteMetric(&buf, "") + res = buf.String() + require.Contains(t, res, `testname2 101`) +}