diff --git a/agreement/abstractions.go b/agreement/abstractions.go index de3e438912..2a1e247d2d 100644 --- a/agreement/abstractions.go +++ b/agreement/abstractions.go @@ -54,16 +54,6 @@ type BlockValidator interface { // and can now be recorded in the ledger. This is an optimized version of // calling EnsureBlock() on the Ledger. type ValidatedBlock interface { - // WithProposer creates a copy of this ValidatedBlock with its - // cryptographically random seed and proposer set. The block's - // ProposerPayout is zero'd if !eligible. Abstractly, it is how the - // agreement code "finishes" a block and makes it a proposal for a specific - // account. - // - // Calls to Seed() or to Digest() on the copy's Block must - // reflect the value of the new seed. - WithProposer(seed committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock - // Block returns the underlying block that has been validated. Block() bookkeeping.Block } @@ -87,7 +77,24 @@ type BlockFactory interface { // produce a ValidatedBlock for the given round. If an insufficient number of // nodes on the network can assemble entries, the agreement protocol may // lose liveness. - AssembleBlock(basics.Round) (ValidatedBlock, error) + AssembleBlock(basics.Round) (AssembledBlock, error) +} + +// An AssembledBlock represents a Block produced by a BlockFactory +// to be included in a proposal by agreement. +type AssembledBlock interface { + // WithProposer creates a copy of this AssembledBlock with its + // cryptographically random seed and proposer set. The block's + // ProposerPayout is zero'd if !eligible. Abstractly, it is how the + // agreement code "finishes" a block and makes it a proposal for a specific + // account. + // + // Calls to Seed() or to Digest() on the copy's Block must + // reflect the value of the new seed. + WithProposer(seed committee.Seed, proposer basics.Address, eligible bool) AssembledBlock + + // Block returns the underlying block that has been assembled. + Block() bookkeeping.Block } // A Ledger represents the sequence of Entries agreed upon by the protocol. diff --git a/agreement/agreementtest/simulate_test.go b/agreement/agreementtest/simulate_test.go index ed1bcd736f..c048b61668 100644 --- a/agreement/agreementtest/simulate_test.go +++ b/agreement/agreementtest/simulate_test.go @@ -79,7 +79,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -98,7 +98,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.ValidatedBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.AssembledBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } diff --git a/agreement/common_test.go b/agreement/common_test.go index 1c961bc3ff..eb37490440 100644 --- a/agreement/common_test.go +++ b/agreement/common_test.go @@ -165,7 +165,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) AssembledBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -184,7 +184,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (ValidatedBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round) (AssembledBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } diff --git a/agreement/fuzzer/ledger_test.go b/agreement/fuzzer/ledger_test.go index e080142d9e..36c5da277a 100644 --- a/agreement/fuzzer/ledger_test.go +++ b/agreement/fuzzer/ledger_test.go @@ -93,7 +93,7 @@ func (b testValidatedBlock) Block() bookkeeping.Block { return b.Inside } -func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { +func (b testValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { b.Inside.BlockHeader.Seed = s b.Inside.BlockHeader.Proposer = proposer if !eligible { @@ -112,7 +112,7 @@ type testBlockFactory struct { Owner int } -func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.ValidatedBlock, error) { +func (f testBlockFactory) AssembleBlock(r basics.Round) (agreement.AssembledBlock, error) { return testValidatedBlock{Inside: bookkeeping.Block{BlockHeader: bookkeeping.BlockHeader{Round: r}}}, nil } diff --git a/agreement/proposal.go b/agreement/proposal.go index b28a05f9e2..58647f0a8a 100644 --- a/agreement/proposal.go +++ b/agreement/proposal.go @@ -102,14 +102,24 @@ type proposal struct { validatedAt time.Duration } -func makeProposal(ve ValidatedBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { +func makeProposalFromAssembledBlock(blk AssembledBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { + e := blk.Block() + var payload unauthenticatedProposal + payload.Block = e + payload.SeedProof = pf + payload.OriginalPeriod = origPer + payload.OriginalProposer = origProp + return proposal{unauthenticatedProposal: payload} +} + +func makeProposalFromValidatedBlock(ve ValidatedBlock, pf crypto.VrfProof, origPer period, origProp basics.Address) proposal { e := ve.Block() var payload unauthenticatedProposal payload.Block = e payload.SeedProof = pf payload.OriginalPeriod = origPer payload.OriginalProposer = origProp - return proposal{unauthenticatedProposal: payload, ve: ve} + return proposal{unauthenticatedProposal: payload, ve: ve} // store ve to use when calling Ledger.EnsureValidatedBlock } func (p proposal) u() unauthenticatedProposal { @@ -285,8 +295,8 @@ func payoutEligible(rnd basics.Round, proposer basics.Address, ledger LedgerRead return eligible, balanceRecord, nil } -func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve ValidatedBlock, period period, ledger LedgerReader) (proposal, proposalValue, error) { - rnd := ve.Block().Round() +func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, blk AssembledBlock, period period, ledger LedgerReader) (proposal, proposalValue, error) { + rnd := blk.Block().Round() cparams, err := ledger.ConsensusParams(ParamsRound(rnd)) if err != nil { @@ -303,16 +313,16 @@ func proposalForBlock(address basics.Address, vrf *crypto.VRFSecrets, ve Validat return proposal{}, proposalValue{}, fmt.Errorf("proposalForBlock: could determine eligibility: %w", err) } - ve = ve.WithProposer(newSeed, address, eligible) - proposal := makeProposal(ve, seedProof, period, address) - proposal.ve = nil + blk = blk.WithProposer(newSeed, address, eligible) + prop := makeProposalFromAssembledBlock(blk, seedProof, period, address) + value := proposalValue{ OriginalPeriod: period, OriginalProposer: address, - BlockDigest: proposal.Block.Digest(), - EncodingDigest: crypto.HashObj(proposal), + BlockDigest: prop.Block.Digest(), + EncodingDigest: crypto.HashObj(prop), } - return proposal, value, nil + return prop, value, nil } // validate returns true if the proposal is valid. @@ -335,5 +345,5 @@ func (p unauthenticatedProposal) validate(ctx context.Context, current round, le return invalid, fmt.Errorf("EntryValidator rejected entry: %w", err) } - return makeProposal(ve, p.SeedProof, p.OriginalPeriod, p.OriginalProposer), nil + return makeProposalFromValidatedBlock(ve, p.SeedProof, p.OriginalPeriod, p.OriginalProposer), nil } diff --git a/data/bookkeeping/block.go b/data/bookkeeping/block.go index b16ee08758..7f2632f3f0 100644 --- a/data/bookkeeping/block.go +++ b/data/bookkeeping/block.go @@ -313,6 +313,24 @@ func (block Block) ProposerPayout() basics.MicroAlgos { return block.BlockHeader.ProposerPayout } +// WithProposer returns a copy of the Block with a modified seed and associated proposer +func (block Block) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) Block { + newblock := block + newblock.BlockHeader.Seed = s + // agreement is telling us who the proposer is and if they're eligible, but + // agreement does not consider the current config params, so here we decide + // what really goes into the BlockHeader. + proto := config.Consensus[block.CurrentProtocol] + if proto.Payouts.Enabled { + newblock.BlockHeader.Proposer = proposer + } + if !proto.Payouts.Enabled || !eligible { + newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} + } + + return newblock +} + // NextRewardsState computes the RewardsState of the subsequent round // given the subsequent consensus parameters, along with the incentive pool // balance and the total reward units in the system as of the current round. diff --git a/data/datatest/impls.go b/data/datatest/impls.go index f1c41bc0cc..c834f99d2d 100644 --- a/data/datatest/impls.go +++ b/data/datatest/impls.go @@ -53,7 +53,7 @@ type entryFactoryImpl struct { } // AssembleBlock implements Ledger.AssembleBlock. -func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.ValidatedBlock, error) { +func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.AssembledBlock, error) { prev, err := i.l.BlockHdr(round - 1) if err != nil { return nil, fmt.Errorf("could not make proposals: could not read block from ledger at round %v: %v", round, err) @@ -64,8 +64,8 @@ func (i entryFactoryImpl) AssembleBlock(round basics.Round) (agreement.Validated return validatedBlock{blk: &b}, nil } -// WithProposer implements the agreement.ValidatedBlock interface. -func (ve validatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { +// WithProposer implements the agreement.AssembledBlock interface. +func (ve validatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { newblock := *ve.blk newblock.BlockHeader.Seed = s newblock.BlockHeader.Proposer = proposer diff --git a/ledger/eval/eval_test.go b/ledger/eval/eval_test.go index fefa936f2b..b45556aebf 100644 --- a/ledger/eval/eval_test.go +++ b/ledger/eval/eval_test.go @@ -990,7 +990,7 @@ func (ledger *evalTestLedger) endBlock(t testing.TB, eval *BlockEvaluator) *ledg // fake agreement's setting of header fields so later validates work. seed := committee.Seed{} crypto.RandBytes(seed[:]) - *validatedBlock = validatedBlock.WithProposer(seed, testPoolAddr, true) + *validatedBlock = ledgercore.MakeValidatedBlock(validatedBlock.Block().WithProposer(seed, testPoolAddr, true), validatedBlock.Delta()) err = ledger.AddValidatedBlock(*validatedBlock, agreement.Certificate{}) require.NoError(t, err) return validatedBlock @@ -1212,7 +1212,7 @@ func TestEvalFunctionForExpiredAccounts(t *testing.T) { require.NoError(t, err) // fake agreement's setting of header fields so later validates work - *validatedBlock = validatedBlock.WithProposer(committee.Seed{}, testPoolAddr, true) + *validatedBlock = ledgercore.MakeValidatedBlock(validatedBlock.Block().WithProposer(committee.Seed{}, testPoolAddr, true), validatedBlock.Delta()) expired := false for _, acct := range validatedBlock.Block().ExpiredParticipationAccounts { @@ -1454,7 +1454,7 @@ func TestAbsenteeChecks(t *testing.T) { require.NoError(t, err) // fake agreement's setting of header fields so later validates work - *validatedBlock = validatedBlock.WithProposer(committee.Seed{}, testPoolAddr, true) + *validatedBlock = ledgercore.MakeValidatedBlock(validatedBlock.Block().WithProposer(committee.Seed{}, testPoolAddr, true), validatedBlock.Delta()) require.Zero(t, validatedBlock.Block().ExpiredParticipationAccounts) require.Contains(t, validatedBlock.Block().AbsentParticipationAccounts, addrs[0], addrs[0].String()) diff --git a/ledger/ledgercore/validatedBlock.go b/ledger/ledgercore/validatedBlock.go index 44cf41e2fa..8d4962fd3b 100644 --- a/ledger/ledgercore/validatedBlock.go +++ b/ledger/ledgercore/validatedBlock.go @@ -17,10 +17,7 @@ package ledgercore import ( - "github.com/algorand/go-algorand/config" - "github.com/algorand/go-algorand/data/basics" "github.com/algorand/go-algorand/data/bookkeeping" - "github.com/algorand/go-algorand/data/committee" ) // ValidatedBlock represents the result of a block validation. It can @@ -41,27 +38,6 @@ func (vb ValidatedBlock) Delta() StateDelta { return vb.delta } -// WithProposer returns a copy of the ValidatedBlock with a modified seed and associated proposer -func (vb ValidatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) ValidatedBlock { - newblock := vb.blk - newblock.BlockHeader.Seed = s - // agreement is telling us who the proposer is and if they're eligible, but - // agreement does not consider the current config params, so here we decide - // what really goes into the BlockHeader. - proto := config.Consensus[vb.blk.CurrentProtocol] - if proto.Payouts.Enabled { - newblock.BlockHeader.Proposer = proposer - } - if !proto.Payouts.Enabled || !eligible { - newblock.BlockHeader.ProposerPayout = basics.MicroAlgos{} - } - - return ValidatedBlock{ - blk: newblock, - delta: vb.delta, - } -} - // MakeValidatedBlock creates a validated block. func MakeValidatedBlock(blk bookkeeping.Block, delta StateDelta) ValidatedBlock { return ValidatedBlock{ diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 555736dbf3..456d1c56fa 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -161,11 +161,11 @@ func endBlock(t testing.TB, ledger *Ledger, eval *eval.BlockEvaluator, proposer // can't call the agreement code, the eligibility of the prp is not // considered. if ledger.GenesisProto().Payouts.Enabled { - *gvb = gvb.WithProposer(committee.Seed(prp), prp, true) + *gvb = ledgercore.MakeValidatedBlock(vb.Block().WithProposer(committee.Seed(prp), prp, true), vb.Delta()) } else { // To more closely mimic the agreement code, we don't // write the proposer when !Payouts.Enabled. - *gvb = gvb.WithProposer(committee.Seed(prp), basics.Address{}, false) + *gvb = ledgercore.MakeValidatedBlock(vb.Block().WithProposer(committee.Seed(prp), basics.Address{}, false), vb.Delta()) } vvb, err := validateWithoutSignatures(t, ledger, gvb.Block()) diff --git a/node/node.go b/node/node.go index 8703bd6175..95f3eb257e 100644 --- a/node/node.go +++ b/node/node.go @@ -1290,20 +1290,30 @@ type validatedBlock struct { vb *ledgercore.ValidatedBlock } -// WithProposer satisfies the agreement.ValidatedBlock interface. -func (vb validatedBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.ValidatedBlock { - lvb := vb.vb.WithProposer(s, proposer, eligible) - return validatedBlock{vb: &lvb} -} - // Block satisfies the agreement.ValidatedBlock interface. func (vb validatedBlock) Block() bookkeeping.Block { blk := vb.vb.Block() return blk } +// assembledBlock satisfies agreement.AssembledBlock +type assembledBlock struct { + blk bookkeeping.Block +} + +// WithProposer satisfies the agreement.ValidatedBlock interface. +func (ab assembledBlock) WithProposer(s committee.Seed, proposer basics.Address, eligible bool) agreement.AssembledBlock { + nb := ab.blk.WithProposer(s, proposer, eligible) + return assembledBlock{blk: nb} +} + +// Block satisfies the agreement.AssembledBlock interface. +func (ab assembledBlock) Block() bookkeeping.Block { + return ab.blk +} + // AssembleBlock implements Ledger.AssembleBlock. -func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.ValidatedBlock, error) { +func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.AssembledBlock, error) { deadline := time.Now().Add(node.config.ProposalAssemblyTime) lvb, err := node.transactionPool.AssembleBlock(round, deadline) if err != nil { @@ -1324,7 +1334,7 @@ func (node *AlgorandFullNode) AssembleBlock(round basics.Round) (agreement.Valid } return nil, err } - return validatedBlock{vb: lvb}, nil + return assembledBlock{blk: lvb.Block()}, nil } // getOfflineClosedStatus will return an int with the appropriate bit(s) set if it is offline and/or online