From f86eb2080c480db1e80b81a8cbe4b416462b7fc9 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 1 Dec 2020 16:07:22 -0800 Subject: [PATCH 001/178] add QuorumCertificate method to Snapshot --- state/protocol/badger/snapshot.go | 127 +++++++++++++++++++++--------- state/protocol/mock/snapshot.go | 23 ++++++ state/protocol/snapshot.go | 4 + 3 files changed, 116 insertions(+), 38 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index f2d1071bac6..573fe320b56 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -41,6 +41,88 @@ func (s *Snapshot) Head() (*flow.Header, error) { return head, err } +// QuorumCertificate returns a valid quorum certificate for the block at the +// head of this snapshot, if one exists. +// +// For root block snapshots, returns the root quorum certificate. For all other +// blocks, generates a quorum certificate from a valid child, if one exists. +func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { + + // CASE 1: for the root block, return the root QC + root, err := s.state.Params().Root() + if err != nil { + return nil, fmt.Errorf("could not get root: %w", err) + } + + head, err := s.Head() + if err != nil { + return nil, fmt.Errorf("could not get head: %w", err) + } + if head.Height == root.Height { + // TODO store root QC and return here + return nil, fmt.Errorf("root qc not stored") + } + + // CASE 2: for any other block, generate the root QC from a valid child + child, err := s.validChild() + if err != nil { + return nil, fmt.Errorf("could not get child: %w", err) + } + + qc := &flow.QuorumCertificate{ + View: head.View, + BlockID: child.ParentID, + SignerIDs: child.ParentVoterIDs, + SigData: child.ParentVoterSig, + } + + return qc, nil +} + +// validChild returns a child of the snapshot head that has been validated +// by HotStuff, if such a child exists. Otherwise returns an error. Any valid +// child may be returned. Subsequent calls are not guaranteed to return the +// same child. +func (s *Snapshot) validChild() (*flow.Header, error) { + + var childIDs []flow.Identifier + err := s.state.db.View(procedure.LookupBlockChildren(s.blockID, &childIDs)) + if err != nil { + return nil, fmt.Errorf("could not look up children: %w", err) + } + + // check we have at least one child + if len(childIDs) == 0 { + return nil, state.NewNoValidChildBlockError("block doesn't have children yet") + } + + // find the first child that has been validated + var validChildID flow.Identifier + for _, childID := range childIDs { + var valid bool + err = s.state.db.View(operation.RetrieveBlockValidity(childID, &valid)) + // skip blocks whose validity hasn't been checked yet + if errors.Is(err, storage.ErrNotFound) { + continue + } + if err != nil { + return nil, fmt.Errorf("could not get child validity: %w", err) + } + if valid { + validChildID = childID + break + } + } + + if validChildID == flow.ZeroID { + return nil, state.NewNoValidChildBlockError("block has no valid children") + } + + // get the header of the first child + child, err := s.state.headers.ByBlockID(validChildID) + return child, err +} + func (s *Snapshot) Phase() (flow.EpochPhase, error) { status, err := s.state.epoch.statuses.ByBlockID(s.blockID) if err != nil { @@ -197,47 +279,12 @@ func (s *Snapshot) pending(blockID flow.Identifier) ([]flow.Identifier, error) { // Seed returns the random seed at the given indices for the current block snapshot. func (s *Snapshot) Seed(indices ...uint32) ([]byte, error) { - // get the current state snapshot head - var childrenIDs []flow.Identifier - err := s.state.db.View(procedure.LookupBlockChildren(s.blockID, &childrenIDs)) - if err != nil { - return nil, fmt.Errorf("could not look up children: %w", err) - } - - // check we have at least one child - if len(childrenIDs) == 0 { - return nil, state.NewNoValidChildBlockError("block doesn't have children yet") - } - - // find the first child that has been validated - var validChildID flow.Identifier - for _, childID := range childrenIDs { - var valid bool - err = s.state.db.View(operation.RetrieveBlockValidity(childID, &valid)) - // skip blocks whose validity hasn't been checked yet - if errors.Is(err, storage.ErrNotFound) { - continue - } - if err != nil { - return nil, fmt.Errorf("could not get child validity: %w", err) - } - if valid { - validChildID = childID - break - } - } - - if validChildID == flow.ZeroID { - return nil, state.NewNoValidChildBlockError("block has no valid children") - } - - // get the header of the first child (they all have the same threshold sig) - head, err := s.state.headers.ByBlockID(validChildID) + child, err := s.validChild() if err != nil { - return nil, fmt.Errorf("could not get head: %w", err) + return nil, fmt.Errorf("could not get child: %w", err) } - seed, err := seed.FromParentSignature(indices, head.ParentVoterSig) + seed, err := seed.FromParentSignature(indices, child.ParentVoterSig) if err != nil { return nil, fmt.Errorf("could not create seed from header's signature: %w", err) } @@ -346,6 +393,10 @@ func (u *InvalidSnapshot) Head() (*flow.Header, error) { return nil, u.err } +func (u *InvalidSnapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { + return nil, u.err +} + func (u *InvalidSnapshot) Phase() (flow.EpochPhase, error) { return 0, u.err } diff --git a/state/protocol/mock/snapshot.go b/state/protocol/mock/snapshot.go index e5483121a3c..0e3430e0cab 100644 --- a/state/protocol/mock/snapshot.go +++ b/state/protocol/mock/snapshot.go @@ -166,6 +166,29 @@ func (_m *Snapshot) Phase() (flow.EpochPhase, error) { return r0, r1 } +// QuorumCertificate provides a mock function with given fields: +func (_m *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { + ret := _m.Called() + + var r0 *flow.QuorumCertificate + if rf, ok := ret.Get(0).(func() *flow.QuorumCertificate); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.QuorumCertificate) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Seed provides a mock function with given fields: indices func (_m *Snapshot) Seed(indices ...uint32) ([]byte, error) { _va := make([]interface{}, len(indices)) diff --git a/state/protocol/snapshot.go b/state/protocol/snapshot.go index eda6f1d1881..6975c8830fd 100644 --- a/state/protocol/snapshot.go +++ b/state/protocol/snapshot.go @@ -19,6 +19,10 @@ type Snapshot interface { // we should build the next block in the context of the selected state. Head() (*flow.Header, error) + // QuorumCertificate returns a valid quorum certificate for the header at + // this snapshot, if one exists. + QuorumCertificate() (*flow.QuorumCertificate, error) + // Identities returns a list of identities at the selected point of the // protocol state history. At the beginning of an epoch, this list includes // identities from the previous epoch that are un-staking during the current From 963cf200f84c920261c57d21a399661306d1e144 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 1 Dec 2020 16:24:51 -0800 Subject: [PATCH 002/178] update snapshot test --- state/protocol/badger/snapshot_test.go | 69 +++++++++++++++++++++----- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index fc81b7a3635..b8c13c58b33 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -129,19 +129,27 @@ func TestClusters(t *testing.T) { }) } -func TestSeed(t *testing.T) { +// test retrieving quorum certificate and seed +func TestQuorumCertificate(t *testing.T) { - // should not be able to get random beacon seed from a block with no children + // should not be able to get QC or random beacon seed from a block with no children t.Run("no children", func(t *testing.T) { util.RunWithProtocolState(t, func(db *badger.DB, state *bprotocol.State) { identities := unittest.IdentityListFixture(5, unittest.WithAllRoles()) root, result, seal := unittest.BootstrapFixture(identities) + err := state.Mutate().Bootstrap(root, result, seal) - require.NoError(t, err) + require.Nil(t, err) + block1 := unittest.BlockWithParentFixture(root.Header) + block1.SetPayload(flow.EmptyPayload()) + err = state.Mutate().Extend(&block1) + require.Nil(t, err) - _, err = state.Final().(*bprotocol.Snapshot).Seed(1, 2, 3, 4) - t.Log(err) + _, err = state.AtBlockID(block1.ID()).QuorumCertificate() + assert.Error(t, err) + + _, err = state.AtBlockID(block1.ID()).Seed(1, 2, 3, 4) assert.Error(t, err) }) }) @@ -156,25 +164,60 @@ func TestSeed(t *testing.T) { err := state.Mutate().Bootstrap(root, result, seal) require.NoError(t, err) + block1 := unittest.BlockWithParentFixture(root.Header) + block1.SetPayload(flow.EmptyPayload()) + err = state.Mutate().Extend(&block1) + require.Nil(t, err) // add child - unvalidatedChild := unittest.BlockWithParentFixture(root.Header) - unvalidatedChild.Payload.Guarantees = nil - unvalidatedChild.Header.PayloadHash = unvalidatedChild.Payload.Hash() + unvalidatedChild := unittest.BlockWithParentFixture(block1.Header) + unvalidatedChild.SetPayload(flow.EmptyPayload()) err = state.Mutate().Extend(&unvalidatedChild) assert.Nil(t, err) - _, err = state.Final().(*bprotocol.Snapshot).Seed(1, 2, 3, 4) - t.Log(err) + _, err = state.AtBlockID(block1.ID()).QuorumCertificate() + assert.Error(t, err) + + _, err = state.AtBlockID(block1.ID()).Seed(1, 2, 3, 4) assert.Error(t, err) }) }) - // should be able to get random beacon seed from a block with a valid child - t.Run("valid child", func(t *testing.T) { - t.Skip() + // should be able to get QC and random beacon seed from root block + t.Run("root block", func(t *testing.T) { // TODO }) + + // should be able to get QC and random beacon seed from a block with a valid child + t.Run("valid child", func(t *testing.T) { + util.RunWithProtocolState(t, func(db *badger.DB, state *bprotocol.State) { + + identities := unittest.IdentityListFixture(5, unittest.WithAllRoles()) + root, result, seal := unittest.BootstrapFixture(identities) + + err := state.Mutate().Bootstrap(root, result, seal) + require.NoError(t, err) + + // add a block so we aren't testing against root + block1 := unittest.BlockWithParentFixture(root.Header) + block1.SetPayload(flow.EmptyPayload()) + err = state.Mutate().Extend(&block1) + require.Nil(t, err) + + // add a valid child to block1 + block2 := unittest.BlockWithParentFixture(block1.Header) + err = state.Mutate().Extend(&block2) + require.Nil(t, err) + err = state.Mutate().MarkValid(block2.ID()) + require.Nil(t, err) + + _, err = state.AtBlockID(block1.ID()).QuorumCertificate() + assert.Error(t, err) + + _, err = state.AtBlockID(block1.ID()).Seed(1, 2, 3, 4) + require.Nil(t, err) + }) + }) } // Test querying identities in different epoch phases. During staking phase we From d79719329e76aa394aade1e66e564d06fbca69fc Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 1 Dec 2020 16:42:28 -0800 Subject: [PATCH 003/178] update snapshot tests for quorum certificate method --- state/protocol/badger/snapshot_test.go | 9 +++++++-- utils/unittest/fixtures.go | 13 ++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index b8c13c58b33..2b1b3173973 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -206,13 +206,18 @@ func TestQuorumCertificate(t *testing.T) { // add a valid child to block1 block2 := unittest.BlockWithParentFixture(block1.Header) + block2.SetPayload(flow.EmptyPayload()) err = state.Mutate().Extend(&block2) require.Nil(t, err) + err = state.Mutate().MarkValid(block1.ID()) + require.Nil(t, err) err = state.Mutate().MarkValid(block2.ID()) require.Nil(t, err) - _, err = state.AtBlockID(block1.ID()).QuorumCertificate() - assert.Error(t, err) + qc, err := state.AtBlockID(block1.ID()).QuorumCertificate() + assert.Nil(t, err) + assert.Equal(t, block2.Header.ParentVoterIDs, qc.SignerIDs) + assert.Equal(t, block2.Header.ParentVoterSig.Bytes(), qc.SigData) _, err = state.AtBlockID(block1.ID()).Seed(1, 2, 3, 4) require.Nil(t, err) diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index d670283a1d3..f96ae68dec3 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -7,6 +7,7 @@ import ( "time" "github.com/onflow/flow-go/model/chunks" + "github.com/onflow/flow-go/module/signature" sdk "github.com/onflow/flow-go-sdk" @@ -252,7 +253,7 @@ func BlockHeaderWithParentFixture(parent *flow.Header) flow.Header { Timestamp: time.Now().UTC(), View: view, ParentVoterIDs: IdentifierListFixture(4), - ParentVoterSig: SignatureFixture(), + ParentVoterSig: CombinedSignatureFixture(2), ProposerID: IdentifierFixture(), ProposerSig: SignatureFixture(), } @@ -735,6 +736,16 @@ func SignatureFixture() crypto.Signature { return sig } +func CombinedSignatureFixture(n int) crypto.Signature { + sigs := SignaturesFixture(n) + combiner := signature.NewCombiner() + sig, err := combiner.Join(sigs...) + if err != nil { + panic(err) + } + return sig +} + func SignaturesFixture(n int) []crypto.Signature { var sigs []crypto.Signature for i := 0; i < n; i++ { From 495e91041d0fb1b82fb9b340e1ce9abed00a1d79 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 1 Dec 2020 17:57:07 -0800 Subject: [PATCH 004/178] add serializable models for snapshot --- state/protocol/serializable/cluster.go | 21 ++++++++ state/protocol/serializable/dkg.go | 28 +++++++++++ state/protocol/serializable/epoch.go | 64 +++++++++++++++++++++++++ state/protocol/serializable/snapshot.go | 64 +++++++++++++++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 state/protocol/serializable/cluster.go create mode 100644 state/protocol/serializable/dkg.go create mode 100644 state/protocol/serializable/epoch.go create mode 100644 state/protocol/serializable/snapshot.go diff --git a/state/protocol/serializable/cluster.go b/state/protocol/serializable/cluster.go new file mode 100644 index 00000000000..41e51d716a2 --- /dev/null +++ b/state/protocol/serializable/cluster.go @@ -0,0 +1,21 @@ +package serializable + +import ( + clustermodel "github.com/onflow/flow-go/model/cluster" + "github.com/onflow/flow-go/model/flow" +) + +type cluster struct { + index uint + counter uint64 + members flow.IdentityList + rootBlock *clustermodel.Block + rootQC *flow.QuorumCertificate +} + +func (c cluster) Index() uint { return c.index } +func (c cluster) ChainID() flow.ChainID { return c.rootBlock.Header.ChainID } +func (c cluster) EpochCounter() uint64 { return c.counter } +func (c cluster) Members() flow.IdentityList { return c.members } +func (c cluster) RootBlock() *clustermodel.Block { return c.rootBlock } +func (c cluster) RootQC() *flow.QuorumCertificate { return c.rootQC } diff --git a/state/protocol/serializable/dkg.go b/state/protocol/serializable/dkg.go new file mode 100644 index 00000000000..7dcdb193964 --- /dev/null +++ b/state/protocol/serializable/dkg.go @@ -0,0 +1,28 @@ +package serializable + +import ( + "github.com/onflow/flow-go/crypto" + "github.com/onflow/flow-go/model/flow" +) + +type dkg struct { + size uint + groupKey crypto.PublicKey + participants map[flow.Identifier]flow.DKGParticipant +} + +func (d *dkg) Size() uint { + return d.size +} + +func (d *dkg) GroupKey() crypto.PublicKey { + return d.groupKey +} + +func (d *dkg) Index(nodeID flow.Identifier) (uint, error) { + return d.participants[nodeID].Index, nil +} + +func (d *dkg) KeyShare(nodeID flow.Identifier) (crypto.PublicKey, error) { + return d.participants[nodeID].KeyShare, nil +} diff --git a/state/protocol/serializable/epoch.go b/state/protocol/serializable/epoch.go new file mode 100644 index 00000000000..ffbcd4aee70 --- /dev/null +++ b/state/protocol/serializable/epoch.go @@ -0,0 +1,64 @@ +package serializable + +import ( + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/seed" +) + +type epoch struct { + counter uint64 + firstView uint64 + finalView uint64 + seed []byte + initialIdentities flow.IdentityList + clustering flow.ClusterList + clusters []cluster + dkg dkg +} + +type epochQuery struct { + prev *epoch + curr *epoch + next *epoch +} + +func (e *epoch) Counter() (uint64, error) { + return e.counter, nil +} + +func (e *epoch) FinalView() (uint64, error) { + return e.finalView, nil +} + +func (e *epoch) Seed(indices ...uint32) ([]byte, error) { + return seed.FromRandomSource(indices, e.seed) +} + +func (e *epoch) InitialIdentities() (flow.IdentityList, error) { + return e.initialIdentities, nil +} + +func (e *epoch) Clustering() (flow.ClusterList, error) { + return e.clustering, nil +} + +func (e *epoch) Cluster(i uint) (protocol.Cluster, error) { + return e.clusters[i], nil +} + +func (e *epoch) DKG() (protocol.DKG, error) { + return e.dkg +} + +func (eq *epochQuery) Previous() protocol.Epoch { + return eq.prev +} + +func (eq *epochQuery) Current() protocol.Epoch { + return eq.curr +} + +func (eq *epochQuery) Next() protocol.Epoch { + return eq.next +} diff --git a/state/protocol/serializable/snapshot.go b/state/protocol/serializable/snapshot.go new file mode 100644 index 00000000000..c7567a4141d --- /dev/null +++ b/state/protocol/serializable/snapshot.go @@ -0,0 +1,64 @@ +package serializable + +import ( + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/seed" +) + +var ( + _ protocol.Snapshot = new(snapshot) + _ protocol.EpochQuery = new(epochQuery) + _ protocol.Epoch = new(epoch) + _ protocol.Cluster = new(cluster) +) + +type snapshot struct { + head *flow.Header + identities flow.IdentityList + commit flow.StateCommitment + qc *flow.QuorumCertificate + phase flow.EpochPhase + epochs epochQuery +} + +func (s *snapshot) Head() (*flow.Header, error) { + return s.head, nil +} + +func (s *snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { + return s.qc, nil +} + +func (s *snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, error) { + return s.identities.Filter(selector), nil +} + +func (s *snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { + identity, ok := s.identities.ByNodeID(nodeID) + if !ok { + return nil, protocol.IdentityNotFoundErr{NodeID: nodeID} + } + return identity, nil +} + +func (s *snapshot) Commit() (flow.StateCommitment, error) { + return s.commit, nil +} + +func (s *snapshot) Pending() ([]flow.Identifier, error) { + // canonical snapshots don't consider the pending tree + return nil, nil +} + +func (s *snapshot) Phase() (flow.EpochPhase, error) { + return s.phase, nil +} + +func (s *snapshot) Seed(indices ...uint32) ([]byte, error) { + return seed.FromParentSignature(indices, s.qc.SigData) +} + +func (s *snapshot) Epochs() protocol.EpochQuery { + return s.epochs +} From b76ccdfe7b49f808ad168c573e95c38e9a9d8b37 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 13:38:13 -0800 Subject: [PATCH 005/178] complete implementation of protocol interfaces --- state/protocol/serializable/epoch.go | 8 ++++++-- state/protocol/serializable/snapshot.go | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/state/protocol/serializable/epoch.go b/state/protocol/serializable/epoch.go index ffbcd4aee70..df05bcf19d9 100644 --- a/state/protocol/serializable/epoch.go +++ b/state/protocol/serializable/epoch.go @@ -14,7 +14,7 @@ type epoch struct { initialIdentities flow.IdentityList clustering flow.ClusterList clusters []cluster - dkg dkg + dkg *dkg } type epochQuery struct { @@ -27,6 +27,10 @@ func (e *epoch) Counter() (uint64, error) { return e.counter, nil } +func (e *epoch) FirstView() (uint64, error) { + return e.firstView, nil +} + func (e *epoch) FinalView() (uint64, error) { return e.finalView, nil } @@ -48,7 +52,7 @@ func (e *epoch) Cluster(i uint) (protocol.Cluster, error) { } func (e *epoch) DKG() (protocol.DKG, error) { - return e.dkg + return e.dkg, nil } func (eq *epochQuery) Previous() protocol.Epoch { diff --git a/state/protocol/serializable/snapshot.go b/state/protocol/serializable/snapshot.go index c7567a4141d..03996db8209 100644 --- a/state/protocol/serializable/snapshot.go +++ b/state/protocol/serializable/snapshot.go @@ -19,7 +19,7 @@ type snapshot struct { commit flow.StateCommitment qc *flow.QuorumCertificate phase flow.EpochPhase - epochs epochQuery + epochs *epochQuery } func (s *snapshot) Head() (*flow.Header, error) { @@ -37,7 +37,7 @@ func (s *snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, func (s *snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { identity, ok := s.identities.ByNodeID(nodeID) if !ok { - return nil, protocol.IdentityNotFoundErr{NodeID: nodeID} + return nil, protocol.IdentityNotFoundError{NodeID: nodeID} } return identity, nil } From 69e35daa03a4fc0ac4b359f84a92787eb5751cc3 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 16:06:21 -0800 Subject: [PATCH 006/178] add encodable models for protocol snapshot --- model/encodable/cluster.go | 14 ++++++++++++++ model/encodable/dkg.go | 11 +++++++++++ model/encodable/epoch.go | 21 +++++++++++++++++++++ model/encodable/snapshot.go | 14 ++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 model/encodable/cluster.go create mode 100644 model/encodable/dkg.go create mode 100644 model/encodable/epoch.go create mode 100644 model/encodable/snapshot.go diff --git a/model/encodable/cluster.go b/model/encodable/cluster.go new file mode 100644 index 00000000000..36b418fcc5c --- /dev/null +++ b/model/encodable/cluster.go @@ -0,0 +1,14 @@ +package encodable + +import ( + "github.com/onflow/flow-go/model/cluster" + "github.com/onflow/flow-go/model/flow" +) + +type Cluster struct { + Index uint + Counter uint64 + Members flow.IdentityList + RootBlock *cluster.Block + RootQC *flow.QuorumCertificate +} diff --git a/model/encodable/dkg.go b/model/encodable/dkg.go new file mode 100644 index 00000000000..d143276c51d --- /dev/null +++ b/model/encodable/dkg.go @@ -0,0 +1,11 @@ +package encodable + +import ( + "github.com/onflow/flow-go/model/flow" +) + +type DKG struct { + Size uint + GroupKey RandomBeaconPubKey + Participants map[flow.Identifier]flow.DKGParticipant +} diff --git a/model/encodable/epoch.go b/model/encodable/epoch.go new file mode 100644 index 00000000000..d0991699f8e --- /dev/null +++ b/model/encodable/epoch.go @@ -0,0 +1,21 @@ +package encodable + +import ( + "github.com/onflow/flow-go/model/flow" +) + +type Epochs struct { + Previous Epoch + Current Epoch + Next Epoch +} + +type Epoch struct { + Counter uint64 + FirstView uint64 + FinalView uint64 + Seed []byte + InitialIdentities flow.IdentityList + Clusters []Cluster + DKG DKG +} diff --git a/model/encodable/snapshot.go b/model/encodable/snapshot.go new file mode 100644 index 00000000000..7fac5fff9e7 --- /dev/null +++ b/model/encodable/snapshot.go @@ -0,0 +1,14 @@ +package encodable + +import ( + "github.com/onflow/flow-go/model/flow" +) + +type Snapshot struct { + Head *flow.Header + Identities flow.IdentityList + Commit flow.StateCommitment + QuorumCertificate *flow.QuorumCertificate + Phase flow.EpochPhase + Epochs Epochs +} From 516ba76583eeaa8764236f8566240fa9ada6bc11 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 16:07:32 -0800 Subject: [PATCH 007/178] use encodable models as base of in-memory snapshot --- state/protocol/serializable/cluster.go | 21 +++---- state/protocol/serializable/convert.go | 9 +++ state/protocol/serializable/dkg.go | 31 +++++----- state/protocol/serializable/epoch.go | 77 +++++++++---------------- state/protocol/serializable/snapshot.go | 54 ++++++++--------- 5 files changed, 88 insertions(+), 104 deletions(-) create mode 100644 state/protocol/serializable/convert.go diff --git a/state/protocol/serializable/cluster.go b/state/protocol/serializable/cluster.go index 41e51d716a2..2994364e70f 100644 --- a/state/protocol/serializable/cluster.go +++ b/state/protocol/serializable/cluster.go @@ -2,20 +2,17 @@ package serializable import ( clustermodel "github.com/onflow/flow-go/model/cluster" + "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" ) -type cluster struct { - index uint - counter uint64 - members flow.IdentityList - rootBlock *clustermodel.Block - rootQC *flow.QuorumCertificate +type Cluster struct { + encodable.Cluster } -func (c cluster) Index() uint { return c.index } -func (c cluster) ChainID() flow.ChainID { return c.rootBlock.Header.ChainID } -func (c cluster) EpochCounter() uint64 { return c.counter } -func (c cluster) Members() flow.IdentityList { return c.members } -func (c cluster) RootBlock() *clustermodel.Block { return c.rootBlock } -func (c cluster) RootQC() *flow.QuorumCertificate { return c.rootQC } +func (c Cluster) Index() uint { return c.Cluster.Index } +func (c Cluster) ChainID() flow.ChainID { return c.Cluster.RootBlock.Header.ChainID } +func (c Cluster) EpochCounter() uint64 { return c.Cluster.Counter } +func (c Cluster) Members() flow.IdentityList { return c.Cluster.Members } +func (c Cluster) RootBlock() *clustermodel.Block { return c.Cluster.RootBlock } +func (c Cluster) RootQC() *flow.QuorumCertificate { return c.RootQC() } diff --git a/state/protocol/serializable/convert.go b/state/protocol/serializable/convert.go new file mode 100644 index 00000000000..05b63899bf7 --- /dev/null +++ b/state/protocol/serializable/convert.go @@ -0,0 +1,9 @@ +package serializable + +import ( + "github.com/onflow/flow-go/state/protocol" +) + +func FromSnapshot(snapshot protocol.Snapshot) (*Snapshot, error) { + panic("TODO") +} diff --git a/state/protocol/serializable/dkg.go b/state/protocol/serializable/dkg.go index 7dcdb193964..623c223f0f8 100644 --- a/state/protocol/serializable/dkg.go +++ b/state/protocol/serializable/dkg.go @@ -2,27 +2,30 @@ package serializable import ( "github.com/onflow/flow-go/crypto" + "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" ) type dkg struct { - size uint - groupKey crypto.PublicKey - participants map[flow.Identifier]flow.DKGParticipant + encodable.DKG } -func (d *dkg) Size() uint { - return d.size -} - -func (d *dkg) GroupKey() crypto.PublicKey { - return d.groupKey -} +func (d dkg) Size() uint { return d.DKG.Size } +func (d dkg) GroupKey() crypto.PublicKey { return d.DKG.GroupKey.PublicKey } -func (d *dkg) Index(nodeID flow.Identifier) (uint, error) { - return d.participants[nodeID].Index, nil +func (d dkg) Index(nodeID flow.Identifier) (uint, error) { + part, exists := d.DKG.Participants[nodeID] + if !exists { + return 0, protocol.IdentityNotFoundError{NodeID: nodeID} + } + return part.Index, nil } -func (d *dkg) KeyShare(nodeID flow.Identifier) (crypto.PublicKey, error) { - return d.participants[nodeID].KeyShare, nil +func (d dkg) KeyShare(nodeID flow.Identifier) (crypto.PublicKey, error) { + part, exists := d.DKG.Participants[nodeID] + if !exists { + return nil, protocol.IdentityNotFoundError{NodeID: nodeID} + } + return part.KeyShare, nil } diff --git a/state/protocol/serializable/epoch.go b/state/protocol/serializable/epoch.go index df05bcf19d9..fda8c1c67a1 100644 --- a/state/protocol/serializable/epoch.go +++ b/state/protocol/serializable/epoch.go @@ -1,68 +1,47 @@ package serializable import ( + "fmt" + + "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/state/protocol/seed" ) -type epoch struct { - counter uint64 - firstView uint64 - finalView uint64 - seed []byte - initialIdentities flow.IdentityList - clustering flow.ClusterList - clusters []cluster - dkg *dkg -} - -type epochQuery struct { - prev *epoch - curr *epoch - next *epoch -} - -func (e *epoch) Counter() (uint64, error) { - return e.counter, nil -} - -func (e *epoch) FirstView() (uint64, error) { - return e.firstView, nil +type Epoch struct { + encodable.Epoch } -func (e *epoch) FinalView() (uint64, error) { - return e.finalView, nil -} +func (e Epoch) Counter() (uint64, error) { return e.Epoch.Counter, nil } +func (e Epoch) FirstView() (uint64, error) { return e.Epoch.FirstView, nil } +func (e Epoch) FinalView() (uint64, error) { return e.Epoch.FinalView, nil } +func (e Epoch) InitialIdentities() (flow.IdentityList, error) { return e.Epoch.InitialIdentities, nil } +func (e Epoch) DKG() (protocol.DKG, error) { return dkg{e.Epoch.DKG}, nil } -func (e *epoch) Seed(indices ...uint32) ([]byte, error) { - return seed.FromRandomSource(indices, e.seed) +func (e Epoch) Seed(indices ...uint32) ([]byte, error) { + return seed.FromRandomSource(indices, e.Epoch.Seed) } -func (e *epoch) InitialIdentities() (flow.IdentityList, error) { - return e.initialIdentities, nil +func (e Epoch) Clustering() (flow.ClusterList, error) { + var clusters flow.ClusterList + for _, cluster := range e.Epoch.Clusters { + clusters = append(clusters, cluster.Members) + } + return clusters, nil } -func (e *epoch) Clustering() (flow.ClusterList, error) { - return e.clustering, nil +func (e Epoch) Cluster(i uint) (protocol.Cluster, error) { + if i >= uint(len(e.Epoch.Clusters)) { + return nil, fmt.Errorf("no cluster with index %d", i) + } + return Cluster{e.Epoch.Clusters[i]}, nil } -func (e *epoch) Cluster(i uint) (protocol.Cluster, error) { - return e.clusters[i], nil +type Epochs struct { + encodable.Epochs } -func (e *epoch) DKG() (protocol.DKG, error) { - return e.dkg, nil -} - -func (eq *epochQuery) Previous() protocol.Epoch { - return eq.prev -} - -func (eq *epochQuery) Current() protocol.Epoch { - return eq.curr -} - -func (eq *epochQuery) Next() protocol.Epoch { - return eq.next -} +func (eq Epochs) Previous() protocol.Epoch { return Epoch{eq.Epochs.Previous} } +func (eq Epochs) Current() protocol.Epoch { return Epoch{eq.Epochs.Current} } +func (eq Epochs) Next() protocol.Epoch { return Epoch{eq.Epochs.Next} } diff --git a/state/protocol/serializable/snapshot.go b/state/protocol/serializable/snapshot.go index 03996db8209..78dd8449817 100644 --- a/state/protocol/serializable/snapshot.go +++ b/state/protocol/serializable/snapshot.go @@ -1,64 +1,60 @@ package serializable import ( + "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/state/protocol/seed" ) var ( - _ protocol.Snapshot = new(snapshot) - _ protocol.EpochQuery = new(epochQuery) - _ protocol.Epoch = new(epoch) - _ protocol.Cluster = new(cluster) + _ protocol.Snapshot = new(Snapshot) + _ protocol.EpochQuery = new(Epochs) + _ protocol.Epoch = new(Epoch) + _ protocol.Cluster = new(Cluster) ) -type snapshot struct { - head *flow.Header - identities flow.IdentityList - commit flow.StateCommitment - qc *flow.QuorumCertificate - phase flow.EpochPhase - epochs *epochQuery +type Snapshot struct { + encodable.Snapshot } -func (s *snapshot) Head() (*flow.Header, error) { - return s.head, nil +func (s *Snapshot) Head() (*flow.Header, error) { + return s.Snapshot.Head, nil } -func (s *snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { - return s.qc, nil +func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { + return s.Snapshot.QuorumCertificate, nil } -func (s *snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, error) { - return s.identities.Filter(selector), nil +func (s *Snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, error) { + return s.Snapshot.Identities.Filter(selector), nil } -func (s *snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { - identity, ok := s.identities.ByNodeID(nodeID) +func (s *Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { + identity, ok := s.Snapshot.Identities.ByNodeID(nodeID) if !ok { return nil, protocol.IdentityNotFoundError{NodeID: nodeID} } return identity, nil } -func (s *snapshot) Commit() (flow.StateCommitment, error) { - return s.commit, nil +func (s *Snapshot) Commit() (flow.StateCommitment, error) { + return s.Snapshot.Commit, nil } -func (s *snapshot) Pending() ([]flow.Identifier, error) { - // canonical snapshots don't consider the pending tree +func (s *Snapshot) Pending() ([]flow.Identifier, error) { + // canonical snapshots don't have any pending blocks return nil, nil } -func (s *snapshot) Phase() (flow.EpochPhase, error) { - return s.phase, nil +func (s *Snapshot) Phase() (flow.EpochPhase, error) { + return s.Snapshot.Phase, nil } -func (s *snapshot) Seed(indices ...uint32) ([]byte, error) { - return seed.FromParentSignature(indices, s.qc.SigData) +func (s *Snapshot) Seed(indices ...uint32) ([]byte, error) { + return seed.FromParentSignature(indices, s.Snapshot.QuorumCertificate.SigData) } -func (s *snapshot) Epochs() protocol.EpochQuery { - return s.epochs +func (s *Snapshot) Epochs() protocol.EpochQuery { + return Epochs{s.Snapshot.Epochs} } From 410eee3a98763360fb20b5af64a07a2122b30e22 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 16:12:54 -0800 Subject: [PATCH 008/178] rename package to inmem --- state/protocol/{serializable => inmem}/cluster.go | 2 +- state/protocol/{serializable => inmem}/convert.go | 2 +- state/protocol/{serializable => inmem}/dkg.go | 2 +- state/protocol/{serializable => inmem}/epoch.go | 2 +- state/protocol/{serializable => inmem}/snapshot.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename state/protocol/{serializable => inmem}/cluster.go (97%) rename state/protocol/{serializable => inmem}/convert.go (87%) rename state/protocol/{serializable => inmem}/dkg.go (97%) rename state/protocol/{serializable => inmem}/epoch.go (98%) rename state/protocol/{serializable => inmem}/snapshot.go (98%) diff --git a/state/protocol/serializable/cluster.go b/state/protocol/inmem/cluster.go similarity index 97% rename from state/protocol/serializable/cluster.go rename to state/protocol/inmem/cluster.go index 2994364e70f..c86e29cec6e 100644 --- a/state/protocol/serializable/cluster.go +++ b/state/protocol/inmem/cluster.go @@ -1,4 +1,4 @@ -package serializable +package inmem import ( clustermodel "github.com/onflow/flow-go/model/cluster" diff --git a/state/protocol/serializable/convert.go b/state/protocol/inmem/convert.go similarity index 87% rename from state/protocol/serializable/convert.go rename to state/protocol/inmem/convert.go index 05b63899bf7..07aaac3834c 100644 --- a/state/protocol/serializable/convert.go +++ b/state/protocol/inmem/convert.go @@ -1,4 +1,4 @@ -package serializable +package inmem import ( "github.com/onflow/flow-go/state/protocol" diff --git a/state/protocol/serializable/dkg.go b/state/protocol/inmem/dkg.go similarity index 97% rename from state/protocol/serializable/dkg.go rename to state/protocol/inmem/dkg.go index 623c223f0f8..5978b59f378 100644 --- a/state/protocol/serializable/dkg.go +++ b/state/protocol/inmem/dkg.go @@ -1,4 +1,4 @@ -package serializable +package inmem import ( "github.com/onflow/flow-go/crypto" diff --git a/state/protocol/serializable/epoch.go b/state/protocol/inmem/epoch.go similarity index 98% rename from state/protocol/serializable/epoch.go rename to state/protocol/inmem/epoch.go index fda8c1c67a1..d958ceaa452 100644 --- a/state/protocol/serializable/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -1,4 +1,4 @@ -package serializable +package inmem import ( "fmt" diff --git a/state/protocol/serializable/snapshot.go b/state/protocol/inmem/snapshot.go similarity index 98% rename from state/protocol/serializable/snapshot.go rename to state/protocol/inmem/snapshot.go index 78dd8449817..9f918fba06b 100644 --- a/state/protocol/serializable/snapshot.go +++ b/state/protocol/inmem/snapshot.go @@ -1,4 +1,4 @@ -package serializable +package inmem import ( "github.com/onflow/flow-go/model/encodable" From 4a06296390b206db4b0485c59e8fea50b641f3c6 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 16:18:41 -0800 Subject: [PATCH 009/178] godocs --- model/encodable/cluster.go | 1 + model/encodable/dkg.go | 2 ++ model/encodable/epoch.go | 2 ++ model/encodable/snapshot.go | 1 + state/protocol/inmem/snapshot.go | 3 +++ 5 files changed, 9 insertions(+) diff --git a/model/encodable/cluster.go b/model/encodable/cluster.go index 36b418fcc5c..1fdf231aa67 100644 --- a/model/encodable/cluster.go +++ b/model/encodable/cluster.go @@ -5,6 +5,7 @@ import ( "github.com/onflow/flow-go/model/flow" ) +// Cluster is the encoding format for protocol.Cluster type Cluster struct { Index uint Counter uint64 diff --git a/model/encodable/dkg.go b/model/encodable/dkg.go index d143276c51d..148112f0e9b 100644 --- a/model/encodable/dkg.go +++ b/model/encodable/dkg.go @@ -4,6 +4,8 @@ import ( "github.com/onflow/flow-go/model/flow" ) +// DKG is the encoding format for protocol.DKG +// TODO consolidate with encodable in flow/epoch.go type DKG struct { Size uint GroupKey RandomBeaconPubKey diff --git a/model/encodable/epoch.go b/model/encodable/epoch.go index d0991699f8e..222dcc071a9 100644 --- a/model/encodable/epoch.go +++ b/model/encodable/epoch.go @@ -4,12 +4,14 @@ import ( "github.com/onflow/flow-go/model/flow" ) +// Epochs is the encoding format for protocol.EpochQuery type Epochs struct { Previous Epoch Current Epoch Next Epoch } +// Epoch is the encoding format for protocol.Epoch type Epoch struct { Counter uint64 FirstView uint64 diff --git a/model/encodable/snapshot.go b/model/encodable/snapshot.go index 7fac5fff9e7..3dddb0fe20a 100644 --- a/model/encodable/snapshot.go +++ b/model/encodable/snapshot.go @@ -4,6 +4,7 @@ import ( "github.com/onflow/flow-go/model/flow" ) +// Snapshot is the encoding format for protocol.Snapshot type Snapshot struct { Head *flow.Header Identities flow.IdentityList diff --git a/state/protocol/inmem/snapshot.go b/state/protocol/inmem/snapshot.go index 9f918fba06b..3351f3edf23 100644 --- a/state/protocol/inmem/snapshot.go +++ b/state/protocol/inmem/snapshot.go @@ -14,6 +14,9 @@ var ( _ protocol.Cluster = new(Cluster) ) +// Snapshot is a memory-backed implementation of protocol.Snapshot. The snapshot +// data is stored in the embedded encodable snapshot model, which defines the +// canonical structure of an encoded snapshot for the purposes of serialization. type Snapshot struct { encodable.Snapshot } From fae3a2ee6df96334c2df3060f5c78ef6f97482ba Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 17:29:41 -0800 Subject: [PATCH 010/178] expose RandomSource in epoch --- model/encodable/epoch.go | 2 +- state/protocol/badger/epoch.go | 4 ++++ state/protocol/epoch.go | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/model/encodable/epoch.go b/model/encodable/epoch.go index 222dcc071a9..36f31dbf716 100644 --- a/model/encodable/epoch.go +++ b/model/encodable/epoch.go @@ -16,7 +16,7 @@ type Epoch struct { Counter uint64 FirstView uint64 FinalView uint64 - Seed []byte + RandomSource []byte InitialIdentities flow.IdentityList Clusters []Cluster DKG DKG diff --git a/state/protocol/badger/epoch.go b/state/protocol/badger/epoch.go index 8f2990ff5d4..f78cf30df66 100644 --- a/state/protocol/badger/epoch.go +++ b/state/protocol/badger/epoch.go @@ -60,6 +60,10 @@ func (es *SetupEpoch) DKG() (protocol.DKG, error) { return nil, fmt.Errorf("EpochCommit event not yet received in fork") } +func (es *SetupEpoch) RandomSource() ([]byte, error) { + return es.setupEvent.RandomSource, nil +} + func (es *SetupEpoch) Seed(indices ...uint32) ([]byte, error) { return seed.FromRandomSource(indices, es.setupEvent.RandomSource) } diff --git a/state/protocol/epoch.go b/state/protocol/epoch.go index 8f7e17eb2ee..fd2e3f991ab 100644 --- a/state/protocol/epoch.go +++ b/state/protocol/epoch.go @@ -52,6 +52,9 @@ type Epoch interface { // FinalView returns the largest view number which still belongs to this epoch. FinalView() (uint64, error) + // RandomSource returns the underlying random source of this epoch. + RandomSource() ([]byte, error) + // Seed generates a random seed using the source of randomness for this // epoch, specified in the EpochSetup service event. Seed(indices ...uint32) ([]byte, error) From bc46fab222d1e5d7b6984b0b1db8b329fe0616ef Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 17:31:31 -0800 Subject: [PATCH 011/178] add conversion to in-memory snapshot --- state/protocol/inmem/convert.go | 158 +++++++++++++++++++++++++++++++- state/protocol/inmem/dkg.go | 10 +- state/protocol/inmem/epoch.go | 5 +- 3 files changed, 164 insertions(+), 9 deletions(-) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 07aaac3834c..89f90832f85 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -1,9 +1,163 @@ package inmem import ( + "github.com/onflow/flow-go/model/encodable" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/state/protocol" ) -func FromSnapshot(snapshot protocol.Snapshot) (*Snapshot, error) { - panic("TODO") +// FromSnapshot generates a memory-backed snapshot from the input snapshot. +// Typically, this would be used to convert a database-backed snapshot to +// one that can easily be serialized to disk or to network. +func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { + + var ( + snap encodable.Snapshot + err error + ) + + // convert top-level fields + snap.Head, err = from.Head() + if err != nil { + return nil, err + } + snap.Identities, err = from.Identities(filter.Any) + if err != nil { + return nil, err + } + snap.Commit, err = from.Commit() + if err != nil { + return nil, err + } + snap.QuorumCertificate, err = from.QuorumCertificate() + if err != nil { + return nil, err + } + snap.Phase, err = from.Phase() + if err != nil { + return nil, err + } + + // convert epochs + previous, err := FromEpoch(from.Epochs().Previous()) + if err != nil { + return nil, err + } + snap.Epochs.Previous = previous.Epoch + current, err := FromEpoch(from.Epochs().Current()) + if err != nil { + return nil, err + } + snap.Epochs.Current = current.Epoch + next, err := FromEpoch(from.Epochs().Next()) + if err != nil { + return nil, err + } + snap.Epochs.Next = next.Epoch + + return &Snapshot{snap}, nil +} + +// FromEpoch converts any protocol.Protocol to a memory-backed Epoch. +func FromEpoch(from protocol.Epoch) (*Epoch, error) { + + var ( + epoch encodable.Epoch + err error + ) + + // convert top-level fields + epoch.Counter, err = from.Counter() + if err != nil { + return nil, err + } + epoch.InitialIdentities, err = from.InitialIdentities() + if err != nil { + return nil, err + } + epoch.FirstView, err = from.FirstView() + if err != nil { + return nil, err + } + epoch.FinalView, err = from.FinalView() + if err != nil { + return nil, err + } + epoch.RandomSource, err = from.RandomSource() + if err != nil { + return nil, err + } + + // convert dkg + dkg, err := from.DKG() + if err != nil { + return nil, err + } + convertedDKG, err := FromDKG(dkg, epoch.InitialIdentities.Filter(filter.HasRole(flow.RoleConsensus))) + if err != nil { + return nil, err + } + epoch.DKG = convertedDKG.DKG + + // convert clusters + clustering, err := from.Clustering() + if err != nil { + return nil, err + } + for index := range clustering { + cluster, err := from.Cluster(uint(index)) + if err != nil { + return nil, err + } + convertedCluster, err := FromCluster(cluster) + if err != nil { + return nil, err + } + epoch.Clusters = append(epoch.Clusters, convertedCluster.Cluster) + } + + return &Epoch{epoch}, nil +} + +// FromCluster converts any protocol.Cluster to a memory-backed Cluster +func FromCluster(from protocol.Cluster) (*Cluster, error) { + cluster := encodable.Cluster{ + Counter: from.EpochCounter(), + Index: from.Index(), + Members: from.Members(), + RootBlock: from.RootBlock(), + RootQC: from.RootQC(), + } + return &Cluster{cluster}, nil +} + +// FromDKG converts any protocol.DKG to a memory-backed DKG. +// +// The given participant list must exactly match the DKG members. +func FromDKG(from protocol.DKG, participants flow.IdentityList) (*DKG, error) { + + var dkg encodable.DKG + dkg.Size = from.Size() + dkg.GroupKey = encodable.RandomBeaconPubKey{PublicKey: from.GroupKey()} + + dkg.Participants = make(map[flow.Identifier]flow.DKGParticipant) + for _, identity := range participants { + + index, err := from.Index(identity.NodeID) + if err != nil { + return nil, err + } + key, err := from.KeyShare(identity.NodeID) + if err != nil { + return nil, err + } + + dkg.Participants[identity.NodeID] = flow.DKGParticipant{ + Index: index, + KeyShare: key, + } + } + + return &DKG{dkg}, nil } diff --git a/state/protocol/inmem/dkg.go b/state/protocol/inmem/dkg.go index 5978b59f378..3bd271730c1 100644 --- a/state/protocol/inmem/dkg.go +++ b/state/protocol/inmem/dkg.go @@ -7,14 +7,14 @@ import ( "github.com/onflow/flow-go/state/protocol" ) -type dkg struct { +type DKG struct { encodable.DKG } -func (d dkg) Size() uint { return d.DKG.Size } -func (d dkg) GroupKey() crypto.PublicKey { return d.DKG.GroupKey.PublicKey } +func (d DKG) Size() uint { return d.DKG.Size } +func (d DKG) GroupKey() crypto.PublicKey { return d.DKG.GroupKey.PublicKey } -func (d dkg) Index(nodeID flow.Identifier) (uint, error) { +func (d DKG) Index(nodeID flow.Identifier) (uint, error) { part, exists := d.DKG.Participants[nodeID] if !exists { return 0, protocol.IdentityNotFoundError{NodeID: nodeID} @@ -22,7 +22,7 @@ func (d dkg) Index(nodeID flow.Identifier) (uint, error) { return part.Index, nil } -func (d dkg) KeyShare(nodeID flow.Identifier) (crypto.PublicKey, error) { +func (d DKG) KeyShare(nodeID flow.Identifier) (crypto.PublicKey, error) { part, exists := d.DKG.Participants[nodeID] if !exists { return nil, protocol.IdentityNotFoundError{NodeID: nodeID} diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index d958ceaa452..18ed1f68040 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -17,10 +17,11 @@ func (e Epoch) Counter() (uint64, error) { return e.Epoch.C func (e Epoch) FirstView() (uint64, error) { return e.Epoch.FirstView, nil } func (e Epoch) FinalView() (uint64, error) { return e.Epoch.FinalView, nil } func (e Epoch) InitialIdentities() (flow.IdentityList, error) { return e.Epoch.InitialIdentities, nil } -func (e Epoch) DKG() (protocol.DKG, error) { return dkg{e.Epoch.DKG}, nil } +func (e Epoch) DKG() (protocol.DKG, error) { return DKG{e.Epoch.DKG}, nil } +func (e Epoch) RandomSource() ([]byte, error) { return e.Epoch.RandomSource, nil } func (e Epoch) Seed(indices ...uint32) ([]byte, error) { - return seed.FromRandomSource(indices, e.Epoch.Seed) + return seed.FromRandomSource(indices, e.Epoch.RandomSource) } func (e Epoch) Clustering() (flow.ClusterList, error) { From 514332a1b58e584602f0fe0a8718319c4e5a1dcf Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 18:16:31 -0800 Subject: [PATCH 012/178] move encodable models to inmem package there are cyclical dependencies otherwise --- model/encodable/cluster.go | 15 --------- model/encodable/dkg.go | 13 -------- model/encodable/epoch.go | 23 ------------- model/encodable/snapshot.go | 15 --------- state/protocol/badger/epoch.go | 4 +++ state/protocol/inmem/cluster.go | 13 ++++---- state/protocol/inmem/convert.go | 34 +++++++++++++------ state/protocol/inmem/convert_test.go | 28 ++++++++++++++++ state/protocol/inmem/dkg.go | 11 +++--- state/protocol/inmem/encodable.go | 50 ++++++++++++++++++++++++++++ state/protocol/inmem/epoch.go | 46 ++++++++++++++++--------- state/protocol/inmem/snapshot.go | 19 +++++------ 12 files changed, 155 insertions(+), 116 deletions(-) delete mode 100644 model/encodable/cluster.go delete mode 100644 model/encodable/dkg.go delete mode 100644 model/encodable/epoch.go delete mode 100644 model/encodable/snapshot.go create mode 100644 state/protocol/inmem/convert_test.go create mode 100644 state/protocol/inmem/encodable.go diff --git a/model/encodable/cluster.go b/model/encodable/cluster.go deleted file mode 100644 index 1fdf231aa67..00000000000 --- a/model/encodable/cluster.go +++ /dev/null @@ -1,15 +0,0 @@ -package encodable - -import ( - "github.com/onflow/flow-go/model/cluster" - "github.com/onflow/flow-go/model/flow" -) - -// Cluster is the encoding format for protocol.Cluster -type Cluster struct { - Index uint - Counter uint64 - Members flow.IdentityList - RootBlock *cluster.Block - RootQC *flow.QuorumCertificate -} diff --git a/model/encodable/dkg.go b/model/encodable/dkg.go deleted file mode 100644 index 148112f0e9b..00000000000 --- a/model/encodable/dkg.go +++ /dev/null @@ -1,13 +0,0 @@ -package encodable - -import ( - "github.com/onflow/flow-go/model/flow" -) - -// DKG is the encoding format for protocol.DKG -// TODO consolidate with encodable in flow/epoch.go -type DKG struct { - Size uint - GroupKey RandomBeaconPubKey - Participants map[flow.Identifier]flow.DKGParticipant -} diff --git a/model/encodable/epoch.go b/model/encodable/epoch.go deleted file mode 100644 index 36f31dbf716..00000000000 --- a/model/encodable/epoch.go +++ /dev/null @@ -1,23 +0,0 @@ -package encodable - -import ( - "github.com/onflow/flow-go/model/flow" -) - -// Epochs is the encoding format for protocol.EpochQuery -type Epochs struct { - Previous Epoch - Current Epoch - Next Epoch -} - -// Epoch is the encoding format for protocol.Epoch -type Epoch struct { - Counter uint64 - FirstView uint64 - FinalView uint64 - RandomSource []byte - InitialIdentities flow.IdentityList - Clusters []Cluster - DKG DKG -} diff --git a/model/encodable/snapshot.go b/model/encodable/snapshot.go deleted file mode 100644 index 3dddb0fe20a..00000000000 --- a/model/encodable/snapshot.go +++ /dev/null @@ -1,15 +0,0 @@ -package encodable - -import ( - "github.com/onflow/flow-go/model/flow" -) - -// Snapshot is the encoding format for protocol.Snapshot -type Snapshot struct { - Head *flow.Header - Identities flow.IdentityList - Commit flow.StateCommitment - QuorumCertificate *flow.QuorumCertificate - Phase flow.EpochPhase - Epochs Epochs -} diff --git a/state/protocol/badger/epoch.go b/state/protocol/badger/epoch.go index f78cf30df66..857d581c059 100644 --- a/state/protocol/badger/epoch.go +++ b/state/protocol/badger/epoch.go @@ -164,6 +164,10 @@ func (u *InvalidEpoch) DKG() (protocol.DKG, error) { return nil, u.err } +func (u *InvalidEpoch) RandomSource() ([]byte, error) { + return nil, u.err +} + func (u *InvalidEpoch) Seed(...uint32) ([]byte, error) { return nil, u.err } diff --git a/state/protocol/inmem/cluster.go b/state/protocol/inmem/cluster.go index c86e29cec6e..1e6b0373b0a 100644 --- a/state/protocol/inmem/cluster.go +++ b/state/protocol/inmem/cluster.go @@ -2,17 +2,16 @@ package inmem import ( clustermodel "github.com/onflow/flow-go/model/cluster" - "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" ) type Cluster struct { - encodable.Cluster + enc EncodableCluster } -func (c Cluster) Index() uint { return c.Cluster.Index } -func (c Cluster) ChainID() flow.ChainID { return c.Cluster.RootBlock.Header.ChainID } -func (c Cluster) EpochCounter() uint64 { return c.Cluster.Counter } -func (c Cluster) Members() flow.IdentityList { return c.Cluster.Members } -func (c Cluster) RootBlock() *clustermodel.Block { return c.Cluster.RootBlock } +func (c Cluster) Index() uint { return c.enc.Index } +func (c Cluster) ChainID() flow.ChainID { return c.enc.RootBlock.Header.ChainID } +func (c Cluster) EpochCounter() uint64 { return c.enc.Counter } +func (c Cluster) Members() flow.IdentityList { return c.enc.Members } +func (c Cluster) RootBlock() *clustermodel.Block { return c.enc.RootBlock } func (c Cluster) RootQC() *flow.QuorumCertificate { return c.RootQC() } diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 89f90832f85..1e410e67036 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -1,6 +1,8 @@ package inmem import ( + "errors" + "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" @@ -13,7 +15,7 @@ import ( func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { var ( - snap encodable.Snapshot + snap EncodableSnapshot err error ) @@ -41,20 +43,30 @@ func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { // convert epochs previous, err := FromEpoch(from.Epochs().Previous()) - if err != nil { + // it is possible for valid snapshots to have no previous epoch + if errors.Is(err, protocol.ErrNoPreviousEpoch) { + snap.Epochs.Previous = nil + } else if err != nil { return nil, err + } else { + snap.Epochs.Previous = &previous.enc } - snap.Epochs.Previous = previous.Epoch + current, err := FromEpoch(from.Epochs().Current()) if err != nil { return nil, err } - snap.Epochs.Current = current.Epoch + snap.Epochs.Current = ¤t.enc + next, err := FromEpoch(from.Epochs().Next()) - if err != nil { + // it is possible for valid snapshots to have no next epoch + if errors.Is(err, protocol.ErrNextEpochNotSetup) { + snap.Epochs.Next = nil + } else if err != nil { return nil, err + } else { + snap.Epochs.Next = &next.enc } - snap.Epochs.Next = next.Epoch return &Snapshot{snap}, nil } @@ -63,7 +75,7 @@ func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { func FromEpoch(from protocol.Epoch) (*Epoch, error) { var ( - epoch encodable.Epoch + epoch EncodableEpoch err error ) @@ -98,7 +110,7 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { if err != nil { return nil, err } - epoch.DKG = convertedDKG.DKG + epoch.DKG = convertedDKG.enc // convert clusters clustering, err := from.Clustering() @@ -114,7 +126,7 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { if err != nil { return nil, err } - epoch.Clusters = append(epoch.Clusters, convertedCluster.Cluster) + epoch.Clusters = append(epoch.Clusters, convertedCluster.enc) } return &Epoch{epoch}, nil @@ -122,7 +134,7 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { // FromCluster converts any protocol.Cluster to a memory-backed Cluster func FromCluster(from protocol.Cluster) (*Cluster, error) { - cluster := encodable.Cluster{ + cluster := EncodableCluster{ Counter: from.EpochCounter(), Index: from.Index(), Members: from.Members(), @@ -137,7 +149,7 @@ func FromCluster(from protocol.Cluster) (*Cluster, error) { // The given participant list must exactly match the DKG members. func FromDKG(from protocol.DKG, participants flow.IdentityList) (*DKG, error) { - var dkg encodable.DKG + var dkg EncodableDKG dkg.Size = from.Size() dkg.GroupKey = encodable.RandomBeaconPubKey{PublicKey: from.GroupKey()} diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go new file mode 100644 index 00000000000..08180bd9ab6 --- /dev/null +++ b/state/protocol/inmem/convert_test.go @@ -0,0 +1,28 @@ +package inmem + +import ( + "testing" + + "github.com/dgraph-io/badger/v2" + "github.com/stretchr/testify/require" + + protocol "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/util" + "github.com/onflow/flow-go/utils/unittest" +) + +// should be able to convert +func TestFromSnapshot(t *testing.T) { + util.RunWithProtocolState(t, func(db *badger.DB, state *protocol.State) { + + identities := unittest.IdentityListFixture(10) + root, result, seal := unittest.BootstrapFixture(identities) + err := state.Mutate().Bootstrap(root, result, seal) + require.Nil(t, err) + + t.Run("root snapshot", func(t *testing.T) { + _, err := FromSnapshot(state.Final()) + require.Nil(t, err) + }) + }) +} diff --git a/state/protocol/inmem/dkg.go b/state/protocol/inmem/dkg.go index 3bd271730c1..fa36080a2ca 100644 --- a/state/protocol/inmem/dkg.go +++ b/state/protocol/inmem/dkg.go @@ -2,20 +2,19 @@ package inmem import ( "github.com/onflow/flow-go/crypto" - "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" ) type DKG struct { - encodable.DKG + enc EncodableDKG } -func (d DKG) Size() uint { return d.DKG.Size } -func (d DKG) GroupKey() crypto.PublicKey { return d.DKG.GroupKey.PublicKey } +func (d DKG) Size() uint { return uint(len(d.enc.Participants)) } +func (d DKG) GroupKey() crypto.PublicKey { return d.enc.GroupKey.PublicKey } func (d DKG) Index(nodeID flow.Identifier) (uint, error) { - part, exists := d.DKG.Participants[nodeID] + part, exists := d.enc.Participants[nodeID] if !exists { return 0, protocol.IdentityNotFoundError{NodeID: nodeID} } @@ -23,7 +22,7 @@ func (d DKG) Index(nodeID flow.Identifier) (uint, error) { } func (d DKG) KeyShare(nodeID flow.Identifier) (crypto.PublicKey, error) { - part, exists := d.DKG.Participants[nodeID] + part, exists := d.enc.Participants[nodeID] if !exists { return nil, protocol.IdentityNotFoundError{NodeID: nodeID} } diff --git a/state/protocol/inmem/encodable.go b/state/protocol/inmem/encodable.go new file mode 100644 index 00000000000..5e0e98a8fc4 --- /dev/null +++ b/state/protocol/inmem/encodable.go @@ -0,0 +1,50 @@ +package inmem + +import ( + "github.com/onflow/flow-go/model/cluster" + "github.com/onflow/flow-go/model/encodable" + "github.com/onflow/flow-go/model/flow" +) + +// Snapshot is the encoding format for protocol.Snapshot +type EncodableSnapshot struct { + Head *flow.Header + Identities flow.IdentityList + Commit flow.StateCommitment + QuorumCertificate *flow.QuorumCertificate + Phase flow.EpochPhase + Epochs EncodableEpochs +} + +// EncodableEpochs is the encoding format for protocol.EpochQuery +type EncodableEpochs struct { + Previous *EncodableEpoch + Current *EncodableEpoch + Next *EncodableEpoch +} + +// Epoch is the encoding format for protocol.Epoch +type EncodableEpoch struct { + Counter uint64 + FirstView uint64 + FinalView uint64 + RandomSource []byte + InitialIdentities flow.IdentityList + Clusters []EncodableCluster + DKG EncodableDKG +} + +// DKG is the encoding format for protocol.DKG +type EncodableDKG struct { + GroupKey encodable.RandomBeaconPubKey + Participants map[flow.Identifier]flow.DKGParticipant +} + +// Cluster is the encoding format for protocol.Cluster +type EncodableCluster struct { + Index uint + Counter uint64 + Members flow.IdentityList + RootBlock *cluster.Block + RootQC *flow.QuorumCertificate +} diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index 18ed1f68040..9d938075e18 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -3,46 +3,60 @@ package inmem import ( "fmt" - "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" + bprotocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/seed" ) type Epoch struct { - encodable.Epoch + enc EncodableEpoch } -func (e Epoch) Counter() (uint64, error) { return e.Epoch.Counter, nil } -func (e Epoch) FirstView() (uint64, error) { return e.Epoch.FirstView, nil } -func (e Epoch) FinalView() (uint64, error) { return e.Epoch.FinalView, nil } -func (e Epoch) InitialIdentities() (flow.IdentityList, error) { return e.Epoch.InitialIdentities, nil } -func (e Epoch) DKG() (protocol.DKG, error) { return DKG{e.Epoch.DKG}, nil } -func (e Epoch) RandomSource() ([]byte, error) { return e.Epoch.RandomSource, nil } +func (e Epoch) Counter() (uint64, error) { return e.enc.Counter, nil } +func (e Epoch) FirstView() (uint64, error) { return e.enc.FirstView, nil } +func (e Epoch) FinalView() (uint64, error) { return e.enc.FinalView, nil } +func (e Epoch) InitialIdentities() (flow.IdentityList, error) { + return e.enc.InitialIdentities, nil +} +func (e Epoch) DKG() (protocol.DKG, error) { return DKG{e.enc.DKG}, nil } +func (e Epoch) RandomSource() ([]byte, error) { return e.enc.RandomSource, nil } func (e Epoch) Seed(indices ...uint32) ([]byte, error) { - return seed.FromRandomSource(indices, e.Epoch.RandomSource) + return seed.FromRandomSource(indices, e.enc.RandomSource) } func (e Epoch) Clustering() (flow.ClusterList, error) { var clusters flow.ClusterList - for _, cluster := range e.Epoch.Clusters { + for _, cluster := range e.enc.Clusters { clusters = append(clusters, cluster.Members) } return clusters, nil } func (e Epoch) Cluster(i uint) (protocol.Cluster, error) { - if i >= uint(len(e.Epoch.Clusters)) { + if i >= uint(len(e.enc.Clusters)) { return nil, fmt.Errorf("no cluster with index %d", i) } - return Cluster{e.Epoch.Clusters[i]}, nil + return Cluster{e.enc.Clusters[i]}, nil } type Epochs struct { - encodable.Epochs + enc EncodableEpochs } -func (eq Epochs) Previous() protocol.Epoch { return Epoch{eq.Epochs.Previous} } -func (eq Epochs) Current() protocol.Epoch { return Epoch{eq.Epochs.Current} } -func (eq Epochs) Next() protocol.Epoch { return Epoch{eq.Epochs.Next} } +func (eq Epochs) Previous() protocol.Epoch { + if eq.enc.Previous != nil { + return Epoch{*eq.enc.Previous} + } + return bprotocol.NewInvalidEpoch(protocol.ErrNoPreviousEpoch) +} +func (eq Epochs) Current() protocol.Epoch { + return Epoch{*eq.enc.Current} +} +func (eq Epochs) Next() protocol.Epoch { + if eq.enc.Next != nil { + return Epoch{*eq.enc.Next} + } + return bprotocol.NewInvalidEpoch(protocol.ErrNextEpochNotSetup) +} diff --git a/state/protocol/inmem/snapshot.go b/state/protocol/inmem/snapshot.go index 3351f3edf23..59632db7ab3 100644 --- a/state/protocol/inmem/snapshot.go +++ b/state/protocol/inmem/snapshot.go @@ -1,7 +1,6 @@ package inmem import ( - "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/state/protocol/seed" @@ -18,23 +17,23 @@ var ( // data is stored in the embedded encodable snapshot model, which defines the // canonical structure of an encoded snapshot for the purposes of serialization. type Snapshot struct { - encodable.Snapshot + enc EncodableSnapshot } func (s *Snapshot) Head() (*flow.Header, error) { - return s.Snapshot.Head, nil + return s.enc.Head, nil } func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { - return s.Snapshot.QuorumCertificate, nil + return s.enc.QuorumCertificate, nil } func (s *Snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, error) { - return s.Snapshot.Identities.Filter(selector), nil + return s.enc.Identities.Filter(selector), nil } func (s *Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { - identity, ok := s.Snapshot.Identities.ByNodeID(nodeID) + identity, ok := s.enc.Identities.ByNodeID(nodeID) if !ok { return nil, protocol.IdentityNotFoundError{NodeID: nodeID} } @@ -42,7 +41,7 @@ func (s *Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { } func (s *Snapshot) Commit() (flow.StateCommitment, error) { - return s.Snapshot.Commit, nil + return s.enc.Commit, nil } func (s *Snapshot) Pending() ([]flow.Identifier, error) { @@ -51,13 +50,13 @@ func (s *Snapshot) Pending() ([]flow.Identifier, error) { } func (s *Snapshot) Phase() (flow.EpochPhase, error) { - return s.Snapshot.Phase, nil + return s.enc.Phase, nil } func (s *Snapshot) Seed(indices ...uint32) ([]byte, error) { - return seed.FromParentSignature(indices, s.Snapshot.QuorumCertificate.SigData) + return seed.FromParentSignature(indices, s.enc.QuorumCertificate.SigData) } func (s *Snapshot) Epochs() protocol.EpochQuery { - return Epochs{s.Snapshot.Epochs} + return Epochs{s.enc.Epochs} } From 2b57e06047509d0a4f1bae0260ee990d2d40d1ac Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 18:32:31 -0800 Subject: [PATCH 013/178] add smoke test for snapshot conversion --- state/protocol/inmem/convert.go | 3 +- state/protocol/inmem/convert_test.go | 41 ++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 1e410e67036..2639b708671 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -150,9 +150,8 @@ func FromCluster(from protocol.Cluster) (*Cluster, error) { func FromDKG(from protocol.DKG, participants flow.IdentityList) (*DKG, error) { var dkg EncodableDKG - dkg.Size = from.Size() - dkg.GroupKey = encodable.RandomBeaconPubKey{PublicKey: from.GroupKey()} + dkg.GroupKey = encodable.RandomBeaconPubKey{PublicKey: from.GroupKey()} dkg.Participants = make(map[flow.Identifier]flow.DKGParticipant) for _, identity := range participants { diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index 08180bd9ab6..6f66179ef9d 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -6,6 +6,7 @@ import ( "github.com/dgraph-io/badger/v2" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/model/flow" protocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/utils/unittest" @@ -15,14 +16,50 @@ import ( func TestFromSnapshot(t *testing.T) { util.RunWithProtocolState(t, func(db *badger.DB, state *protocol.State) { - identities := unittest.IdentityListFixture(10) + identities := unittest.IdentityListFixture(10, unittest.WithAllRoles()) root, result, seal := unittest.BootstrapFixture(identities) err := state.Mutate().Bootstrap(root, result, seal) require.Nil(t, err) t.Run("root snapshot", func(t *testing.T) { - _, err := FromSnapshot(state.Final()) + // TODO need root QC stored for this test to pass + t.Skip() + _, err = FromSnapshot(state.Final()) require.Nil(t, err) }) + + t.Run("non-root snapshot", func(t *testing.T) { + // add a block on the root block and a valid child + block1 := unittest.BlockWithParentFixture(root.Header) + block1.SetPayload(flow.EmptyPayload()) + err = state.Mutate().Extend(&block1) + require.Nil(t, err) + err = state.Mutate().MarkValid(block1.ID()) + require.Nil(t, err) + block2 := unittest.BlockWithParentFixture(block1.Header) + block2.SetPayload(flow.EmptyPayload()) + err = state.Mutate().Extend(&block2) + require.Nil(t, err) + err = state.Mutate().MarkValid(block2.ID()) + require.Nil(t, err) + + _, err = FromSnapshot(state.AtBlockID(block1.ID())) + require.Nil(t, err) + // TODO compare serialization + }) + + // TODO + t.Run("epoch 1", func(t *testing.T) { + t.Run("staking phase", func(t *testing.T) {}) + t.Run("setup phase", func(t *testing.T) {}) + t.Run("committed phase", func(t *testing.T) {}) + }) + + // TODO + t.Run("epoch 2", func(t *testing.T) { + t.Run("staking phase", func(t *testing.T) {}) + t.Run("setup phase", func(t *testing.T) {}) + t.Run("committed phase", func(t *testing.T) {}) + }) }) } From 4f9313e1a445d855bca33ed74a442af93e238b3c Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 20:11:50 -0800 Subject: [PATCH 014/178] add sentinel error for un-committed epochs --- state/protocol/badger/epoch.go | 4 ++-- state/protocol/errors.go | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/state/protocol/badger/epoch.go b/state/protocol/badger/epoch.go index 857d581c059..3dd6b6d51f6 100644 --- a/state/protocol/badger/epoch.go +++ b/state/protocol/badger/epoch.go @@ -53,11 +53,11 @@ func (es *SetupEpoch) Clustering() (flow.ClusterList, error) { } func (es *SetupEpoch) Cluster(_ uint) (protocol.Cluster, error) { - return nil, fmt.Errorf("EpochCommit event not yet received in fork") + return nil, protocol.ErrEpochNotCommitted } func (es *SetupEpoch) DKG() (protocol.DKG, error) { - return nil, fmt.Errorf("EpochCommit event not yet received in fork") + return nil, protocol.ErrEpochNotCommitted } func (es *SetupEpoch) RandomSource() ([]byte, error) { diff --git a/state/protocol/errors.go b/state/protocol/errors.go index f9e8b811467..89d536cb76c 100644 --- a/state/protocol/errors.go +++ b/state/protocol/errors.go @@ -12,8 +12,14 @@ var ( // queried from a snapshot within the first epoch after the root block. ErrNoPreviousEpoch = fmt.Errorf("no previous epoch exists") - // ErrNextEpochNotSetup is a sentinal error returned when + // ErrNextEpochNotSetup is a sentinel error returned when the next epoch + // has not been set up yet. ErrNextEpochNotSetup = fmt.Errorf("next epoch has not yet been set up") + + // ErrEpochNotCommitted is a sentinel error returned when the epoch has + // not been committed and information is queried that is only accessible + // in the EpochCommitted phase. + ErrEpochNotCommitted = fmt.Errorf("EpochCommit has not been received yet") ) type IdentityNotFoundError struct { From 93521fa5b545fb8ce5e668adb1959b3afc3e9ddf Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 20:12:37 -0800 Subject: [PATCH 015/178] mark blocks valid in epoch builder mock --- utils/unittest/epoch_builder.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/utils/unittest/epoch_builder.go b/utils/unittest/epoch_builder.go index 71641838f0e..8894f231710 100644 --- a/utils/unittest/epoch_builder.go +++ b/utils/unittest/epoch_builder.go @@ -94,9 +94,11 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { B.SetPayload(flow.Payload{Seals: seals}) err = builder.state.Mutate().Extend(&B) require.Nil(builder.t, err) - // finalize block B + // finalize and validate block B err = builder.state.Mutate().Finalize(B.ID()) require.Nil(builder.t, err) + err = builder.state.Mutate().MarkValid(B.ID()) + require.Nil(builder.t, err) // defaults for the EpochSetup event setupDefaults := []func(*flow.EpochSetup){ @@ -118,9 +120,11 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { }) err = builder.state.Mutate().Extend(&C) require.Nil(builder.t, err) - // finalize block C + // finalize and validate block C err = builder.state.Mutate().Finalize(C.ID()) require.Nil(builder.t, err) + err = builder.state.Mutate().MarkValid(C.ID()) + require.Nil(builder.t, err) // defaults for the EpochCommit event commitDefaults := []func(*flow.EpochCommit){ @@ -141,9 +145,11 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { }) err = builder.state.Mutate().Extend(&D) require.Nil(builder.t, err) - // finalize block D + // finalize and validate block D err = builder.state.Mutate().Finalize(D.ID()) require.Nil(builder.t, err) + err = builder.state.Mutate().MarkValid(D.ID()) + require.Nil(builder.t, err) return builder } @@ -171,4 +177,6 @@ func (builder *EpochBuilder) CompleteEpoch() { require.Nil(builder.t, err) err = builder.state.Mutate().Finalize(A.ID()) require.Nil(builder.t, err) + err = builder.state.Mutate().MarkValid(A.ID()) + require.Nil(builder.t, err) } From 0f603364a6f144f75f2bbd421a22dc319122cc17 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 20:14:05 -0800 Subject: [PATCH 016/178] update mocks --- state/protocol/mock/epoch.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/state/protocol/mock/epoch.go b/state/protocol/mock/epoch.go index 598352fdd8b..18c2ad1d66e 100644 --- a/state/protocol/mock/epoch.go +++ b/state/protocol/mock/epoch.go @@ -169,6 +169,29 @@ func (_m *Epoch) InitialIdentities() (flow.IdentityList, error) { return r0, r1 } +// RandomSource provides a mock function with given fields: +func (_m *Epoch) RandomSource() ([]byte, error) { + ret := _m.Called() + + var r0 []byte + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Seed provides a mock function with given fields: indices func (_m *Epoch) Seed(indices ...uint32) ([]byte, error) { _va := make([]interface{}, len(indices)) From 540b0239c97720bbb96fe0583f5db5facd1f8da3 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 2 Dec 2020 20:14:45 -0800 Subject: [PATCH 017/178] improved snapshot conversion tests test all phases from epochs 1-2 and encode/decode cycle --- state/protocol/inmem/cluster.go | 2 +- state/protocol/inmem/convert.go | 6 +- state/protocol/inmem/convert_test.go | 145 +++++++++++++++++++++------ state/protocol/inmem/encodable.go | 2 +- state/protocol/inmem/epoch.go | 17 +++- state/protocol/inmem/snapshot.go | 10 ++ 6 files changed, 142 insertions(+), 40 deletions(-) diff --git a/state/protocol/inmem/cluster.go b/state/protocol/inmem/cluster.go index 1e6b0373b0a..df0ccf71297 100644 --- a/state/protocol/inmem/cluster.go +++ b/state/protocol/inmem/cluster.go @@ -14,4 +14,4 @@ func (c Cluster) ChainID() flow.ChainID { return c.enc.RootBlock.Heade func (c Cluster) EpochCounter() uint64 { return c.enc.Counter } func (c Cluster) Members() flow.IdentityList { return c.enc.Members } func (c Cluster) RootBlock() *clustermodel.Block { return c.enc.RootBlock } -func (c Cluster) RootQC() *flow.QuorumCertificate { return c.RootQC() } +func (c Cluster) RootQC() *flow.QuorumCertificate { return c.enc.RootQC } diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 2639b708671..31eab076d34 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -103,6 +103,10 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { // convert dkg dkg, err := from.DKG() + // if this epoch hasn't been committed yet, return the epoch as-is + if errors.Is(err, protocol.ErrEpochNotCommitted) { + return &Epoch{epoch}, nil + } if err != nil { return nil, err } @@ -110,7 +114,7 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { if err != nil { return nil, err } - epoch.DKG = convertedDKG.enc + epoch.DKG = &convertedDKG.enc // convert clusters clustering, err := from.Clustering() diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index 6f66179ef9d..78abc60f3e6 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -1,65 +1,144 @@ package inmem import ( + "bytes" + "encoding/json" "testing" "github.com/dgraph-io/badger/v2" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/onflow/flow-go/model/flow" - protocol "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol" + bprotocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/utils/unittest" ) -// should be able to convert +// should be able to convert from protocol.Snapshot func TestFromSnapshot(t *testing.T) { - util.RunWithProtocolState(t, func(db *badger.DB, state *protocol.State) { + util.RunWithProtocolState(t, func(db *badger.DB, state *bprotocol.State) { identities := unittest.IdentityListFixture(10, unittest.WithAllRoles()) root, result, seal := unittest.BootstrapFixture(identities) err := state.Mutate().Bootstrap(root, result, seal) require.Nil(t, err) + // Prepare an epoch builder, which builds epochs with 4 blocks, A,B,C,D + // See EpochBuilder documentation for details of these blocks. + // + epochBuilder := unittest.NewEpochBuilder(t, state) + // build blocks WITHIN epoch 1 - PREPARING epoch 2 + // A - height 0 (root block) + // B - height 1 - staking phase + // C - height 2 - setup phase + // D - height 3 - committed phase + epochBuilder. + BuildEpoch(). + CompleteEpoch() + // build blocks WITHIN epoch 2 - PREPARING epoch 3 + // A - height 4 + // B - height 5 - staking phase + // C - height 6 - setup phase + // D - height 7 - committed phase + epochBuilder. + BuildEpoch(). + CompleteEpoch() + + // test that we are able retrieve an in-memory version of root snapshot t.Run("root snapshot", func(t *testing.T) { // TODO need root QC stored for this test to pass - t.Skip() - _, err = FromSnapshot(state.Final()) - require.Nil(t, err) - }) - - t.Run("non-root snapshot", func(t *testing.T) { - // add a block on the root block and a valid child - block1 := unittest.BlockWithParentFixture(root.Header) - block1.SetPayload(flow.EmptyPayload()) - err = state.Mutate().Extend(&block1) - require.Nil(t, err) - err = state.Mutate().MarkValid(block1.ID()) - require.Nil(t, err) - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.SetPayload(flow.EmptyPayload()) - err = state.Mutate().Extend(&block2) - require.Nil(t, err) - err = state.Mutate().MarkValid(block2.ID()) - require.Nil(t, err) + t.SkipNow() - _, err = FromSnapshot(state.AtBlockID(block1.ID())) + expected := state.AtHeight(0) + actual, err := FromSnapshot(expected) require.Nil(t, err) - // TODO compare serialization + assertSnapshotsEqual(t, expected, actual) + testEncodeDecode(t, actual) }) - // TODO + // test getting an in-memory snapshot for all phase of epoch 1 t.Run("epoch 1", func(t *testing.T) { - t.Run("staking phase", func(t *testing.T) {}) - t.Run("setup phase", func(t *testing.T) {}) - t.Run("committed phase", func(t *testing.T) {}) + t.Run("staking phase", func(t *testing.T) { + expected := state.AtHeight(1) + actual, err := FromSnapshot(expected) + require.Nil(t, err) + assertSnapshotsEqual(t, expected, actual) + testEncodeDecode(t, actual) + }) + t.Run("setup phase", func(t *testing.T) { + expected := state.AtHeight(2) + actual, err := FromSnapshot(expected) + require.Nil(t, err) + assertSnapshotsEqual(t, expected, actual) + testEncodeDecode(t, actual) + }) + t.Run("committed phase", func(t *testing.T) { + expected := state.AtHeight(3) + actual, err := FromSnapshot(expected) + require.Nil(t, err) + assertSnapshotsEqual(t, expected, actual) + testEncodeDecode(t, actual) + }) }) - // TODO + // test getting an in-memory snapshot for all phase of epoch 2 t.Run("epoch 2", func(t *testing.T) { - t.Run("staking phase", func(t *testing.T) {}) - t.Run("setup phase", func(t *testing.T) {}) - t.Run("committed phase", func(t *testing.T) {}) + t.Run("staking phase", func(t *testing.T) { + expected := state.AtHeight(5) + actual, err := FromSnapshot(expected) + require.Nil(t, err) + assertSnapshotsEqual(t, expected, actual) + testEncodeDecode(t, actual) + }) + t.Run("setup phase", func(t *testing.T) { + expected := state.AtHeight(6) + actual, err := FromSnapshot(expected) + require.Nil(t, err) + assertSnapshotsEqual(t, expected, actual) + testEncodeDecode(t, actual) + }) + t.Run("committed phase", func(t *testing.T) { + expected := state.AtHeight(7) + actual, err := FromSnapshot(expected) + require.Nil(t, err) + assertSnapshotsEqual(t, expected, actual) + testEncodeDecode(t, actual) + }) }) }) } + +// checks that a snapshot is equivalent after encoding and decoding +func testEncodeDecode(t *testing.T, snap *Snapshot) { + + bz, err := json.Marshal(snap.Encodable()) + require.Nil(t, err) + + var encoded EncodableSnapshot + err = json.Unmarshal(bz, &encoded) + require.Nil(t, err) + + fromEncoded := SnapshotFromEncodable(encoded) + assertSnapshotsEqual(t, snap, fromEncoded) +} + +// checks that 2 snapshots are equivalent by converting to a serializable +// representation and comparing the serializations +func snapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) bool { + enc1, err := FromSnapshot(snap1) + require.Nil(t, err) + enc2, err := FromSnapshot(snap2) + require.Nil(t, err) + + bz1, err := json.Marshal(enc1.Encodable()) + require.Nil(t, err) + bz2, err := json.Marshal(enc2.Encodable()) + require.Nil(t, err) + + return bytes.Equal(bz1, bz2) +} + +func assertSnapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) { + assert.True(t, snapshotsEqual(t, snap1, snap2)) +} diff --git a/state/protocol/inmem/encodable.go b/state/protocol/inmem/encodable.go index 5e0e98a8fc4..1e61ca9560b 100644 --- a/state/protocol/inmem/encodable.go +++ b/state/protocol/inmem/encodable.go @@ -31,7 +31,7 @@ type EncodableEpoch struct { RandomSource []byte InitialIdentities flow.IdentityList Clusters []EncodableCluster - DKG EncodableDKG + DKG *EncodableDKG } // DKG is the encoding format for protocol.DKG diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index 9d938075e18..77f02ed01f1 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -19,7 +19,6 @@ func (e Epoch) FinalView() (uint64, error) { return e.enc.FinalView, nil } func (e Epoch) InitialIdentities() (flow.IdentityList, error) { return e.enc.InitialIdentities, nil } -func (e Epoch) DKG() (protocol.DKG, error) { return DKG{e.enc.DKG}, nil } func (e Epoch) RandomSource() ([]byte, error) { return e.enc.RandomSource, nil } func (e Epoch) Seed(indices ...uint32) ([]byte, error) { @@ -34,11 +33,21 @@ func (e Epoch) Clustering() (flow.ClusterList, error) { return clusters, nil } +func (e Epoch) DKG() (protocol.DKG, error) { + if e.enc.DKG != nil { + return DKG{*e.enc.DKG}, nil + } + return nil, protocol.ErrEpochNotCommitted +} + func (e Epoch) Cluster(i uint) (protocol.Cluster, error) { - if i >= uint(len(e.enc.Clusters)) { - return nil, fmt.Errorf("no cluster with index %d", i) + if e.enc.Clusters != nil { + if i >= uint(len(e.enc.Clusters)) { + return nil, fmt.Errorf("no cluster with index %d", i) + } + return Cluster{e.enc.Clusters[i]}, nil } - return Cluster{e.enc.Clusters[i]}, nil + return nil, protocol.ErrEpochNotCommitted } type Epochs struct { diff --git a/state/protocol/inmem/snapshot.go b/state/protocol/inmem/snapshot.go index 59632db7ab3..6fdfd98bdbb 100644 --- a/state/protocol/inmem/snapshot.go +++ b/state/protocol/inmem/snapshot.go @@ -60,3 +60,13 @@ func (s *Snapshot) Seed(indices ...uint32) ([]byte, error) { func (s *Snapshot) Epochs() protocol.EpochQuery { return Epochs{s.enc.Epochs} } + +func (s *Snapshot) Encodable() EncodableSnapshot { + return s.enc +} + +func SnapshotFromEncodable(enc EncodableSnapshot) *Snapshot { + return &Snapshot{ + enc: enc, + } +} From 11a2244b46f2bd1baa379cb234fe2e4f09f25501 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 08:59:25 -0800 Subject: [PATCH 018/178] test qc view --- state/protocol/badger/snapshot_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index 3b17840a3e6..cfe2192313a 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -217,8 +217,11 @@ func TestQuorumCertificate(t *testing.T) { qc, err := state.AtBlockID(block1.ID()).QuorumCertificate() assert.Nil(t, err) + // should have signatures from valid child (block 2) assert.Equal(t, block2.Header.ParentVoterIDs, qc.SignerIDs) assert.Equal(t, block2.Header.ParentVoterSig.Bytes(), qc.SigData) + // should have view matching block1 view + assert.Equal(t, block1.Header.View, qc.View) _, err = state.AtBlockID(block1.ID()).Seed(1, 2, 3, 4) require.Nil(t, err) From 26c4cd8cb713904dc25867340df30f0f0f6ed6e9 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 12:40:59 -0800 Subject: [PATCH 019/178] move invalid types to invalid package --- state/protocol/badger/epoch.go | 47 --------------- state/protocol/badger/snapshot.go | 88 ++++------------------------ state/protocol/badger/state.go | 7 ++- state/protocol/inmem/convert.go | 10 ++++ state/protocol/inmem/convert_test.go | 1 + state/protocol/inmem/epoch.go | 6 +- state/protocol/invalid/epoch.go | 72 +++++++++++++++++++++++ state/protocol/invalid/snapshot.go | 47 +++++++++++++++ utils/unittest/mocks/epoch_query.go | 6 +- 9 files changed, 153 insertions(+), 131 deletions(-) create mode 100644 state/protocol/invalid/epoch.go create mode 100644 state/protocol/invalid/snapshot.go diff --git a/state/protocol/badger/epoch.go b/state/protocol/badger/epoch.go index 3dd6b6d51f6..055010df8d4 100644 --- a/state/protocol/badger/epoch.go +++ b/state/protocol/badger/epoch.go @@ -128,50 +128,3 @@ func NewCommittedEpoch(setupEvent *flow.EpochSetup, commitEvent *flow.EpochCommi } // **************************************** - -// InvalidEpoch represents an epoch that does not exist. -// Neither the EpochSetup nor EpochCommitted events for the epoch have been -// emitted as of the point at which the epoch was queried. -type InvalidEpoch struct { - err error -} - -func (u *InvalidEpoch) Counter() (uint64, error) { - return 0, u.err -} - -func (u *InvalidEpoch) FirstView() (uint64, error) { - return 0, u.err -} - -func (u *InvalidEpoch) FinalView() (uint64, error) { - return 0, u.err -} - -func (u *InvalidEpoch) InitialIdentities() (flow.IdentityList, error) { - return nil, u.err -} - -func (u *InvalidEpoch) Clustering() (flow.ClusterList, error) { - return nil, u.err -} - -func (u *InvalidEpoch) Cluster(uint) (protocol.Cluster, error) { - return nil, u.err -} - -func (u *InvalidEpoch) DKG() (protocol.DKG, error) { - return nil, u.err -} - -func (u *InvalidEpoch) RandomSource() ([]byte, error) { - return nil, u.err -} - -func (u *InvalidEpoch) Seed(...uint32) ([]byte, error) { - return nil, u.err -} - -func NewInvalidEpoch(err error) *InvalidEpoch { - return &InvalidEpoch{err: err} -} diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index 4097f378164..548651a63b6 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -12,6 +12,7 @@ import ( "github.com/onflow/flow-go/model/flow/order" "github.com/onflow/flow-go/state" "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/invalid" "github.com/onflow/flow-go/state/protocol/seed" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/badger/operation" @@ -308,15 +309,15 @@ func (q *EpochQuery) Current() protocol.Epoch { status, err := q.snap.state.epoch.statuses.ByBlockID(q.snap.blockID) if err != nil { - return NewInvalidEpoch(err) + return invalid.NewEpoch(err) } setup, err := q.snap.state.epoch.setups.ByID(status.CurrentEpoch.SetupID) if err != nil { - return NewInvalidEpoch(err) + return invalid.NewEpoch(err) } commit, err := q.snap.state.epoch.commits.ByID(status.CurrentEpoch.CommitID) if err != nil { - return NewInvalidEpoch(err) + return invalid.NewEpoch(err) } return NewCommittedEpoch(setup, commit) @@ -327,21 +328,21 @@ func (q *EpochQuery) Next() protocol.Epoch { status, err := q.snap.state.epoch.statuses.ByBlockID(q.snap.blockID) if err != nil { - return NewInvalidEpoch(err) + return invalid.NewEpoch(err) } phase, err := status.Phase() if err != nil { - return NewInvalidEpoch(err) + return invalid.NewEpoch(err) } // if we are in the staking phase, the next epoch is not setup yet if phase == flow.EpochPhaseStaking { - return NewInvalidEpoch(protocol.ErrNextEpochNotSetup) + return invalid.NewEpoch(protocol.ErrNextEpochNotSetup) } // if we are in setup phase, return a SetupEpoch nextSetup, err := q.snap.state.epoch.setups.ByID(status.NextEpoch.SetupID) if err != nil { - return NewInvalidEpoch(fmt.Errorf("failed to retrieve setup event for next epoch: %w", err)) + return invalid.NewEpoch(fmt.Errorf("failed to retrieve setup event for next epoch: %w", err)) } if phase == flow.EpochPhaseSetup { return NewSetupEpoch(nextSetup) @@ -350,7 +351,7 @@ func (q *EpochQuery) Next() protocol.Epoch { // if we are in committed phase, return a CommittedEpoch nextCommit, err := q.snap.state.epoch.commits.ByID(status.NextEpoch.CommitID) if err != nil { - return NewInvalidEpoch(fmt.Errorf("failed to retrieve commit event for next epoch: %w", err)) + return invalid.NewEpoch(fmt.Errorf("failed to retrieve commit event for next epoch: %w", err)) } return NewCommittedEpoch(nextSetup, nextCommit) } @@ -362,21 +363,21 @@ func (q *EpochQuery) Previous() protocol.Epoch { status, err := q.snap.state.epoch.statuses.ByBlockID(q.snap.blockID) if err != nil { - return NewInvalidEpoch(err) + return invalid.NewEpoch(err) } first, err := q.snap.state.headers.ByBlockID(status.FirstBlockID) if err != nil { - return NewInvalidEpoch(err) + return invalid.NewEpoch(err) } // CASE 1: we are in the first epoch after the root block, in which case // we return a sentinel error root, err := q.snap.state.Params().Root() if err != nil { - return NewInvalidEpoch(err) + return invalid.NewEpoch(err) } if first.Height == root.Height { - return NewInvalidEpoch(protocol.ErrNoPreviousEpoch) + return invalid.NewEpoch(protocol.ErrNoPreviousEpoch) } // CASE 2: we are in any other epoch, return the current epoch w.r.t. the @@ -384,66 +385,3 @@ func (q *EpochQuery) Previous() protocol.Epoch { // previous epoch return q.snap.state.AtBlockID(first.ParentID).Epochs().Current() } - -// InvalidSnapshot represents a snapshot referencing an invalid block, or for -// which an error occurred while resolving the reference block. -type InvalidSnapshot struct { - err error -} - -func NewInvalidSnapshot(err error) *InvalidSnapshot { - return &InvalidSnapshot{err: err} -} - -func (u *InvalidSnapshot) Head() (*flow.Header, error) { - return nil, u.err -} - -func (u *InvalidSnapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { - return nil, u.err -} - -func (u *InvalidSnapshot) Phase() (flow.EpochPhase, error) { - return 0, u.err -} - -func (u *InvalidSnapshot) Identities(_ flow.IdentityFilter) (flow.IdentityList, error) { - return nil, u.err -} - -func (u *InvalidSnapshot) Identity(_ flow.Identifier) (*flow.Identity, error) { - return nil, u.err -} - -func (u *InvalidSnapshot) Commit() (flow.StateCommitment, error) { - return nil, u.err -} - -func (u *InvalidSnapshot) Pending() ([]flow.Identifier, error) { - return nil, u.err -} - -func (u *InvalidSnapshot) Seed(_ ...uint32) ([]byte, error) { - return nil, u.err -} - -// InvalidEpochQuery is an epoch query for an invalid snapshot. -type InvalidEpochQuery struct { - err error -} - -func (u *InvalidSnapshot) Epochs() protocol.EpochQuery { - return &InvalidEpochQuery{err: u.err} -} - -func (u *InvalidEpochQuery) Current() protocol.Epoch { - return NewInvalidEpoch(u.err) -} - -func (u *InvalidEpochQuery) Next() protocol.Epoch { - return NewInvalidEpoch(u.err) -} - -func (u *InvalidEpochQuery) Previous() protocol.Epoch { - return NewInvalidEpoch(u.err) -} diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 892d9a1e11f..408b02a0d71 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -10,6 +10,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/invalid" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/storage/badger/operation" ) @@ -83,7 +84,7 @@ func (s *State) Sealed() protocol.Snapshot { var sealed uint64 err := s.db.View(operation.RetrieveSealedHeight(&sealed)) if err != nil { - return NewInvalidSnapshot(fmt.Errorf("could not retrieve sealed height: %w", err)) + return invalid.NewSnapshot(fmt.Errorf("could not retrieve sealed height: %w", err)) } return s.AtHeight(sealed) } @@ -93,7 +94,7 @@ func (s *State) Final() protocol.Snapshot { var finalized uint64 err := s.db.View(operation.RetrieveFinalizedHeight(&finalized)) if err != nil { - return NewInvalidSnapshot(fmt.Errorf("could not retrieve finalized height: %w", err)) + return invalid.NewSnapshot(fmt.Errorf("could not retrieve finalized height: %w", err)) } return s.AtHeight(finalized) } @@ -103,7 +104,7 @@ func (s *State) AtHeight(height uint64) protocol.Snapshot { var blockID flow.Identifier err := s.db.View(operation.LookupBlockHeight(height, &blockID)) if err != nil { - return NewInvalidSnapshot(fmt.Errorf("could not look up block by height: %w", err)) + return invalid.NewSnapshot(fmt.Errorf("could not look up block by height: %w", err)) } return s.AtBlockID(blockID) } diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 31eab076d34..dd093fc004f 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -176,3 +176,13 @@ func FromDKG(from protocol.DKG, participants flow.IdentityList) (*DKG, error) { return &DKG{dkg}, nil } + +// DKGFromEncodable returns a DKG backed by the given encodable representation. +func DKGFromEncodable(enc EncodableDKG) (*DKG, error) { + return &DKG{enc}, nil +} + +// ClusterFromEncodable returns a Cluster backed by the given encodable representation. +func ClusterFromEncodable(enc EncodableCluster) (*Cluster, error) { + return &Cluster{enc}, nil +} diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index 78abc60f3e6..06db204e47d 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -125,6 +125,7 @@ func testEncodeDecode(t *testing.T, snap *Snapshot) { // checks that 2 snapshots are equivalent by converting to a serializable // representation and comparing the serializations +// TODO check equality manually func snapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) bool { enc1, err := FromSnapshot(snap1) require.Nil(t, err) diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index 77f02ed01f1..3c014e4e361 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -5,7 +5,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" - bprotocol "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/invalid" "github.com/onflow/flow-go/state/protocol/seed" ) @@ -58,7 +58,7 @@ func (eq Epochs) Previous() protocol.Epoch { if eq.enc.Previous != nil { return Epoch{*eq.enc.Previous} } - return bprotocol.NewInvalidEpoch(protocol.ErrNoPreviousEpoch) + return invalid.NewEpoch(protocol.ErrNoPreviousEpoch) } func (eq Epochs) Current() protocol.Epoch { return Epoch{*eq.enc.Current} @@ -67,5 +67,5 @@ func (eq Epochs) Next() protocol.Epoch { if eq.enc.Next != nil { return Epoch{*eq.enc.Next} } - return bprotocol.NewInvalidEpoch(protocol.ErrNextEpochNotSetup) + return invalid.NewEpoch(protocol.ErrNextEpochNotSetup) } diff --git a/state/protocol/invalid/epoch.go b/state/protocol/invalid/epoch.go new file mode 100644 index 00000000000..d03e46044bd --- /dev/null +++ b/state/protocol/invalid/epoch.go @@ -0,0 +1,72 @@ +package invalid + +import ( + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" +) + +// Epoch represents an epoch that does not exist or could not be retrieved. +type Epoch struct { + err error +} + +func (u *Epoch) Counter() (uint64, error) { + return 0, u.err +} + +func (u *Epoch) FirstView() (uint64, error) { + return 0, u.err +} + +func (u *Epoch) FinalView() (uint64, error) { + return 0, u.err +} + +func (u *Epoch) InitialIdentities() (flow.IdentityList, error) { + return nil, u.err +} + +func (u *Epoch) Clustering() (flow.ClusterList, error) { + return nil, u.err +} + +func (u *Epoch) Cluster(uint) (protocol.Cluster, error) { + return nil, u.err +} + +func (u *Epoch) DKG() (protocol.DKG, error) { + return nil, u.err +} + +func (u *Epoch) RandomSource() ([]byte, error) { + return nil, u.err +} + +func (u *Epoch) Seed(...uint32) ([]byte, error) { + return nil, u.err +} + +func NewEpoch(err error) *Epoch { + return &Epoch{err: err} +} + +// Epochs is an epoch query for an invalid snapshot. +type Epochs struct { + err error +} + +func (u *Snapshot) Epochs() protocol.EpochQuery { + return &Epochs{err: u.err} +} + +func (u *Epochs) Current() protocol.Epoch { + return NewEpoch(u.err) +} + +func (u *Epochs) Next() protocol.Epoch { + return NewEpoch(u.err) +} + +func (u *Epochs) Previous() protocol.Epoch { + return NewEpoch(u.err) +} diff --git a/state/protocol/invalid/snapshot.go b/state/protocol/invalid/snapshot.go new file mode 100644 index 00000000000..e0a04632ff5 --- /dev/null +++ b/state/protocol/invalid/snapshot.go @@ -0,0 +1,47 @@ +package invalid + +import ( + "github.com/onflow/flow-go/model/flow" +) + +// Snapshot represents a snapshot referencing an invalid block, or for +// which an error occurred while resolving the reference block. +type Snapshot struct { + err error +} + +func NewSnapshot(err error) *Snapshot { + return &Snapshot{err: err} +} + +func (u *Snapshot) Head() (*flow.Header, error) { + return nil, u.err +} + +func (u *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { + return nil, u.err +} + +func (u *Snapshot) Phase() (flow.EpochPhase, error) { + return 0, u.err +} + +func (u *Snapshot) Identities(_ flow.IdentityFilter) (flow.IdentityList, error) { + return nil, u.err +} + +func (u *Snapshot) Identity(_ flow.Identifier) (*flow.Identity, error) { + return nil, u.err +} + +func (u *Snapshot) Commit() (flow.StateCommitment, error) { + return nil, u.err +} + +func (u *Snapshot) Pending() ([]flow.Identifier, error) { + return nil, u.err +} + +func (u *Snapshot) Seed(_ ...uint32) ([]byte, error) { + return nil, u.err +} diff --git a/utils/unittest/mocks/epoch_query.go b/utils/unittest/mocks/epoch_query.go index 8e047c81e37..02b450e23e4 100644 --- a/utils/unittest/mocks/epoch_query.go +++ b/utils/unittest/mocks/epoch_query.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/onflow/flow-go/state/protocol" - bprotocol "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/invalid" ) // EpochQuery implements protocol.EpochQuery for testing purposes. @@ -37,7 +37,7 @@ func (mock *EpochQuery) Current() protocol.Epoch { func (mock *EpochQuery) Next() protocol.Epoch { epoch, exists := mock.byCounter[mock.counter+1] if !exists { - return bprotocol.NewInvalidEpoch(protocol.ErrNextEpochNotSetup) + return invalid.NewEpoch(protocol.ErrNextEpochNotSetup) } return epoch } @@ -45,7 +45,7 @@ func (mock *EpochQuery) Next() protocol.Epoch { func (mock *EpochQuery) Previous() protocol.Epoch { epoch, exists := mock.byCounter[mock.counter-1] if !exists { - return bprotocol.NewInvalidEpoch(protocol.ErrNoPreviousEpoch) + return invalid.NewEpoch(protocol.ErrNoPreviousEpoch) } return epoch } From 6ce812b98240bb3e9a799787fb9b25a6786e23ba Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 12:45:41 -0800 Subject: [PATCH 020/178] use inmem models for dkg/cluster These were effectively already using similar memory-backed implementations, this removes the duplication --- state/protocol/badger/cluster.go | 23 -------------------- state/protocol/badger/dkg.go | 37 -------------------------------- state/protocol/badger/epoch.go | 25 ++++++++++++--------- 3 files changed, 15 insertions(+), 70 deletions(-) delete mode 100644 state/protocol/badger/cluster.go delete mode 100644 state/protocol/badger/dkg.go diff --git a/state/protocol/badger/cluster.go b/state/protocol/badger/cluster.go deleted file mode 100644 index 3a2721d48b2..00000000000 --- a/state/protocol/badger/cluster.go +++ /dev/null @@ -1,23 +0,0 @@ -package badger - -import ( - "github.com/onflow/flow-go/model/cluster" - "github.com/onflow/flow-go/model/flow" -) - -// Cluster implements interface protocol.Cluster -// It is a container with all information about a specific cluster. -type Cluster struct { - index uint - counter uint64 - members flow.IdentityList - rootBlock *cluster.Block - rootQC *flow.QuorumCertificate -} - -func (c Cluster) Index() uint { return c.index } -func (c Cluster) ChainID() flow.ChainID { return c.rootBlock.Header.ChainID } -func (c Cluster) EpochCounter() uint64 { return c.counter } -func (c Cluster) Members() flow.IdentityList { return c.members } -func (c Cluster) RootBlock() *cluster.Block { return c.rootBlock } -func (c Cluster) RootQC() *flow.QuorumCertificate { return c.rootQC } diff --git a/state/protocol/badger/dkg.go b/state/protocol/badger/dkg.go deleted file mode 100644 index 5f003f2bac1..00000000000 --- a/state/protocol/badger/dkg.go +++ /dev/null @@ -1,37 +0,0 @@ -package badger - -import ( - "fmt" - - "github.com/onflow/flow-go/crypto" - "github.com/onflow/flow-go/model/flow" -) - -type DKG struct { - commitEvent *flow.EpochCommit -} - -func (d *DKG) Size() uint { - participants := d.commitEvent.DKGParticipants - return uint(len(participants)) -} - -func (d *DKG) GroupKey() crypto.PublicKey { - return d.commitEvent.DKGGroupKey -} - -func (d *DKG) Index(nodeID flow.Identifier) (uint, error) { - participant, found := d.commitEvent.DKGParticipants[nodeID] - if !found { - return 0, fmt.Errorf("could not find *DKG participant data (%x)", nodeID) - } - return participant.Index, nil -} - -func (d *DKG) KeyShare(nodeID flow.Identifier) (crypto.PublicKey, error) { - participant, found := d.commitEvent.DKGParticipants[nodeID] - if !found { - return nil, fmt.Errorf("could not find *DKG participant data (%x)", nodeID) - } - return participant.KeyShare, nil -} diff --git a/state/protocol/badger/epoch.go b/state/protocol/badger/epoch.go index 055010df8d4..654bed6f171 100644 --- a/state/protocol/badger/epoch.go +++ b/state/protocol/badger/epoch.go @@ -5,11 +5,13 @@ package badger import ( "fmt" + "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/model/flow/order" "github.com/onflow/flow-go/state/cluster" "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/state/protocol/seed" ) @@ -103,19 +105,22 @@ func (es *CommittedEpoch) Cluster(index uint) (protocol.Cluster, error) { } epochCounter := es.setupEvent.Counter - inf := Cluster{ - index: index, - counter: epochCounter, - members: members, - rootBlock: cluster.CanonicalRootBlock(epochCounter, members), - rootQC: rootQC, - } - - return &inf, nil + cluster, err := inmem.ClusterFromEncodable(inmem.EncodableCluster{ + Index: index, + Counter: epochCounter, + Members: members, + RootBlock: cluster.CanonicalRootBlock(epochCounter, members), + RootQC: rootQC, + }) + return cluster, err } func (es *CommittedEpoch) DKG() (protocol.DKG, error) { - return &DKG{commitEvent: es.commitEvent}, nil + dkg, err := inmem.DKGFromEncodable(inmem.EncodableDKG{ + GroupKey: encodable.RandomBeaconPubKey{es.commitEvent.DKGGroupKey}, + Participants: es.commitEvent.DKGParticipants, + }) + return dkg, err } func NewCommittedEpoch(setupEvent *flow.EpochSetup, commitEvent *flow.EpochCommit) *CommittedEpoch { From 360c02b531df89b81b52eafde90649fc31c7e9e3 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 12:49:48 -0800 Subject: [PATCH 021/178] make current epoch non-nullable in encodable model --- state/protocol/inmem/convert.go | 2 +- state/protocol/inmem/convert_test.go | 27 ++++++++++++++------------- state/protocol/inmem/encodable.go | 2 +- state/protocol/inmem/epoch.go | 2 +- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index dd093fc004f..24927623137 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -56,7 +56,7 @@ func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { if err != nil { return nil, err } - snap.Epochs.Current = ¤t.enc + snap.Epochs.Current = current.enc next, err := FromEpoch(from.Epochs().Next()) // it is possible for valid snapshots to have no next epoch diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index 06db204e47d..45f31b5a751 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -1,4 +1,4 @@ -package inmem +package inmem_test import ( "bytes" @@ -11,6 +11,7 @@ import ( "github.com/onflow/flow-go/state/protocol" bprotocol "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/utils/unittest" ) @@ -51,7 +52,7 @@ func TestFromSnapshot(t *testing.T) { t.SkipNow() expected := state.AtHeight(0) - actual, err := FromSnapshot(expected) + actual, err := inmem.FromSnapshot(expected) require.Nil(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) @@ -61,21 +62,21 @@ func TestFromSnapshot(t *testing.T) { t.Run("epoch 1", func(t *testing.T) { t.Run("staking phase", func(t *testing.T) { expected := state.AtHeight(1) - actual, err := FromSnapshot(expected) + actual, err := inmem.FromSnapshot(expected) require.Nil(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("setup phase", func(t *testing.T) { expected := state.AtHeight(2) - actual, err := FromSnapshot(expected) + actual, err := inmem.FromSnapshot(expected) require.Nil(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("committed phase", func(t *testing.T) { expected := state.AtHeight(3) - actual, err := FromSnapshot(expected) + actual, err := inmem.FromSnapshot(expected) require.Nil(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) @@ -86,21 +87,21 @@ func TestFromSnapshot(t *testing.T) { t.Run("epoch 2", func(t *testing.T) { t.Run("staking phase", func(t *testing.T) { expected := state.AtHeight(5) - actual, err := FromSnapshot(expected) + actual, err := inmem.FromSnapshot(expected) require.Nil(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("setup phase", func(t *testing.T) { expected := state.AtHeight(6) - actual, err := FromSnapshot(expected) + actual, err := inmem.FromSnapshot(expected) require.Nil(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("committed phase", func(t *testing.T) { expected := state.AtHeight(7) - actual, err := FromSnapshot(expected) + actual, err := inmem.FromSnapshot(expected) require.Nil(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) @@ -110,16 +111,16 @@ func TestFromSnapshot(t *testing.T) { } // checks that a snapshot is equivalent after encoding and decoding -func testEncodeDecode(t *testing.T, snap *Snapshot) { +func testEncodeDecode(t *testing.T, snap *inmem.Snapshot) { bz, err := json.Marshal(snap.Encodable()) require.Nil(t, err) - var encoded EncodableSnapshot + var encoded inmem.EncodableSnapshot err = json.Unmarshal(bz, &encoded) require.Nil(t, err) - fromEncoded := SnapshotFromEncodable(encoded) + fromEncoded := inmem.SnapshotFromEncodable(encoded) assertSnapshotsEqual(t, snap, fromEncoded) } @@ -127,9 +128,9 @@ func testEncodeDecode(t *testing.T, snap *Snapshot) { // representation and comparing the serializations // TODO check equality manually func snapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) bool { - enc1, err := FromSnapshot(snap1) + enc1, err := inmem.FromSnapshot(snap1) require.Nil(t, err) - enc2, err := FromSnapshot(snap2) + enc2, err := inmem.FromSnapshot(snap2) require.Nil(t, err) bz1, err := json.Marshal(enc1.Encodable()) diff --git a/state/protocol/inmem/encodable.go b/state/protocol/inmem/encodable.go index 1e61ca9560b..29bf050c7a6 100644 --- a/state/protocol/inmem/encodable.go +++ b/state/protocol/inmem/encodable.go @@ -19,7 +19,7 @@ type EncodableSnapshot struct { // EncodableEpochs is the encoding format for protocol.EpochQuery type EncodableEpochs struct { Previous *EncodableEpoch - Current *EncodableEpoch + Current EncodableEpoch // cannot be nil Next *EncodableEpoch } diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index 3c014e4e361..8efa5fdc3f5 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -61,7 +61,7 @@ func (eq Epochs) Previous() protocol.Epoch { return invalid.NewEpoch(protocol.ErrNoPreviousEpoch) } func (eq Epochs) Current() protocol.Epoch { - return Epoch{*eq.enc.Current} + return Epoch{eq.enc.Current} } func (eq Epochs) Next() protocol.Epoch { if eq.enc.Next != nil { From 7f383638d3cf618bf861e01a3e649c6f3b7b64a3 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 16:36:13 -0800 Subject: [PATCH 022/178] add sanity check for child when getting QC --- state/protocol/badger/snapshot.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index 4097f378164..476ad8d1017 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -69,9 +69,14 @@ func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { return nil, fmt.Errorf("could not get child: %w", err) } + // sanity check: ensure the child has the snapshot block as parent + if child.ParentID != s.blockID { + return nil, fmt.Errorf("child parent id (%x) does not match snapshot id (%x)", child.ParentID, s.blockID) + } + qc := &flow.QuorumCertificate{ View: head.View, - BlockID: child.ParentID, + BlockID: s.blockID, SignerIDs: child.ParentVoterIDs, SigData: child.ParentVoterSig, } From 8f5992ea9cfc8c97ec471b5fe59c39eafcf7fa26 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 16:42:16 -0800 Subject: [PATCH 023/178] improve QC godoc --- state/protocol/badger/snapshot.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index 476ad8d1017..e83c601bd3f 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -41,8 +41,10 @@ func (s *Snapshot) Head() (*flow.Header, error) { return head, err } -// QuorumCertificate returns a valid quorum certificate for the block at the -// head of this snapshot, if one exists. +// QuorumCertificate (QC) returns a valid quorum certificate pointing to the +// header at this snapshot. With the exception of the root block, a valid child +// block must be which contains the desired QC. The sentinel error +// state.NoValidChildBlockError is returned if the the QC is unknown. // // For root block snapshots, returns the root quorum certificate. For all other // blocks, generates a quorum certificate from a valid child, if one exists. From f3ca5f1461153d55905e40cf6fe418eda7e800b1 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 16:45:53 -0800 Subject: [PATCH 024/178] compare to root block using ID rather than Height --- state/protocol/badger/snapshot.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index e83c601bd3f..6cba862f771 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -56,11 +56,7 @@ func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { return nil, fmt.Errorf("could not get root: %w", err) } - head, err := s.Head() - if err != nil { - return nil, fmt.Errorf("could not get head: %w", err) - } - if head.Height == root.Height { + if s.blockID == root.ID() { // TODO store root QC and return here return nil, fmt.Errorf("root qc not stored") } @@ -76,6 +72,12 @@ func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { return nil, fmt.Errorf("child parent id (%x) does not match snapshot id (%x)", child.ParentID, s.blockID) } + // retrieve the full header as we need the view for the quorum certificate + head, err := s.Head() + if err != nil { + return nil, fmt.Errorf("could not get head: %w", err) + } + qc := &flow.QuorumCertificate{ View: head.View, BlockID: s.blockID, @@ -181,7 +183,7 @@ func (s *Snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, if err != nil { return nil, fmt.Errorf("could not get root block: %w", err) } - if first.Height == root.Height { + if first.ID() == root.ID() { break } @@ -382,7 +384,7 @@ func (q *EpochQuery) Previous() protocol.Epoch { if err != nil { return NewInvalidEpoch(err) } - if first.Height == root.Height { + if first.ID() == root.ID() { return NewInvalidEpoch(protocol.ErrNoPreviousEpoch) } From 8ebbb8c7b04da86fb537f58eb605b53321d73377 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 16:49:47 -0800 Subject: [PATCH 025/178] include sentinel in godoc for validChild --- state/protocol/badger/snapshot.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index 6cba862f771..64712f59161 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -89,9 +89,10 @@ func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { } // validChild returns a child of the snapshot head that has been validated -// by HotStuff, if such a child exists. Otherwise returns an error. Any valid -// child may be returned. Subsequent calls are not guaranteed to return the -// same child. +// by HotStuff. Returns state.NoValidChildBlockError if no valid child exists. +// +// Any valid child may be returned. Subsequent calls are not guaranteed to +// return the same child. func (s *Snapshot) validChild() (*flow.Header, error) { var childIDs []flow.Identifier From a490ba83ff4d77be97b2f51c1db92846a1c48ad2 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 16:54:03 -0800 Subject: [PATCH 026/178] check for sentinel error type in test --- state/protocol/badger/snapshot_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index cfe2192313a..5f276b4122c 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -14,6 +14,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" + staterr "github.com/onflow/flow-go/state" "github.com/onflow/flow-go/state/protocol" bprotocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/util" @@ -149,6 +150,7 @@ func TestQuorumCertificate(t *testing.T) { _, err = state.AtBlockID(block1.ID()).QuorumCertificate() assert.Error(t, err) + assert.True(t, staterr.IsNoValidChildBlockError(err)) _, err = state.AtBlockID(block1.ID()).Seed(1, 2, 3, 4) assert.Error(t, err) @@ -178,9 +180,11 @@ func TestQuorumCertificate(t *testing.T) { _, err = state.AtBlockID(block1.ID()).QuorumCertificate() assert.Error(t, err) + assert.True(t, staterr.IsNoValidChildBlockError(err)) _, err = state.AtBlockID(block1.ID()).Seed(1, 2, 3, 4) assert.Error(t, err) + assert.True(t, staterr.IsNoValidChildBlockError(err)) }) }) From ff6f97cf8f1e40bf9a7009541739908c6cb4b0ff Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 3 Dec 2020 17:00:41 -0800 Subject: [PATCH 027/178] remove unecessary check from validChild --- state/errors.go | 4 ++++ state/protocol/badger/snapshot.go | 9 ++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/state/errors.go b/state/errors.go index 08a2f63570b..8f5266d1590 100644 --- a/state/errors.go +++ b/state/errors.go @@ -69,6 +69,10 @@ func NewNoValidChildBlockError(msg string) error { } } +func NewNoValidChildBlockErrorf(msg string, args ...interface{}) error { + return NewNoValidChildBlockError(fmt.Sprintf(msg, args...)) +} + func (e NoValidChildBlockError) Error() string { return e.msg } diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index 64712f59161..07c3234997c 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -101,13 +101,8 @@ func (s *Snapshot) validChild() (*flow.Header, error) { return nil, fmt.Errorf("could not look up children: %w", err) } - // check we have at least one child - if len(childIDs) == 0 { - return nil, state.NewNoValidChildBlockError("block doesn't have children yet") - } - // find the first child that has been validated - var validChildID flow.Identifier + validChildID := flow.ZeroID for _, childID := range childIDs { var valid bool err = s.state.db.View(operation.RetrieveBlockValidity(childID, &valid)) @@ -125,7 +120,7 @@ func (s *Snapshot) validChild() (*flow.Header, error) { } if validChildID == flow.ZeroID { - return nil, state.NewNoValidChildBlockError("block has no valid children") + return nil, state.NewNoValidChildBlockErrorf("block has no valid children (total children: %d)", len(childIDs)) } // get the header of the first child From 9009ba77ffcc5a7d86c9ccbe9d404a9aadc47ef4 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 9 Dec 2020 14:30:26 -0800 Subject: [PATCH 028/178] update error string wording --- state/protocol/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state/protocol/errors.go b/state/protocol/errors.go index 89d536cb76c..b4508228b13 100644 --- a/state/protocol/errors.go +++ b/state/protocol/errors.go @@ -19,7 +19,7 @@ var ( // ErrEpochNotCommitted is a sentinel error returned when the epoch has // not been committed and information is queried that is only accessible // in the EpochCommitted phase. - ErrEpochNotCommitted = fmt.Errorf("EpochCommit has not been received yet") + ErrEpochNotCommitted = fmt.Errorf("queried info from EpochCommit event before it was emitted") ) type IdentityNotFoundError struct { From 444c0d1440c83a77f8eb3842e321bdb6035c9123 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 9 Dec 2020 14:43:44 -0800 Subject: [PATCH 029/178] use NoError in epoch builder --- utils/unittest/epoch_builder.go | 44 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/utils/unittest/epoch_builder.go b/utils/unittest/epoch_builder.go index b272cd8b6ef..ad4523356df 100644 --- a/utils/unittest/epoch_builder.go +++ b/utils/unittest/epoch_builder.go @@ -62,30 +62,30 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { // prepare default values for the service events based on the current state identities, err := builder.state.Final().Identities(filter.Any) - require.Nil(builder.t, err) + require.NoError(builder.t, err) epoch := builder.state.Final().Epochs().Current() counter, err := epoch.Counter() - require.Nil(builder.t, err) + require.NoError(builder.t, err) finalView, err := epoch.FinalView() - require.Nil(builder.t, err) + require.NoError(builder.t, err) // retrieve block A A, err := builder.state.Final().Head() - require.Nil(builder.t, err) + require.NoError(builder.t, err) // check that block A satisfies initial condition phase, err := builder.state.Final().Phase() - require.Nil(builder.t, err) + require.NoError(builder.t, err) require.Equal(builder.t, flow.EpochPhaseStaking, phase) // retrieve the sealed height to determine what seals to include in B sealed, err := builder.state.Sealed().Head() - require.Nil(builder.t, err) + require.NoError(builder.t, err) var seals []*flow.Seal for height := sealed.Height + 1; height <= A.Height; height++ { next, err := builder.state.AtHeight(height).Head() - require.Nil(builder.t, err) + require.NoError(builder.t, err) seals = append(seals, Seal.Fixture(Seal.WithBlockID(next.ID()))) } @@ -93,12 +93,12 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { B := BlockWithParentFixture(A) B.SetPayload(flow.Payload{Seals: seals}) err = builder.state.Mutate().Extend(&B) - require.Nil(builder.t, err) + require.NoError(builder.t, err) // finalize and validate block B err = builder.state.Mutate().Finalize(B.ID()) - require.Nil(builder.t, err) + require.NoError(builder.t, err) err = builder.state.Mutate().MarkValid(B.ID()) - require.Nil(builder.t, err) + require.NoError(builder.t, err) // defaults for the EpochSetup event setupDefaults := []func(*flow.EpochSetup){ @@ -119,12 +119,12 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { Seals: []*flow.Seal{sealForB}, }) err = builder.state.Mutate().Extend(&C) - require.Nil(builder.t, err) + require.NoError(builder.t, err) // finalize and validate block C err = builder.state.Mutate().Finalize(C.ID()) - require.Nil(builder.t, err) + require.NoError(builder.t, err) err = builder.state.Mutate().MarkValid(C.ID()) - require.Nil(builder.t, err) + require.NoError(builder.t, err) // defaults for the EpochCommit event commitDefaults := []func(*flow.EpochCommit){ @@ -144,12 +144,12 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { Seals: []*flow.Seal{sealForC}, }) err = builder.state.Mutate().Extend(&D) - require.Nil(builder.t, err) + require.NoError(builder.t, err) // finalize and validate block D err = builder.state.Mutate().Finalize(D.ID()) - require.Nil(builder.t, err) + require.NoError(builder.t, err) err = builder.state.Mutate().MarkValid(D.ID()) - require.Nil(builder.t, err) + require.NoError(builder.t, err) return builder } @@ -160,13 +160,13 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { func (builder *EpochBuilder) CompleteEpoch() { phase, err := builder.state.Final().Phase() - require.Nil(builder.t, err) + require.NoError(builder.t, err) require.Equal(builder.t, flow.EpochPhaseCommitted, phase) finalView, err := builder.state.Final().Epochs().Current().FinalView() - require.Nil(builder.t, err) + require.NoError(builder.t, err) final, err := builder.state.Final().Head() - require.Nil(builder.t, err) + require.NoError(builder.t, err) // A is the first block of the next epoch (see diagram in BuildEpoch) A := BlockWithParentFixture(final) @@ -174,9 +174,9 @@ func (builder *EpochBuilder) CompleteEpoch() { A.Header.View = finalView + (rand.Uint64() % 4) + 1 A.SetPayload(flow.EmptyPayload()) err = builder.state.Mutate().Extend(&A) - require.Nil(builder.t, err) + require.NoError(builder.t, err) err = builder.state.Mutate().Finalize(A.ID()) - require.Nil(builder.t, err) + require.NoError(builder.t, err) err = builder.state.Mutate().MarkValid(A.ID()) - require.Nil(builder.t, err) + require.NoError(builder.t, err) } From 50210b4094280094e7d1ed4bc83d2bd06f86eae4 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 9 Dec 2020 14:54:54 -0800 Subject: [PATCH 030/178] wrap all errors in snapshot conversion --- state/protocol/inmem/convert.go | 39 +++++++++++++++++---------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 24927623137..726bd71e523 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -2,6 +2,7 @@ package inmem import ( "errors" + "fmt" "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" @@ -22,23 +23,23 @@ func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { // convert top-level fields snap.Head, err = from.Head() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get head: %w", err) } snap.Identities, err = from.Identities(filter.Any) if err != nil { - return nil, err + return nil, fmt.Errorf("could not get identities: %w", err) } snap.Commit, err = from.Commit() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get commit: %w", err) } snap.QuorumCertificate, err = from.QuorumCertificate() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get qc: %w", err) } snap.Phase, err = from.Phase() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get phase: %w", err) } // convert epochs @@ -47,14 +48,14 @@ func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { if errors.Is(err, protocol.ErrNoPreviousEpoch) { snap.Epochs.Previous = nil } else if err != nil { - return nil, err + return nil, fmt.Errorf("could not get previous epoch: %w", err) } else { snap.Epochs.Previous = &previous.enc } current, err := FromEpoch(from.Epochs().Current()) if err != nil { - return nil, err + return nil, fmt.Errorf("could not get current epoch: %w", err) } snap.Epochs.Current = current.enc @@ -63,7 +64,7 @@ func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { if errors.Is(err, protocol.ErrNextEpochNotSetup) { snap.Epochs.Next = nil } else if err != nil { - return nil, err + return nil, fmt.Errorf("could not get next epoch: %w", err) } else { snap.Epochs.Next = &next.enc } @@ -82,23 +83,23 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { // convert top-level fields epoch.Counter, err = from.Counter() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get counter: %w", err) } epoch.InitialIdentities, err = from.InitialIdentities() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get initial identities: %w", err) } epoch.FirstView, err = from.FirstView() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get first view: %w", err) } epoch.FinalView, err = from.FinalView() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get final view: %w", err) } epoch.RandomSource, err = from.RandomSource() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get random source: %w", err) } // convert dkg @@ -108,7 +109,7 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { return &Epoch{epoch}, nil } if err != nil { - return nil, err + return nil, fmt.Errorf("could not get dkg: %w", err) } convertedDKG, err := FromDKG(dkg, epoch.InitialIdentities.Filter(filter.HasRole(flow.RoleConsensus))) if err != nil { @@ -119,16 +120,16 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { // convert clusters clustering, err := from.Clustering() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get clustering: %w", err) } for index := range clustering { cluster, err := from.Cluster(uint(index)) if err != nil { - return nil, err + return nil, fmt.Errorf("could not get cluster %d: %w", index, err) } convertedCluster, err := FromCluster(cluster) if err != nil { - return nil, err + return nil, fmt.Errorf("could not convert cluster %d: %w", index, err) } epoch.Clusters = append(epoch.Clusters, convertedCluster.enc) } @@ -161,11 +162,11 @@ func FromDKG(from protocol.DKG, participants flow.IdentityList) (*DKG, error) { index, err := from.Index(identity.NodeID) if err != nil { - return nil, err + return nil, fmt.Errorf("could not get index (node=%x): %w", identity.NodeID, err) } key, err := from.KeyShare(identity.NodeID) if err != nil { - return nil, err + return nil, fmt.Errorf("could not get key share (node=%x): %w", identity.NodeID, err) } dkg.Participants[identity.NodeID] = flow.DKGParticipant{ From fae28e55cee6c063961dd5c091efbf96d5fe9deb Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 9 Dec 2020 14:58:38 -0800 Subject: [PATCH 031/178] use godoc convention for test functions --- state/protocol/inmem/convert_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index 45f31b5a751..f421267f1c0 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -16,7 +16,8 @@ import ( "github.com/onflow/flow-go/utils/unittest" ) -// should be able to convert from protocol.Snapshot +// TestFromSnapshot tests that we are able to convert a database-backed snapshot +// to a memory-backed snapshot. func TestFromSnapshot(t *testing.T) { util.RunWithProtocolState(t, func(db *badger.DB, state *bprotocol.State) { @@ -126,7 +127,6 @@ func testEncodeDecode(t *testing.T, snap *inmem.Snapshot) { // checks that 2 snapshots are equivalent by converting to a serializable // representation and comparing the serializations -// TODO check equality manually func snapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) bool { enc1, err := inmem.FromSnapshot(snap1) require.Nil(t, err) From 9d59fbbc1afd4f5197b46b5bb070c7db57ac3ad7 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 9 Dec 2020 15:00:03 -0800 Subject: [PATCH 032/178] use NoError over Nil in convert_test.go --- state/protocol/inmem/convert_test.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index f421267f1c0..09a78978412 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -24,7 +24,7 @@ func TestFromSnapshot(t *testing.T) { identities := unittest.IdentityListFixture(10, unittest.WithAllRoles()) root, result, seal := unittest.BootstrapFixture(identities) err := state.Mutate().Bootstrap(root, result, seal) - require.Nil(t, err) + require.NoError(t, err) // Prepare an epoch builder, which builds epochs with 4 blocks, A,B,C,D // See EpochBuilder documentation for details of these blocks. @@ -54,7 +54,7 @@ func TestFromSnapshot(t *testing.T) { expected := state.AtHeight(0) actual, err := inmem.FromSnapshot(expected) - require.Nil(t, err) + require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) @@ -64,21 +64,21 @@ func TestFromSnapshot(t *testing.T) { t.Run("staking phase", func(t *testing.T) { expected := state.AtHeight(1) actual, err := inmem.FromSnapshot(expected) - require.Nil(t, err) + require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("setup phase", func(t *testing.T) { expected := state.AtHeight(2) actual, err := inmem.FromSnapshot(expected) - require.Nil(t, err) + require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("committed phase", func(t *testing.T) { expected := state.AtHeight(3) actual, err := inmem.FromSnapshot(expected) - require.Nil(t, err) + require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) @@ -89,21 +89,21 @@ func TestFromSnapshot(t *testing.T) { t.Run("staking phase", func(t *testing.T) { expected := state.AtHeight(5) actual, err := inmem.FromSnapshot(expected) - require.Nil(t, err) + require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("setup phase", func(t *testing.T) { expected := state.AtHeight(6) actual, err := inmem.FromSnapshot(expected) - require.Nil(t, err) + require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("committed phase", func(t *testing.T) { expected := state.AtHeight(7) actual, err := inmem.FromSnapshot(expected) - require.Nil(t, err) + require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) @@ -115,11 +115,11 @@ func TestFromSnapshot(t *testing.T) { func testEncodeDecode(t *testing.T, snap *inmem.Snapshot) { bz, err := json.Marshal(snap.Encodable()) - require.Nil(t, err) + require.NoError(t, err) var encoded inmem.EncodableSnapshot err = json.Unmarshal(bz, &encoded) - require.Nil(t, err) + require.NoError(t, err) fromEncoded := inmem.SnapshotFromEncodable(encoded) assertSnapshotsEqual(t, snap, fromEncoded) @@ -129,14 +129,14 @@ func testEncodeDecode(t *testing.T, snap *inmem.Snapshot) { // representation and comparing the serializations func snapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) bool { enc1, err := inmem.FromSnapshot(snap1) - require.Nil(t, err) + require.NoError(t, err) enc2, err := inmem.FromSnapshot(snap2) - require.Nil(t, err) + require.NoError(t, err) bz1, err := json.Marshal(enc1.Encodable()) - require.Nil(t, err) + require.NoError(t, err) bz2, err := json.Marshal(enc2.Encodable()) - require.Nil(t, err) + require.NoError(t, err) return bytes.Equal(bz1, bz2) } From 4812a9cd73c5f4e40d09614e0cc26572e28cf16a Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 9 Dec 2020 15:04:09 -0800 Subject: [PATCH 033/178] update encodable type names --- state/protocol/inmem/encodable.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/state/protocol/inmem/encodable.go b/state/protocol/inmem/encodable.go index 29bf050c7a6..8a61ea13ba7 100644 --- a/state/protocol/inmem/encodable.go +++ b/state/protocol/inmem/encodable.go @@ -6,7 +6,7 @@ import ( "github.com/onflow/flow-go/model/flow" ) -// Snapshot is the encoding format for protocol.Snapshot +// EncodableSnapshot is the encoding format for protocol.Snapshot type EncodableSnapshot struct { Head *flow.Header Identities flow.IdentityList @@ -23,7 +23,7 @@ type EncodableEpochs struct { Next *EncodableEpoch } -// Epoch is the encoding format for protocol.Epoch +// EncodableEpoch is the encoding format for protocol.Epoch type EncodableEpoch struct { Counter uint64 FirstView uint64 @@ -34,13 +34,13 @@ type EncodableEpoch struct { DKG *EncodableDKG } -// DKG is the encoding format for protocol.DKG +// EncodableDKG is the encoding format for protocol.DKG type EncodableDKG struct { GroupKey encodable.RandomBeaconPubKey Participants map[flow.Identifier]flow.DKGParticipant } -// Cluster is the encoding format for protocol.Cluster +// EncodableCluster is the encoding format for protocol.Cluster type EncodableCluster struct { Index uint Counter uint64 From 2c7c16c44b30b9e032df96bfbe2cf6fbba44c22e Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 9 Dec 2020 15:08:52 -0800 Subject: [PATCH 034/178] use non-pointer receiver for inmem.Snapshot --- state/protocol/inmem/snapshot.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/state/protocol/inmem/snapshot.go b/state/protocol/inmem/snapshot.go index 6fdfd98bdbb..c2f048a375f 100644 --- a/state/protocol/inmem/snapshot.go +++ b/state/protocol/inmem/snapshot.go @@ -20,19 +20,19 @@ type Snapshot struct { enc EncodableSnapshot } -func (s *Snapshot) Head() (*flow.Header, error) { +func (s Snapshot) Head() (*flow.Header, error) { return s.enc.Head, nil } -func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { +func (s Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { return s.enc.QuorumCertificate, nil } -func (s *Snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, error) { +func (s Snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, error) { return s.enc.Identities.Filter(selector), nil } -func (s *Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { +func (s Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { identity, ok := s.enc.Identities.ByNodeID(nodeID) if !ok { return nil, protocol.IdentityNotFoundError{NodeID: nodeID} @@ -40,28 +40,28 @@ func (s *Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { return identity, nil } -func (s *Snapshot) Commit() (flow.StateCommitment, error) { +func (s Snapshot) Commit() (flow.StateCommitment, error) { return s.enc.Commit, nil } -func (s *Snapshot) Pending() ([]flow.Identifier, error) { +func (s Snapshot) Pending() ([]flow.Identifier, error) { // canonical snapshots don't have any pending blocks return nil, nil } -func (s *Snapshot) Phase() (flow.EpochPhase, error) { +func (s Snapshot) Phase() (flow.EpochPhase, error) { return s.enc.Phase, nil } -func (s *Snapshot) Seed(indices ...uint32) ([]byte, error) { +func (s Snapshot) Seed(indices ...uint32) ([]byte, error) { return seed.FromParentSignature(indices, s.enc.QuorumCertificate.SigData) } -func (s *Snapshot) Epochs() protocol.EpochQuery { +func (s Snapshot) Epochs() protocol.EpochQuery { return Epochs{s.enc.Epochs} } -func (s *Snapshot) Encodable() EncodableSnapshot { +func (s Snapshot) Encodable() EncodableSnapshot { return s.enc } From 5a8d06fcf76ddb9fa4cf09640974405fecc95329 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 9 Dec 2020 17:04:36 -0800 Subject: [PATCH 035/178] lint --- state/protocol/badger/epoch.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/state/protocol/badger/epoch.go b/state/protocol/badger/epoch.go index 654bed6f171..eb74d249b55 100644 --- a/state/protocol/badger/epoch.go +++ b/state/protocol/badger/epoch.go @@ -117,7 +117,9 @@ func (es *CommittedEpoch) Cluster(index uint) (protocol.Cluster, error) { func (es *CommittedEpoch) DKG() (protocol.DKG, error) { dkg, err := inmem.DKGFromEncodable(inmem.EncodableDKG{ - GroupKey: encodable.RandomBeaconPubKey{es.commitEvent.DKGGroupKey}, + GroupKey: encodable.RandomBeaconPubKey{ + PublicKey: es.commitEvent.DKGGroupKey, + }, Participants: es.commitEvent.DKGParticipants, }) return dkg, err From 7df476031a73739993f1d157ac07312f8e69d4b3 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 8 Jan 2021 16:19:47 -0800 Subject: [PATCH 036/178] update inmem snapshot to use state root --- state/protocol/inmem/convert_test.go | 16 +++++++++++----- utils/unittest/fixtures.go | 4 ++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index 09a78978412..39199fb990f 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" bprotocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/inmem" @@ -19,12 +20,10 @@ import ( // TestFromSnapshot tests that we are able to convert a database-backed snapshot // to a memory-backed snapshot. func TestFromSnapshot(t *testing.T) { - util.RunWithProtocolState(t, func(db *badger.DB, state *bprotocol.State) { + identities := unittest.IdentityListFixture(10, unittest.WithAllRoles()) + stateRoot := fixtureStateRoot(t, identities) - identities := unittest.IdentityListFixture(10, unittest.WithAllRoles()) - root, result, seal := unittest.BootstrapFixture(identities) - err := state.Mutate().Bootstrap(root, result, seal) - require.NoError(t, err) + util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *bprotocol.FollowerState) { // Prepare an epoch builder, which builds epochs with 4 blocks, A,B,C,D // See EpochBuilder documentation for details of these blocks. @@ -144,3 +143,10 @@ func snapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) bool { func assertSnapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) { assert.True(t, snapshotsEqual(t, snap1, snap2)) } + +func fixtureStateRoot(t *testing.T, participants flow.IdentityList) *bprotocol.StateRoot { + root, result, seal := unittest.BootstrapFixture(participants) + stateRoot, err := bprotocol.NewStateRoot(root, result, seal, 0) + require.NoError(t, err) + return stateRoot +} diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 833a870bdb4..f85891aaae6 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -6,11 +6,11 @@ import ( "math/rand" "time" + sdk "github.com/onflow/flow-go-sdk" + "github.com/onflow/flow-go/model/chunks" "github.com/onflow/flow-go/module/signature" - sdk "github.com/onflow/flow-go-sdk" - hotstuff "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/crypto/hash" From 2695068602f8af9880ab252532148c04abc3ef0d Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 11 Jan 2021 17:49:59 -0800 Subject: [PATCH 037/178] adds requirements for bootstrapping to protocol.Snapshot --- state/protocol/snapshot.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/state/protocol/snapshot.go b/state/protocol/snapshot.go index 6975c8830fd..1a8397490fe 100644 --- a/state/protocol/snapshot.go +++ b/state/protocol/snapshot.go @@ -37,8 +37,21 @@ type Snapshot interface { // selected point of the protocol state history. It will error if it doesn't exist. Identity(nodeID flow.Identifier) (*flow.Identity, error) - // Commit return the sealed execution state commitment at this block. - Commit() (flow.StateCommitment, error) + // LatestSeal returns the most recent included seal as of this block. The seal + // may have been included in a parent block, if this block is empty. If this + // block contains multiple seals, this returns the seal for the block with + // the greatest height. + LatestSeal() (*flow.Seal, error) + + // LatestResult returns the execution result referenced by the most recent + // included seal as of this block (see LatestSeal). + LatestResult() (*flow.ExecutionResult, error) + + // SealingSegment returns the chain segment such that the head (greatest + // height) is this snapshot's reference block and the tail (least height) + // is the most recently sealed block as of this snapshot (ie. the block + // referenced by LatestSeal). + SealingSegment() ([]*flow.Block, error) // Pending returns the IDs of all descendants of the Head block. The IDs // are ordered such that parents are included before their children. These From 41942bddeab32d2ebd2fe951d96dbb2d43b267d8 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 12 Jan 2021 09:14:08 -0800 Subject: [PATCH 038/178] implement additional protocol methods --- state/protocol/badger/snapshot.go | 63 +++++++++++++++++++++++++++++++ state/protocol/badger/state.go | 1 + state/protocol/snapshot.go | 3 ++ 3 files changed, 67 insertions(+) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index a70704457d3..7c75ee2626d 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -57,6 +57,7 @@ func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { return nil, fmt.Errorf("could not get root: %w", err) } + // TODO: store root QC if s.blockID == root.ID() { // TODO store root QC and return here return nil, fmt.Errorf("root qc not stored") @@ -261,6 +262,68 @@ func (s *Snapshot) Commit() (flow.StateCommitment, error) { return seal.FinalState, nil } +func (s *Snapshot) LatestSeal() (*flow.Seal, error) { + seal, err := s.state.seals.ByBlockID(s.blockID) + if err != nil { + return nil, fmt.Errorf("could not look up latest seal: %w", err) + } + return seal, nil +} + +// TODO inject results storage +func (s *Snapshot) LatestResult() (*flow.ExecutionResult, error) { + seal, err := s.LatestSeal() + if err != nil { + return nil, fmt.Errorf("could not get latest seal: %w", err) + } + result, err := s.state.results.ByID(seal.ResultID) + if err != nil { + return nil, fmt.Errorf("could not get latest result: %w", err) + } + return result, nil +} + +func (s *Snapshot) SealingSegment() ([]*flow.Block, error) { + seal, err := s.LatestSeal() + if err != nil { + return nil, fmt.Errorf("could not get seal for sealing segment: %w", err) + } + head, err := s.state.blocks.ByID(s.blockID) + if err != nil { + return nil, fmt.Errorf("could not get head block: %w", err) + } + + // if this snapshot references the root block, the sealing segment + // consists only of the root block + segment := []*flow.Block{head} + if s.blockID == seal.BlockID { + return segment, nil + } + + // for all other cases we walk through the chain backward until we reach + // the block referenced by the latest seal - the returned segment includes + // this block + nextID := head.Header.ParentID + for { + next, err := s.state.blocks.ByID(nextID) + if err != nil { + return nil, fmt.Errorf("could not get next block (id=%x): %w", nextID, err) + } + + segment = append(segment, next) + if nextID == seal.BlockID { + break + } + nextID = next.Header.ParentID + } + + // reverse the segment so it is in ascending order by height + for i, j := 0, len(segment)-1; i < j; i, j = i+1, j-1 { + segment[i], segment[j] = segment[j], segment[i] + } + return segment, nil +} + func (s *Snapshot) Pending() ([]flow.Identifier, error) { return s.pending(s.blockID) } diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 1a748428e71..1d02b945d2f 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -21,6 +21,7 @@ type State struct { db *badger.DB headers storage.Headers blocks storage.Blocks + results storage.ExecutionResults seals storage.Seals epoch struct { setups storage.EpochSetups diff --git a/state/protocol/snapshot.go b/state/protocol/snapshot.go index 1a8397490fe..7e614b15f1f 100644 --- a/state/protocol/snapshot.go +++ b/state/protocol/snapshot.go @@ -51,6 +51,9 @@ type Snapshot interface { // height) is this snapshot's reference block and the tail (least height) // is the most recently sealed block as of this snapshot (ie. the block // referenced by LatestSeal). + // + // This segment is used as the initial state for non-spork and non-genesis + // root states. SealingSegment() ([]*flow.Block, error) // Pending returns the IDs of all descendants of the Head block. The IDs From 6860bcc8bac35538b4d765be7a7d04d630533393 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 12 Jan 2021 09:18:30 -0800 Subject: [PATCH 039/178] update invalid snapshot --- state/protocol/invalid/snapshot.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/state/protocol/invalid/snapshot.go b/state/protocol/invalid/snapshot.go index e0a04632ff5..b2166887b34 100644 --- a/state/protocol/invalid/snapshot.go +++ b/state/protocol/invalid/snapshot.go @@ -38,6 +38,18 @@ func (u *Snapshot) Commit() (flow.StateCommitment, error) { return nil, u.err } +func (u *Snapshot) LatestSeal() (*flow.Seal, error) { + return nil, u.err +} + +func (u *Snapshot) LatestResult() (*flow.ExecutionResult, error) { + return nil, u.err +} + +func (u *Snapshot) SealingSegment() ([]*flow.Block, error) { + return nil, u.err +} + func (u *Snapshot) Pending() ([]flow.Identifier, error) { return nil, u.err } From 9d62ad5debcea73a7cb13d0a84f94611ce9bf6b8 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 12 Jan 2021 15:15:18 -0800 Subject: [PATCH 040/178] update inmem snapshot with new methods --- state/protocol/inmem/encodable.go | 4 +++- state/protocol/inmem/snapshot.go | 14 +++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/state/protocol/inmem/encodable.go b/state/protocol/inmem/encodable.go index 8a61ea13ba7..ce5d06b7b4b 100644 --- a/state/protocol/inmem/encodable.go +++ b/state/protocol/inmem/encodable.go @@ -10,7 +10,9 @@ import ( type EncodableSnapshot struct { Head *flow.Header Identities flow.IdentityList - Commit flow.StateCommitment + LatestSeal *flow.Seal + LatestResult *flow.ExecutionResult + SealingSegment []*flow.Block QuorumCertificate *flow.QuorumCertificate Phase flow.EpochPhase Epochs EncodableEpochs diff --git a/state/protocol/inmem/snapshot.go b/state/protocol/inmem/snapshot.go index c2f048a375f..4bc40684309 100644 --- a/state/protocol/inmem/snapshot.go +++ b/state/protocol/inmem/snapshot.go @@ -41,7 +41,19 @@ func (s Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { } func (s Snapshot) Commit() (flow.StateCommitment, error) { - return s.enc.Commit, nil + return s.enc.LatestSeal.FinalState, nil +} + +func (s Snapshot) LatestSeal() (*flow.Seal, error) { + return s.enc.LatestSeal, nil +} + +func (s Snapshot) LatestResult() (*flow.ExecutionResult, error) { + return s.enc.LatestResult, nil +} + +func (s Snapshot) SealingSegment() ([]*flow.Block, error) { + return s.enc.SealingSegment, nil } func (s Snapshot) Pending() ([]flow.Identifier, error) { From d07c8bc045b2da691999d003847f3ec56367e9ec Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 13 Jan 2021 15:40:36 -0800 Subject: [PATCH 041/178] add cluster.Assignments method to convert cluster list back to underlying assignments --- model/flow/cluster.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/model/flow/cluster.go b/model/flow/cluster.go index 4d89f29e405..39fdbd0ba2a 100644 --- a/model/flow/cluster.go +++ b/model/flow/cluster.go @@ -13,6 +13,20 @@ type AssignmentList [][]Identifier // nodes assigned to a specific cluster. type ClusterList []IdentityList +// Assignments returns the assignment list for a cluster. +// TODO test +func (clusters ClusterList) Assignments() AssignmentList { + assignments := make(AssignmentList, 0, len(clusters)) + for _, cluster := range clusters { + assignment := make([]Identifier, 0, len(cluster)) + for _, collector := range cluster { + assignment = append(assignment, collector.NodeID) + } + assignments = append(assignments, assignment) + } + return assignments +} + // NewClusterList creates a new cluster list based on the given cluster assignment // and the provided list of identities. func NewClusterList(assignments AssignmentList, collectors IdentityList) (ClusterList, error) { From 677209d1c3070b2ac1dd925f1cba229c3363a6b6 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 13 Jan 2021 15:41:02 -0800 Subject: [PATCH 042/178] add previous epoch to epoch status Replace FirstBlock with PreviousEpoch in EpochStatus. Since we will allow bootstrapping from arbitary blocks, we can no longer guarantee a reference to the first block of an epoch (in particular the first) will exist. On the other hand it remains necessary for the protocol state to have a reference to the configuation to the previous epoch --- model/flow/epoch.go | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/model/flow/epoch.go b/model/flow/epoch.go index c9b91bafc9a..bc01ec732a7 100644 --- a/model/flow/epoch.go +++ b/model/flow/epoch.go @@ -263,9 +263,9 @@ func (part DKGParticipant) EncodeRLP(w io.Writer) error { // service events emitted as of the reference block. Events not yet emitted are // represented by ZeroID. type EpochStatus struct { - FirstBlockID Identifier // ID of the first block in current epoch - CurrentEpoch EventIDs // EpochSetup and EpochCommit events for the current epoch - NextEpoch EventIDs // EpochSetup and EpochCommit events for the next epoch + PreviousEpoch EventIDs // EpochSetup and EpochCommit events for the previous epoch + CurrentEpoch EventIDs // EpochSetup and EpochCommit events for the current epoch + NextEpoch EventIDs // EpochSetup and EpochCommit events for the next epoch } // EventIDs is a container for IDs of epoch service events. @@ -276,9 +276,12 @@ type EventIDs struct { CommitID Identifier } -func NewEpochStatus(firstBlockID, currentSetup, currentCommit, nextSetup, nextCommit Identifier) (*EpochStatus, error) { +func NewEpochStatus(previousSetup, previousCommit, currentSetup, currentCommit, nextSetup, nextCommit Identifier) (*EpochStatus, error) { status := &EpochStatus{ - FirstBlockID: firstBlockID, + PreviousEpoch: EventIDs{ + SetupID: previousSetup, + CommitID: previousCommit, + }, CurrentEpoch: EventIDs{ SetupID: currentSetup, CommitID: currentCommit, @@ -289,22 +292,22 @@ func NewEpochStatus(firstBlockID, currentSetup, currentCommit, nextSetup, nextCo }, } - err := status.check() + err := status.Check() if err != nil { return nil, err } return status, nil } -// check checks that the status is well-formed, returning an error if it is not. -func (es *EpochStatus) check() error { +// Check checks that the status is well-formed, returning an error if it is not. +func (es *EpochStatus) Check() error { if es == nil { return fmt.Errorf("nil epoch status") } - // must reference first block of current epoch - if es.FirstBlockID == ZeroID { - return fmt.Errorf("epoch status with empty first block") + // must reference either both or neither event IDs for previous epoch + if (es.PreviousEpoch.SetupID == ZeroID) != (es.PreviousEpoch.CommitID == ZeroID) { + return fmt.Errorf("epoch status with only setup or only commit service event") } // must reference event IDs for current epoch if es.CurrentEpoch.SetupID == ZeroID || es.CurrentEpoch.CommitID == ZeroID { @@ -320,7 +323,7 @@ func (es *EpochStatus) check() error { // Phase returns the phase for the CURRENT epoch, given this epoch status. func (es *EpochStatus) Phase() (EpochPhase, error) { - err := es.check() + err := es.Check() if err != nil { return EpochPhaseUndefined, err } @@ -332,3 +335,7 @@ func (es *EpochStatus) Phase() (EpochPhase, error) { } return EpochPhaseCommitted, nil } + +func (es *EpochStatus) HasPrevious() bool { + return es.PreviousEpoch.SetupID != ZeroID && es.PreviousEpoch.CommitID != ZeroID +} From c914364c45ca66cd5e85eaa9c1c2fc73627e57b5 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 13 Jan 2021 15:44:06 -0800 Subject: [PATCH 043/178] add conversion from protocol.Epoch to service event Since we use service event models directly in the storage layer we need to be able to convert an abstract Epoch implementation to a concrete service event model when storing --- state/protocol/convert.go | 84 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 state/protocol/convert.go diff --git a/state/protocol/convert.go b/state/protocol/convert.go new file mode 100644 index 00000000000..44684ccc5b7 --- /dev/null +++ b/state/protocol/convert.go @@ -0,0 +1,84 @@ +package protocol + +import ( + "fmt" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" +) + +// TODO error handling +func ToEpochSetup(epoch Epoch) (*flow.EpochSetup, error) { + counter, err := epoch.Counter() + if err != nil { + return nil, err + } + firstView, err := epoch.FirstView() + finalView, err := epoch.FinalView() + participants, err := epoch.InitialIdentities() + clustering, err := epoch.Clustering() + assignments := clustering.Assignments() + src, err := epoch.RandomSource() + + setup := &flow.EpochSetup{ + Counter: counter, + FirstView: firstView, + FinalView: finalView, + Participants: participants, + Assignments: assignments, + RandomSource: src, + } + return setup, nil +} + +// TODO error handling +func ToEpochCommit(epoch Epoch) (*flow.EpochCommit, error) { + counter, err := epoch.Counter() + if err != nil { + return nil, err + } + clustering, err := epoch.Clustering() + qcs := make([]*flow.QuorumCertificate, 0, len(clustering)) + for i := range clustering { + cluster, err := epoch.Cluster(uint(i)) + if err != nil { + return nil, err + } + qcs = append(qcs, cluster.RootQC()) + } + participants, err := epoch.InitialIdentities() + dkg, err := epoch.DKG() + dkgParticipants, err := ToDKGParticipantLookup(dkg, participants.Filter(filter.HasRole(flow.RoleConsensus))) + + commit := &flow.EpochCommit{ + Counter: counter, + ClusterQCs: qcs, + DKGGroupKey: dkg.GroupKey(), + DKGParticipants: dkgParticipants, + } + return commit, nil +} + +// TODO doc +func ToDKGParticipantLookup(dkg DKG, participants flow.IdentityList) (map[flow.Identifier]flow.DKGParticipant, error) { + + lookup := make(map[flow.Identifier]flow.DKGParticipant) + for _, identity := range participants { + + index, err := dkg.Index(identity.NodeID) + if err != nil { + return nil, fmt.Errorf("could not get index (node=%x): %w", identity.NodeID, err) + } + key, err := dkg.KeyShare(identity.NodeID) + if err != nil { + return nil, fmt.Errorf("could not get key share (node=%x): %w", identity.NodeID, err) + } + + lookup[identity.NodeID] = flow.DKGParticipant{ + Index: index, + KeyShare: key, + } + } + + return lookup, nil +} From 5c950d61096472e46205c55476615a52f704da72 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 13 Jan 2021 15:45:53 -0800 Subject: [PATCH 044/178] update snapshot to use previous epoch instead of first block --- state/protocol/badger/snapshot.go | 46 +++++++++++-------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index 7c75ee2626d..f65c1dbda42 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -171,30 +171,16 @@ func (s *Snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, // from the previous epoch that are now un-staking case flow.EpochPhaseStaking: - first, err := s.state.AtBlockID(status.FirstBlockID).Head() - if err != nil { - return nil, fmt.Errorf("could not get first block of epoch: %w", err) - } - // check whether this is the first epoch after the root block - in this - // case there are no previous epoch identities to check anyway - root, err := s.state.Params().Root() - if err != nil { - return nil, fmt.Errorf("could not get root block: %w", err) - } - if first.ID() == root.ID() { + if !status.HasPrevious() { break } - lastStatus, err := s.state.epoch.statuses.ByBlockID(first.ParentID) + previousSetup, err := s.state.epoch.setups.ByID(status.PreviousEpoch.SetupID) if err != nil { - return nil, fmt.Errorf("could not get last epoch status: %w", err) - } - lastSetup, err := s.state.epoch.setups.ByID(lastStatus.CurrentEpoch.SetupID) - if err != nil { - return nil, fmt.Errorf("could not get last epoch setup event: %w", err) + return nil, fmt.Errorf("could not get previous epoch setup event: %w", err) } - for _, identity := range lastSetup.Participants { + for _, identity := range previousSetup.Participants { _, exists := lookup[identity.NodeID] // add identity from previous epoch that is not in current epoch if !exists { @@ -433,23 +419,23 @@ func (q *EpochQuery) Previous() protocol.Epoch { if err != nil { return invalid.NewEpoch(err) } - first, err := q.snap.state.headers.ByBlockID(status.FirstBlockID) - if err != nil { - return invalid.NewEpoch(err) + + // CASE 1: there is no previous epoch - this indicates we are in the first + // epoch after a spork root or genesis block + if !status.HasPrevious() { + return invalid.NewEpoch(protocol.ErrNoPreviousEpoch) } - // CASE 1: we are in the first epoch after the root block, in which case - // we return a sentinel error - root, err := q.snap.state.Params().Root() + // CASE 2: we are in any other epoch - retrieve the setup and commit events + // for the previous epoch + setup, err := q.snap.state.epoch.setups.ByID(status.PreviousEpoch.SetupID) if err != nil { return invalid.NewEpoch(err) } - if first.ID() == root.ID() { - return invalid.NewEpoch(protocol.ErrNoPreviousEpoch) + commit, err := q.snap.state.epoch.commits.ByID(status.PreviousEpoch.CommitID) + if err != nil { + return invalid.NewEpoch(err) } - // CASE 2: we are in any other epoch, return the current epoch w.r.t. the - // parent block of the first block in this epoch, which must be in the - // previous epoch - return q.snap.state.AtBlockID(first.ParentID).Epochs().Current() + return NewCommittedEpoch(setup, commit) } From d5e0b6df3fe510c28cb221ba978f60890e35d5e0 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 13 Jan 2021 15:47:59 -0800 Subject: [PATCH 045/178] bootstrap from protocol.Snapshot rather than state root --- state/protocol/badger/mutator.go | 4 +- state/protocol/badger/state.go | 212 ++++++++++++++++++++++++------ state/protocol/badger/validity.go | 5 + 3 files changed, 177 insertions(+), 44 deletions(-) diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index c322e5e7f2f..7e07ddd8f83 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -849,7 +849,7 @@ func (m *FollowerState) epochStatus(block *flow.Header) (*flow.EpochStatus, erro return nil, fmt.Errorf("missing commit event for starting next epoch") } status, err := flow.NewEpochStatus( - block.ID(), + parentStatus.CurrentEpoch.SetupID, parentStatus.CurrentEpoch.CommitID, parentStatus.NextEpoch.SetupID, parentStatus.NextEpoch.CommitID, flow.ZeroID, flow.ZeroID, ) @@ -859,7 +859,7 @@ func (m *FollowerState) epochStatus(block *flow.Header) (*flow.EpochStatus, erro // Block is in the same epoch as its parent, re-use the same epoch status // IMPORTANT: copy the status to avoid modifying the parent status in the cache status, err := flow.NewEpochStatus( - parentStatus.FirstBlockID, + parentStatus.PreviousEpoch.SetupID, parentStatus.PreviousEpoch.CommitID, parentStatus.CurrentEpoch.SetupID, parentStatus.CurrentEpoch.CommitID, parentStatus.NextEpoch.SetupID, parentStatus.NextEpoch.CommitID, ) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 819ee9fae2f..1fee4069757 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -39,7 +39,7 @@ func Bootstrap( setups storage.EpochSetups, commits storage.EpochCommits, statuses storage.EpochStatuses, - stateRoot *StateRoot, + root protocol.Snapshot, ) (*State, error) { isBootstrapped, err := IsBootstrapped(db) if err != nil { @@ -51,100 +51,228 @@ func Bootstrap( state := newState(metrics, db, headers, seals, blocks, setups, commits, statuses) err = operation.RetryOnConflict(db.Update, func(tx *badger.Txn) error { - // 1) insert the root block with its payload into the state and index it - err = state.blocks.StoreTx(stateRoot.Block())(tx) - if err != nil { - return fmt.Errorf("could not insert root block: %w", err) - } - err = operation.InsertBlockValidity(stateRoot.Block().ID(), true)(tx) - if err != nil { - return fmt.Errorf("could not mark root block as valid: %w", err) + + // 1) insert each block in the root chain segment + segment, err := root.SealingSegment() + if len(segment) == 0 { + return fmt.Errorf("root sealing segment must contain at least one block") } - err = operation.IndexBlockHeight(stateRoot.Block().Header.Height, stateRoot.Block().ID())(tx) + // sealing segment is in ascending height order, so the tail is the + // oldest ancestor and head is the newest child in the segment + tail := segment[0] + head := segment[len(segment)-1] + if err != nil { - return fmt.Errorf("could not index root block: %w", err) + return fmt.Errorf("could not get sealing segment: %w", err) } - // root block has no parent, so only needs to add one index - // to indicate the root block has no child yet - err = operation.InsertBlockChildren(stateRoot.Block().ID(), nil)(tx) - if err != nil { - return fmt.Errorf("could not initialize root child index: %w", err) + for i, block := range segment { + + blockID := block.ID() + height := block.Header.Height + err = state.blocks.StoreTx(block)(tx) + if err != nil { + return fmt.Errorf("could not insert root block: %w", err) + } + err = operation.InsertBlockValidity(blockID, true)(tx) + if err != nil { + return fmt.Errorf("could not mark root block as valid: %w", err) + } + err = operation.IndexBlockHeight(height, blockID)(tx) + if err != nil { + return fmt.Errorf("could not index root block segment (id=%x): %w", blockID, err) + } + + // for the final block in the segment, insert an empty child index + if i == len(segment)-1 { + err = operation.InsertBlockChildren(blockID, nil)(tx) + if err != nil { + return fmt.Errorf("could not initialize child index for final segment block: %w", err) + } + } + // for all but the first block in the segment, index the parent->child relationship + if i > 0 { + err = operation.InsertBlockChildren(block.Header.ParentID, []flow.Identifier{blockID})(tx) + if err != nil { + return fmt.Errorf("could not insert child index for block (id=%x): %w", blockID, err) + } + } } // 2) insert the root execution result into the database and index it - err = operation.InsertExecutionResult(stateRoot.Result())(tx) + result, err := root.LatestResult() + if err != nil { + return fmt.Errorf("could not get latest result from root snapshot: %w", err) + } + err = operation.SkipDuplicates(operation.InsertExecutionResult(result))(tx) if err != nil { return fmt.Errorf("could not insert root result: %w", err) } - err = operation.IndexExecutionResult(stateRoot.Block().ID(), stateRoot.Result().ID())(tx) + err = operation.IndexExecutionResult(result.BlockID, result.ID())(tx) if err != nil { return fmt.Errorf("could not index root result: %w", err) } // 3) insert the root block seal into the database and index it - err = operation.InsertSeal(stateRoot.Seal().ID(), stateRoot.Seal())(tx) + seal, err := root.LatestSeal() + if err != nil { + return fmt.Errorf("could not get latest seal from root snapshot: %w", err) + } + err = operation.SkipDuplicates(operation.InsertSeal(seal.ID(), seal))(tx) if err != nil { return fmt.Errorf("could not insert root seal: %w", err) } - err = operation.IndexBlockSeal(stateRoot.Block().ID(), stateRoot.Seal().ID())(tx) + err = operation.IndexBlockSeal(seal.BlockID, seal.ID())(tx) if err != nil { return fmt.Errorf("could not index root block seal: %w", err) } // 4) initialize the current protocol state values - err = operation.InsertStartedView(stateRoot.Block().Header.ChainID, stateRoot.Block().Header.View)(tx) + err = operation.InsertStartedView(head.Header.ChainID, head.Header.View)(tx) if err != nil { return fmt.Errorf("could not insert started view: %w", err) } - err = operation.InsertVotedView(stateRoot.Block().Header.ChainID, stateRoot.Block().Header.View)(tx) + err = operation.InsertVotedView(head.Header.ChainID, head.Header.View)(tx) if err != nil { return fmt.Errorf("could not insert started view: %w", err) } - err = operation.InsertRootHeight(stateRoot.Block().Header.Height)(tx) + err = operation.InsertRootHeight(tail.Header.Height)(tx) if err != nil { return fmt.Errorf("could not insert root height: %w", err) } - err = operation.InsertFinalizedHeight(stateRoot.Block().Header.Height)(tx) + err = operation.InsertFinalizedHeight(head.Header.Height)(tx) if err != nil { return fmt.Errorf("could not insert finalized height: %w", err) } - err = operation.InsertSealedHeight(stateRoot.Block().Header.Height)(tx) + err = operation.InsertSealedHeight(tail.Header.Height)(tx) if err != nil { return fmt.Errorf("could not insert sealed height: %w", err) } // 5) initialize values related to the epoch logic - err = state.epoch.setups.StoreTx(stateRoot.EpochSetupEvent())(tx) + err = state.bootstrapEpoch(root)(tx) if err != nil { - return fmt.Errorf("could not insert EpochSetup event: %w", err) + return fmt.Errorf("could not bootstrap epoch values: %w", err) } - err = state.epoch.commits.StoreTx(stateRoot.EpochCommitEvent())(tx) + + state.metrics.BlockSealed(tail) + state.metrics.SealedHeight(tail.Header.Height) + state.metrics.FinalizedHeight(head.Header.Height) + for _, block := range segment { + state.metrics.BlockFinalized(block) + } + + return nil + }) + if err != nil { + return nil, fmt.Errorf("bootstrapping failed: %w", err) + } + + return state, nil +} + +func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) error { + return func(tx *badger.Txn) error { + previous := root.Epochs().Previous() + current := root.Epochs().Current() + next := root.Epochs().Next() + + status := new(flow.EpochStatus) + var setups []*flow.EpochSetup + var commits []*flow.EpochCommit + + // insert previous epoch if it exists + _, err := previous.Counter() + if err != nil && !errors.Is(err, protocol.ErrNoPreviousEpoch) { + return fmt.Errorf("could not retrieve previous epoch: %w", err) + } + if err == nil { + // if there is a previous epoch, both setup and commit events must exist + setup, err := protocol.ToEpochSetup(previous) + if err != nil { + return fmt.Errorf("could not get previous epoch setup event: %w", err) + } + commit, err := protocol.ToEpochCommit(previous) + if err != nil { + return fmt.Errorf("could not get previous epoch commit event: %w", err) + } + + setups = append(setups, setup) + commits = append(commits, commit) + status.PreviousEpoch.SetupID = setup.ID() + status.PreviousEpoch.CommitID = commit.ID() + } + + // insert current epoch - both setup and commit events must exist + setup, err := protocol.ToEpochSetup(current) if err != nil { - return fmt.Errorf("could not insert EpochCommit event: %w", err) + return fmt.Errorf("could not get current epoch setup event: %w", err) } - status, err := flow.NewEpochStatus(stateRoot.Block().ID(), stateRoot.EpochSetupEvent().ID(), stateRoot.EpochCommitEvent().ID(), flow.ZeroID, flow.ZeroID) + commit, err := protocol.ToEpochCommit(current) if err != nil { - return fmt.Errorf("could not construct root epoch status: %w", err) + return fmt.Errorf("could not get current epoch commit event: %w", err) + } + + setups = append(setups, setup) + commits = append(commits, commit) + status.CurrentEpoch.SetupID = setup.ID() + status.CurrentEpoch.CommitID = commit.ID() + + _, err = next.Counter() + if err != nil && !errors.Is(err, protocol.ErrNextEpochNotSetup) { + return fmt.Errorf("could not get next epoch: %w", err) + } + if err == nil { + setup, err := protocol.ToEpochSetup(next) + if err != nil { + return fmt.Errorf("could not get next epoch setup event: %w", err) + } + setups = append(setups, setup) + status.NextEpoch.SetupID = setup.ID() + commit, err := protocol.ToEpochCommit(next) + if err != nil && !errors.Is(err, protocol.ErrEpochNotCommitted) { + return fmt.Errorf("could not get next epoch commit event: %w", err) + } + if err == nil { + commits = append(commits, commit) + status.NextEpoch.CommitID = commit.ID() + } } - err = state.epoch.statuses.StoreTx(stateRoot.Block().ID(), status)(tx) + + // sanity check: ensure epoch status is valid + err = status.Check() if err != nil { - return fmt.Errorf("could not insert EpochStatus: %w", err) + return fmt.Errorf("bootstrapping resulting in invalid epoch status: %w", err) } - state.metrics.FinalizedHeight(stateRoot.Block().Header.Height) - state.metrics.BlockFinalized(stateRoot.Block()) + for _, setup := range setups { + err = state.epoch.setups.StoreTx(setup)(tx) + if err != nil { + return fmt.Errorf("could not store epoch setup event: %w", err) + } + } + for _, commit := range commits { + err = state.epoch.commits.StoreTx(commit)(tx) + if err != nil { + return fmt.Errorf("could not store epoch commit event: %w", err) + } + } - state.metrics.SealedHeight(stateRoot.Block().Header.Height) - state.metrics.BlockSealed(stateRoot.Block()) + // TODO: handle or document constraint - sealing segment must not contain epoch or epoch phase transitions + segment, err := root.SealingSegment() + if err != nil { + return fmt.Errorf("could not get sealing segment: %w", err) + } + for _, block := range segment { + blockID := block.ID() + err = state.epoch.statuses.StoreTx(blockID, status)(tx) + if err != nil { + return fmt.Errorf("could not store epoch status for block (id=%x): %w", blockID, err) + } + } return nil - }) - if err != nil { - return nil, fmt.Errorf("bootstrapping failed: %w", err) } - - return state, nil } func OpenState( diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index 1d4ba1a5086..a3ecf0f3989 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -110,3 +110,8 @@ func validCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error { return nil } + +// TODO +func validSealingSegment(segment []*flow.Block) error { + return nil +} From 41d7eb8077b3370ef8b1e12615b311b0f2d3d885 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 13 Jan 2021 16:29:28 -0800 Subject: [PATCH 046/178] update inmem conversion --- state/protocol/inmem/convert.go | 36 ++++++------- state/protocol/mock/snapshot.go | 92 ++++++++++++++++++++++++--------- 2 files changed, 85 insertions(+), 43 deletions(-) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 726bd71e523..f4d42c3b15b 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -13,6 +13,7 @@ import ( // FromSnapshot generates a memory-backed snapshot from the input snapshot. // Typically, this would be used to convert a database-backed snapshot to // one that can easily be serialized to disk or to network. +// func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { var ( @@ -29,9 +30,17 @@ func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { if err != nil { return nil, fmt.Errorf("could not get identities: %w", err) } - snap.Commit, err = from.Commit() + snap.LatestSeal, err = from.LatestSeal() + if err != nil { + return nil, fmt.Errorf("could not get seal: %w", err) + } + snap.LatestResult, err = from.LatestResult() if err != nil { - return nil, fmt.Errorf("could not get commit: %w", err) + return nil, fmt.Errorf("could not get result: %w", err) + } + snap.SealingSegment, err = from.SealingSegment() + if err != nil { + return nil, fmt.Errorf("could not get sealing segment: %w", err) } snap.QuorumCertificate, err = from.QuorumCertificate() if err != nil { @@ -72,7 +81,7 @@ func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { return &Snapshot{snap}, nil } -// FromEpoch converts any protocol.Protocol to a memory-backed Epoch. +// FromEpoch converts any protocol.Epoch to a memory-backed Epoch. func FromEpoch(from protocol.Epoch) (*Epoch, error) { var ( @@ -153,27 +162,14 @@ func FromCluster(from protocol.Cluster) (*Cluster, error) { // // The given participant list must exactly match the DKG members. func FromDKG(from protocol.DKG, participants flow.IdentityList) (*DKG, error) { - var dkg EncodableDKG - dkg.GroupKey = encodable.RandomBeaconPubKey{PublicKey: from.GroupKey()} - dkg.Participants = make(map[flow.Identifier]flow.DKGParticipant) - for _, identity := range participants { - index, err := from.Index(identity.NodeID) - if err != nil { - return nil, fmt.Errorf("could not get index (node=%x): %w", identity.NodeID, err) - } - key, err := from.KeyShare(identity.NodeID) - if err != nil { - return nil, fmt.Errorf("could not get key share (node=%x): %w", identity.NodeID, err) - } - - dkg.Participants[identity.NodeID] = flow.DKGParticipant{ - Index: index, - KeyShare: key, - } + lookup, err := protocol.ToDKGParticipantLookup(from, participants) + if err != nil { + return nil, fmt.Errorf("could not generate dkg participant lookup: %w", err) } + dkg.Participants = lookup return &DKG{dkg}, nil } diff --git a/state/protocol/mock/snapshot.go b/state/protocol/mock/snapshot.go index 0e3430e0cab..ea866c6dc4c 100644 --- a/state/protocol/mock/snapshot.go +++ b/state/protocol/mock/snapshot.go @@ -14,29 +14,6 @@ type Snapshot struct { mock.Mock } -// Commit provides a mock function with given fields: -func (_m *Snapshot) Commit() ([]byte, error) { - ret := _m.Called() - - var r0 []byte - if rf, ok := ret.Get(0).(func() []byte); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]byte) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // Epochs provides a mock function with given fields: func (_m *Snapshot) Epochs() protocol.EpochQuery { ret := _m.Called() @@ -122,6 +99,52 @@ func (_m *Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { return r0, r1 } +// LatestResult provides a mock function with given fields: +func (_m *Snapshot) LatestResult() (*flow.ExecutionResult, error) { + ret := _m.Called() + + var r0 *flow.ExecutionResult + if rf, ok := ret.Get(0).(func() *flow.ExecutionResult); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.ExecutionResult) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// LatestSeal provides a mock function with given fields: +func (_m *Snapshot) LatestSeal() (*flow.Seal, error) { + ret := _m.Called() + + var r0 *flow.Seal + if rf, ok := ret.Get(0).(func() *flow.Seal); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.Seal) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Pending provides a mock function with given fields: func (_m *Snapshot) Pending() ([]flow.Identifier, error) { ret := _m.Called() @@ -189,6 +212,29 @@ func (_m *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { return r0, r1 } +// SealingSegment provides a mock function with given fields: +func (_m *Snapshot) SealingSegment() ([]*flow.Block, error) { + ret := _m.Called() + + var r0 []*flow.Block + if rf, ok := ret.Get(0).(func() []*flow.Block); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*flow.Block) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Seed provides a mock function with given fields: indices func (_m *Snapshot) Seed(indices ...uint32) ([]byte, error) { _va := make([]interface{}, len(indices)) From 615c266d843f7cfc75278149cc38e6e3d2f20448 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 13 Jan 2021 16:57:35 -0800 Subject: [PATCH 047/178] add conversion from root bootstrap state to snapshot --- state/protocol/badger/epoch.go | 2 -- state/protocol/badger/state_root.go | 1 + state/protocol/inmem/convert.go | 34 +++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/state/protocol/badger/epoch.go b/state/protocol/badger/epoch.go index eb74d249b55..28649c3f91e 100644 --- a/state/protocol/badger/epoch.go +++ b/state/protocol/badger/epoch.go @@ -133,5 +133,3 @@ func NewCommittedEpoch(setupEvent *flow.EpochSetup, commitEvent *flow.EpochCommi commitEvent: commitEvent, } } - -// **************************************** diff --git a/state/protocol/badger/state_root.go b/state/protocol/badger/state_root.go index b25bcf94f1d..935895988e0 100644 --- a/state/protocol/badger/state_root.go +++ b/state/protocol/badger/state_root.go @@ -7,6 +7,7 @@ import ( ) // StateRoot is the root information required to bootstrap the protocol state +// TODO remove type StateRoot struct { block *flow.Block result *flow.ExecutionResult diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index f4d42c3b15b..e4efb3a6da5 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -8,6 +8,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/state/protocol" + bprotocol "github.com/onflow/flow-go/state/protocol/badger" ) // FromSnapshot generates a memory-backed snapshot from the input snapshot. @@ -183,3 +184,36 @@ func DKGFromEncodable(enc EncodableDKG) (*DKG, error) { func ClusterFromEncodable(enc EncodableCluster) (*Cluster, error) { return &Cluster{enc}, nil } + +func SnapshotFromBootstrapState(root *flow.Block, seal *flow.Seal, result *flow.ExecutionResult, qc *flow.QuorumCertificate) (*Snapshot, error) { + + setup, ok := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + if !ok { + return nil, fmt.Errorf("invalid setup event type (%T)", seal.ServiceEvents[0].Event) + } + commit, ok := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + if !ok { + return nil, fmt.Errorf("invalid commit event type (%T)", seal.ServiceEvents[1].Event) + } + + // TODO consolidate with inmem.Epoch + current, err := FromEpoch(bprotocol.NewCommittedEpoch(setup, commit)) + if err != nil { + return nil, fmt.Errorf("could not convert epoch: %w", err) + } + epochs := EncodableEpochs{ + Current: current.enc, + } + + snap := SnapshotFromEncodable(EncodableSnapshot{ + Head: root.Header, + Identities: setup.Participants, + LatestSeal: seal, + LatestResult: result, + SealingSegment: []*flow.Block{root}, + QuorumCertificate: qc, + Phase: flow.EpochPhaseStaking, + Epochs: epochs, + }) + return snap, nil +} From 71af00812a4e026947d319e30a868e802d211bed Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 14:06:03 -0800 Subject: [PATCH 048/178] temporarily copy epoch model to inmem to resolve circular dependency while testing --- state/protocol/inmem/convert.go | 7 +- state/protocol/inmem/epoch.go | 128 ++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 4 deletions(-) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index e4efb3a6da5..1a595cfa6b6 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -8,7 +8,6 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/state/protocol" - bprotocol "github.com/onflow/flow-go/state/protocol/badger" ) // FromSnapshot generates a memory-backed snapshot from the input snapshot. @@ -185,7 +184,7 @@ func ClusterFromEncodable(enc EncodableCluster) (*Cluster, error) { return &Cluster{enc}, nil } -func SnapshotFromBootstrapState(root *flow.Block, seal *flow.Seal, result *flow.ExecutionResult, qc *flow.QuorumCertificate) (*Snapshot, error) { +func SnapshotFromBootstrapState(root *flow.Block, result *flow.ExecutionResult, seal *flow.Seal, qc *flow.QuorumCertificate) (*Snapshot, error) { setup, ok := seal.ServiceEvents[0].Event.(*flow.EpochSetup) if !ok { @@ -196,8 +195,8 @@ func SnapshotFromBootstrapState(root *flow.Block, seal *flow.Seal, result *flow. return nil, fmt.Errorf("invalid commit event type (%T)", seal.ServiceEvents[1].Event) } - // TODO consolidate with inmem.Epoch - current, err := FromEpoch(bprotocol.NewCommittedEpoch(setup, commit)) + // TODO consolidate with inmem.Epoch (copied from badger currently) + current, err := FromEpoch(NewCommittedEpoch(setup, commit)) if err != nil { return nil, fmt.Errorf("could not convert epoch: %w", err) } diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index 8efa5fdc3f5..f77735825a2 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -3,7 +3,11 @@ package inmem import ( "fmt" + "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/model/flow/order" + "github.com/onflow/flow-go/state/cluster" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/state/protocol/invalid" "github.com/onflow/flow-go/state/protocol/seed" @@ -69,3 +73,127 @@ func (eq Epochs) Next() protocol.Epoch { } return invalid.NewEpoch(protocol.ErrNextEpochNotSetup) } + +// ===== +// from badger +// TODO consolidate this with above +// ===== + +// SetupEpoch represents an epoch that has been setup, but not committed. +// Only the EpochSetup event for the epoch has been emitted as of the point +// at which the epoch was queried. +type SetupEpoch struct { + // EpochSetup service event + setupEvent *flow.EpochSetup +} + +func (es *SetupEpoch) Counter() (uint64, error) { + return es.setupEvent.Counter, nil +} + +func (es *SetupEpoch) FirstView() (uint64, error) { + return es.setupEvent.FirstView, nil +} + +func (es *SetupEpoch) FinalView() (uint64, error) { + return es.setupEvent.FinalView, nil +} + +func (es *SetupEpoch) InitialIdentities() (flow.IdentityList, error) { + + identities := es.setupEvent.Participants.Filter(filter.Any) + // apply a deterministic sort to the participants + identities = identities.Order(order.ByNodeIDAsc) + + return identities, nil +} + +func (es *SetupEpoch) Clustering() (flow.ClusterList, error) { + + collectorFilter := filter.And(filter.HasStake(true), filter.HasRole(flow.RoleCollection)) + clustering, err := flow.NewClusterList(es.setupEvent.Assignments, es.setupEvent.Participants.Filter(collectorFilter)) + if err != nil { + return nil, fmt.Errorf("failed to generate ClusterList from collector identities: %w", err) + } + return clustering, nil +} + +func (es *SetupEpoch) Cluster(_ uint) (protocol.Cluster, error) { + return nil, protocol.ErrEpochNotCommitted +} + +func (es *SetupEpoch) DKG() (protocol.DKG, error) { + return nil, protocol.ErrEpochNotCommitted +} + +func (es *SetupEpoch) RandomSource() ([]byte, error) { + return es.setupEvent.RandomSource, nil +} + +func (es *SetupEpoch) Seed(indices ...uint32) ([]byte, error) { + return seed.FromRandomSource(indices, es.setupEvent.RandomSource) +} + +func NewSetupEpoch(setupEvent *flow.EpochSetup) *SetupEpoch { + return &SetupEpoch{ + setupEvent: setupEvent, + } +} + +// **************************************** + +// CommittedEpoch represents an epoch that has been committed. +// Both the EpochSetup and EpochCommitted events for the epoch have been emitted +// as of the point at which the epoch was queried. +type CommittedEpoch struct { + SetupEpoch + commitEvent *flow.EpochCommit +} + +func (es *CommittedEpoch) Cluster(index uint) (protocol.Cluster, error) { + + qcs := es.commitEvent.ClusterQCs + if uint(len(qcs)) <= index { + return nil, fmt.Errorf("no cluster with index %d", index) + } + rootQC := qcs[index] + + clustering, err := es.Clustering() + if err != nil { + return nil, fmt.Errorf("failed to generate clustering: %w", err) + } + + members, ok := clustering.ByIndex(index) + if !ok { + return nil, fmt.Errorf("failed to get members of cluster %d: %w", index, err) + } + epochCounter := es.setupEvent.Counter + + cluster, err := ClusterFromEncodable(EncodableCluster{ + Index: index, + Counter: epochCounter, + Members: members, + RootBlock: cluster.CanonicalRootBlock(epochCounter, members), + RootQC: rootQC, + }) + return cluster, err +} + +func (es *CommittedEpoch) DKG() (protocol.DKG, error) { + dkg, err := DKGFromEncodable(EncodableDKG{ + GroupKey: encodable.RandomBeaconPubKey{ + PublicKey: es.commitEvent.DKGGroupKey, + }, + Participants: es.commitEvent.DKGParticipants, + }) + return dkg, err +} + +func NewCommittedEpoch(setupEvent *flow.EpochSetup, commitEvent *flow.EpochCommit) *CommittedEpoch { + return &CommittedEpoch{ + SetupEpoch: SetupEpoch{ + setupEvent: setupEvent, + }, + commitEvent: commitEvent, + } +} From 1a53b6a674941dff603b91bb5598a8a63c7ed206 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 14:07:05 -0800 Subject: [PATCH 049/178] add PreviousEpochs to fixture --- utils/unittest/fixtures.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index d1ca4567f6e..b18b8373a9c 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -1055,7 +1055,10 @@ func EpochSetupFixture(opts ...func(setup *flow.EpochSetup)) *flow.EpochSetup { func EpochStatusFixture() *flow.EpochStatus { return &flow.EpochStatus{ - FirstBlockID: IdentifierFixture(), + PreviousEpoch: flow.EventIDs{ + SetupID: IdentifierFixture(), + CommitID: IdentifierFixture(), + }, CurrentEpoch: flow.EventIDs{ SetupID: IdentifierFixture(), CommitID: IdentifierFixture(), From 9a6022a0a437a31c8cd3437a3387c47702221972 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 14:07:19 -0800 Subject: [PATCH 050/178] update cluster state tests --- state/cluster/badger/mutator_test.go | 7 ++++--- state/cluster/badger/snapshot_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/state/cluster/badger/mutator_test.go b/state/cluster/badger/mutator_test.go index 824790dcf95..91d059b8de3 100644 --- a/state/cluster/badger/mutator_test.go +++ b/state/cluster/badger/mutator_test.go @@ -20,6 +20,7 @@ import ( "github.com/onflow/flow-go/state/protocol" pbadger "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" + "github.com/onflow/flow-go/state/protocol/inmem" storage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/storage/badger/operation" "github.com/onflow/flow-go/storage/badger/procedure" @@ -74,12 +75,12 @@ func (suite *MutatorSuite) SetupTest() { // ensure we don't enter a new epoch for tests that build many blocks seal.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = root.Header.View + 100000 - stateRoot, err := pbadger.NewStateRoot(root, result, seal, 0) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, unittest.QuorumCertificateFixture()) require.NoError(suite.T(), err) - suite.protoGenesis = stateRoot.Block().Header + suite.protoGenesis = root.Header - state, err := pbadger.Bootstrap(metrics, suite.db, headers, seals, blocks, setups, commits, statuses, stateRoot) + state, err := pbadger.Bootstrap(metrics, suite.db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) require.NoError(suite.T(), err) suite.protoState, err = pbadger.NewFollowerState(state, index, conPayloads, tracer, consumer) diff --git a/state/cluster/badger/snapshot_test.go b/state/cluster/badger/snapshot_test.go index 1750648bb5b..b3c27ab7746 100644 --- a/state/cluster/badger/snapshot_test.go +++ b/state/cluster/badger/snapshot_test.go @@ -19,6 +19,7 @@ import ( "github.com/onflow/flow-go/state/cluster" "github.com/onflow/flow-go/state/protocol" pbadger "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/inmem" storage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/storage/badger/operation" "github.com/onflow/flow-go/storage/badger/procedure" @@ -66,11 +67,10 @@ func (suite *SnapshotSuite) SetupTest() { suite.Assert().Nil(err) participants := unittest.IdentityListFixture(5, unittest.WithAllRoles()) - genesis, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := pbadger.NewStateRoot(genesis, result, seal, 0) - require.NoError(suite.T(), err) + root, result, seal := unittest.BootstrapFixture(participants) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, unittest.QuorumCertificateFixture()) - suite.protoState, err = pbadger.Bootstrap(metrics, suite.db, headers, seals, blocks, setups, commits, statuses, stateRoot) + suite.protoState, err = pbadger.Bootstrap(metrics, suite.db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) require.NoError(suite.T(), err) suite.Require().Nil(err) From f247d3608e035402947c219046dc51b502ff2a22 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 14:18:53 -0800 Subject: [PATCH 051/178] update protocol state tests --- state/protocol/badger/mutator_test.go | 5 +++-- state/protocol/mock/snapshot.go | 23 +++++++++++++++++++++++ state/protocol/snapshot.go | 4 ++++ state/protocol/util/testing.go | 27 +++++++++++++++++++++------ 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 4c0f24eb70d..50d0a9a8e16 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -25,6 +25,7 @@ import ( st "github.com/onflow/flow-go/state" protocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" + "github.com/onflow/flow-go/state/protocol/inmem" mockprotocol "github.com/onflow/flow-go/state/protocol/mock" "github.com/onflow/flow-go/state/protocol/util" stoerr "github.com/onflow/flow-go/storage" @@ -306,10 +307,10 @@ func TestExtendValid(t *testing.T) { distributor.AddConsumer(consumer) block, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := protocol.NewStateRoot(block, result, seal, 0) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(block, result, seal, unittest.QuorumCertificateFixture()) require.NoError(t, err) - state, err := protocol.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, stateRoot) + state, err := protocol.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) fullState, err := protocol.NewFullConsensusState(state, index, payloads, tracer, consumer, util.MockReceiptValidator()) diff --git a/state/protocol/mock/snapshot.go b/state/protocol/mock/snapshot.go index ea866c6dc4c..bd8db021678 100644 --- a/state/protocol/mock/snapshot.go +++ b/state/protocol/mock/snapshot.go @@ -14,6 +14,29 @@ type Snapshot struct { mock.Mock } +// Commit provides a mock function with given fields: +func (_m *Snapshot) Commit() ([]byte, error) { + ret := _m.Called() + + var r0 []byte + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Epochs provides a mock function with given fields: func (_m *Snapshot) Epochs() protocol.EpochQuery { ret := _m.Called() diff --git a/state/protocol/snapshot.go b/state/protocol/snapshot.go index 7e614b15f1f..971ff1457ce 100644 --- a/state/protocol/snapshot.go +++ b/state/protocol/snapshot.go @@ -47,6 +47,10 @@ type Snapshot interface { // included seal as of this block (see LatestSeal). LatestResult() (*flow.ExecutionResult, error) + // Commit returns the state commitment of the most recently included seal + // as of this block. It represents the sealed state. + Commit() (flow.StateCommitment, error) + // SealingSegment returns the chain segment such that the head (greatest // height) is this snapshot's reference block and the tail (least height) // is the most recently sealed block as of this snapshot (ie. the block diff --git a/state/protocol/util/testing.go b/state/protocol/util/testing.go index fb29283ac2a..9b1e8d40847 100644 --- a/state/protocol/util/testing.go +++ b/state/protocol/util/testing.go @@ -14,6 +14,7 @@ import ( "github.com/onflow/flow-go/state/protocol" pbadger "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" + "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/storage/util" "github.com/onflow/flow-go/utils/unittest" ) @@ -27,13 +28,15 @@ func MockReceiptValidator() module.ReceiptValidator { return validator } +// TODO update state root func RunWithBootstrapState(t testing.TB, stateRoot *pbadger.StateRoot, f func(*badger.DB, *pbadger.State)) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { metrics := metrics.NewNoopCollector() headers, _, seals, _, _, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - stateRoot, err := pbadger.NewStateRoot(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), stateRoot.EpochSetupEvent().FirstView) + qc := unittest.QuorumCertificateFixture() // TODO replace this + rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) require.NoError(t, err) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, stateRoot) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) f(db, state) }) @@ -45,7 +48,10 @@ func RunWithFullProtocolState(t testing.TB, stateRoot *pbadger.StateRoot, f func tracer := trace.NewNoopTracer() consumer := events.NewNoop() headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, stateRoot) + qc := unittest.QuorumCertificateFixture() // TODO replace this + rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) + require.NoError(t, err) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) receiptValidator := MockReceiptValidator() fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, receiptValidator) @@ -60,7 +66,10 @@ func RunWithFullProtocolStateAndValidator(t testing.TB, stateRoot *pbadger.State tracer := trace.NewNoopTracer() consumer := events.NewNoop() headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, stateRoot) + qc := unittest.QuorumCertificateFixture() // TODO replace this + rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) + require.NoError(t, err) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, validator) require.NoError(t, err) @@ -74,7 +83,10 @@ func RunWithFollowerProtocolState(t testing.TB, stateRoot *pbadger.StateRoot, f tracer := trace.NewNoopTracer() consumer := events.NewNoop() headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, stateRoot) + qc := unittest.QuorumCertificateFixture() // TODO replace this + rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) + require.NoError(t, err) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) followerState, err := pbadger.NewFollowerState(state, index, payloads, tracer, consumer) require.NoError(t, err) @@ -87,7 +99,10 @@ func RunWithFullProtocolStateAndConsumer(t testing.TB, stateRoot *pbadger.StateR metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, stateRoot) + qc := unittest.QuorumCertificateFixture() // TODO replace this + rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) + require.NoError(t, err) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) receiptValidator := MockReceiptValidator() fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, receiptValidator) From dc150f44297e96239b0e127c9d45efc33804fd82 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 14:32:17 -0800 Subject: [PATCH 052/178] add test for cluster assignment conversion --- model/flow/cluster.go | 1 - model/flow/cluster_test.go | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 model/flow/cluster_test.go diff --git a/model/flow/cluster.go b/model/flow/cluster.go index 39fdbd0ba2a..b474f1f759f 100644 --- a/model/flow/cluster.go +++ b/model/flow/cluster.go @@ -14,7 +14,6 @@ type AssignmentList [][]Identifier type ClusterList []IdentityList // Assignments returns the assignment list for a cluster. -// TODO test func (clusters ClusterList) Assignments() AssignmentList { assignments := make(AssignmentList, 0, len(clusters)) for _, cluster := range clusters { diff --git a/model/flow/cluster_test.go b/model/flow/cluster_test.go new file mode 100644 index 00000000000..a4bd99b2972 --- /dev/null +++ b/model/flow/cluster_test.go @@ -0,0 +1,23 @@ +package flow_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/utils/unittest" +) + +// Test that converting between assignments and clusters does not change contents or order. +func TestClusterAssignments(t *testing.T) { + + identities := unittest.IdentityListFixture(100, unittest.WithRole(flow.RoleCollection)) + assignments := unittest.ClusterAssignment(10, identities) + assert.Len(t, assignments, 10) + + clusters, err := flow.NewClusterList(assignments, identities) + require.NoError(t, err) + assert.Equal(t, assignments, clusters.Assignments()) +} From 493f2ff4a9164d1d9fc4e6d1e0c62fa48901e110 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 14:40:37 -0800 Subject: [PATCH 053/178] remove empty result.go file --- model/bootstrap/result.go | 1 - 1 file changed, 1 deletion(-) delete mode 100644 model/bootstrap/result.go diff --git a/model/bootstrap/result.go b/model/bootstrap/result.go deleted file mode 100644 index 5802ce358fa..00000000000 --- a/model/bootstrap/result.go +++ /dev/null @@ -1 +0,0 @@ -package bootstrap From 21ba6f4cc1cddcef875741d6db8f74f757b36837 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 14:50:06 -0800 Subject: [PATCH 054/178] use root snapshot in scaffold --- cmd/scaffold.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index b592ebf8daf..4b9f6312e3d 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -33,6 +33,7 @@ import ( badgerState "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" "github.com/onflow/flow-go/state/protocol/events/gadgets" + "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/storage" bstorage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/storage/badger/operation" @@ -487,7 +488,7 @@ func (fnb *FlowNodeBuilder) initState() { fnb.MustNot(err).Msg("could not load root seal") // bootstrap the protocol state with the loaded data - stateRoot, err := badgerState.NewStateRoot(fnb.RootBlock, fnb.RootResult, fnb.RootSeal, fnb.RootBlock.Header.View) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(fnb.RootBlock, fnb.RootResult, fnb.RootSeal, fnb.RootQC) fnb.MustNot(err).Msg("failed to construct state root") fnb.State, err = badgerState.Bootstrap( @@ -499,7 +500,7 @@ func (fnb *FlowNodeBuilder) initState() { fnb.Storage.Setups, fnb.Storage.Commits, fnb.Storage.Statuses, - stateRoot, + rootSnapshot, ) fnb.MustNot(err).Msg("could not bootstrap protocol state") From 9717d7e0d9c34dd5dfeb8f14f3f52eebe63b097d Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 14:51:30 -0800 Subject: [PATCH 055/178] add docs for epoch bootstrapping --- state/protocol/badger/state.go | 11 ++++++++++- state/protocol/badger/state_root.go | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 1fee4069757..57c92a488b7 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -171,12 +171,18 @@ func Bootstrap( return state, nil } +// bootstrapEpoch bootstraps the protocol state database with information about +// the previous, current, and next epochs as of the root snapshot. +// +// The root snapshot's sealing segment must not straddle any epoch transitions +// or epoch phase transitions. func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) error { return func(tx *badger.Txn) error { previous := root.Epochs().Previous() current := root.Epochs().Current() next := root.Epochs().Next() + // build the status as we go status := new(flow.EpochStatus) var setups []*flow.EpochSetup var commits []*flow.EpochCommit @@ -218,6 +224,7 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err status.CurrentEpoch.SetupID = setup.ID() status.CurrentEpoch.CommitID = commit.ID() + // insert next epoch, if it exists _, err = next.Counter() if err != nil && !errors.Is(err, protocol.ErrNextEpochNotSetup) { return fmt.Errorf("could not get next epoch: %w", err) @@ -245,6 +252,7 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err return fmt.Errorf("bootstrapping resulting in invalid epoch status: %w", err) } + // insert all epoch setup/commit service events for _, setup := range setups { err = state.epoch.setups.StoreTx(setup)(tx) if err != nil { @@ -258,7 +266,8 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err } } - // TODO: handle or document constraint - sealing segment must not contain epoch or epoch phase transitions + // NOTE: as specified in the godoc, this code assumes that each block + // in the sealing segment in within the same phase within the same epoch. segment, err := root.SealingSegment() if err != nil { return fmt.Errorf("could not get sealing segment: %w", err) diff --git a/state/protocol/badger/state_root.go b/state/protocol/badger/state_root.go index 935895988e0..7f19e980c8a 100644 --- a/state/protocol/badger/state_root.go +++ b/state/protocol/badger/state_root.go @@ -7,12 +7,12 @@ import ( ) // StateRoot is the root information required to bootstrap the protocol state -// TODO remove +// TODO remove or make container for (root, result, seal, qc) type StateRoot struct { block *flow.Block result *flow.ExecutionResult seal *flow.Seal - epochFirstView uint64 + epochFirstView uint64 // TODO remove - this will be part of EpochSetup } func NewStateRoot(block *flow.Block, result *flow.ExecutionResult, seal *flow.Seal, epochFirstView uint64) (*StateRoot, error) { From 0f1fcc88cad3c8f7268199cf4c61d854dc0c34d4 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 15:58:02 -0800 Subject: [PATCH 056/178] document setupEpoch/committedEpoch as conversion layer --- state/protocol/inmem/convert.go | 3 +- state/protocol/inmem/epoch.go | 72 ++++++++++++++++----------------- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 1a595cfa6b6..5d069420fec 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -195,8 +195,7 @@ func SnapshotFromBootstrapState(root *flow.Block, result *flow.ExecutionResult, return nil, fmt.Errorf("invalid commit event type (%T)", seal.ServiceEvents[1].Event) } - // TODO consolidate with inmem.Epoch (copied from badger currently) - current, err := FromEpoch(NewCommittedEpoch(setup, commit)) + current, err := NewCommittedEpoch(setup, commit) if err != nil { return nil, fmt.Errorf("could not convert epoch: %w", err) } diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index f77735825a2..8016b40873d 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -13,6 +13,7 @@ import ( "github.com/onflow/flow-go/state/protocol/seed" ) +// Epoch is a memory-backed implementation of protocol.Epoch. type Epoch struct { enc EncodableEpoch } @@ -74,32 +75,26 @@ func (eq Epochs) Next() protocol.Epoch { return invalid.NewEpoch(protocol.ErrNextEpochNotSetup) } -// ===== -// from badger -// TODO consolidate this with above -// ===== - -// SetupEpoch represents an epoch that has been setup, but not committed. -// Only the EpochSetup event for the epoch has been emitted as of the point -// at which the epoch was queried. -type SetupEpoch struct { +// setupEpoch is an implementation of protocol.Epoch backed by an EpochSetup +// service event. This is used for converting service events to inmem.Epoch. +type setupEpoch struct { // EpochSetup service event setupEvent *flow.EpochSetup } -func (es *SetupEpoch) Counter() (uint64, error) { +func (es *setupEpoch) Counter() (uint64, error) { return es.setupEvent.Counter, nil } -func (es *SetupEpoch) FirstView() (uint64, error) { +func (es *setupEpoch) FirstView() (uint64, error) { return es.setupEvent.FirstView, nil } -func (es *SetupEpoch) FinalView() (uint64, error) { +func (es *setupEpoch) FinalView() (uint64, error) { return es.setupEvent.FinalView, nil } -func (es *SetupEpoch) InitialIdentities() (flow.IdentityList, error) { +func (es *setupEpoch) InitialIdentities() (flow.IdentityList, error) { identities := es.setupEvent.Participants.Filter(filter.Any) // apply a deterministic sort to the participants @@ -108,7 +103,7 @@ func (es *SetupEpoch) InitialIdentities() (flow.IdentityList, error) { return identities, nil } -func (es *SetupEpoch) Clustering() (flow.ClusterList, error) { +func (es *setupEpoch) Clustering() (flow.ClusterList, error) { collectorFilter := filter.And(filter.HasStake(true), filter.HasRole(flow.RoleCollection)) clustering, err := flow.NewClusterList(es.setupEvent.Assignments, es.setupEvent.Participants.Filter(collectorFilter)) @@ -118,39 +113,31 @@ func (es *SetupEpoch) Clustering() (flow.ClusterList, error) { return clustering, nil } -func (es *SetupEpoch) Cluster(_ uint) (protocol.Cluster, error) { +func (es *setupEpoch) Cluster(_ uint) (protocol.Cluster, error) { return nil, protocol.ErrEpochNotCommitted } -func (es *SetupEpoch) DKG() (protocol.DKG, error) { +func (es *setupEpoch) DKG() (protocol.DKG, error) { return nil, protocol.ErrEpochNotCommitted } -func (es *SetupEpoch) RandomSource() ([]byte, error) { +func (es *setupEpoch) RandomSource() ([]byte, error) { return es.setupEvent.RandomSource, nil } -func (es *SetupEpoch) Seed(indices ...uint32) ([]byte, error) { +func (es *setupEpoch) Seed(indices ...uint32) ([]byte, error) { return seed.FromRandomSource(indices, es.setupEvent.RandomSource) } -func NewSetupEpoch(setupEvent *flow.EpochSetup) *SetupEpoch { - return &SetupEpoch{ - setupEvent: setupEvent, - } -} - -// **************************************** - -// CommittedEpoch represents an epoch that has been committed. -// Both the EpochSetup and EpochCommitted events for the epoch have been emitted -// as of the point at which the epoch was queried. -type CommittedEpoch struct { - SetupEpoch +// committedEpoch is an implementation of protocol.Epoch backed by an EpochSetup +// and EpochCommit service event. This is used for converting service events to +// inmem.Epoch. +type committedEpoch struct { + setupEpoch commitEvent *flow.EpochCommit } -func (es *CommittedEpoch) Cluster(index uint) (protocol.Cluster, error) { +func (es *committedEpoch) Cluster(index uint) (protocol.Cluster, error) { qcs := es.commitEvent.ClusterQCs if uint(len(qcs)) <= index { @@ -179,7 +166,7 @@ func (es *CommittedEpoch) Cluster(index uint) (protocol.Cluster, error) { return cluster, err } -func (es *CommittedEpoch) DKG() (protocol.DKG, error) { +func (es *committedEpoch) DKG() (protocol.DKG, error) { dkg, err := DKGFromEncodable(EncodableDKG{ GroupKey: encodable.RandomBeaconPubKey{ PublicKey: es.commitEvent.DKGGroupKey, @@ -189,11 +176,24 @@ func (es *CommittedEpoch) DKG() (protocol.DKG, error) { return dkg, err } -func NewCommittedEpoch(setupEvent *flow.EpochSetup, commitEvent *flow.EpochCommit) *CommittedEpoch { - return &CommittedEpoch{ - SetupEpoch: SetupEpoch{ +// NewSetupEpoch returns an memory-backed epoch implementation based on an +// EpochSetup event. Epoch information available after the setup phase will +// not be accessible in the resulting epoch instance. +func NewSetupEpoch(setupEvent *flow.EpochSetup) (*Epoch, error) { + convertible := &setupEpoch{ + setupEvent: setupEvent, + } + return FromEpoch(convertible) +} + +// NewSetupEpoch returns an memory-backed epoch implementation based on an +// EpochSetup and EpochCommit event. +func NewCommittedEpoch(setupEvent *flow.EpochSetup, commitEvent *flow.EpochCommit) (*Epoch, error) { + convertible := &committedEpoch{ + setupEpoch: setupEpoch{ setupEvent: setupEvent, }, commitEvent: commitEvent, } + return FromEpoch(convertible) } From 7482af88156b87b83484fb5ac52579a20a92b4f2 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 16:01:47 -0800 Subject: [PATCH 057/178] remove usages of badger.{Setup,Committed}Epoch --- state/protocol/badger/snapshot.go | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index f65c1dbda42..e6c790c96c8 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -12,6 +12,7 @@ import ( "github.com/onflow/flow-go/model/flow/order" "github.com/onflow/flow-go/state" "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/state/protocol/invalid" "github.com/onflow/flow-go/state/protocol/seed" "github.com/onflow/flow-go/storage" @@ -374,7 +375,11 @@ func (q *EpochQuery) Current() protocol.Epoch { return invalid.NewEpoch(err) } - return NewCommittedEpoch(setup, commit) + epoch, err := inmem.NewCommittedEpoch(setup, commit) + if err != nil { + return invalid.NewEpoch(err) + } + return epoch } // Next returns the next epoch, if it is available. @@ -399,7 +404,11 @@ func (q *EpochQuery) Next() protocol.Epoch { return invalid.NewEpoch(fmt.Errorf("failed to retrieve setup event for next epoch: %w", err)) } if phase == flow.EpochPhaseSetup { - return NewSetupEpoch(nextSetup) + epoch, err := inmem.NewSetupEpoch(nextSetup) + if err != nil { + return invalid.NewEpoch(err) + } + return epoch } // if we are in committed phase, return a CommittedEpoch @@ -407,7 +416,11 @@ func (q *EpochQuery) Next() protocol.Epoch { if err != nil { return invalid.NewEpoch(fmt.Errorf("failed to retrieve commit event for next epoch: %w", err)) } - return NewCommittedEpoch(nextSetup, nextCommit) + epoch, err := inmem.NewCommittedEpoch(nextSetup, nextCommit) + if err != nil { + return invalid.NewEpoch(err) + } + return epoch } // Previous returns the previous epoch. During the first epoch after the root @@ -437,5 +450,9 @@ func (q *EpochQuery) Previous() protocol.Epoch { return invalid.NewEpoch(err) } - return NewCommittedEpoch(setup, commit) + epoch, err := inmem.NewCommittedEpoch(setup, commit) + if err != nil { + return invalid.NewEpoch(err) + } + return epoch } From 3e6ecc7c151e2a4aa69b2e7f3c1159f5f3b601d2 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 16:02:22 -0800 Subject: [PATCH 058/178] remove badger epoch models these are replaced by inmem.Epoch --- state/protocol/badger/epoch.go | 135 --------------------------------- 1 file changed, 135 deletions(-) delete mode 100644 state/protocol/badger/epoch.go diff --git a/state/protocol/badger/epoch.go b/state/protocol/badger/epoch.go deleted file mode 100644 index 28649c3f91e..00000000000 --- a/state/protocol/badger/epoch.go +++ /dev/null @@ -1,135 +0,0 @@ -// (c) 2019 Dapper Labs - ALL RIGHTS RESERVED - -package badger - -import ( - "fmt" - - "github.com/onflow/flow-go/model/encodable" - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/model/flow/filter" - "github.com/onflow/flow-go/model/flow/order" - "github.com/onflow/flow-go/state/cluster" - "github.com/onflow/flow-go/state/protocol" - "github.com/onflow/flow-go/state/protocol/inmem" - "github.com/onflow/flow-go/state/protocol/seed" -) - -// SetupEpoch represents an epoch that has been setup, but not committed. -// Only the EpochSetup event for the epoch has been emitted as of the point -// at which the epoch was queried. -type SetupEpoch struct { - // EpochSetup service event - setupEvent *flow.EpochSetup -} - -func (es *SetupEpoch) Counter() (uint64, error) { - return es.setupEvent.Counter, nil -} - -func (es *SetupEpoch) FirstView() (uint64, error) { - return es.setupEvent.FirstView, nil -} - -func (es *SetupEpoch) FinalView() (uint64, error) { - return es.setupEvent.FinalView, nil -} - -func (es *SetupEpoch) InitialIdentities() (flow.IdentityList, error) { - - identities := es.setupEvent.Participants.Filter(filter.Any) - // apply a deterministic sort to the participants - identities = identities.Order(order.ByNodeIDAsc) - - return identities, nil -} - -func (es *SetupEpoch) Clustering() (flow.ClusterList, error) { - - collectorFilter := filter.And(filter.HasStake(true), filter.HasRole(flow.RoleCollection)) - clustering, err := flow.NewClusterList(es.setupEvent.Assignments, es.setupEvent.Participants.Filter(collectorFilter)) - if err != nil { - return nil, fmt.Errorf("failed to generate ClusterList from collector identities: %w", err) - } - return clustering, nil -} - -func (es *SetupEpoch) Cluster(_ uint) (protocol.Cluster, error) { - return nil, protocol.ErrEpochNotCommitted -} - -func (es *SetupEpoch) DKG() (protocol.DKG, error) { - return nil, protocol.ErrEpochNotCommitted -} - -func (es *SetupEpoch) RandomSource() ([]byte, error) { - return es.setupEvent.RandomSource, nil -} - -func (es *SetupEpoch) Seed(indices ...uint32) ([]byte, error) { - return seed.FromRandomSource(indices, es.setupEvent.RandomSource) -} - -func NewSetupEpoch(setupEvent *flow.EpochSetup) *SetupEpoch { - return &SetupEpoch{ - setupEvent: setupEvent, - } -} - -// **************************************** - -// CommittedEpoch represents an epoch that has been committed. -// Both the EpochSetup and EpochCommitted events for the epoch have been emitted -// as of the point at which the epoch was queried. -type CommittedEpoch struct { - SetupEpoch - commitEvent *flow.EpochCommit -} - -func (es *CommittedEpoch) Cluster(index uint) (protocol.Cluster, error) { - - qcs := es.commitEvent.ClusterQCs - if uint(len(qcs)) <= index { - return nil, fmt.Errorf("no cluster with index %d", index) - } - rootQC := qcs[index] - - clustering, err := es.Clustering() - if err != nil { - return nil, fmt.Errorf("failed to generate clustering: %w", err) - } - - members, ok := clustering.ByIndex(index) - if !ok { - return nil, fmt.Errorf("failed to get members of cluster %d: %w", index, err) - } - epochCounter := es.setupEvent.Counter - - cluster, err := inmem.ClusterFromEncodable(inmem.EncodableCluster{ - Index: index, - Counter: epochCounter, - Members: members, - RootBlock: cluster.CanonicalRootBlock(epochCounter, members), - RootQC: rootQC, - }) - return cluster, err -} - -func (es *CommittedEpoch) DKG() (protocol.DKG, error) { - dkg, err := inmem.DKGFromEncodable(inmem.EncodableDKG{ - GroupKey: encodable.RandomBeaconPubKey{ - PublicKey: es.commitEvent.DKGGroupKey, - }, - Participants: es.commitEvent.DKGParticipants, - }) - return dkg, err -} - -func NewCommittedEpoch(setupEvent *flow.EpochSetup, commitEvent *flow.EpochCommit) *CommittedEpoch { - return &CommittedEpoch{ - SetupEpoch: SetupEpoch{ - setupEvent: setupEvent, - }, - commitEvent: commitEvent, - } -} From 6ec0a398c0f360dfb7e64f184a71035c740150f3 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 16:06:39 -0800 Subject: [PATCH 059/178] remove unused validate method --- state/protocol/badger/validity.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index a3ecf0f3989..1d4ba1a5086 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -110,8 +110,3 @@ func validCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error { return nil } - -// TODO -func validSealingSegment(segment []*flow.Block) error { - return nil -} From 9bfc912df634dcc43ec7297ddd668f629ecb1f36 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 15 Jan 2021 16:14:45 -0800 Subject: [PATCH 060/178] error handling and docs in service event <-> Epoch conversion --- state/protocol/convert.go | 46 ++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/state/protocol/convert.go b/state/protocol/convert.go index 44684ccc5b7..300c1840aed 100644 --- a/state/protocol/convert.go +++ b/state/protocol/convert.go @@ -7,18 +7,34 @@ import ( "github.com/onflow/flow-go/model/flow/filter" ) -// TODO error handling +// ToEpochSetup converts an Epoch interface instance to the underlying +// concrete epoch setup service event. func ToEpochSetup(epoch Epoch) (*flow.EpochSetup, error) { counter, err := epoch.Counter() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get epoch counter: %w", err) } firstView, err := epoch.FirstView() + if err != nil { + return nil, fmt.Errorf("could not get epoch first view: %w", err) + } finalView, err := epoch.FinalView() + if err != nil { + return nil, fmt.Errorf("could not get epoch final view: %w", err) + } participants, err := epoch.InitialIdentities() + if err != nil { + return nil, fmt.Errorf("could not get epoch participants: %w", err) + } clustering, err := epoch.Clustering() + if err != nil { + return nil, fmt.Errorf("could not get epoch clustering: %w", err) + } assignments := clustering.Assignments() - src, err := epoch.RandomSource() + randomSource, err := epoch.RandomSource() + if err != nil { + return nil, fmt.Errorf("could not get epoch random source: %w", err) + } setup := &flow.EpochSetup{ Counter: counter, @@ -26,29 +42,42 @@ func ToEpochSetup(epoch Epoch) (*flow.EpochSetup, error) { FinalView: finalView, Participants: participants, Assignments: assignments, - RandomSource: src, + RandomSource: randomSource, } return setup, nil } -// TODO error handling +// ToEpochCommit converts an Epoch interface instance to the underlying +// concrete epoch commit service event. The epoch must have been committed. func ToEpochCommit(epoch Epoch) (*flow.EpochCommit, error) { counter, err := epoch.Counter() if err != nil { - return nil, err + return nil, fmt.Errorf("could not get epoch counter: %w", err) } clustering, err := epoch.Clustering() + if err != nil { + return nil, fmt.Errorf("could not get epoch clustering: %w", err) + } qcs := make([]*flow.QuorumCertificate, 0, len(clustering)) for i := range clustering { cluster, err := epoch.Cluster(uint(i)) if err != nil { - return nil, err + return nil, fmt.Errorf("could not get epoch cluster (index=%d): %w", i, err) } qcs = append(qcs, cluster.RootQC()) } participants, err := epoch.InitialIdentities() + if err != nil { + return nil, fmt.Errorf("could not get epoch participants: %w", err) + } dkg, err := epoch.DKG() + if err != nil { + return nil, fmt.Errorf("could not get epoch dkg: %w", err) + } dkgParticipants, err := ToDKGParticipantLookup(dkg, participants.Filter(filter.HasRole(flow.RoleConsensus))) + if err != nil { + return nil, fmt.Errorf("could not compute dkg participant lookup: %w", err) + } commit := &flow.EpochCommit{ Counter: counter, @@ -59,7 +88,8 @@ func ToEpochCommit(epoch Epoch) (*flow.EpochCommit, error) { return commit, nil } -// TODO doc +// ToDKGParticipantLookup computes the nodeID -> DKGParticipant lookup for a +// DKG instance. The participants must exactly match the DKG instance configuration. func ToDKGParticipantLookup(dkg DKG, participants flow.IdentityList) (map[flow.Identifier]flow.DKGParticipant, error) { lookup := make(map[flow.Identifier]flow.DKGParticipant) From a439bd93936499a6d508dd0b1e2922cbd7c26268 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 19 Jan 2021 17:26:42 -0800 Subject: [PATCH 061/178] add godoc --- state/protocol/inmem/convert.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 5d069420fec..776ce32af4a 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -184,6 +184,9 @@ func ClusterFromEncodable(enc EncodableCluster) (*Cluster, error) { return &Cluster{enc}, nil } +// SnapshotFromBootstrapState generates a protocol.Snapshot representing a +// root bootstrap state. This is used to bootstrap the protocol state for +// genesis or post-spork states. func SnapshotFromBootstrapState(root *flow.Block, result *flow.ExecutionResult, seal *flow.Seal, qc *flow.QuorumCertificate) (*Snapshot, error) { setup, ok := seal.ServiceEvents[0].Event.(*flow.EpochSetup) From a3ea0f0d68fbe9af896ee48bd5c182a550517173 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 19 Jan 2021 17:44:45 -0800 Subject: [PATCH 062/178] review pt. 1 - code style --- state/protocol/badger/state.go | 32 +++++++++++++++----------------- state/protocol/inmem/epoch.go | 3 --- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 57c92a488b7..f5dbf12dbcf 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -54,6 +54,9 @@ func Bootstrap( // 1) insert each block in the root chain segment segment, err := root.SealingSegment() + if err != nil { + return fmt.Errorf("could not get sealing segment: %w", err) + } if len(segment) == 0 { return fmt.Errorf("root sealing segment must contain at least one block") } @@ -63,13 +66,10 @@ func Bootstrap( tail := segment[0] head := segment[len(segment)-1] - if err != nil { - return fmt.Errorf("could not get sealing segment: %w", err) - } for i, block := range segment { - blockID := block.ID() height := block.Header.Height + err = state.blocks.StoreTx(block)(tx) if err != nil { return fmt.Errorf("could not insert root block: %w", err) @@ -83,13 +83,6 @@ func Bootstrap( return fmt.Errorf("could not index root block segment (id=%x): %w", blockID, err) } - // for the final block in the segment, insert an empty child index - if i == len(segment)-1 { - err = operation.InsertBlockChildren(blockID, nil)(tx) - if err != nil { - return fmt.Errorf("could not initialize child index for final segment block: %w", err) - } - } // for all but the first block in the segment, index the parent->child relationship if i > 0 { err = operation.InsertBlockChildren(block.Header.ParentID, []flow.Identifier{blockID})(tx) @@ -99,6 +92,12 @@ func Bootstrap( } } + // insert an empty child index for the final block in the segment + err = operation.InsertBlockChildren(head.ID(), nil)(tx) + if err != nil { + return fmt.Errorf("could not insert child index for head block (id=%x): %w", head.ID(), err) + } + // 2) insert the root execution result into the database and index it result, err := root.LatestResult() if err != nil { @@ -189,9 +188,6 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err // insert previous epoch if it exists _, err := previous.Counter() - if err != nil && !errors.Is(err, protocol.ErrNoPreviousEpoch) { - return fmt.Errorf("could not retrieve previous epoch: %w", err) - } if err == nil { // if there is a previous epoch, both setup and commit events must exist setup, err := protocol.ToEpochSetup(previous) @@ -207,6 +203,8 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err commits = append(commits, commit) status.PreviousEpoch.SetupID = setup.ID() status.PreviousEpoch.CommitID = commit.ID() + } else if !errors.Is(err, protocol.ErrNoPreviousEpoch) { + return fmt.Errorf("could not retrieve previous epoch: %w", err) } // insert current epoch - both setup and commit events must exist @@ -226,10 +224,8 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err // insert next epoch, if it exists _, err = next.Counter() - if err != nil && !errors.Is(err, protocol.ErrNextEpochNotSetup) { - return fmt.Errorf("could not get next epoch: %w", err) - } if err == nil { + // either only the setup event, or both the setup and commit events must exist setup, err := protocol.ToEpochSetup(next) if err != nil { return fmt.Errorf("could not get next epoch setup event: %w", err) @@ -244,6 +240,8 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err commits = append(commits, commit) status.NextEpoch.CommitID = commit.ID() } + } else if !errors.Is(err, protocol.ErrNextEpochNotSetup) { + return fmt.Errorf("could not get next epoch: %w", err) } // sanity check: ensure epoch status is valid diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index 8016b40873d..e085e4403f6 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -95,7 +95,6 @@ func (es *setupEpoch) FinalView() (uint64, error) { } func (es *setupEpoch) InitialIdentities() (flow.IdentityList, error) { - identities := es.setupEvent.Participants.Filter(filter.Any) // apply a deterministic sort to the participants identities = identities.Order(order.ByNodeIDAsc) @@ -104,7 +103,6 @@ func (es *setupEpoch) InitialIdentities() (flow.IdentityList, error) { } func (es *setupEpoch) Clustering() (flow.ClusterList, error) { - collectorFilter := filter.And(filter.HasStake(true), filter.HasRole(flow.RoleCollection)) clustering, err := flow.NewClusterList(es.setupEvent.Assignments, es.setupEvent.Participants.Filter(collectorFilter)) if err != nil { @@ -138,7 +136,6 @@ type committedEpoch struct { } func (es *committedEpoch) Cluster(index uint) (protocol.Cluster, error) { - qcs := es.commitEvent.ClusterQCs if uint(len(qcs)) <= index { return nil, fmt.Errorf("no cluster with index %d", index) From f4e1578f27a8d3a38509009ac6cc0f1087bd69d9 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 20 Jan 2021 17:21:59 -0800 Subject: [PATCH 063/178] add helper for traversing chain segment --- state/traverse.go | 33 ++++++++++++ state/traverse_test.go | 119 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 state/traverse.go create mode 100644 state/traverse_test.go diff --git a/state/traverse.go b/state/traverse.go new file mode 100644 index 00000000000..b58b30d4f8c --- /dev/null +++ b/state/traverse.go @@ -0,0 +1,33 @@ +package state + +import ( + "fmt" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/storage" +) + +// Traverse traverses a chain segment beginning with the start block (inclusive) +// and ending with the end block (inclusive). Blocks are traversed in reverse +// height order, meaning the end block must be an ancestor of the start block. +// The callback is called for each block in this segment. +func Traverse(headers storage.Headers, start, end flow.Identifier, callback func(header *flow.Header) error) error { + + nextID := start + for { + // retrieve the next block in the segment and pass it to the callback + next, err := headers.ByBlockID(nextID) + if err != nil { + return fmt.Errorf("could not get segment block (id=%x): %w", nextID, err) + } + err = callback(next) + if err != nil { + return fmt.Errorf("error in callback: %w", err) + } + + if nextID == end { + return nil + } + nextID = next.ParentID + } +} diff --git a/state/traverse_test.go b/state/traverse_test.go new file mode 100644 index 00000000000..b1432d7755c --- /dev/null +++ b/state/traverse_test.go @@ -0,0 +1,119 @@ +package state + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/storage" + mockstorage "github.com/onflow/flow-go/storage/mock" + "github.com/onflow/flow-go/utils/unittest" +) + +func TestTraverse(t *testing.T) { + + // create a storage.Headers mock with a backing map + byID := make(map[flow.Identifier]*flow.Header) + byHeight := make(map[uint64]*flow.Header) + headers := new(mockstorage.Headers) + headers.On("ByBlockID", mock.Anything).Return( + func(id flow.Identifier) *flow.Header { + return byID[id] + }, + func(id flow.Identifier) error { + _, ok := byID[id] + if !ok { + return storage.ErrNotFound + } + return nil + }) + + // populate the mocked header storage with genesis and 10 child blocks + genesis := unittest.BlockHeaderFixture() + genesis.Height = 0 + byID[genesis.ID()] = &genesis + byHeight[genesis.Height] = &genesis + + parent := &genesis + for i := 0; i < 10; i++ { + child := unittest.BlockHeaderWithParentFixture(parent) + byID[child.ID()] = &child + t.Log(child.Height) + byHeight[child.Height] = &child + + parent = &child + } + + // should return error and not call callback when start block doesn't exist + t.Run("non-existent start block", func(t *testing.T) { + start := unittest.IdentifierFixture() + end := byHeight[3].ID() + err := Traverse(headers, start, end, func(_ *flow.Header) error { + // should not be called + t.Fail() + return nil + }) + assert.Error(t, err) + }) + + // should return error when end block doesn't exist + t.Run("non-existent end block", func(t *testing.T) { + start := byHeight[8].ID() + end := unittest.IdentifierFixture() + err := Traverse(headers, start, end, func(_ *flow.Header) error { + return nil + }) + assert.Error(t, err) + }) + + // should return error if the callback returns an error + t.Run("callback error", func(t *testing.T) { + start := byHeight[8].ID() + end := byHeight[5].ID() + err := Traverse(headers, start, end, func(_ *flow.Header) error { + return fmt.Errorf("callback error") + }) + assert.Error(t, err) + }) + + // should call the callback exactly once and not return an error when start == end + t.Run("single-block traversal", func(t *testing.T) { + start := byHeight[5].ID() + end := start + + called := 0 + err := Traverse(headers, start, end, func(header *flow.Header) error { + // should call callback for single block in traversal path + assert.Equal(t, start, header.ID()) + // track calls - should only be called once + called++ + return nil + }) + assert.NoError(t, err) + assert.Equal(t, 1, called) + }) + + // should call the callback exactly once for each block in traversal path + // and not return an error + t.Run("multi-block traversal", func(t *testing.T) { + startHeight := uint64(8) + endHeight := uint64(4) + + start := byHeight[startHeight].ID() + end := byHeight[endHeight].ID() + + // assert that we are receiving the correct block at each height + height := startHeight + err := Traverse(headers, start, end, func(header *flow.Header) error { + expectedID := byHeight[height].ID() + assert.Equal(t, expectedID, header.ID()) + height-- + return nil + }) + assert.NoError(t, err) + assert.Equal(t, endHeight, height+1) + }) +} From de2ede9b504b24207d53516b57a7c69037b00e7f Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 20 Jan 2021 17:22:18 -0800 Subject: [PATCH 064/178] update sealing segment to use traverse helper --- state/protocol/badger/snapshot.go | 35 ++++++++++--------------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index e6c790c96c8..446916b429c 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -275,33 +275,20 @@ func (s *Snapshot) SealingSegment() ([]*flow.Block, error) { if err != nil { return nil, fmt.Errorf("could not get seal for sealing segment: %w", err) } - head, err := s.state.blocks.ByID(s.blockID) - if err != nil { - return nil, fmt.Errorf("could not get head block: %w", err) - } - - // if this snapshot references the root block, the sealing segment - // consists only of the root block - segment := []*flow.Block{head} - if s.blockID == seal.BlockID { - return segment, nil - } - // for all other cases we walk through the chain backward until we reach - // the block referenced by the latest seal - the returned segment includes - // this block - nextID := head.Header.ParentID - for { - next, err := s.state.blocks.ByID(nextID) + // walk through the chain backward until we reach the block referenced by + // the latest seal - the returned segment includes this block + var segment []*flow.Block + err = state.Traverse(s.state.headers, s.blockID, seal.BlockID, func(header *flow.Header) error { + block, err := s.state.blocks.ByID(header.ID()) if err != nil { - return nil, fmt.Errorf("could not get next block (id=%x): %w", nextID, err) + return fmt.Errorf("could not get block: %w", err) } - - segment = append(segment, next) - if nextID == seal.BlockID { - break - } - nextID = next.Header.ParentID + segment = append(segment, block) + return nil + }) + if err != nil { + return nil, fmt.Errorf("could not traverse sealing segment: %w", err) } // reverse the segment so it is in ascending order by height From ae8d392ff8a2867b6fde625bfc5bf7f51c8ed94d Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 19:15:49 -0800 Subject: [PATCH 065/178] add results to state constructors --- state/protocol/badger/state.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index f5dbf12dbcf..071ec476244 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -35,6 +35,7 @@ func Bootstrap( db *badger.DB, headers storage.Headers, seals storage.Seals, + results storage.ExecutionResults, blocks storage.Blocks, setups storage.EpochSetups, commits storage.EpochCommits, @@ -48,7 +49,7 @@ func Bootstrap( if isBootstrapped { return nil, fmt.Errorf("expected empty database") } - state := newState(metrics, db, headers, seals, blocks, setups, commits, statuses) + state := newState(metrics, db, headers, seals, results, blocks, setups, commits, statuses) err = operation.RetryOnConflict(db.Update, func(tx *badger.Txn) error { @@ -287,6 +288,7 @@ func OpenState( db *badger.DB, headers storage.Headers, seals storage.Seals, + results storage.ExecutionResults, blocks storage.Blocks, setups storage.EpochSetups, commits storage.EpochCommits, @@ -299,7 +301,7 @@ func OpenState( if !isBootstrapped { return nil, nil, fmt.Errorf("expected database to contain bootstrapped state") } - state := newState(metrics, db, headers, seals, blocks, setups, commits, statuses) + state := newState(metrics, db, headers, seals, results, blocks, setups, commits, statuses) // read root block from database: var rootHeight uint64 @@ -396,6 +398,7 @@ func newState( db *badger.DB, headers storage.Headers, seals storage.Seals, + results storage.ExecutionResults, blocks storage.Blocks, setups storage.EpochSetups, commits storage.EpochCommits, @@ -405,6 +408,7 @@ func newState( metrics: metrics, db: db, headers: headers, + results: results, seals: seals, blocks: blocks, epoch: struct { From 42f2be520ef60a2ba3cf6c7f127eb9226c410f71 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 19:32:17 -0800 Subject: [PATCH 066/178] add root snapshot fixture --- utils/unittest/fixtures.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index b18b8373a9c..5dcd424df49 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -10,6 +10,7 @@ import ( "github.com/onflow/flow-go/model/chunks" "github.com/onflow/flow-go/module/signature" + "github.com/onflow/flow-go/state/protocol/inmem" hotstuff "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/crypto" @@ -1000,7 +1001,7 @@ func KeyFixture(algo crypto.SigningAlgorithm) crypto.PrivateKey { return key } -func QuorumCertificateFixture() *flow.QuorumCertificate { +func QuorumCertificateFixture(opts ...func(*flow.QuorumCertificate)) *flow.QuorumCertificate { return &flow.QuorumCertificate{ View: uint64(rand.Uint32()), BlockID: IdentifierFixture(), @@ -1009,6 +1010,12 @@ func QuorumCertificateFixture() *flow.QuorumCertificate { } } +func QCWithBlockID(blockID flow.Identifier) func(*flow.QuorumCertificate) { + return func(qc *flow.QuorumCertificate) { + qc.BlockID = blockID + } +} + func VoteFixture() *hotstuff.Vote { return &hotstuff.Vote{ View: uint64(rand.Uint32()), @@ -1131,9 +1138,22 @@ func BootstrapFixture(participants flow.IdentityList, opts ...func(*flow.Block)) Seal.WithResult(result), Seal.WithServiceEvents(setup.ServiceEvent(), commit.ServiceEvent()), ) + return root, result, seal } +// RootSnapshotFixture returns a snapshot representing a root chain state, for +// example one as returned from BootstrapFixture. +func RootSnapshotFixture(participants flow.IdentityList, opts ...func(*flow.Block)) *inmem.Snapshot { + block, result, seal := BootstrapFixture(participants, opts...) + qc := QuorumCertificateFixture(QCWithBlockID(block.ID())) + root, err := inmem.SnapshotFromBootstrapState(block, result, seal, qc) + if err != nil { + panic(err) + } + return root +} + // ChainFixture creates a list of blocks that forms a chain func ChainFixture(nonGenesisCount int) ([]*flow.Block, *flow.ExecutionResult, *flow.Seal) { chain := make([]*flow.Block, 0, nonGenesisCount+1) From fd50104118e7de790412b73408e79ab8d9e75551 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 20:16:25 -0800 Subject: [PATCH 067/178] make first view a standard field of EpochSetup --- model/flow/epoch.go | 26 +---- state/protocol/badger/state_root.go | 126 ------------------------- state/protocol/badger/validity_test.go | 1 - utils/unittest/epoch_builder.go | 1 + utils/unittest/fixtures.go | 7 ++ 5 files changed, 10 insertions(+), 151 deletions(-) delete mode 100644 state/protocol/badger/state_root.go delete mode 100644 state/protocol/badger/validity_test.go diff --git a/model/flow/epoch.go b/model/flow/epoch.go index bc01ec732a7..ae9855d1818 100644 --- a/model/flow/epoch.go +++ b/model/flow/epoch.go @@ -43,33 +43,11 @@ func (p EpochPhase) String() string { // length, the cluster assignment, and the seed for leader selection. type EpochSetup struct { Counter uint64 // the number of the epoch + FirstView uint64 // the first view of the epoch FinalView uint64 // the final view of the epoch Participants IdentityList // all participants of the epoch Assignments AssignmentList // cluster assignment for the epoch RandomSource []byte // source of randomness for epoch-specific setup tasks - - // FirstView is the first view of the epoch. It is NOT included in the service - // event, but is cached here when stored to simplify epoch queries. - // TODO separate this more explicitly from canonical service event - FirstView uint64 -} - -// Body returns the canonical body of the EpochSetup event (notably omitting -// the FirstView which is a computed property). -func (setup *EpochSetup) Body() interface{} { - return struct { - Counter uint64 - FinalView uint64 - Participants IdentityList - Assignments AssignmentList - RandomSource []byte - }{ - Counter: setup.Counter, - FinalView: setup.FinalView, - Participants: setup.Participants, - Assignments: setup.Assignments, - RandomSource: setup.RandomSource, - } } func (setup *EpochSetup) ServiceEvent() ServiceEvent { @@ -81,7 +59,7 @@ func (setup *EpochSetup) ServiceEvent() ServiceEvent { // ID returns the hash of the event contents. func (setup *EpochSetup) ID() Identifier { - return MakeID(setup.Body()) + return MakeID(setup) } // EpochCommit is a service event emitted when epoch setup has been completed. diff --git a/state/protocol/badger/state_root.go b/state/protocol/badger/state_root.go deleted file mode 100644 index 7f19e980c8a..00000000000 --- a/state/protocol/badger/state_root.go +++ /dev/null @@ -1,126 +0,0 @@ -package badger - -import ( - "fmt" - - "github.com/onflow/flow-go/model/flow" -) - -// StateRoot is the root information required to bootstrap the protocol state -// TODO remove or make container for (root, result, seal, qc) -type StateRoot struct { - block *flow.Block - result *flow.ExecutionResult - seal *flow.Seal - epochFirstView uint64 // TODO remove - this will be part of EpochSetup -} - -func NewStateRoot(block *flow.Block, result *flow.ExecutionResult, seal *flow.Seal, epochFirstView uint64) (*StateRoot, error) { - err := validate(block, result, seal, epochFirstView) - if err != nil { - return nil, fmt.Errorf("inconsistent state root: %w", err) - } - return &StateRoot{ - block: block, - result: result, - seal: seal, - epochFirstView: epochFirstView, - }, nil -} - -// validate checks internal consistency of state root -func validate(block *flow.Block, result *flow.ExecutionResult, seal *flow.Seal, epochFirstView uint64) error { - if result.BlockID != block.ID() { - return fmt.Errorf("root execution result for wrong block (%x != %x)", result.BlockID, block.ID()) - } - - if seal.BlockID != block.ID() { - return fmt.Errorf("root block seal for wrong block (%x != %x)", seal.BlockID, block.ID()) - } - - if seal.ResultID != result.ID() { - return fmt.Errorf("root block seal for wrong execution result (%x != %x)", seal.ResultID, result.ID()) - } - - // We should have exactly two service events, one epoch setup and one epoch commit. - if len(seal.ServiceEvents) != 2 { - return fmt.Errorf("root block seal must contain two system events (have %d)", len(seal.ServiceEvents)) - } - setup, valid := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - if !valid { - return fmt.Errorf("first service event should be epoch setup (%T)", seal.ServiceEvents[0]) - } - commit, valid := seal.ServiceEvents[1].Event.(*flow.EpochCommit) - if !valid { - return fmt.Errorf("second event should be epoch commit (%T)", seal.ServiceEvents[1]) - } - - // They should both have the same epoch counter to be valid. - if setup.Counter != commit.Counter { - return fmt.Errorf("epoch setup counter differs from epoch commit counter (%d != %d)", setup.Counter, commit.Counter) - } - - // the root block's view must be within the Epoch - if epochFirstView > block.Header.View { - return fmt.Errorf("root block has lower view than first view of epoch") - } - if block.Header.View >= setup.FinalView { - return fmt.Errorf("final view of epoch less than first block view") - } - - // They should also both be valid within themselves - err := validSetup(setup) - if err != nil { - return fmt.Errorf("invalid epoch setup event: %w", err) - } - err = validCommit(commit, setup) - if err != nil { - return fmt.Errorf("invalid epoch commit event: %w", err) - } - - // Validate the root block and its payload - // NOTE: we might need to relax these restrictions and find a way to - // process the payload of the root block once we implement epochs - - // the root block should have an empty guarantee payload - if len(block.Payload.Guarantees) > 0 { - return fmt.Errorf("root block must not have guarantees") - } - - // the root block should have an empty seal payload - if len(block.Payload.Seals) > 0 { - return fmt.Errorf("root block must not have seals") - } - - return nil -} - -func (s StateRoot) EpochSetupEvent() *flow.EpochSetup { - // CAUTION: the epoch setup event as emitted by the epoch smart contract does - // NOT include the Epoch's first view. Instead, this information is added - // locally (trusted) when stored to simplify epoch queries. - - // copy service event from seal (to not modify seal in-place) - // and set Epoch's first view on the copy - es := s.seal.ServiceEvents[0].Event.(*flow.EpochSetup) - var event flow.EpochSetup = *es - event.FirstView = s.epochFirstView - - return &event -} - -func (s StateRoot) EpochCommitEvent() *flow.EpochCommit { - return s.seal.ServiceEvents[1].Event.(*flow.EpochCommit) -} - -func (s StateRoot) Block() *flow.Block { - return s.block -} - -func (s StateRoot) Seal() *flow.Seal { - return s.seal -} - -func (s StateRoot) Result() *flow.ExecutionResult { - return s.result -} diff --git a/state/protocol/badger/validity_test.go b/state/protocol/badger/validity_test.go deleted file mode 100644 index 2b90f775cf1..00000000000 --- a/state/protocol/badger/validity_test.go +++ /dev/null @@ -1 +0,0 @@ -package badger diff --git a/utils/unittest/epoch_builder.go b/utils/unittest/epoch_builder.go index 44d7d79f26a..7e4b83a0813 100644 --- a/utils/unittest/epoch_builder.go +++ b/utils/unittest/epoch_builder.go @@ -145,6 +145,7 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { setupDefaults := []func(*flow.EpochSetup){ WithParticipants(identities), SetupWithCounter(counter + 1), + WithFirstView(finalView + 1), WithFinalView(finalView + 1000), } diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 5dcd424df49..7aecc300d50 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -1044,6 +1044,12 @@ func WithFinalView(view uint64) func(*flow.EpochSetup) { } } +func WithFirstView(view uint64) func(*flow.EpochSetup) { + return func(setup *flow.EpochSetup) { + setup.FirstView = view + } +} + func EpochSetupFixture(opts ...func(setup *flow.EpochSetup)) *flow.EpochSetup { participants := IdentityListFixture(5, WithAllRoles()) assignments := ClusterAssignment(1, participants) @@ -1131,6 +1137,7 @@ func BootstrapFixture(participants flow.IdentityList, opts ...func(*flow.Block)) setup := EpochSetupFixture( WithParticipants(participants), SetupWithCounter(counter), + WithFirstView(root.Header.View), WithFinalView(root.Header.View+1000), ) commit := EpochCommitFixture(WithDKGFromParticipants(participants), CommitWithCounter(counter)) From 28af2af82f09a63a41cf19859ca1ea94f84c789c Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 20:18:11 -0800 Subject: [PATCH 068/178] use root snapshot in protocol test methods --- state/protocol/util/testing.go | 47 +++++++++++----------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/state/protocol/util/testing.go b/state/protocol/util/testing.go index 9b1e8d40847..a14c0439957 100644 --- a/state/protocol/util/testing.go +++ b/state/protocol/util/testing.go @@ -14,7 +14,6 @@ import ( "github.com/onflow/flow-go/state/protocol" pbadger "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" - "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/storage/util" "github.com/onflow/flow-go/utils/unittest" ) @@ -28,30 +27,23 @@ func MockReceiptValidator() module.ReceiptValidator { return validator } -// TODO update state root -func RunWithBootstrapState(t testing.TB, stateRoot *pbadger.StateRoot, f func(*badger.DB, *pbadger.State)) { +func RunWithBootstrapState(t testing.TB, rootSnapshot protocol.Snapshot, f func(*badger.DB, *pbadger.State)) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { metrics := metrics.NewNoopCollector() - headers, _, seals, _, _, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - qc := unittest.QuorumCertificateFixture() // TODO replace this - rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) - require.NoError(t, err) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) + headers, _, seals, _, _, blocks, setups, commits, statuses, results := util.StorageLayer(t, db) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) f(db, state) }) } -func RunWithFullProtocolState(t testing.TB, stateRoot *pbadger.StateRoot, f func(*badger.DB, *pbadger.MutableState)) { +func RunWithFullProtocolState(t testing.TB, rootSnapshot protocol.Snapshot, f func(*badger.DB, *pbadger.MutableState)) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() consumer := events.NewNoop() - headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - qc := unittest.QuorumCertificateFixture() // TODO replace this - rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) - require.NoError(t, err) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) + headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := util.StorageLayer(t, db) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) receiptValidator := MockReceiptValidator() fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, receiptValidator) @@ -60,16 +52,13 @@ func RunWithFullProtocolState(t testing.TB, stateRoot *pbadger.StateRoot, f func }) } -func RunWithFullProtocolStateAndValidator(t testing.TB, stateRoot *pbadger.StateRoot, validator module.ReceiptValidator, f func(*badger.DB, *pbadger.MutableState)) { +func RunWithFullProtocolStateAndValidator(t testing.TB, rootSnapshot protocol.Snapshot, validator module.ReceiptValidator, f func(*badger.DB, *pbadger.MutableState)) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() consumer := events.NewNoop() - headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - qc := unittest.QuorumCertificateFixture() // TODO replace this - rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) - require.NoError(t, err) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) + headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := util.StorageLayer(t, db) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, validator) require.NoError(t, err) @@ -77,16 +66,13 @@ func RunWithFullProtocolStateAndValidator(t testing.TB, stateRoot *pbadger.State }) } -func RunWithFollowerProtocolState(t testing.TB, stateRoot *pbadger.StateRoot, f func(*badger.DB, *pbadger.FollowerState)) { +func RunWithFollowerProtocolState(t testing.TB, rootSnapshot protocol.Snapshot, f func(*badger.DB, *pbadger.FollowerState)) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() consumer := events.NewNoop() - headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - qc := unittest.QuorumCertificateFixture() // TODO replace this - rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) - require.NoError(t, err) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) + headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := util.StorageLayer(t, db) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) followerState, err := pbadger.NewFollowerState(state, index, payloads, tracer, consumer) require.NoError(t, err) @@ -94,15 +80,12 @@ func RunWithFollowerProtocolState(t testing.TB, stateRoot *pbadger.StateRoot, f }) } -func RunWithFullProtocolStateAndConsumer(t testing.TB, stateRoot *pbadger.StateRoot, consumer protocol.Consumer, f func(*badger.DB, *pbadger.MutableState)) { +func RunWithFullProtocolStateAndConsumer(t testing.TB, rootSnapshot protocol.Snapshot, consumer protocol.Consumer, f func(*badger.DB, *pbadger.MutableState)) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() - headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := util.StorageLayer(t, db) - qc := unittest.QuorumCertificateFixture() // TODO replace this - rootSnapshot, err := inmem.SnapshotFromBootstrapState(stateRoot.Block(), stateRoot.Result(), stateRoot.Seal(), qc) - require.NoError(t, err) - state, err := pbadger.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) + headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := util.StorageLayer(t, db) + state, err := pbadger.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) receiptValidator := MockReceiptValidator() fullState, err := pbadger.NewFullConsensusState(state, index, payloads, tracer, consumer, receiptValidator) From ceaddd2509d70bdc20abc737b03a76b36ee11a80 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 20:18:53 -0800 Subject: [PATCH 069/178] update scaffold with OpenState change --- cmd/scaffold.go | 26 +++++++++++++++++--------- cmd/util/cmd/common/state.go | 3 ++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index adc61cb2c89..8e4bb899d62 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -414,11 +414,12 @@ func (fnb *FlowNodeBuilder) initState() { isBootStrapped, err := badgerState.IsBootstrapped(fnb.DB) fnb.MustNot(err).Msg("failed to determine whether database contains bootstrapped state") if isBootStrapped { - state, stateRoot, err := badgerState.OpenState( + state, err := badgerState.OpenState( fnb.Metrics.Compliance, fnb.DB, fnb.Storage.Headers, fnb.Storage.Seals, + fnb.Storage.Results, fnb.Storage.Blocks, fnb.Storage.Setups, fnb.Storage.Commits, @@ -432,14 +433,17 @@ func (fnb *FlowNodeBuilder) initState() { // but the protocol state is not updated, so they don't match // when this happens during a spork, we could try deleting the protocol state database. // TODO: revisit this check when implementing Epoch - rootBlock, err := loadRootBlock(fnb.BaseConfig.BootstrapDir) - fnb.MustNot(err).Msg("could not load root block") - if rootBlock.ID() != stateRoot.Block().ID() { + rootBlockFromBootstrap, err := loadRootBlock(fnb.BaseConfig.BootstrapDir) + fnb.MustNot(err).Msg("could not load root block from disk") + + rootBlock, err := state.Params().Root() + fnb.MustNot(err).Msg("could not load root block from protocol state") + if rootBlockFromBootstrap.ID() != rootBlock.ID() { fnb.Logger.Fatal().Msgf("mismatching root block ID, protocol state block ID: %v, bootstrap root block ID: %v", - rootBlock.ID(), + rootBlockFromBootstrap.ID(), fnb.RootBlock.ID()) } - fnb.RootBlock = stateRoot.Block() + fnb.RootBlock = rootBlockFromBootstrap // TODO: we shouldn't have to load any files again after bootstrapping; in // order to make it unnecessary, we need to changes: @@ -453,14 +457,17 @@ func (fnb *FlowNodeBuilder) initState() { // not use a global variable for chain ID anymore, but rely on the protocol // state as final authority on what the chain ID is // => https://github.com/dapperlabs/flow-go/issues/4167 - fnb.RootChainID = stateRoot.Block().Header.ChainID + fnb.RootChainID = rootBlock.ChainID // load the root QC data from bootstrap files + // TODO get from protocol state fnb.RootQC, err = loadRootQC(fnb.BaseConfig.BootstrapDir) fnb.MustNot(err).Msg("could not load root QC") - fnb.RootResult = stateRoot.Result() - fnb.RootSeal = stateRoot.Seal() + fnb.RootResult, err = state.AtBlockID(rootBlock.ID()).LatestResult() + fnb.MustNot(err).Msg("could not get root result") + fnb.RootSeal, err = state.AtBlockID(rootBlock.ID()).LatestSeal() + fnb.MustNot(err).Msg("could not get root seal") } else { // Bootstrap! @@ -494,6 +501,7 @@ func (fnb *FlowNodeBuilder) initState() { fnb.DB, fnb.Storage.Headers, fnb.Storage.Seals, + fnb.Storage.Results, fnb.Storage.Blocks, fnb.Storage.Setups, fnb.Storage.Commits, diff --git a/cmd/util/cmd/common/state.go b/cmd/util/cmd/common/state.go index dbfabdab4b0..84d81230c7b 100644 --- a/cmd/util/cmd/common/state.go +++ b/cmd/util/cmd/common/state.go @@ -14,11 +14,12 @@ import ( func InitProtocolState(db *badger.DB, storages *storage.All) (protocol.State, error) { metrics := &metrics.NoopCollector{} - protocolState, _, err := protocolbadger.OpenState( + protocolState, err := protocolbadger.OpenState( metrics, db, storages.Headers, storages.Seals, + storages.Results, storages.Blocks, storages.Setups, storages.EpochCommits, From 3a7c87afcc63bf6df42d568e67911c76a54d4258 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 20:20:56 -0800 Subject: [PATCH 070/178] update consensus tests --- consensus/integration/nodes_test.go | 5 +++-- consensus/recovery/protocol/state_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/consensus/integration/nodes_test.go b/consensus/integration/nodes_test.go index 0b8cf33dbd7..0908db8efa0 100644 --- a/consensus/integration/nodes_test.go +++ b/consensus/integration/nodes_test.go @@ -33,6 +33,7 @@ import ( "github.com/onflow/flow-go/network/mocknetwork" protocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" + "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/state/protocol/util" storage "github.com/onflow/flow-go/storage/badger" storagemock "github.com/onflow/flow-go/storage/mock" @@ -139,10 +140,10 @@ func createNode( statusesDB := storage.NewEpochStatuses(metrics, db) consumer := events.NewNoop() - stateRoot, err := protocol.NewStateRoot(root, result, seal, 0) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, rootQC) require.NoError(t, err) - state, err := protocol.Bootstrap(metrics, db, headersDB, sealsDB, blocksDB, setupsDB, commitsDB, statusesDB, stateRoot) + state, err := protocol.Bootstrap(metrics, db, headersDB, sealsDB, resultsDB, blocksDB, setupsDB, commitsDB, statusesDB, rootSnapshot) require.NoError(t, err) fullState, err := protocol.NewFullConsensusState(state, indexDB, payloadsDB, tracer, consumer, util.MockReceiptValidator()) diff --git a/consensus/recovery/protocol/state_test.go b/consensus/recovery/protocol/state_test.go index e58d80d60e4..2a480a5314b 100644 --- a/consensus/recovery/protocol/state_test.go +++ b/consensus/recovery/protocol/state_test.go @@ -19,11 +19,11 @@ import ( // if it's not finalized yet, this block should be returned by latest func TestSaveBlockAsReplica(t *testing.T) { participants := unittest.IdentityListFixture(5, unittest.WithAllRoles()) - b0, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := protocol.NewStateRoot(b0, result, seal, 0) + rootSnapshot := unittest.RootSnapshotFixture(participants) + b0, err := rootSnapshot.Head() require.NoError(t, err) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - b1 := unittest.BlockWithParentFixture(b0.Header) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + b1 := unittest.BlockWithParentFixture(b0) b1.SetPayload(flow.Payload{}) err = state.Extend(&b1) From 8fb84ddacc03bedc9ba57d397fa8cc9f6aa867fd Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 20:21:12 -0800 Subject: [PATCH 071/178] update engine node testutil --- engine/testutil/nodes.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/engine/testutil/nodes.go b/engine/testutil/nodes.go index ec8cf8097e1..6c98c85b56e 100644 --- a/engine/testutil/nodes.go +++ b/engine/testutil/nodes.go @@ -145,11 +145,8 @@ func CompleteStateFixture(t testing.TB, log zerolog.Logger, metric *metrics.Noop s := storage.InitAll(metric, db) consumer := events.NewNoop() - root, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := badgerstate.NewStateRoot(root, result, seal, 0) - require.NoError(t, err) - - state, err := badgerstate.Bootstrap(metric, db, s.Headers, s.Seals, s.Blocks, s.Setups, s.EpochCommits, s.Statuses, stateRoot) + rootSnapshot := unittest.RootSnapshotFixture(participants) + state, err := badgerstate.Bootstrap(metric, db, s.Headers, s.Seals, s.Results, s.Blocks, s.Setups, s.EpochCommits, s.Statuses, rootSnapshot) require.NoError(t, err) mutableState, err := badgerstate.NewFullConsensusState(state, s.Index, s.Payloads, tracer, consumer, util.MockReceiptValidator()) From 0eceb7be49eb1977f5b4334a85c35259f6584922 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 20:21:35 -0800 Subject: [PATCH 072/178] update collector builder --- module/builder/collection/builder_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/module/builder/collection/builder_test.go b/module/builder/collection/builder_test.go index 9e7dfc8fedb..389b7cb37e1 100644 --- a/module/builder/collection/builder_test.go +++ b/module/builder/collection/builder_test.go @@ -22,6 +22,7 @@ import ( "github.com/onflow/flow-go/state/protocol" pbadger "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/events" + "github.com/onflow/flow-go/state/protocol/inmem" storage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/storage/badger/procedure" sutil "github.com/onflow/flow-go/storage/util" @@ -68,7 +69,7 @@ func (suite *BuilderSuite) SetupTest() { metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() - headers, _, seals, index, conPayloads, blocks, setups, commits, statuses, _ := sutil.StorageLayer(suite.T(), suite.db) + headers, _, seals, index, conPayloads, blocks, setups, commits, statuses, results := sutil.StorageLayer(suite.T(), suite.db) consumer := events.NewNoop() suite.headers = headers suite.blocks = blocks @@ -85,13 +86,14 @@ func (suite *BuilderSuite) SetupTest() { // just bootstrap with a genesis block, we'll use this as reference participants := unittest.IdentityListFixture(5, unittest.WithAllRoles()) root, result, seal := unittest.BootstrapFixture(participants) + qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(root.ID())) // ensure we don't enter a new epoch for tests that build many blocks seal.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = root.Header.View + 100000 - stateRoot, err := pbadger.NewStateRoot(root, result, seal, 0) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc) require.NoError(suite.T(), err) - state, err := pbadger.Bootstrap(metrics, suite.db, headers, seals, blocks, setups, commits, statuses, stateRoot) + state, err := pbadger.Bootstrap(metrics, suite.db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(suite.T(), err) suite.protoState, err = pbadger.NewFollowerState(state, index, conPayloads, tracer, consumer) From d45eff72b6d5bfd4bd65982dbd4471d22a27881a Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 20:22:39 -0800 Subject: [PATCH 073/178] udpate cluster state tests --- state/cluster/badger/mutator_test.go | 13 +++++++------ state/cluster/badger/snapshot_test.go | 8 +++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/state/cluster/badger/mutator_test.go b/state/cluster/badger/mutator_test.go index 91d059b8de3..5d27cff459f 100644 --- a/state/cluster/badger/mutator_test.go +++ b/state/cluster/badger/mutator_test.go @@ -58,7 +58,7 @@ func (suite *MutatorSuite) SetupTest() { metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() - headers, _, seals, index, conPayloads, blocks, setups, commits, statuses, _ := util.StorageLayer(suite.T(), suite.db) + headers, _, seals, index, conPayloads, blocks, setups, commits, statuses, results := util.StorageLayer(suite.T(), suite.db) colPayloads := storage.NewClusterPayloads(metrics, suite.db) clusterStateRoot, err := NewStateRoot(suite.genesis) @@ -71,16 +71,17 @@ func (suite *MutatorSuite) SetupTest() { // just bootstrap with a genesis block, we'll use this as reference participants := unittest.IdentityListFixture(5, unittest.WithAllRoles()) - root, result, seal := unittest.BootstrapFixture(participants) + genesis, result, seal := unittest.BootstrapFixture(participants) + qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(genesis.ID())) // ensure we don't enter a new epoch for tests that build many blocks - seal.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = root.Header.View + 100000 + seal.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = genesis.Header.View + 100000 - rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, unittest.QuorumCertificateFixture()) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(genesis, result, seal, qc) require.NoError(suite.T(), err) - suite.protoGenesis = root.Header + suite.protoGenesis = genesis.Header - state, err := pbadger.Bootstrap(metrics, suite.db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) + state, err := pbadger.Bootstrap(metrics, suite.db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(suite.T(), err) suite.protoState, err = pbadger.NewFollowerState(state, index, conPayloads, tracer, consumer) diff --git a/state/cluster/badger/snapshot_test.go b/state/cluster/badger/snapshot_test.go index b3c27ab7746..57a413115ac 100644 --- a/state/cluster/badger/snapshot_test.go +++ b/state/cluster/badger/snapshot_test.go @@ -19,7 +19,6 @@ import ( "github.com/onflow/flow-go/state/cluster" "github.com/onflow/flow-go/state/protocol" pbadger "github.com/onflow/flow-go/state/protocol/badger" - "github.com/onflow/flow-go/state/protocol/inmem" storage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/storage/badger/operation" "github.com/onflow/flow-go/storage/badger/procedure" @@ -56,7 +55,7 @@ func (suite *SnapshotSuite) SetupTest() { metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() - headers, _, seals, _, _, blocks, setups, commits, statuses, _ := util.StorageLayer(suite.T(), suite.db) + headers, _, seals, _, _, blocks, setups, commits, statuses, results := util.StorageLayer(suite.T(), suite.db) colPayloads := storage.NewClusterPayloads(metrics, suite.db) clusterStateRoot, err := NewStateRoot(suite.genesis) @@ -67,10 +66,9 @@ func (suite *SnapshotSuite) SetupTest() { suite.Assert().Nil(err) participants := unittest.IdentityListFixture(5, unittest.WithAllRoles()) - root, result, seal := unittest.BootstrapFixture(participants) - rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, unittest.QuorumCertificateFixture()) + root := unittest.RootSnapshotFixture(participants) - suite.protoState, err = pbadger.Bootstrap(metrics, suite.db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) + suite.protoState, err = pbadger.Bootstrap(metrics, suite.db, headers, seals, results, blocks, setups, commits, statuses, root) require.NoError(suite.T(), err) suite.Require().Nil(err) From c620091e79050446bae195fc39975e1f82ac939c Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 20:25:07 -0800 Subject: [PATCH 074/178] update inmem snapshot conversion --- state/protocol/inmem/convert_test.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index 39199fb990f..d6e05def875 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/state/protocol" bprotocol "github.com/onflow/flow-go/state/protocol/badger" "github.com/onflow/flow-go/state/protocol/inmem" @@ -21,9 +20,9 @@ import ( // to a memory-backed snapshot. func TestFromSnapshot(t *testing.T) { identities := unittest.IdentityListFixture(10, unittest.WithAllRoles()) - stateRoot := fixtureStateRoot(t, identities) + rootSnapshot := unittest.RootSnapshotFixture(identities) - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *bprotocol.FollowerState) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { // Prepare an epoch builder, which builds epochs with 4 blocks, A,B,C,D // See EpochBuilder documentation for details of these blocks. @@ -143,10 +142,3 @@ func snapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) bool { func assertSnapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) { assert.True(t, snapshotsEqual(t, snap1, snap2)) } - -func fixtureStateRoot(t *testing.T, participants flow.IdentityList) *bprotocol.StateRoot { - root, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := bprotocol.NewStateRoot(root, result, seal, 0) - require.NoError(t, err) - return stateRoot -} From e3f74423ae96a8b59a73b28827216bb7281eb589 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 21 Jan 2021 21:01:45 -0800 Subject: [PATCH 075/178] move validity tests to validity_test --- state/protocol/badger/mutator.go | 1 + state/protocol/badger/mutator_test.go | 251 ++----------------------- state/protocol/badger/snapshot_test.go | 2 +- state/protocol/badger/state.go | 74 +++----- state/protocol/badger/state_test.go | 22 ++- state/protocol/badger/validity.go | 55 ++++++ state/protocol/badger/validity_test.go | 216 +++++++++++++++++++++ 7 files changed, 327 insertions(+), 294 deletions(-) create mode 100644 state/protocol/badger/validity_test.go diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index 7e07ddd8f83..d3cab3ff9ab 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -931,6 +931,7 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T } // cache the first view to simplify epoch queries later on + // TODO remove ev.FirstView = activeSetup.FinalView + 1 // prevents multiple setup events for same Epoch (including multiple setup events in payload of same block) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 50d0a9a8e16..7ed02643744 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -4,7 +4,6 @@ package badger_test import ( "errors" - "math/rand" "testing" "time" @@ -41,10 +40,8 @@ func init() { var participants = unittest.IdentityListFixture(5, unittest.WithAllRoles()) func TestBootstrapValid(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := protocol.NewStateRoot(root, result, seal, 0) - require.NoError(t, err) - util.RunWithBootstrapState(t, stateRoot, func(db *badger.DB, state *protocol.State) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *protocol.State) { var finalized uint64 err := db.View(operation.RetrieveFinalizedHeight(&finalized)) require.NoError(t, err) @@ -65,233 +62,18 @@ func TestBootstrapValid(t *testing.T) { err = db.View(operation.LookupBlockSeal(genesisID, &sealID)) require.NoError(t, err) - seal := stateRoot.Seal() + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) err = db.View(operation.RetrieveSeal(sealID, seal)) require.NoError(t, err) - block := stateRoot.Block() - require.Equal(t, block.Header.Height, finalized) - require.Equal(t, block.Header.Height, sealed) + block, err := rootSnapshot.Head() + require.NoError(t, err) + require.Equal(t, block.Height, finalized) + require.Equal(t, block.Height, sealed) require.Equal(t, block.ID(), genesisID) require.Equal(t, block.ID(), seal.BlockID) - require.Equal(t, block.Header, &header) - }) -} - -func TestBootstrapDuplicateID(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - root, result, seal := unittest.BootstrapFixture(participants) - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapZeroStake(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 0}, - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - root, result, seal := unittest.BootstrapFixture(participants) - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapNoCollection(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - - root, result, seal := unittest.BootstrapFixture(participants) - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapNoConsensus(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - - root, result, seal := unittest.BootstrapFixture(participants) - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapNoExecution(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - - root, result, seal := unittest.BootstrapFixture(participants) - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapNoVerification(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - } - - root, result, seal := unittest.BootstrapFixture(participants) - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapExistingAddress(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x02}, Address: "a1", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - - root, result, seal := unittest.BootstrapFixture(participants) - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapNonZeroParent(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants, func(block *flow.Block) { - block.Header.Height = 13 - block.Header.ParentID = unittest.IdentifierFixture() - }) - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.NoError(t, err) -} - -func TestBootstrapNonEmptyCollections(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants, func(block *flow.Block) { - block.Payload.Guarantees = unittest.CollectionGuaranteesFixture(1) - }) - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapWithSeal(t *testing.T) { - block := unittest.GenesisFixture(participants) - block.Payload.Seals = []*flow.Seal{unittest.Seal.Fixture()} - block.Header.PayloadHash = block.Payload.Hash() - - result := unittest.ExecutionResultFixture() - result.BlockID = block.ID() - - finalState, ok := result.FinalStateCommitment() - require.True(t, ok) - - seal := unittest.Seal.Fixture() - seal.BlockID = block.ID() - seal.ResultID = result.ID() - seal.FinalState = finalState - - _, err := protocol.NewStateRoot(block, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapMissingServiceEvents(t *testing.T) { - t.Run("missing setup", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - seal.ServiceEvents = seal.ServiceEvents[1:] - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("missing commit", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - seal.ServiceEvents = seal.ServiceEvents[:1] - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) -} - -func TestBootstrapInvalidEpochSetup(t *testing.T) { - t.Run("invalid final view", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - // set an invalid final view for the first epoch - setup.FinalView = root.Header.View - - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("invalid cluster assignments", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - // create an invalid cluster assignment (node appears in multiple clusters) - collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] - setup.Assignments = append(setup.Assignments, []flow.Identifier{collector.NodeID}) - - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("empty seed", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - setup.RandomSource = nil - - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) -} - -func TestBootstrapInvalidEpochCommit(t *testing.T) { - t.Run("inconsistent counter", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) - // use a different counter for the commit - commit.Counter = setup.Counter + 1 - - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("inconsistent cluster QCs", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) - // add an extra QC to commit - commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) - - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("missing dkg group key", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) - commit.DKGGroupKey = nil - - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("inconsistent DKG participants", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) - // add an invalid DKG participant - collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] - commit.DKGParticipants[collector.NodeID] = flow.DKGParticipant{ - KeyShare: unittest.KeyFixture(crypto.BLSBLS12381).PublicKey(), - Index: 1, - } - - _, err := protocol.NewStateRoot(root, result, seal, 0) - require.Error(t, err) + require.Equal(t, block, &header) }) } @@ -299,7 +81,7 @@ func TestExtendValid(t *testing.T) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() - headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := storeutil.StorageLayer(t, db) + headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := storeutil.StorageLayer(t, db) // create a event consumer to test epoch transition events distributor := events.NewDistributor() @@ -307,10 +89,11 @@ func TestExtendValid(t *testing.T) { distributor.AddConsumer(consumer) block, result, seal := unittest.BootstrapFixture(participants) - rootSnapshot, err := inmem.SnapshotFromBootstrapState(block, result, seal, unittest.QuorumCertificateFixture()) + qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(block.ID())) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(block, result, seal, qc) require.NoError(t, err) - state, err := protocol.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, rootSnapshot) + state, err := protocol.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) fullState, err := protocol.NewFullConsensusState(state, index, payloads, tracer, consumer, util.MockReceiptValidator()) @@ -451,7 +234,7 @@ func TestExtendHeightTooSmall(t *testing.T) { func TestExtendHeightTooLarge(t *testing.T) { root, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := protocol.NewStateRoot(root, result, seal, 0) + stateRoot, err := flow.NewStateRoot(root, result, seal, 0) require.NoError(t, err) util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { @@ -1889,13 +1672,13 @@ func saveBlock(t *testing.T, block *flow.Block, finalizes *flow.Block, state *pr require.NoError(t, err) } -func fixtureStateRoot(t *testing.T) *protocol.StateRoot { +func fixtureStateRoot(t *testing.T) *flow.StateRoot { return fixtureStateRootWithParticipants(t, participants) } -func fixtureStateRootWithParticipants(t *testing.T, participants flow.IdentityList) *protocol.StateRoot { +func fixtureStateRootWithParticipants(t *testing.T, participants flow.IdentityList) *flow.StateRoot { root, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := protocol.NewStateRoot(root, result, seal, 0) + stateRoot, err := flow.NewStateRoot(root, result, seal, 0) require.NoError(t, err) return stateRoot } diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index fd08c6ae24c..1c9775f6f5f 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -541,7 +541,7 @@ func TestSnapshot_PostSporkIdentities(t *testing.T) { block.Header.ParentID = unittest.IdentifierFixture() }) - stateRoot, err := bprotocol.NewStateRoot(root, result, seal, 0) + stateRoot, err := flow.NewStateRoot(root, result, seal, 0) require.NoError(t, err) util.RunWithBootstrapState(t, stateRoot, func(db *badger.DB, state *bprotocol.State) { diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 071ec476244..e9f92e0cec3 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -200,6 +200,13 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err return fmt.Errorf("could not get previous epoch commit event: %w", err) } + if err := validSetup(setup); err != nil { + return fmt.Errorf("invalid setup: %w", err) + } + if err := validCommit(commit, setup); err != nil { + return fmt.Errorf("invalid commit") + } + setups = append(setups, setup) commits = append(commits, commit) status.PreviousEpoch.SetupID = setup.ID() @@ -218,6 +225,13 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err return fmt.Errorf("could not get current epoch commit event: %w", err) } + if err := validSetup(setup); err != nil { + return fmt.Errorf("invalid setup: %w", err) + } + if err := validCommit(commit, setup); err != nil { + return fmt.Errorf("invalid commit") + } + setups = append(setups, setup) commits = append(commits, commit) status.CurrentEpoch.SetupID = setup.ID() @@ -231,6 +245,10 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err if err != nil { return fmt.Errorf("could not get next epoch setup event: %w", err) } + if err := validSetup(setup); err != nil { + return fmt.Errorf("invalid setup: %w", err) + } + setups = append(setups, setup) status.NextEpoch.SetupID = setup.ID() commit, err := protocol.ToEpochCommit(next) @@ -238,6 +256,9 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err return fmt.Errorf("could not get next epoch commit event: %w", err) } if err == nil { + if err := validCommit(commit, setup); err != nil { + return fmt.Errorf("invalid commit") + } commits = append(commits, commit) status.NextEpoch.CommitID = commit.ID() } @@ -293,62 +314,17 @@ func OpenState( setups storage.EpochSetups, commits storage.EpochCommits, statuses storage.EpochStatuses, -) (*State, *StateRoot, error) { +) (*State, error) { isBootstrapped, err := IsBootstrapped(db) if err != nil { - return nil, nil, fmt.Errorf("failed to determine whether database contains bootstrapped state: %w", err) + return nil, fmt.Errorf("failed to determine whether database contains bootstrapped state: %w", err) } if !isBootstrapped { - return nil, nil, fmt.Errorf("expected database to contain bootstrapped state") + return nil, fmt.Errorf("expected database to contain bootstrapped state") } state := newState(metrics, db, headers, seals, results, blocks, setups, commits, statuses) - // read root block from database: - var rootHeight uint64 - err = db.View(operation.RetrieveRootHeight(&rootHeight)) - if err != nil { - return nil, nil, fmt.Errorf("failed retrieve root height: %w", err) - } - rootBlock, err := blocks.ByHeight(rootHeight) - if err != nil { - return nil, nil, fmt.Errorf("failed retrieve root block: %w", err) - } - - // read root execution result - var resultID flow.Identifier - err = db.View(operation.LookupExecutionResult(rootBlock.ID(), &resultID)) - if err != nil { - return nil, nil, fmt.Errorf("failed retrieve root block's execution result ID: %w", err) - } - var result flow.ExecutionResult - err = db.View(operation.RetrieveExecutionResult(resultID, &result)) - if err != nil { - return nil, nil, fmt.Errorf("failed retrieve root block's execution result: %w", err) - } - - // read root seal - seal, err := seals.ByBlockID(rootBlock.ID()) - if err != nil { - return nil, nil, fmt.Errorf("failed retrieve root block's seal: %w", err) - } - - // read root seal - epochStatus, err := statuses.ByBlockID(rootBlock.ID()) - if err != nil { - return nil, nil, fmt.Errorf("failed retrieve root block's epoch status: %w", err) - } - epochSetup, err := setups.ByID(epochStatus.CurrentEpoch.SetupID) - if err != nil { - return nil, nil, fmt.Errorf("failed retrieve root epochs's setup event: %w", err) - } - - // construct state Root - stateRoot, err := NewStateRoot(rootBlock, &result, seal, epochSetup.FirstView) - if err != nil { - return nil, nil, fmt.Errorf("constructing state root failed: %w", err) - } - - return state, stateRoot, nil + return state, nil } func (s *State) Params() protocol.Params { diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index 3dea6951f17..293847bf029 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/dgraph-io/badger/v2" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/model/flow" @@ -19,34 +20,35 @@ import ( func TestBootstrapAndOpen(t *testing.T) { // create a state root and bootstrap the protocol state with it - expected := unittest.CompleteIdentitySet() - root, result, seal := unittest.BootstrapFixture(expected, func(block *flow.Block) { + participants := unittest.CompleteIdentitySet() + rootSnapshot := unittest.RootSnapshotFixture(participants, func(block *flow.Block) { block.Header.ParentID = unittest.IdentifierFixture() }) - stateRoot, err := bprotocol.NewStateRoot(root, result, seal, 0) - require.NoError(t, err) - - util.RunWithBootstrapState(t, stateRoot, func(db *badger.DB, state *bprotocol.State) { + util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { // protocol state has been bootstrapped, now open a protocol state with // the database metrics := &metrics.NoopCollector{} all := storagebadger.InitAll(metrics, db) - _, openedRoot, err := bprotocol.OpenState( + state, err := bprotocol.OpenState( metrics, db, all.Headers, all.Seals, + all.Results, all.Blocks, all.Setups, all.EpochCommits, all.Statuses) require.NoError(t, err) - // the opened root should be the same as the orignal root - require.Equal(t, stateRoot.EpochCommitEvent(), openedRoot.EpochCommitEvent()) - require.Equal(t, stateRoot.EpochSetupEvent(), openedRoot.EpochSetupEvent()) + expected, err := rootSnapshot.Head() + require.NoError(t, err) + actual, err := state.Final().Head() + require.NoError(t, err) + + assert.Equal(t, expected, actual) }) } diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index 1d4ba1a5086..ec92cd47980 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -6,6 +6,7 @@ import ( "github.com/onflow/flow-go-sdk/crypto" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/state/protocol" ) func validSetup(setup *flow.EpochSetup) error { @@ -110,3 +111,57 @@ func validCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error { return nil } + +// validRootSnapshot checks internal consistency of root state snapshot +func validRootSnapshot(snap protocol.Snapshot) error { + + segment, err := snap.SealingSegment() + if err != nil { + return fmt.Errorf("could not get sealing segment: %w", err) + } + seal, err := snap.LatestSeal() + if err != nil { + return fmt.Errorf("could not latest seal: %w", err) + } + result, err := snap.LatestResult() + if err != nil { + return fmt.Errorf("could not get latest result: %w", err) + } + + if len(segment) == 0 { + return fmt.Errorf("invalid empty sealing segment") + } + head := segment[len(segment)-1] // reference block of the snapshot + tail := segment[0] // last sealed block + + if result.BlockID != tail.ID() { + return fmt.Errorf("root execution result for wrong block (%x != %x)", result.BlockID, tail.ID()) + } + + if seal.BlockID != tail.ID() { + return fmt.Errorf("root block seal for wrong block (%x != %x)", seal.BlockID, tail.ID()) + } + + if seal.ResultID != result.ID() { + return fmt.Errorf("root block seal for wrong execution result (%x != %x)", seal.ResultID, result.ID()) + } + + firstView, err := snap.Epochs().Current().FirstView() + if err != nil { + return fmt.Errorf("could not get first view: %w", err) + } + finalView, err := snap.Epochs().Current().FinalView() + if err != nil { + return fmt.Errorf("could not get final view: %w", err) + } + + // the segment must be fully within the current epoch + if firstView > tail.Header.View { + return fmt.Errorf("root block has lower view than first view of epoch") + } + if head.Header.View >= finalView { + return fmt.Errorf("final view of epoch less than first block view") + } + + return nil +} diff --git a/state/protocol/badger/validity_test.go b/state/protocol/badger/validity_test.go new file mode 100644 index 00000000000..1311acbeb30 --- /dev/null +++ b/state/protocol/badger/validity_test.go @@ -0,0 +1,216 @@ +package badger + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/crypto" + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/utils/unittest" +) + +var participants = unittest.IdentityListFixture(5, unittest.WithAllRoles()) + +func TestBootstrapDuplicateID(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, + {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, + {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + } + root := unittest.RootSnapshotFixture(participants) + err := validRootSnapshot(root) + require.Error(t, err) +} + +func TestBootstrapZeroStake(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 0}, + {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, + {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + } + root := unittest.RootSnapshotFixture(participants) + err := validRootSnapshot(root) + require.Error(t, err) +} + +func TestBootstrapNoCollection(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, + {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + } + + root := unittest.RootSnapshotFixture(participants) + err := validRootSnapshot(root) + require.Error(t, err) +} + +func TestBootstrapNoConsensus(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, + {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + } + + root := unittest.RootSnapshotFixture(participants) + err := validRootSnapshot(root) + require.Error(t, err) +} + +func TestBootstrapNoExecution(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, + {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + } + + root := unittest.RootSnapshotFixture(participants) + err := validRootSnapshot(root) + require.Error(t, err) +} + +func TestBootstrapNoVerification(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, + {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, + {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + } + + root := unittest.RootSnapshotFixture(participants) + err := validRootSnapshot(root) + require.Error(t, err) +} + +func TestBootstrapExistingAddress(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, + {NodeID: flow.Identifier{0x02}, Address: "a1", Role: flow.RoleConsensus, Stake: 2}, + {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + } + + root := unittest.RootSnapshotFixture(participants) + err := validRootSnapshot(root) + require.Error(t, err) +} + +// TODO re-work following tests - need to be different now that we can bootstrap +// from any snapshot +func TestBootstrapWithSeal(t *testing.T) { + block := unittest.GenesisFixture(participants) + block.Payload.Seals = []*flow.Seal{unittest.Seal.Fixture()} + block.Header.PayloadHash = block.Payload.Hash() + + result := unittest.ExecutionResultFixture() + result.BlockID = block.ID() + + finalState, ok := result.FinalStateCommitment() + require.True(t, ok) + + seal := unittest.Seal.Fixture() + seal.BlockID = block.ID() + seal.ResultID = result.ID() + seal.FinalState = finalState + + _, err := flow.NewStateRoot(block, result, seal, 0) + require.Error(t, err) +} + +func TestBootstrapMissingServiceEvents(t *testing.T) { + t.Run("missing setup", func(t *testing.T) { + root, result, seal := unittest.BootstrapFixture(participants) + seal.ServiceEvents = seal.ServiceEvents[1:] + _, err := flow.NewStateRoot(root, result, seal, 0) + require.Error(t, err) + }) + + t.Run("missing commit", func(t *testing.T) { + root, result, seal := unittest.BootstrapFixture(participants) + seal.ServiceEvents = seal.ServiceEvents[:1] + _, err := flow.NewStateRoot(root, result, seal, 0) + require.Error(t, err) + }) +} + +func TestBootstrapInvalidEpochSetup(t *testing.T) { + t.Run("invalid final view", func(t *testing.T) { + root, result, seal := unittest.BootstrapFixture(participants) + setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + // set an invalid final view for the first epoch + setup.FinalView = root.Header.View + + _, err := flow.NewStateRoot(root, result, seal, 0) + require.Error(t, err) + }) + + t.Run("invalid cluster assignments", func(t *testing.T) { + root, result, seal := unittest.BootstrapFixture(participants) + setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + // create an invalid cluster assignment (node appears in multiple clusters) + collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] + setup.Assignments = append(setup.Assignments, []flow.Identifier{collector.NodeID}) + + _, err := flow.NewStateRoot(root, result, seal, 0) + require.Error(t, err) + }) + + t.Run("empty seed", func(t *testing.T) { + root, result, seal := unittest.BootstrapFixture(participants) + setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + setup.RandomSource = nil + + _, err := flow.NewStateRoot(root, result, seal, 0) + require.Error(t, err) + }) +} + +func TestBootstrapInvalidEpochCommit(t *testing.T) { + t.Run("inconsistent counter", func(t *testing.T) { + root, result, seal := unittest.BootstrapFixture(participants) + setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + // use a different counter for the commit + commit.Counter = setup.Counter + 1 + + _, err := flow.NewStateRoot(root, result, seal, 0) + require.Error(t, err) + }) + + t.Run("inconsistent cluster QCs", func(t *testing.T) { + root, result, seal := unittest.BootstrapFixture(participants) + commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + // add an extra QC to commit + commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) + + _, err := flow.NewStateRoot(root, result, seal, 0) + require.Error(t, err) + }) + + t.Run("missing dkg group key", func(t *testing.T) { + root, result, seal := unittest.BootstrapFixture(participants) + commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + commit.DKGGroupKey = nil + + _, err := flow.NewStateRoot(root, result, seal, 0) + require.Error(t, err) + }) + + t.Run("inconsistent DKG participants", func(t *testing.T) { + root, result, seal := unittest.BootstrapFixture(participants) + commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + // add an invalid DKG participant + collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] + commit.DKGParticipants[collector.NodeID] = flow.DKGParticipant{ + KeyShare: unittest.KeyFixture(crypto.BLSBLS12381).PublicKey(), + Index: 1, + } + + _, err := flow.NewStateRoot(root, result, seal, 0) + require.Error(t, err) + }) +} From ad968840dd524629108a242576bc667df1621249 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 2 Feb 2021 16:42:04 -0800 Subject: [PATCH 076/178] update protocol/mutator tests confirmed mutator_test.go passes at this commit (with rest of pkg commented) --- state/protocol/badger/mutator_test.go | 451 ++++++++++++-------------- 1 file changed, 211 insertions(+), 240 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index d90f90e3c98..83c1e097e76 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -19,7 +19,7 @@ import ( "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/model/flow/order" "github.com/onflow/flow-go/module/metrics" - mock2 "github.com/onflow/flow-go/module/mock" + mockmodule "github.com/onflow/flow-go/module/mock" "github.com/onflow/flow-go/module/trace" st "github.com/onflow/flow-go/state" protocol "github.com/onflow/flow-go/state/protocol/badger" @@ -119,21 +119,23 @@ func TestExtendValid(t *testing.T) { } func TestExtendSealedBoundary(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - root, rootSeal := stateRoot.Block(), stateRoot.Seal() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) finalCommit, err := state.Final().Commit() require.NoError(t, err) - require.Equal(t, stateRoot.Seal().FinalState, finalCommit, "original commit should be root commit") + require.Equal(t, seal.FinalState, finalCommit, "original commit should be root commit") - // Create a first block on top of root - block1 := unittest.BlockWithParentFixture(root.Header) - block1.SetPayload(flow.Payload{}) + // Create a first block on top of the snapshot + block1 := unittest.BlockWithParentFixture(head) + block1.SetPayload(flow.EmptyPayload()) err = state.Extend(&block1) require.NoError(t, err) - // Add a second block that contains a receipt committing to the first - // block + // Add a second block containing a receipt committing to the first block block1Receipt := unittest.ReceiptForBlockFixture(&block1) block2 := unittest.BlockWithParentFixture(block1.Header) block2.SetPayload(flow.Payload{ @@ -153,23 +155,21 @@ func TestExtendSealedBoundary(t *testing.T) { finalCommit, err = state.Final().Commit() require.NoError(t, err) - require.Equal(t, stateRoot.Seal().FinalState, finalCommit, "commit should not change before finalizing") - require.Equal(t, rootSeal.FinalState, finalCommit, "commit should not change before finalizing") + require.Equal(t, seal.FinalState, finalCommit, "commit should not change before finalizing") err = state.Finalize(block1.ID()) require.NoError(t, err) finalCommit, err = state.Final().Commit() require.NoError(t, err) - require.Equal(t, stateRoot.Seal().FinalState, finalCommit, "commit should not change after finalizing non-sealing block") - require.Equal(t, rootSeal.FinalState, finalCommit, "commit should not change after finalizing non-sealing block") + require.Equal(t, seal.FinalState, finalCommit, "commit should not change after finalizing non-sealing block") err = state.Finalize(block2.ID()) require.NoError(t, err) finalCommit, err = state.Final().Commit() require.NoError(t, err) - require.Equal(t, rootSeal.FinalState, finalCommit, "commit should not change after finalizing non-sealing block") + require.Equal(t, seal.FinalState, finalCommit, "commit should not change after finalizing non-sealing block") err = state.Finalize(block3.ID()) require.NoError(t, err) @@ -181,8 +181,8 @@ func TestExtendSealedBoundary(t *testing.T) { } func TestExtendMissingParent(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { extend := unittest.BlockFixture() extend.Payload.Guarantees = nil extend.Payload.Seals = nil @@ -204,17 +204,18 @@ func TestExtendMissingParent(t *testing.T) { } func TestExtendHeightTooSmall(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + extend := unittest.BlockFixture() - extend.Payload.Guarantees = nil - extend.Payload.Seals = nil + extend.SetPayload(flow.EmptyPayload()) extend.Header.Height = 1 extend.Header.View = 1 - extend.Header.ParentID = stateRoot.Block().Header.ID() - extend.Header.PayloadHash = extend.Payload.Hash() + extend.Header.ParentID = head.ID() - err := state.Extend(&extend) + err = state.Extend(&extend) require.NoError(t, err) // create another block with the same height and view, that is coming after @@ -234,37 +235,34 @@ func TestExtendHeightTooSmall(t *testing.T) { } func TestExtendHeightTooLarge(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := flow.NewStateRoot(root, result, seal, 0) - require.NoError(t, err) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { - root := unittest.GenesisFixture(participants) + head, err := rootSnapshot.Head() + require.NoError(t, err) - block := unittest.BlockWithParentFixture(root.Header) - block.SetPayload(flow.Payload{}) + block := unittest.BlockWithParentFixture(head) + block.SetPayload(flow.EmptyPayload()) // set an invalid height - block.Header.Height = root.Header.Height + 2 + block.Header.Height = head.Height + 2 - err := state.Extend(&block) + err = state.Extend(&block) require.Error(t, err) }) } func TestExtendBlockNotConnected(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + + head, err := rootSnapshot.Head() + require.NoError(t, err) // add 2 blocks, the second finalizing/sealing the state of the first - extend := unittest.BlockFixture() - extend.Payload.Guarantees = nil - extend.Payload.Seals = nil - extend.Header.Height = 1 - extend.Header.View = 1 - extend.Header.ParentID = stateRoot.Block().Header.ID() - extend.Header.PayloadHash = extend.Payload.Hash() + extend := unittest.BlockWithParentFixture(head) + extend.SetPayload(flow.EmptyPayload()) - err := state.Extend(&extend) + err = state.Extend(&extend) require.NoError(t, err) err = state.Finalize(extend.ID()) @@ -272,7 +270,7 @@ func TestExtendBlockNotConnected(t *testing.T) { // create a fork at view/height 1 and try to connect it to root extend.Header.Timestamp = extend.Header.Timestamp.Add(time.Second) - extend.Header.ParentID = stateRoot.Block().Header.ID() + extend.Header.ParentID = head.ID() err = state.Extend(&extend) require.Error(t, err) @@ -285,32 +283,18 @@ func TestExtendBlockNotConnected(t *testing.T) { }) } -func TestExtendWrongIdentity(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - extend := unittest.BlockFixture() - extend.Header.Height = 1 - extend.Header.View = 1 - extend.Header.ParentID = stateRoot.Block().ID() - extend.Header.PayloadHash = extend.Payload.Hash() - extend.Payload.Guarantees = nil - - err := state.Extend(&extend) - require.Error(t, err) - require.True(t, st.IsInvalidExtensionError(err), err) - }) -} - func TestExtendInvalidChainID(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - root := stateRoot.Block() - block := unittest.BlockWithParentFixture(root.Header) - block.SetPayload(flow.Payload{}) + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + + block := unittest.BlockWithParentFixture(head) + block.SetPayload(flow.EmptyPayload()) // use an invalid chain ID - block.Header.ChainID = root.Header.ChainID + "-invalid" + block.Header.ChainID = head.ChainID + "-invalid" - err := state.Extend(&block) + err = state.Extend(&block) require.Error(t, err) require.True(t, st.IsInvalidExtensionError(err), err) }) @@ -319,13 +303,14 @@ func TestExtendInvalidChainID(t *testing.T) { // Test that Extend will refuse payloads that contain duplicate receipts, where // duplicates can be in another block on the fork, or within the payload. func TestExtendReceiptsDuplicate(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - block1 := stateRoot.Block() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.SetPayload(flow.Payload{}) - err := state.Extend(&block2) + block2 := unittest.BlockWithParentFixture(head) + block2.SetPayload(flow.EmptyPayload()) + err = state.Extend(&block2) require.Nil(t, err) receipt := unittest.ReceiptForBlockFixture(&block2) @@ -369,15 +354,15 @@ func TestExtendReceiptsDuplicate(t *testing.T) { // are already sealed on the fork, but will accept receipts for blocks that are // sealed on another fork. func TestExtendReceiptsSealedBlock(t *testing.T) { - - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - block1 := stateRoot.Block() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) // create block2 - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.SetPayload(flow.Payload{}) - err := state.Extend(&block2) + block2 := unittest.BlockWithParentFixture(head) + block2.SetPayload(flow.EmptyPayload()) + err = state.Extend(&block2) require.Nil(t, err) block2Receipt := unittest.ReceiptForBlockFixture(&block2) @@ -437,13 +422,12 @@ func TestExtendReceiptsSealedBlock(t *testing.T) { // | // +----B4{R(B3)} func TestExtendReceiptsBlockNotOnFork(t *testing.T) { - stateRoot := fixtureStateRoot(t) - block1 := stateRoot.Block() - block1.Payload.Guarantees = nil - block1.Header.PayloadHash = block1.Payload.Hash() - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + head, err := rootSnapshot.Head() + require.NoError(t, err) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { // create block2 - block2 := unittest.BlockWithParentFixture(block1.Header) + block2 := unittest.BlockWithParentFixture(head) block2.Payload.Guarantees = nil block2.Header.PayloadHash = block2.Payload.Hash() err := state.Extend(&block2) @@ -451,7 +435,7 @@ func TestExtendReceiptsBlockNotOnFork(t *testing.T) { // create block3 block3 := unittest.BlockWithParentFixture(block2.Header) - block3.SetPayload(flow.Payload{}) + block3.SetPayload(flow.EmptyPayload()) err = state.Extend(&block3) require.Nil(t, err) @@ -474,13 +458,12 @@ func TestExtendReceiptsNotSorted(t *testing.T) { // a full ordering by height t.Skip() - stateRoot := fixtureStateRoot(t) - block1 := stateRoot.Block() - block1.Payload.Guarantees = nil - block1.Header.PayloadHash = block1.Payload.Hash() - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + head, err := rootSnapshot.Head() + require.NoError(t, err) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { // create block2 and block3 - block2 := unittest.BlockWithParentFixture(block1.Header) + block2 := unittest.BlockWithParentFixture(head) block2.Payload.Guarantees = nil block2.Header.PayloadHash = block2.Payload.Hash() err := state.Extend(&block2) @@ -507,17 +490,17 @@ func TestExtendReceiptsNotSorted(t *testing.T) { } func TestExtendReceiptsInvalid(t *testing.T) { - validator := &mock2.ReceiptValidator{} + validator := &mockmodule.ReceiptValidator{} - stateRoot := fixtureStateRoot(t) - stateRoot.Block().SetPayload(flow.EmptyPayload()) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - block1 := stateRoot.Block() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) // create block2 and block3 - block2 := unittest.BlockWithParentFixture(block1.Header) + block2 := unittest.BlockWithParentFixture(head) block2.SetPayload(flow.EmptyPayload()) - err := state.Extend(&block2) + err = state.Extend(&block2) require.Nil(t, err) // Add a receipt for block 2 @@ -537,12 +520,13 @@ func TestExtendReceiptsInvalid(t *testing.T) { } func TestExtendReceiptsValid(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - block1 := stateRoot.Block() - block2 := unittest.BlockWithParentFixture(block1.Header) + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + block2 := unittest.BlockWithParentFixture(head) block2.SetPayload(flow.EmptyPayload()) - err := state.Extend(&block2) + err = state.Extend(&block2) require.Nil(t, err) block3 := unittest.BlockWithParentFixture(block2.Header) @@ -578,18 +562,21 @@ func TestExtendEpochTransitionValid(t *testing.T) { // create a event consumer to test epoch transition events consumer := new(mockprotocol.Consumer) consumer.On("BlockFinalized", mock.Anything) - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolStateAndConsumer(t, stateRoot, consumer, func(db *badger.DB, state *protocol.MutableState) { - root, rootSeal := stateRoot.Block(), stateRoot.Seal() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolStateAndConsumer(t, rootSnapshot, consumer, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) // we should begin the epoch in the staking phase - phase, err := state.AtBlockID(root.ID()).Phase() + phase, err := state.AtBlockID(head.ID()).Phase() assert.Nil(t, err) require.Equal(t, flow.EpochPhaseStaking, phase) // add a block for the first seal to reference - block1 := unittest.BlockWithParentFixture(root.Header) - block1.SetPayload(flow.Payload{}) + block1 := unittest.BlockWithParentFixture(head) + block1.SetPayload(flow.EmptyPayload()) err = state.Extend(&block1) require.Nil(t, err) err = state.Finalize(block1.ID()) @@ -608,7 +595,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { err = state.Finalize(block2.ID()) require.Nil(t, err) - epoch1Setup := rootSeal.ServiceEvents[0].Event.(*flow.EpochSetup) + epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) epoch1FinalView := epoch1Setup.FinalView // add a participant for the next epoch @@ -725,7 +712,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { // block 5 has the final view of the epoch block5 := unittest.BlockWithParentFixture(block4.Header) - block5.SetPayload(flow.Payload{}) + block5.SetPayload(flow.EmptyPayload()) block5.Header.View = epoch1FinalView err = state.Extend(&block5) @@ -738,7 +725,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { // block 6 has a view > final view of epoch 1, it will be considered the first block of epoch 2 block6 := unittest.BlockWithParentFixture(block5.Header) - block6.SetPayload(flow.Payload{}) + block6.SetPayload(flow.EmptyPayload()) // we should handle view that aren't exactly the first valid view of the epoch block6.Header.View = epoch1FinalView + uint64(1+rand.Intn(10)) @@ -774,18 +761,21 @@ func TestExtendEpochTransitionValid(t *testing.T) { // \-->BLOCK2-->BLOCK4-->BLOCK6 // func TestExtendConflictingEpochEvents(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - root, rootSeal := stateRoot.Block(), stateRoot.Seal() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) // add two conflicting blocks for each service event to reference - block1 := unittest.BlockWithParentFixture(root.Header) - block1.SetPayload(flow.Payload{}) - err := state.Extend(&block1) + block1 := unittest.BlockWithParentFixture(head) + block1.SetPayload(flow.EmptyPayload()) + err = state.Extend(&block1) require.Nil(t, err) - block2 := unittest.BlockWithParentFixture(root.Header) - block2.SetPayload(flow.Payload{}) + block2 := unittest.BlockWithParentFixture(head) + block2.SetPayload(flow.EmptyPayload()) err = state.Extend(&block2) require.Nil(t, err) @@ -807,7 +797,7 @@ func TestExtendConflictingEpochEvents(t *testing.T) { err = state.Extend(&block4) require.Nil(t, err) - rootSetup := rootSeal.ServiceEvents[0].Event.(*flow.EpochSetup) + rootSetup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) // create two conflicting epoch setup events for the next epoch (final view differs) nextEpochSetup1 := unittest.EpochSetupFixture( @@ -862,18 +852,22 @@ func TestExtendConflictingEpochEvents(t *testing.T) { // extending protocol state with an invalid epoch setup service event should cause an error func TestExtendEpochSetupInvalid(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - root, rootSeal := stateRoot.Block(), stateRoot.Seal() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) + // add a block for the first seal to reference - block1 := unittest.BlockWithParentFixture(root.Header) - block1.SetPayload(flow.Payload{}) - err := state.Extend(&block1) + block1 := unittest.BlockWithParentFixture(head) + block1.SetPayload(flow.EmptyPayload()) + err = state.Extend(&block1) require.Nil(t, err) err = state.Finalize(block1.ID()) require.Nil(t, err) - epoch1Setup := rootSeal.ServiceEvents[0].Event.(*flow.EpochSetup) + epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) // add a participant for the next epoch epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) @@ -939,14 +933,17 @@ func TestExtendEpochSetupInvalid(t *testing.T) { // extending protocol state with an invalid epoch commit service event should cause an error func TestExtendEpochCommitInvalid(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - root, rootSeal := stateRoot.Block(), stateRoot.Seal() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) // add a block for the first seal to reference - block1 := unittest.BlockWithParentFixture(root.Header) - block1.SetPayload(flow.Payload{}) - err := state.Extend(&block1) + block1 := unittest.BlockWithParentFixture(head) + block1.SetPayload(flow.EmptyPayload()) + err = state.Extend(&block1) require.Nil(t, err) err = state.Finalize(block1.ID()) require.Nil(t, err) @@ -962,7 +959,7 @@ func TestExtendEpochCommitInvalid(t *testing.T) { err = state.Finalize(block2.ID()) require.Nil(t, err) - epoch1Setup := rootSeal.ServiceEvents[0].Event.(*flow.EpochSetup) + epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) // swap consensus node for a new one for epoch 2 epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleConsensus)) @@ -1082,14 +1079,17 @@ func TestExtendEpochCommitInvalid(t *testing.T) { // if we reach the first block of the next epoch before both setup and commit // service events are finalized, the chain should halt func TestExtendEpochTransitionWithoutCommit(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.MutableState) { - root, rootSeal := stateRoot.Block(), stateRoot.Seal() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) // add a block for the first seal to reference - block1 := unittest.BlockWithParentFixture(root.Header) - block1.SetPayload(flow.Payload{}) - err := state.Extend(&block1) + block1 := unittest.BlockWithParentFixture(head) + block1.SetPayload(flow.EmptyPayload()) + err = state.Extend(&block1) require.Nil(t, err) err = state.Finalize(block1.ID()) require.Nil(t, err) @@ -1105,7 +1105,7 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { err = state.Finalize(block2.ID()) require.Nil(t, err) - epoch1Setup := rootSeal.ServiceEvents[0].Event.(*flow.EpochSetup) + epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) epoch1FinalView := epoch1Setup.FinalView // add a participant for the next epoch @@ -1148,21 +1148,22 @@ func TestExtendInvalidSealsInBlock(t *testing.T) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { metrics := metrics.NewNoopCollector() tracer := trace.NewNoopTracer() - headers, _, seals, index, payloads, blocks, setups, commits, statuses, _ := storeutil.StorageLayer(t, db) + headers, _, seals, index, payloads, blocks, setups, commits, statuses, results := storeutil.StorageLayer(t, db) // create a event consumer to test epoch transition events distributor := events.NewDistributor() consumer := new(mockprotocol.Consumer) distributor.AddConsumer(consumer) - block, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := protocol.NewStateRoot(block, result, seal, 0) + rootSnapshot := unittest.RootSnapshotFixture(participants) + + state, err := protocol.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) require.NoError(t, err) - state, err := protocol.Bootstrap(metrics, db, headers, seals, blocks, setups, commits, statuses, stateRoot) + head, err := rootSnapshot.Head() require.NoError(t, err) - block1 := unittest.BlockWithParentFixture(block.Header) + block1 := unittest.BlockWithParentFixture(head) block1.Payload.Guarantees = nil block1.Header.PayloadHash = block1.Payload.Hash() @@ -1178,7 +1179,7 @@ func TestExtendInvalidSealsInBlock(t *testing.T) { Seals: []*flow.Seal{block1Seal}, }) - sealValidator := &mock2.SealValidator{} + sealValidator := &mockmodule.SealValidator{} sealValidator.On("Validate", mock.Anything). Return(func(candidate *flow.Block) *flow.Seal { if candidate.ID() == block3.ID() { @@ -1212,15 +1213,17 @@ func TestExtendInvalidSealsInBlock(t *testing.T) { } func TestHeaderExtendValid(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.FollowerState) { - block, seal := stateRoot.Block(), stateRoot.Seal() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) - extend := unittest.BlockWithParentFixture(block.Header) - extend.Payload.Guarantees = nil - extend.Header.PayloadHash = extend.Payload.Hash() + extend := unittest.BlockWithParentFixture(head) + extend.SetPayload(flow.EmptyPayload()) - err := state.Extend(&extend) + err = state.Extend(&extend) require.NoError(t, err) finalCommit, err := state.Final().Commit() @@ -1230,8 +1233,8 @@ func TestHeaderExtendValid(t *testing.T) { } func TestHeaderExtendMissingParent(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.FollowerState) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { extend := unittest.BlockFixture() extend.Payload.Guarantees = nil extend.Payload.Seals = nil @@ -1253,111 +1256,93 @@ func TestHeaderExtendMissingParent(t *testing.T) { } func TestHeaderExtendHeightTooSmall(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.FollowerState) { - block := stateRoot.Block() - - extend := unittest.BlockFixture() - extend.Payload.Guarantees = nil - extend.Payload.Seals = nil - extend.Header.Height = 1 - extend.Header.View = 1 - extend.Header.ParentID = block.Header.ID() - extend.Header.PayloadHash = extend.Payload.Hash() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) - err := state.Extend(&extend) + block1 := unittest.BlockWithParentFixture(head) + err = state.Extend(&block1) require.NoError(t, err) // create another block that points to the previous block `extend` as parent // but has _same_ height as parent. This violates the condition that a child's // height must increment the parent's height by one, i.e. it should be rejected // by the follower right away - extend.Header.ParentID = extend.Header.ID() - extend.Header.Height = 1 - extend.Header.View = 2 + block2 := unittest.BlockWithParentFixture(block1.Header) + block2.Header.Height = block1.Header.Height - err = state.Extend(&extend) + err = state.Extend(&block2) require.Error(t, err) // verify seal not indexed var sealID flow.Identifier - err = db.View(operation.LookupBlockSeal(extend.ID(), &sealID)) + err = db.View(operation.LookupBlockSeal(block2.ID(), &sealID)) require.Error(t, err) require.True(t, errors.Is(err, stoerr.ErrNotFound), err) }) } func TestHeaderExtendHeightTooLarge(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.FollowerState) { - root := stateRoot.Block() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) - block := unittest.BlockWithParentFixture(root.Header) - block.SetPayload(flow.Payload{}) + block := unittest.BlockWithParentFixture(head) + block.SetPayload(flow.EmptyPayload()) // set an invalid height - block.Header.Height = root.Header.Height + 2 + block.Header.Height = head.Height + 2 - err := state.Extend(&block) + err = state.Extend(&block) require.Error(t, err) }) } func TestHeaderExtendBlockNotConnected(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.FollowerState) { - block := stateRoot.Block() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) // add 2 blocks, where: // first block is added and then finalized; // second block is a sibling to the finalized block // The Follower should reject this block as an outdated chain extension - extend := unittest.BlockFixture() - extend.Payload.Guarantees = nil - extend.Payload.Seals = nil - extend.Header.Height = 1 - extend.Header.View = 1 - extend.Header.ParentID = block.Header.ID() - extend.Header.PayloadHash = extend.Payload.Hash() - - err := state.Extend(&extend) + block1 := unittest.BlockWithParentFixture(head) + err = state.Extend(&block1) require.NoError(t, err) - err = state.Finalize(extend.ID()) + err = state.Finalize(block1.ID()) require.NoError(t, err) // create a fork at view/height 1 and try to connect it to root - extend.Header.Timestamp = extend.Header.Timestamp.Add(time.Second) - extend.Header.ParentID = block.Header.ID() - - err = state.Extend(&extend) + block2 := unittest.BlockWithParentFixture(head) + err = state.Extend(&block2) require.Error(t, err) require.True(t, st.IsOutdatedExtensionError(err), err) // verify seal not indexed var sealID flow.Identifier - err = db.View(operation.LookupBlockSeal(extend.ID(), &sealID)) + err = db.View(operation.LookupBlockSeal(block2.ID(), &sealID)) require.Error(t, err) require.True(t, errors.Is(err, stoerr.ErrNotFound), err) }) } func TestHeaderExtendHighestSeal(t *testing.T) { - stateRoot := fixtureStateRoot(t) - block1 := stateRoot.Block() - // bootstrap the root block - block1.Payload.Guarantees = nil - block1.Header.PayloadHash = block1.Payload.Hash() - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.FollowerState) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + head, err := rootSnapshot.Head() + require.NoError(t, err) + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { // create block2 and block3 - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.Payload.Guarantees = nil - block2.Header.PayloadHash = block2.Payload.Hash() + block2 := unittest.BlockWithParentFixture(head) + block2.SetPayload(flow.EmptyPayload()) err := state.Extend(&block2) require.Nil(t, err) block3 := unittest.BlockWithParentFixture(block2.Header) - block3.Payload.Guarantees = nil - block3.Header.PayloadHash = block3.Payload.Hash() + block3.SetPayload(flow.EmptyPayload()) err = state.Extend(&block3) require.Nil(t, err) @@ -1371,13 +1356,12 @@ func TestHeaderExtendHighestSeal(t *testing.T) { // include the seals in block4 block4 := unittest.BlockWithParentFixture(block3.Header) - block4.Payload.Guarantees = nil block4.SetPayload(flow.Payload{ // placing seals in the reversed order to test // Extend will pick the highest sealed block - Seals: []*flow.Seal{seal3, seal2}, + Seals: []*flow.Seal{seal3, seal2}, + Guarantees: nil, }) - block4.Header.PayloadHash = block4.Payload.Hash() err = state.Extend(&block4) require.Nil(t, err) @@ -1390,21 +1374,18 @@ func TestHeaderExtendHighestSeal(t *testing.T) { func TestMakeValid(t *testing.T) { t.Run("should trigger BlockProcessable with parent block", func(t *testing.T) { consumer := &mockprotocol.Consumer{} - stateRoot := fixtureStateRoot(t) - block1 := stateRoot.Block() - block1.Payload.Guarantees = nil - block1.Header.PayloadHash = block1.Payload.Hash() - util.RunWithFullProtocolStateAndConsumer(t, stateRoot, consumer, func(db *badger.DB, state *protocol.MutableState) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + head, err := rootSnapshot.Head() + require.NoError(t, err) + util.RunWithFullProtocolStateAndConsumer(t, rootSnapshot, consumer, func(db *badger.DB, state *protocol.MutableState) { // create block2 and block3 - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.Payload.Guarantees = nil - block2.Header.PayloadHash = block2.Payload.Hash() + block2 := unittest.BlockWithParentFixture(head) + block2.SetPayload(flow.EmptyPayload()) err := state.Extend(&block2) require.Nil(t, err) block3 := unittest.BlockWithParentFixture(block2.Header) - block3.Payload.Guarantees = nil - block3.Header.PayloadHash = block3.Payload.Hash() + block3.SetPayload(flow.EmptyPayload()) err = state.Extend(&block3) require.Nil(t, err) @@ -1430,12 +1411,13 @@ func TestMakeValid(t *testing.T) { // If block A is finalized and contains a seal to block B, then B is the last sealed block func TestSealed(t *testing.T) { - stateRoot := fixtureStateRoot(t) - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *protocol.FollowerState) { - genesis := stateRoot.Block() + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { + head, err := rootSnapshot.Head() + require.NoError(t, err) // A <- B <- C <- D <- E <- F <- G - blockA := unittest.BlockWithParentAndSeal(genesis.Header, nil) + blockA := unittest.BlockWithParentAndSeal(head, nil) blockB := unittest.BlockWithParentAndSeal(blockA.Header, nil) blockC := unittest.BlockWithParentAndSeal(blockB.Header, blockA.Header) blockD := unittest.BlockWithParentAndSeal(blockC.Header, blockB.Header) @@ -1471,14 +1453,3 @@ func saveBlock(t *testing.T, block *flow.Block, finalizes *flow.Block, state *pr err = state.MarkValid(block.Header.ID()) require.NoError(t, err) } - -func fixtureStateRoot(t *testing.T) *flow.StateRoot { - return fixtureStateRootWithParticipants(t, participants) -} - -func fixtureStateRootWithParticipants(t *testing.T, participants flow.IdentityList) *flow.StateRoot { - root, result, seal := unittest.BootstrapFixture(participants) - stateRoot, err := flow.NewStateRoot(root, result, seal, 0) - require.NoError(t, err) - return stateRoot -} From 92d4b5912e6eb960ab1aca6b70b3a0cbd72f5d5e Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 2 Feb 2021 17:18:12 -0800 Subject: [PATCH 077/178] update validity tests pt 1 --- state/protocol/badger/validity_test.go | 95 ++++++++++++-------------- state/protocol/inmem/epoch.go | 2 +- 2 files changed, 45 insertions(+), 52 deletions(-) diff --git a/state/protocol/badger/validity_test.go b/state/protocol/badger/validity_test.go index 1311acbeb30..1db7fc9ebb1 100644 --- a/state/protocol/badger/validity_test.go +++ b/state/protocol/badger/validity_test.go @@ -1,29 +1,49 @@ package badger import ( + "fmt" + "os" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" + "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/storage/util" "github.com/onflow/flow-go/utils/unittest" ) var participants = unittest.IdentityListFixture(5, unittest.WithAllRoles()) +// bootstraps protocol state with the given snapshot and invokes the callback +// with the result of the constructor +func bootstrap(t *testing.T, rootSnapshot protocol.Snapshot, f func(*State, error)) { + metrics := metrics.NewNoopCollector() + dir := unittest.TempDir(t) + defer os.RemoveAll(dir) + db := unittest.BadgerDB(t, dir) + defer db.Close() + headers, _, seals, _, _, blocks, setups, commits, statuses, results := util.StorageLayer(t, db) + state, err := Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) + f(state, err) +} + func TestBootstrapDuplicateID(t *testing.T) { participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, // dupe } root := unittest.RootSnapshotFixture(participants) - err := validRootSnapshot(root) - require.Error(t, err) + bootstrap(t, root, func(state *State, err error) { + assert.Error(t, err) + }) } func TestBootstrapZeroStake(t *testing.T) { @@ -34,69 +54,42 @@ func TestBootstrapZeroStake(t *testing.T) { {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, } root := unittest.RootSnapshotFixture(participants) - err := validRootSnapshot(root) - require.Error(t, err) -} - -func TestBootstrapNoCollection(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - - root := unittest.RootSnapshotFixture(participants) - err := validRootSnapshot(root) - require.Error(t, err) -} - -func TestBootstrapNoConsensus(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - - root := unittest.RootSnapshotFixture(participants) - err := validRootSnapshot(root) - require.Error(t, err) + bootstrap(t, root, func(state *State, err error) { + assert.Error(t, err) + }) } -func TestBootstrapNoExecution(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, +func TestBootstrapMissingRole(t *testing.T) { + requiredRoles := []flow.Role{ + flow.RoleConsensus, + flow.RoleCollection, + flow.RoleExecution, + flow.RoleVerification, } - root := unittest.RootSnapshotFixture(participants) - err := validRootSnapshot(root) - require.Error(t, err) -} - -func TestBootstrapNoVerification(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + for _, role := range requiredRoles { + t.Run(fmt.Sprintf("no %s nodes", role.String()), func(t *testing.T) { + participants := unittest.IdentityListFixture(5, unittest.WithAllRolesExcept(role)) + root := unittest.RootSnapshotFixture(participants) + bootstrap(t, root, func(state *State, err error) { + assert.Error(t, err) + }) + }) } - - root := unittest.RootSnapshotFixture(participants) - err := validRootSnapshot(root) - require.Error(t, err) } func TestBootstrapExistingAddress(t *testing.T) { participants := flow.IdentityList{ {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x02}, Address: "a1", Role: flow.RoleConsensus, Stake: 2}, + {NodeID: flow.Identifier{0x02}, Address: "a1", Role: flow.RoleConsensus, Stake: 2}, // dupe address {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, } root := unittest.RootSnapshotFixture(participants) - err := validRootSnapshot(root) - require.Error(t, err) + bootstrap(t, root, func(state *State, err error) { + assert.Error(t, err) + }) } // TODO re-work following tests - need to be different now that we can bootstrap diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index e085e4403f6..8431c8b10fa 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -103,7 +103,7 @@ func (es *setupEpoch) InitialIdentities() (flow.IdentityList, error) { } func (es *setupEpoch) Clustering() (flow.ClusterList, error) { - collectorFilter := filter.And(filter.HasStake(true), filter.HasRole(flow.RoleCollection)) + collectorFilter := filter.HasRole(flow.RoleCollection) clustering, err := flow.NewClusterList(es.setupEvent.Assignments, es.setupEvent.Participants.Filter(collectorFilter)) if err != nil { return nil, fmt.Errorf("failed to generate ClusterList from collector identities: %w", err) From 6188b0a9b4777d01ca874dbe97a0fabf0f59e28b Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 3 Feb 2021 17:07:50 -0800 Subject: [PATCH 078/178] update validity test --- state/protocol/badger/validity.go | 5 + state/protocol/badger/validity_test.go | 153 ++++++++++--------------- 2 files changed, 63 insertions(+), 95 deletions(-) diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index ec92cd47980..ddfc7f7c127 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -69,6 +69,11 @@ func validSetup(setup *flow.EpochSetup) error { return fmt.Errorf("need at least one verification node") } + // first view must be before final view + if setup.FirstView >= setup.FinalView { + return fmt.Errorf("first view (%d) must be before final view (%d)", setup.FirstView, setup.FinalView) + } + // we need at least one collection cluster if len(setup.Assignments) == 0 { return fmt.Errorf("need at least one collection cluster") diff --git a/state/protocol/badger/validity_test.go b/state/protocol/badger/validity_test.go index 1db7fc9ebb1..4bf72bbc6e2 100644 --- a/state/protocol/badger/validity_test.go +++ b/state/protocol/badger/validity_test.go @@ -5,10 +5,10 @@ import ( "os" "testing" + "github.com/onflow/flow-go-sdk/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/module/metrics" @@ -92,118 +92,81 @@ func TestBootstrapExistingAddress(t *testing.T) { }) } -// TODO re-work following tests - need to be different now that we can bootstrap -// from any snapshot -func TestBootstrapWithSeal(t *testing.T) { - block := unittest.GenesisFixture(participants) - block.Payload.Seals = []*flow.Seal{unittest.Seal.Fixture()} - block.Header.PayloadHash = block.Payload.Hash() - - result := unittest.ExecutionResultFixture() - result.BlockID = block.ID() - - finalState, ok := result.FinalStateCommitment() - require.True(t, ok) - - seal := unittest.Seal.Fixture() - seal.BlockID = block.ID() - seal.ResultID = result.ID() - seal.FinalState = finalState - - _, err := flow.NewStateRoot(block, result, seal, 0) - require.Error(t, err) -} - -func TestBootstrapMissingServiceEvents(t *testing.T) { - t.Run("missing setup", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - seal.ServiceEvents = seal.ServiceEvents[1:] - _, err := flow.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("missing commit", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - seal.ServiceEvents = seal.ServiceEvents[:1] - _, err := flow.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) -} - -func TestBootstrapInvalidEpochSetup(t *testing.T) { - t.Run("invalid final view", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) +func TestEpochSetupValidity(t *testing.T) { + t.Run("invalid first/final view", func(t *testing.T) { + _, _, seal := unittest.BootstrapFixture(participants) setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) // set an invalid final view for the first epoch - setup.FinalView = root.Header.View + setup.FinalView = setup.FirstView - _, err := flow.NewStateRoot(root, result, seal, 0) + err := validSetup(setup) require.Error(t, err) }) t.Run("invalid cluster assignments", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) + _, _, seal := unittest.BootstrapFixture(participants) setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) // create an invalid cluster assignment (node appears in multiple clusters) collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] setup.Assignments = append(setup.Assignments, []flow.Identifier{collector.NodeID}) - _, err := flow.NewStateRoot(root, result, seal, 0) + err := validSetup(setup) require.Error(t, err) }) - t.Run("empty seed", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) + t.Run("short seed", func(t *testing.T) { + _, _, seal := unittest.BootstrapFixture(participants) setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - setup.RandomSource = nil + setup.RandomSource = unittest.SeedFixture(crypto.MinSeedLength - 1) - _, err := flow.NewStateRoot(root, result, seal, 0) + err := validSetup(setup) require.Error(t, err) }) } -func TestBootstrapInvalidEpochCommit(t *testing.T) { - t.Run("inconsistent counter", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) - // use a different counter for the commit - commit.Counter = setup.Counter + 1 - - _, err := flow.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("inconsistent cluster QCs", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) - // add an extra QC to commit - commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) - - _, err := flow.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("missing dkg group key", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) - commit.DKGGroupKey = nil - - _, err := flow.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) - - t.Run("inconsistent DKG participants", func(t *testing.T) { - root, result, seal := unittest.BootstrapFixture(participants) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) - // add an invalid DKG participant - collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] - commit.DKGParticipants[collector.NodeID] = flow.DKGParticipant{ - KeyShare: unittest.KeyFixture(crypto.BLSBLS12381).PublicKey(), - Index: 1, - } - - _, err := flow.NewStateRoot(root, result, seal, 0) - require.Error(t, err) - }) -} +// +//func TestBootstrapInvalidEpochCommit(t *testing.T) { +// t.Run("inconsistent counter", func(t *testing.T) { +// root, result, seal := unittest.BootstrapFixture(participants) +// setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) +// commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) +// // use a different counter for the commit +// commit.Counter = setup.Counter + 1 +// +// _, err := flow.NewStateRoot(root, result, seal, 0) +// require.Error(t, err) +// }) +// +// t.Run("inconsistent cluster QCs", func(t *testing.T) { +// root, result, seal := unittest.BootstrapFixture(participants) +// commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) +// // add an extra QC to commit +// commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) +// +// _, err := flow.NewStateRoot(root, result, seal, 0) +// require.Error(t, err) +// }) +// +// t.Run("missing dkg group key", func(t *testing.T) { +// root, result, seal := unittest.BootstrapFixture(participants) +// commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) +// commit.DKGGroupKey = nil +// +// _, err := flow.NewStateRoot(root, result, seal, 0) +// require.Error(t, err) +// }) +// +// t.Run("inconsistent DKG participants", func(t *testing.T) { +// root, result, seal := unittest.BootstrapFixture(participants) +// commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) +// // add an invalid DKG participant +// collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] +// commit.DKGParticipants[collector.NodeID] = flow.DKGParticipant{ +// KeyShare: unittest.KeyFixture(crypto.BLSBLS12381).PublicKey(), +// Index: 1, +// } +// +// _, err := flow.NewStateRoot(root, result, seal, 0) +// require.Error(t, err) +// }) +//} From 638e8d036f022c8b6449518394276af18c7a9f6c Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 3 Feb 2021 17:27:19 -0800 Subject: [PATCH 079/178] update validity tests --- state/protocol/badger/validity.go | 11 ++- state/protocol/badger/validity_test.go | 98 +++++++++++++------------- 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index ddfc7f7c127..97f8d718cb2 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -3,7 +3,7 @@ package badger import ( "fmt" - "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/state/protocol" @@ -12,8 +12,9 @@ import ( func validSetup(setup *flow.EpochSetup) error { // STEP 1: general sanity checks // the seed needs to be at least minimum length - if len(setup.RandomSource) < crypto.MinSeedLength { - return fmt.Errorf("seed has insufficient length (%d < %d)", len(setup.RandomSource), crypto.MinSeedLength) + // TODO: what is the appropriate minimum length here? Previously used sdk/crypto.SeedMinLength + if len(setup.RandomSource) < crypto.SeedMinLenDKG { + return fmt.Errorf("seed has insufficient length (%d < %d)", len(setup.RandomSource), crypto.SeedMinLenDKG) } // STEP 2: sanity checks of all nodes listed as participants @@ -94,6 +95,10 @@ func validCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error { return fmt.Errorf("number of clusters (%d) does not number of QCs (%d)", len(setup.Assignments), len(commit.ClusterQCs)) } + if commit.Counter != setup.Counter { + return fmt.Errorf("inconsistent epoch counter between commit (%d) and setup (%d) events in same epoch", commit.Counter, setup.Counter) + } + // make sure we have a valid DKG public key if commit.DKGGroupKey == nil { return fmt.Errorf("missing DKG public group key") diff --git a/state/protocol/badger/validity_test.go b/state/protocol/badger/validity_test.go index 4bf72bbc6e2..1787ca0b004 100644 --- a/state/protocol/badger/validity_test.go +++ b/state/protocol/badger/validity_test.go @@ -5,10 +5,10 @@ import ( "os" "testing" - "github.com/onflow/flow-go-sdk/crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/module/metrics" @@ -117,56 +117,58 @@ func TestEpochSetupValidity(t *testing.T) { t.Run("short seed", func(t *testing.T) { _, _, seal := unittest.BootstrapFixture(participants) setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - setup.RandomSource = unittest.SeedFixture(crypto.MinSeedLength - 1) + setup.RandomSource = unittest.SeedFixture(crypto.SeedMinLenDKG - 1) err := validSetup(setup) require.Error(t, err) }) } -// -//func TestBootstrapInvalidEpochCommit(t *testing.T) { -// t.Run("inconsistent counter", func(t *testing.T) { -// root, result, seal := unittest.BootstrapFixture(participants) -// setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) -// commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) -// // use a different counter for the commit -// commit.Counter = setup.Counter + 1 -// -// _, err := flow.NewStateRoot(root, result, seal, 0) -// require.Error(t, err) -// }) -// -// t.Run("inconsistent cluster QCs", func(t *testing.T) { -// root, result, seal := unittest.BootstrapFixture(participants) -// commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) -// // add an extra QC to commit -// commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) -// -// _, err := flow.NewStateRoot(root, result, seal, 0) -// require.Error(t, err) -// }) -// -// t.Run("missing dkg group key", func(t *testing.T) { -// root, result, seal := unittest.BootstrapFixture(participants) -// commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) -// commit.DKGGroupKey = nil -// -// _, err := flow.NewStateRoot(root, result, seal, 0) -// require.Error(t, err) -// }) -// -// t.Run("inconsistent DKG participants", func(t *testing.T) { -// root, result, seal := unittest.BootstrapFixture(participants) -// commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) -// // add an invalid DKG participant -// collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] -// commit.DKGParticipants[collector.NodeID] = flow.DKGParticipant{ -// KeyShare: unittest.KeyFixture(crypto.BLSBLS12381).PublicKey(), -// Index: 1, -// } -// -// _, err := flow.NewStateRoot(root, result, seal, 0) -// require.Error(t, err) -// }) -//} +func TestBootstrapInvalidEpochCommit(t *testing.T) { + t.Run("inconsistent counter", func(t *testing.T) { + _, _, seal := unittest.BootstrapFixture(participants) + setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + // use a different counter for the commit + commit.Counter = setup.Counter + 1 + + err := validCommit(commit, setup) + require.Error(t, err) + }) + + t.Run("inconsistent cluster QCs", func(t *testing.T) { + _, _, seal := unittest.BootstrapFixture(participants) + setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + // add an extra QC to commit + commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) + + err := validCommit(commit, setup) + require.Error(t, err) + }) + + t.Run("missing dkg group key", func(t *testing.T) { + _, _, seal := unittest.BootstrapFixture(participants) + setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + commit.DKGGroupKey = nil + + err := validCommit(commit, setup) + require.Error(t, err) + }) + + t.Run("inconsistent DKG participants", func(t *testing.T) { + _, _, seal := unittest.BootstrapFixture(participants) + setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + // add an invalid DKG participant + collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] + commit.DKGParticipants[collector.NodeID] = flow.DKGParticipant{ + KeyShare: unittest.KeyFixture(crypto.BLSBLS12381).PublicKey(), + Index: 1, + } + + err := validCommit(commit, setup) + require.Error(t, err) + }) +} From 284eaac9d90f8fa0e9974acf4cc89736cdfe89b0 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 3 Feb 2021 18:31:12 -0800 Subject: [PATCH 080/178] update snapshot tests qc test outstanding -- need to store root qc --- state/protocol/badger/snapshot.go | 1 - state/protocol/badger/snapshot_test.go | 87 +++++++++++++++----------- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index 446916b429c..bc0038c0734 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -257,7 +257,6 @@ func (s *Snapshot) LatestSeal() (*flow.Seal, error) { return seal, nil } -// TODO inject results storage func (s *Snapshot) LatestResult() (*flow.ExecutionResult, error) { seal, err := s.LatestSeal() if err != nil { diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index 1c9775f6f5f..b63533d042f 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -16,6 +16,7 @@ import ( "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/state/protocol" bprotocol "github.com/onflow/flow-go/state/protocol/badger" + "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/state/protocol/util" "github.com/onflow/flow-go/utils/unittest" ) @@ -25,35 +26,36 @@ func init() { } func TestHead(t *testing.T) { - identities := unittest.IdentityListFixture(5, unittest.WithAllRoles()) - stateRoot := fixtureStateRootWithParticipants(t, identities) - util.RunWithBootstrapState(t, stateRoot, func(db *badger.DB, state *bprotocol.State) { - header := stateRoot.Block().Header + participants := unittest.IdentityListFixture(5, unittest.WithAllRoles()) + rootSnapshot := unittest.RootSnapshotFixture(participants) + head, err := rootSnapshot.Head() + require.NoError(t, err) + util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { t.Run("works with block number", func(t *testing.T) { - retrieved, err := state.AtHeight(header.Height).Head() + retrieved, err := state.AtHeight(head.Height).Head() require.NoError(t, err) - require.Equal(t, header.ID(), retrieved.ID()) + require.Equal(t, head.ID(), retrieved.ID()) }) t.Run("works with block id", func(t *testing.T) { - retrieved, err := state.AtBlockID(header.ID()).Head() + retrieved, err := state.AtBlockID(head.ID()).Head() require.NoError(t, err) - require.Equal(t, header.ID(), retrieved.ID()) + require.Equal(t, head.ID(), retrieved.ID()) }) t.Run("works with finalized block", func(t *testing.T) { retrieved, err := state.Final().Head() require.NoError(t, err) - require.Equal(t, header.ID(), retrieved.ID()) + require.Equal(t, head.ID(), retrieved.ID()) }) }) } func TestIdentities(t *testing.T) { identities := unittest.IdentityListFixture(5, unittest.WithAllRoles()) - stateRoot := fixtureStateRootWithParticipants(t, identities) - util.RunWithBootstrapState(t, stateRoot, func(db *badger.DB, state *bprotocol.State) { + rootSnapshot := unittest.RootSnapshotFixture(identities) + util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { t.Run("no filter", func(t *testing.T) { actual, err := state.Final().Identities(filter.Any) @@ -92,9 +94,7 @@ func TestClusters(t *testing.T) { collectors := unittest.IdentityListFixture(nCollectors, unittest.WithRole(flow.RoleCollection)) identities := append(unittest.IdentityListFixture(4, unittest.WithAllRolesExcept(flow.RoleCollection)), collectors...) - stateRoot := fixtureStateRootWithParticipants(t, identities) - seal := stateRoot.Seal() - + root, result, seal := unittest.BootstrapFixture(identities) setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) setup.Assignments = unittest.ClusterAssignment(uint(nClusters), collectors) @@ -103,7 +103,10 @@ func TestClusters(t *testing.T) { commit.ClusterQCs[i] = unittest.QuorumCertificateFixture() } - util.RunWithBootstrapState(t, stateRoot, func(db *badger.DB, state *bprotocol.State) { + rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, unittest.QuorumCertificateFixture()) + require.NoError(t, err) + + util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { expectedClusters, err := flow.NewClusterList(setup.Assignments, collectors) require.NoError(t, err) actualClusters, err := state.Final().Epochs().Current().Clustering() @@ -125,14 +128,16 @@ func TestClusters(t *testing.T) { // test retrieving quorum certificate and seed func TestQuorumCertificate(t *testing.T) { identities := unittest.IdentityListFixture(5, unittest.WithAllRoles()) - stateRoot := fixtureStateRootWithParticipants(t, identities) + rootSnapshot := unittest.RootSnapshotFixture(identities) + head, err := rootSnapshot.Head() + require.NoError(t, err) // should not be able to get QC or random beacon seed from a block with no children t.Run("no children", func(t *testing.T) { - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *bprotocol.FollowerState) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { // create a block to query - block1 := unittest.BlockWithParentFixture(stateRoot.Block().Header) + block1 := unittest.BlockWithParentFixture(head) block1.SetPayload(flow.EmptyPayload()) err := state.Extend(&block1) require.Nil(t, err) @@ -148,16 +153,16 @@ func TestQuorumCertificate(t *testing.T) { // should not be able to get random beacon seed from a block with only invalid // or unvalidated children t.Run("un-validated child", func(t *testing.T) { - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *bprotocol.FollowerState) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { // create a block to query - block1 := unittest.BlockWithParentFixture(stateRoot.Block().Header) + block1 := unittest.BlockWithParentFixture(head) block1.SetPayload(flow.EmptyPayload()) err := state.Extend(&block1) require.Nil(t, err) // add child - unvalidatedChild := unittest.BlockWithParentFixture(stateRoot.Block().Header) + unvalidatedChild := unittest.BlockWithParentFixture(head) unvalidatedChild.SetPayload(flow.EmptyPayload()) err = state.Extend(&unvalidatedChild) assert.Nil(t, err) @@ -172,15 +177,21 @@ func TestQuorumCertificate(t *testing.T) { // should be able to get QC and random beacon seed from root block t.Run("root block", func(t *testing.T) { - // TODO + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + // since we bootstrap with a root snapshot, this will be the root block + _, err := state.AtBlockID(head.ID()).QuorumCertificate() + assert.NoError(t, err) + _, err = state.AtBlockID(head.ID()).Seed(1, 2, 3, 4) + assert.NoError(t, err) + }) }) // should be able to get QC and random beacon seed from a block with a valid child t.Run("valid child", func(t *testing.T) { - util.RunWithFollowerProtocolState(t, stateRoot, func(db *badger.DB, state *bprotocol.FollowerState) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { // add a block so we aren't testing against root - block1 := unittest.BlockWithParentFixture(stateRoot.Block().Header) + block1 := unittest.BlockWithParentFixture(head) block1.SetPayload(flow.EmptyPayload()) err := state.Extend(&block1) require.Nil(t, err) @@ -213,9 +224,11 @@ func TestQuorumCertificate(t *testing.T) { // test that we can query current/next/previous epochs from a snapshot func TestSnapshot_EpochQuery(t *testing.T) { identities := unittest.CompleteIdentitySet() - stateRoot := fixtureStateRootWithParticipants(t, identities) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *bprotocol.MutableState) { - seal := stateRoot.Seal() + rootSnapshot := unittest.RootSnapshotFixture(identities) + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) + + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.MutableState) { epoch1Counter := seal.ServiceEvents[0].Event.(*flow.EpochSetup).Counter epoch2Counter := epoch1Counter + 1 @@ -312,9 +325,13 @@ func TestSnapshot_EpochQuery(t *testing.T) { // test that querying the first view of an epoch returns the appropriate value func TestSnapshot_EpochFirstView(t *testing.T) { identities := unittest.CompleteIdentitySet() - stateRoot := fixtureStateRootWithParticipants(t, identities) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *bprotocol.MutableState) { - root, seal := stateRoot.Block(), stateRoot.Seal() + rootSnapshot := unittest.RootSnapshotFixture(identities) + head, err := rootSnapshot.Head() + require.NoError(t, err) + seal, err := rootSnapshot.LatestSeal() + require.NoError(t, err) + + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.MutableState) { // Prepare an epoch builder, which builds epochs with 6 blocks, A,B,C,D,E,F // See EpochBuilder documentation for details of these blocks. @@ -341,7 +358,7 @@ func TestSnapshot_EpochFirstView(t *testing.T) { CompleteEpoch() // figure out the expected first views of the epochs - epoch1FirstView := root.Header.View + epoch1FirstView := head.View epoch2FirstView := seal.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView + 1 epoch1Heights := []uint64{0, 1, 2, 3, 4, 5} @@ -415,8 +432,8 @@ func TestSnapshot_CrossEpochIdentities(t *testing.T) { // epoch 3 has no overlap with epoch 2 epoch3Identities := unittest.IdentityListFixture(10, unittest.WithAllRoles()) - stateRoot := fixtureStateRootWithParticipants(t, epoch1Identities) - util.RunWithFullProtocolState(t, stateRoot, func(db *badger.DB, state *bprotocol.MutableState) { + rootSnapshot := unittest.RootSnapshotFixture(epoch1Identities) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.MutableState) { // Prepare an epoch builder, which builds epochs with 6 blocks, A,B,C,D,E,F // See EpochBuilder documentation for details of these blocks. @@ -541,10 +558,10 @@ func TestSnapshot_PostSporkIdentities(t *testing.T) { block.Header.ParentID = unittest.IdentifierFixture() }) - stateRoot, err := flow.NewStateRoot(root, result, seal, 0) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, unittest.QuorumCertificateFixture()) require.NoError(t, err) - util.RunWithBootstrapState(t, stateRoot, func(db *badger.DB, state *bprotocol.State) { + util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { actual, err := state.Final().Identities(filter.Any) require.Nil(t, err) assert.ElementsMatch(t, expected, actual) From 66be8e9df86791945c7daf86b925c0e95658cb70 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 3 Feb 2021 18:47:18 -0800 Subject: [PATCH 081/178] add storage method for root qc --- storage/badger/operation/prefix.go | 5 ++-- storage/badger/operation/root_qc.go | 26 +++++++++++++++++++ storage/badger/operation/root_qc_test.go | 32 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 storage/badger/operation/root_qc.go create mode 100644 storage/badger/operation/root_qc_test.go diff --git a/storage/badger/operation/prefix.go b/storage/badger/operation/prefix.go index 5ace70fa59b..00eefb4d81c 100644 --- a/storage/badger/operation/prefix.go +++ b/storage/badger/operation/prefix.go @@ -15,8 +15,9 @@ const ( codeMax = 1 // keeps track of the maximum key size // codes for views with special meaning - codeStartedView = 10 // latest view hotstuff started - codeVotedView = 11 // latest view hotstuff voted on + codeStartedView = 10 // latest view hotstuff started + codeVotedView = 11 // latest view hotstuff voted on + codeRootQuorumCertificate = 12 // code for heights with special meaning codeFinalizedHeight = 20 // latest finalized block height diff --git a/storage/badger/operation/root_qc.go b/storage/badger/operation/root_qc.go new file mode 100644 index 00000000000..d4bf0fbf2fa --- /dev/null +++ b/storage/badger/operation/root_qc.go @@ -0,0 +1,26 @@ +package operation + +import ( + "github.com/dgraph-io/badger/v2" + + "github.com/onflow/flow-go/model/flow" +) + +// InsertRootQuorumCertificate inserts the root quorum certificate for the +// local blockchain state. The root quorum certificate certifies the root +// block and is used to bootstrap HotStuff. +// +// Only the root quorum certificate must be explicitly stored in this way! +// All other quorum certificates are implicitly included in the child of +// block they certify in the ParentSigs field. +func InsertRootQuorumCertificate(qc *flow.QuorumCertificate) func(txn *badger.Txn) error { + return func(txn *badger.Txn) error { + return insert(makePrefix(codeRootQuorumCertificate), qc)(txn) + } +} + +func RetrieveRootQuorumCertificate(qc *flow.QuorumCertificate) func(txn *badger.Txn) error { + return func(txn *badger.Txn) error { + return retrieve(makePrefix(codeRootQuorumCertificate), qc)(txn) + } +} diff --git a/storage/badger/operation/root_qc_test.go b/storage/badger/operation/root_qc_test.go new file mode 100644 index 00000000000..53335ea7457 --- /dev/null +++ b/storage/badger/operation/root_qc_test.go @@ -0,0 +1,32 @@ +package operation + +import ( + "testing" + + "github.com/dgraph-io/badger/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/utils/unittest" +) + +func TestInsertRetrieveRootQC(t *testing.T) { + qc := unittest.QuorumCertificateFixture() + + unittest.RunWithBadgerDB(t, func(db *badger.DB) { + err := db.Update(InsertRootQuorumCertificate(qc)) + require.NoError(t, err) + + // should be able to retrieve + var retrieved flow.QuorumCertificate + err = db.View(RetrieveRootQuorumCertificate(&retrieved)) + require.NoError(t, err) + assert.Equal(t, qc, &retrieved) + + // should not be able to overwrite + qc2 := unittest.QuorumCertificateFixture() + err = db.Update(InsertRootQuorumCertificate(qc2)) + require.Error(t, err) + }) +} From 5cbc865904e5b73ae59fca16edb5ffd6e541692e Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Feb 2021 12:46:37 -0800 Subject: [PATCH 082/178] store root qc on bootstrap --- state/protocol/badger/snapshot.go | 31 ++++++++++++++++++++++++++++--- state/protocol/badger/state.go | 14 ++++++++++++-- utils/unittest/fixtures.go | 2 +- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index bc0038c0734..e45b9bae014 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -58,10 +58,13 @@ func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { return nil, fmt.Errorf("could not get root: %w", err) } - // TODO: store root QC if s.blockID == root.ID() { - // TODO store root QC and return here - return nil, fmt.Errorf("root qc not stored") + var rootQC flow.QuorumCertificate + err := s.state.db.View(operation.RetrieveRootQuorumCertificate(&rootQC)) + if err != nil { + return nil, fmt.Errorf("could not retrieve root qc: %w", err) + } + return &rootQC, nil } // CASE 2: for any other block, generate the root QC from a valid child @@ -321,6 +324,28 @@ func (s *Snapshot) pending(blockID flow.Identifier) ([]flow.Identifier, error) { // Seed returns the random seed at the given indices for the current block snapshot. func (s *Snapshot) Seed(indices ...uint32) ([]byte, error) { + // CASE 1: for the root block, generate the seed from the root qc + root, err := s.state.Params().Root() + if err != nil { + return nil, fmt.Errorf("could not get root: %w", err) + } + + if s.blockID == root.ID() { + var rootQC flow.QuorumCertificate + err := s.state.db.View(operation.RetrieveRootQuorumCertificate(&rootQC)) + if err != nil { + return nil, fmt.Errorf("could not retrieve root qc: %w", err) + } + + seed, err := seed.FromParentSignature(indices, rootQC.SigData) + if err != nil { + return nil, fmt.Errorf("could not create seed from root qc: %w", err) + } + + return seed, nil + } + + // CASE 2: for any other block, use any valid child child, err := s.validChild() if err != nil { return nil, fmt.Errorf("could not get child: %w", err) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index e9f92e0cec3..82ff1189912 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -127,7 +127,17 @@ func Bootstrap( return fmt.Errorf("could not index root block seal: %w", err) } - // 4) initialize the current protocol state values + // 4) insert the root quorum certificate into the database + qc, err := root.QuorumCertificate() + if err != nil { + return fmt.Errorf("could not get root qc: %w", err) + } + err = operation.InsertRootQuorumCertificate(qc)(tx) + if err != nil { + return fmt.Errorf("could not insert root qc: %w", err) + } + + // 5) initialize the current protocol state values err = operation.InsertStartedView(head.Header.ChainID, head.Header.View)(tx) if err != nil { return fmt.Errorf("could not insert started view: %w", err) @@ -149,7 +159,7 @@ func Bootstrap( return fmt.Errorf("could not insert sealed height: %w", err) } - // 5) initialize values related to the epoch logic + // 6) initialize values related to the epoch logic err = state.bootstrapEpoch(root)(tx) if err != nil { return fmt.Errorf("could not bootstrap epoch values: %w", err) diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index a04c1a97835..969e381ead7 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -998,7 +998,7 @@ func QuorumCertificateFixture(opts ...func(*flow.QuorumCertificate)) *flow.Quoru View: uint64(rand.Uint32()), BlockID: IdentifierFixture(), SignerIDs: IdentifierListFixture(3), - SigData: SeedFixture(32 * 3), + SigData: CombinedSignatureFixture(2), } } From 1b6ffe4f545e699a9a621497eb190a7a7f97cb30 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Feb 2021 16:19:58 -0800 Subject: [PATCH 083/178] remove epoch test this was a test that FirstView not be included in the event ID -- however this has changed so that FirstView is now an intrinsic rather than computed property of EpochSetup --- model/flow/epoch_test.go | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 model/flow/epoch_test.go diff --git a/model/flow/epoch_test.go b/model/flow/epoch_test.go deleted file mode 100644 index c250f0304ea..00000000000 --- a/model/flow/epoch_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package flow_test - -import ( - "math/rand" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/onflow/flow-go/utils/unittest" -) - -// test that the FirstView field does not impact the ID computation. -func TestEpochSetup_ID(t *testing.T) { - setup := unittest.EpochSetupFixture() - id := setup.ID() - setup.FirstView = rand.Uint64() - assert.Equal(t, id, setup.ID()) -} From 42b80d4eeedcf5b5a319c06766609cc114bbf65f Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Feb 2021 17:23:30 -0800 Subject: [PATCH 084/178] set FirstView in root seal generated during bootstrapping --- cmd/bootstrap/cmd/seal.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/bootstrap/cmd/seal.go b/cmd/bootstrap/cmd/seal.go index 738d79c7b6f..bfa1147a79c 100644 --- a/cmd/bootstrap/cmd/seal.go +++ b/cmd/bootstrap/cmd/seal.go @@ -28,6 +28,7 @@ func constructRootResultAndSeal( epochSetup := &flow.EpochSetup{ Counter: flagEpochCounter, + FirstView: block.Header.View, FinalView: block.Header.View + leader.EstimatedSixMonthOfViews, Participants: participants, Assignments: assignments, From bbd90242218fcb8a02ecf2a22dd98d29ddf7f5ea Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Feb 2021 17:23:58 -0800 Subject: [PATCH 085/178] remove computing of FirstView field in mutator --- state/protocol/badger/mutator.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index e19940d1c05..92090069fc8 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -750,6 +750,7 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T switch ev := event.Event.(type) { case *flow.EpochSetup: + // TODO use validSetup // We should only have a single epoch setup event per epoch. if epochStatus.NextEpoch.SetupID != flow.ZeroID { // true iff EpochSetup event for NEXT epoch was already included before @@ -773,10 +774,6 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T return nil, state.NewInvalidExtensionErrorf("invalid epoch setup: %s", err) } - // cache the first view to simplify epoch queries later on - // TODO remove - ev.FirstView = activeSetup.FinalView + 1 - // prevents multiple setup events for same Epoch (including multiple setup events in payload of same block) epochStatus.NextEpoch.SetupID = ev.ID() @@ -785,6 +782,7 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T case *flow.EpochCommit: + // TODO use validCommit // We should only have a single epoch commit event per epoch. if epochStatus.NextEpoch.CommitID != flow.ZeroID { // true iff EpochCommit event for NEXT epoch was already included before From 6bac705b6c78fe29ed2cee16a2c8e34a6fb8e9bb Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Feb 2021 17:36:30 -0800 Subject: [PATCH 086/178] adjust checks of setup event views --- state/protocol/badger/mutator.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index 92090069fc8..23712f70566 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -750,7 +750,6 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T switch ev := event.Event.(type) { case *flow.EpochSetup: - // TODO use validSetup // We should only have a single epoch setup event per epoch. if epochStatus.NextEpoch.SetupID != flow.ZeroID { // true iff EpochSetup event for NEXT epoch was already included before @@ -762,10 +761,13 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T return nil, state.NewInvalidExtensionErrorf("next epoch setup has invalid counter (%d => %d)", counter, ev.Counter) } - // The final view needs to be after the current epoch final view. - // NOTE: This kind of operates as an overflow check for the other checks. - if ev.FinalView <= activeSetup.FinalView { - return nil, state.NewInvalidExtensionErrorf("next epoch must be after current epoch (%d <= %d)", ev.FinalView, activeSetup.FinalView) + // The first view needs to be exactly one greater than the current epoch final view + if ev.FirstView != activeSetup.FinalView+1 { + return nil, state.NewInvalidExtensionErrorf( + "next epoch first view must be exactly 1 more than current epoch final view (%d != %d+1)", + ev.FirstView, + activeSetup.FinalView, + ) } // Finally, the epoch setup event must contain all necessary information. @@ -782,7 +784,6 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T case *flow.EpochCommit: - // TODO use validCommit // We should only have a single epoch commit event per epoch. if epochStatus.NextEpoch.CommitID != flow.ZeroID { // true iff EpochCommit event for NEXT epoch was already included before From f84d47731a95429e41eafbffcd4cdcd364d71fac Mon Sep 17 00:00:00 2001 From: danuio Date: Fri, 5 Feb 2021 11:20:00 +0000 Subject: [PATCH 087/178] Add Access API endpoints and bump `onflow/flow` -> v0.1.9 --- access/api.go | 3 +++ access/handler.go | 25 +++++++++++++++++++++++++ engine/access/rpc/backend/backend.go | 5 +++++ go.mod | 2 +- go.sum | 2 ++ 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/access/api.go b/access/api.go index 5032d817f14..55906604d19 100644 --- a/access/api.go +++ b/access/api.go @@ -8,6 +8,7 @@ import ( "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" ) // API provides all public-facing functionality of the Flow Access API. @@ -39,6 +40,8 @@ type API interface { GetEventsForHeightRange(ctx context.Context, eventType string, startHeight, endHeight uint64) ([]flow.BlockEvents, error) GetEventsForBlockIDs(ctx context.Context, eventType string, blockIDs []flow.Identifier) ([]flow.BlockEvents, error) + + GetLatestProtocolStateSnapshot(ctx context.Context) (protocol.Snapshot, error) } // TODO: Combine this with flow.TransactionResult? diff --git a/access/handler.go b/access/handler.go index f7950592599..74bf483fbfc 100644 --- a/access/handler.go +++ b/access/handler.go @@ -2,6 +2,7 @@ package access import ( "context" + "encoding/json" "github.com/golang/protobuf/ptypes" "github.com/onflow/flow/protobuf/go/flow/access" @@ -11,6 +12,8 @@ import ( "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" + + "github.com/onflow/flow-go/state/protocol/inmem" ) type Handler struct { @@ -405,6 +408,28 @@ func (h *Handler) GetEventsForBlockIDs( }, nil } +// GetLatestProtocolStateSnapshot returns the latest serializable Snapshot +func (h *Handler) GetLatestProtocolStateSnapshot(ctx context.Context, req *access.GetLatestProtocolStateSnapshotRequest) (*access.ProtocolStateSnapshotResponse, error) { + snapshot, err := h.api.GetLatestProtocolStateSnapshot(ctx) + if err != nil { + return nil, err + } + + serializable, err := inmem.FromSnapshot(snapshot) + if err != nil { + return nil, err + } + + data, err := json.Marshal(serializable) + if err != nil { + return nil, err + } + + return &access.ProtocolStateSnapshotResponse{ + SerializedSnapshot: data, + }, nil +} + func blockResponse(block *flow.Block) (*access.BlockResponse, error) { msg, err := convert.BlockToMessage(block) if err != nil { diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 0e716a0b778..72d9ebd5fb1 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -185,6 +185,11 @@ func (b *Backend) GetNetworkParameters(_ context.Context) access.NetworkParamete } } +func (b *Backend) GetLatestProtocolStateSnapshot(_ context.Context) (protocol.Snapshot, error) { + latestSealed := b.state.Sealed() + return latestSealed, nil +} + func convertStorageError(err error) error { if err == nil { return nil diff --git a/go.mod b/go.mod index 55e9d8a9dc1..cebeaa60290 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1 github.com/onflow/flow-go-sdk v0.14.3 github.com/onflow/flow-go/crypto v0.12.0 - github.com/onflow/flow/protobuf/go/flow v0.1.8 + github.com/onflow/flow/protobuf/go/flow v0.1.9 github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.7.1 diff --git a/go.sum b/go.sum index 499714613aa..3c21191b4bb 100644 --- a/go.sum +++ b/go.sum @@ -831,6 +831,8 @@ github.com/onflow/flow-go-sdk v0.14.3 h1:6t1ycWJSPpgz7LeMDaZ3cIbiKa24JNxUEFyAu3V github.com/onflow/flow-go-sdk v0.14.3/go.mod h1:VAXKnZQlRRPfIkm8nw71B6bwhXNnmTfqV4wNrP3fK9I= github.com/onflow/flow/protobuf/go/flow v0.1.8 h1:jBR8aXEL0MOh3gVJmCr0KYXmtG3JUBhzADonKkYE6oI= github.com/onflow/flow/protobuf/go/flow v0.1.8/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM= +github.com/onflow/flow/protobuf/go/flow v0.1.9 h1:ugK6/9K4AkMxqPbCvQzbbV24AH50Ozze43nqpukQoOM= +github.com/onflow/flow/protobuf/go/flow v0.1.9/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= From 0130321f25d89909a8b07a6c6e1cc5fffe66b67b Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Feb 2021 13:08:06 -0800 Subject: [PATCH 088/178] use correct value for random source min length --- model/flow/epoch.go | 4 ++++ state/protocol/badger/validity.go | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/model/flow/epoch.go b/model/flow/epoch.go index ae9855d1818..7e326c8452a 100644 --- a/model/flow/epoch.go +++ b/model/flow/epoch.go @@ -38,6 +38,10 @@ func (p EpochPhase) String() string { }[p] } +// EpochSetupRandomSourceMinLength is the minimum length of the random source +// included in an EpochSetup service event. +const EpochSetupRandomSourceMinLength = crypto.SignatureLenBLSBLS12381 + // EpochSetup is a service event emitted when the network is ready to set up // for the upcoming epoch. It contains the participants in the epoch, the // length, the cluster assignment, and the seed for leader selection. diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index 97f8d718cb2..49384a0b66f 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -12,8 +12,7 @@ import ( func validSetup(setup *flow.EpochSetup) error { // STEP 1: general sanity checks // the seed needs to be at least minimum length - // TODO: what is the appropriate minimum length here? Previously used sdk/crypto.SeedMinLength - if len(setup.RandomSource) < crypto.SeedMinLenDKG { + if len(setup.RandomSource) < flow.EpochSetupRandomSourceMinLength { return fmt.Errorf("seed has insufficient length (%d < %d)", len(setup.RandomSource), crypto.SeedMinLenDKG) } From eb919a65177a7938366960c984201e89a64669e2 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Feb 2021 14:01:54 -0800 Subject: [PATCH 089/178] check equality of random source --- model/flow/epoch.go | 4 ++-- state/protocol/badger/validity.go | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/model/flow/epoch.go b/model/flow/epoch.go index 7e326c8452a..2ae44445b32 100644 --- a/model/flow/epoch.go +++ b/model/flow/epoch.go @@ -38,9 +38,9 @@ func (p EpochPhase) String() string { }[p] } -// EpochSetupRandomSourceMinLength is the minimum length of the random source +// EpochSetupRandomSourceLength is the required length of the random source // included in an EpochSetup service event. -const EpochSetupRandomSourceMinLength = crypto.SignatureLenBLSBLS12381 +const EpochSetupRandomSourceLength = crypto.SignatureLenBLSBLS12381 // EpochSetup is a service event emitted when the network is ready to set up // for the upcoming epoch. It contains the participants in the epoch, the diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index 49384a0b66f..d8471df4b9e 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -3,7 +3,6 @@ package badger import ( "fmt" - "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/state/protocol" @@ -12,8 +11,8 @@ import ( func validSetup(setup *flow.EpochSetup) error { // STEP 1: general sanity checks // the seed needs to be at least minimum length - if len(setup.RandomSource) < flow.EpochSetupRandomSourceMinLength { - return fmt.Errorf("seed has insufficient length (%d < %d)", len(setup.RandomSource), crypto.SeedMinLenDKG) + if len(setup.RandomSource) != flow.EpochSetupRandomSourceLength { + return fmt.Errorf("seed has incorrect length (%d != %d)", len(setup.RandomSource), flow.EpochSetupRandomSourceLength) } // STEP 2: sanity checks of all nodes listed as participants From 090a6a8c5ad130ae166b772760c2f2ba81b8e738 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Feb 2021 14:26:11 -0800 Subject: [PATCH 090/178] improve docs on sealing segment --- state/protocol/snapshot.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/state/protocol/snapshot.go b/state/protocol/snapshot.go index 971ff1457ce..efc4d6b6267 100644 --- a/state/protocol/snapshot.go +++ b/state/protocol/snapshot.go @@ -56,8 +56,16 @@ type Snapshot interface { // is the most recently sealed block as of this snapshot (ie. the block // referenced by LatestSeal). // - // This segment is used as the initial state for non-spork and non-genesis - // root states. + // TAIL <- B1 <- ... <- BN <- HEAD + // + // NOTE 1: TAIL is not always sealed by HEAD. In the case that the head of + // the snapshot contains no seals, TAIL must be sealed by the first ancestor + // of HEAD which contains any seal. + // + // NOTE 2: In the special case of a root snapshot generated for a spork, + // the sealing segment has exactly one block (the root block for the spork). + // For all other snapshots, the sealing segment contains at least 2 blocks. + // SealingSegment() ([]*flow.Block, error) // Pending returns the IDs of all descendants of the Head block. The IDs From b163ea98ea9266776c40f8e0a9064b7d48bc914c Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Feb 2021 14:32:16 -0800 Subject: [PATCH 091/178] use correct random source length in fixture --- utils/unittest/fixtures.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 969e381ead7..5960125cd16 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -1050,7 +1050,7 @@ func EpochSetupFixture(opts ...func(setup *flow.EpochSetup)) *flow.EpochSetup { FinalView: uint64(rand.Uint32() + 1000), Participants: participants, Assignments: assignments, - RandomSource: SeedFixture(32), + RandomSource: SeedFixture(flow.EpochSetupRandomSourceLength), } for _, apply := range opts { apply(setup) From 7f265ff1e2a38adae35101699dac362dab5c54eb Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Feb 2021 14:39:38 -0800 Subject: [PATCH 092/178] fix tests manually setting first view --- state/protocol/badger/mutator_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 83c1e097e76..60ab6228c9f 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -607,6 +607,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { unittest.WithParticipants(epoch2Participants), unittest.SetupWithCounter(epoch1Setup.Counter+1), unittest.WithFinalView(epoch1FinalView+1000), + unittest.WithFirstView(epoch1FinalView+1), ) // create the seal referencing block1 and including the setup event @@ -804,11 +805,13 @@ func TestExtendConflictingEpochEvents(t *testing.T) { unittest.WithParticipants(rootSetup.Participants), unittest.SetupWithCounter(rootSetup.Counter+1), unittest.WithFinalView(rootSetup.FinalView+1000), + unittest.WithFirstView(rootSetup.FinalView+1), ) nextEpochSetup2 := unittest.EpochSetupFixture( unittest.WithParticipants(rootSetup.Participants), unittest.SetupWithCounter(rootSetup.Counter+1), - unittest.WithFinalView(rootSetup.FinalView+2000), + unittest.WithFinalView(rootSetup.FinalView+2000), // final view differs + unittest.WithFirstView(rootSetup.FinalView+1), ) // create one seal containing the first setup event @@ -880,6 +883,7 @@ func TestExtendEpochSetupInvalid(t *testing.T) { unittest.WithParticipants(epoch2Participants), unittest.SetupWithCounter(epoch1Setup.Counter+1), unittest.WithFinalView(epoch1Setup.FinalView+1000), + unittest.WithFirstView(epoch1Setup.FinalView+1), ) seal := unittest.Seal.Fixture( unittest.Seal.WithBlockID(block1.ID()), @@ -973,6 +977,7 @@ func TestExtendEpochCommitInvalid(t *testing.T) { unittest.WithParticipants(epoch2Participants), unittest.SetupWithCounter(epoch1Setup.Counter+1), unittest.WithFinalView(epoch1Setup.FinalView+1000), + unittest.WithFirstView(epoch1Setup.FinalView+1), ) seal := unittest.Seal.Fixture( unittest.Seal.WithResult(sealedResult), @@ -1117,6 +1122,7 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { unittest.WithParticipants(epoch2Participants), unittest.SetupWithCounter(epoch1Setup.Counter+1), unittest.WithFinalView(epoch1FinalView+1000), + unittest.WithFirstView(epoch1FinalView+1), ) // create the seal referencing block1 and including the setup event From 8b35d4e252fd3e5333fe6ed8f36bfe94e015d6df Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Feb 2021 15:18:26 -0800 Subject: [PATCH 093/178] add sealing segment tests for post-spork cases --- state/protocol/badger/snapshot_test.go | 145 +++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index b63533d042f..e05e614f23a 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -125,6 +125,151 @@ func TestClusters(t *testing.T) { }) } +// TestSealingSegment tests querying sealing segment with respect to various snapshots. +func TestSealingSegment(t *testing.T) { + t.Run("bootstrapped from spork snapshot", func(t *testing.T) { + identities := unittest.CompleteIdentitySet() + rootSnapshot := unittest.RootSnapshotFixture(identities) + head, err := rootSnapshot.Head() + require.NoError(t, err) + + t.Run("root sealing segment", func(t *testing.T) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + expected, err := rootSnapshot.SealingSegment() + require.NoError(t, err) + actual, err := state.AtBlockID(head.ID()).SealingSegment() + require.NoError(t, err) + + assert.Len(t, actual, 1) + assert.Equal(t, len(expected), len(actual)) + assert.Equal(t, expected[0].ID(), actual[0].ID()) + }) + }) + + // test sealing segment for non-root segment with simple sealing structure + // (no blocks in between reference block and latest sealed) + // ROOT <- B1 <- B2(S1) + // Expected sealing segment: [B1, B2] + t.Run("non-root", func(t *testing.T) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + // build a block to seal + block1 := unittest.BlockWithParentFixture(head) + err := state.Extend(&block1) + require.NoError(t, err) + + // build a block sealing block1 + block2 := unittest.BlockWithParentFixture(block1.Header) + block2.SetPayload(flow.Payload{ + Seals: []*flow.Seal{ + unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), + }, + }) + err = state.Extend(&block2) + require.NoError(t, err) + + segment, err := state.AtBlockID(block2.ID()).SealingSegment() + require.NoError(t, err) + + // sealing segment should contain B1 and B2 + // B2 is reference of snapshot, B1 is latest sealed + assert.Len(t, segment, 2) + assert.Equal(t, block1.ID(), segment[0].ID()) + assert.Equal(t, block2.ID(), segment[1].ID()) + }) + }) + + // test sealing segment for sealing segment with a large number of blocks + // between the reference block and latest sealed + // ROOT <- B1 <- .... <- BN(S1) + // Expected sealing segment: [B1, ..., BN] + t.Run("long sealing segment", func(t *testing.T) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + + // build a block to seal + block1 := unittest.BlockWithParentFixture(head) + err := state.Extend(&block1) + require.NoError(t, err) + + parent := block1 + // build a large chain of intermediary blocks + for i := 0; i < 100; i++ { + next := unittest.BlockWithParentFixture(parent.Header) + err = state.Extend(&next) + require.NoError(t, err) + parent = next + } + + // build the block sealing block 1 + blockN := unittest.BlockWithParentFixture(parent.Header) + blockN.SetPayload(flow.Payload{ + Seals: []*flow.Seal{ + unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), + }, + }) + err = state.Extend(&blockN) + require.NoError(t, err) + + segment, err := state.AtBlockID(blockN.ID()).SealingSegment() + require.NoError(t, err) + + // sealing segment should cover range [B1, BN] + assert.Len(t, segment, 102) + // first and last blocks should be B1, BN + assert.Equal(t, block1.ID(), segment[0].ID()) + assert.Equal(t, blockN.ID(), segment[101].ID()) + }) + }) + + // test sealing segment where the segment blocks contain seals for + // ancestor blocks prior to the sealing segment + // ROOT -> B1 -> B2 -> B3(S1) -> B4(S2) + // Expected sealing segment: [B2, B3, B4] + t.Run("overlapping sealing segment", func(t *testing.T) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + + block1 := unittest.BlockWithParentFixture(head) + err := state.Extend(&block1) + require.NoError(t, err) + + block2 := unittest.BlockWithParentFixture(block1.Header) + err = state.Extend(&block2) + require.NoError(t, err) + + block3 := unittest.BlockWithParentFixture(block2.Header) + block3.SetPayload(flow.Payload{ + Seals: []*flow.Seal{ + unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), + }, + }) + err = state.Extend(&block3) + require.NoError(t, err) + + block4 := unittest.BlockWithParentFixture(block3.Header) + block4.SetPayload(flow.Payload{ + Seals: []*flow.Seal{ + unittest.Seal.Fixture(unittest.Seal.WithBlockID(block2.ID())), + }, + }) + err = state.Extend(&block4) + require.NoError(t, err) + + segment, err := state.AtBlockID(block4.ID()).SealingSegment() + require.NoError(t, err) + + // sealing segment should be [B2, B3, B4] + assert.Len(t, segment, 3) + assert.Equal(t, block2.ID(), segment[0].ID()) + assert.Equal(t, block3.ID(), segment[1].ID()) + assert.Equal(t, block4.ID(), segment[2].ID()) + }) + }) + + }) + +} + +func TestLatestResultAndSeal(t *testing.T) {} + // test retrieving quorum certificate and seed func TestQuorumCertificate(t *testing.T) { identities := unittest.IdentityListFixture(5, unittest.WithAllRoles()) From f07b3a56bf2e90c22585a2e3f212f863fdd219b7 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Feb 2021 16:04:35 -0800 Subject: [PATCH 094/178] skeleton of seal/result tests --- state/protocol/badger/snapshot_test.go | 243 +++++++++++++------------ 1 file changed, 130 insertions(+), 113 deletions(-) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index e05e614f23a..2c82f4378c4 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -127,149 +127,166 @@ func TestClusters(t *testing.T) { // TestSealingSegment tests querying sealing segment with respect to various snapshots. func TestSealingSegment(t *testing.T) { - t.Run("bootstrapped from spork snapshot", func(t *testing.T) { - identities := unittest.CompleteIdentitySet() - rootSnapshot := unittest.RootSnapshotFixture(identities) - head, err := rootSnapshot.Head() - require.NoError(t, err) + identities := unittest.CompleteIdentitySet() + rootSnapshot := unittest.RootSnapshotFixture(identities) + head, err := rootSnapshot.Head() + require.NoError(t, err) - t.Run("root sealing segment", func(t *testing.T) { - util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { - expected, err := rootSnapshot.SealingSegment() - require.NoError(t, err) - actual, err := state.AtBlockID(head.ID()).SealingSegment() - require.NoError(t, err) + t.Run("root sealing segment", func(t *testing.T) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + expected, err := rootSnapshot.SealingSegment() + require.NoError(t, err) + actual, err := state.AtBlockID(head.ID()).SealingSegment() + require.NoError(t, err) + + assert.Len(t, actual, 1) + assert.Equal(t, len(expected), len(actual)) + assert.Equal(t, expected[0].ID(), actual[0].ID()) + }) + }) + + // test sealing segment for non-root segment with simple sealing structure + // (no blocks in between reference block and latest sealed) + // ROOT <- B1 <- B2(S1) + // Expected sealing segment: [B1, B2] + t.Run("non-root", func(t *testing.T) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + // build a block to seal + block1 := unittest.BlockWithParentFixture(head) + err := state.Extend(&block1) + require.NoError(t, err) - assert.Len(t, actual, 1) - assert.Equal(t, len(expected), len(actual)) - assert.Equal(t, expected[0].ID(), actual[0].ID()) + // build a block sealing block1 + block2 := unittest.BlockWithParentFixture(block1.Header) + block2.SetPayload(flow.Payload{ + Seals: []*flow.Seal{ + unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), + }, }) + err = state.Extend(&block2) + require.NoError(t, err) + + segment, err := state.AtBlockID(block2.ID()).SealingSegment() + require.NoError(t, err) + + // sealing segment should contain B1 and B2 + // B2 is reference of snapshot, B1 is latest sealed + assert.Len(t, segment, 2) + assert.Equal(t, block1.ID(), segment[0].ID()) + assert.Equal(t, block2.ID(), segment[1].ID()) }) + }) - // test sealing segment for non-root segment with simple sealing structure - // (no blocks in between reference block and latest sealed) - // ROOT <- B1 <- B2(S1) - // Expected sealing segment: [B1, B2] - t.Run("non-root", func(t *testing.T) { - util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { - // build a block to seal - block1 := unittest.BlockWithParentFixture(head) - err := state.Extend(&block1) - require.NoError(t, err) + // test sealing segment for sealing segment with a large number of blocks + // between the reference block and latest sealed + // ROOT <- B1 <- .... <- BN(S1) + // Expected sealing segment: [B1, ..., BN] + t.Run("long sealing segment", func(t *testing.T) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { - // build a block sealing block1 - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.SetPayload(flow.Payload{ - Seals: []*flow.Seal{ - unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), - }, - }) - err = state.Extend(&block2) - require.NoError(t, err) + // build a block to seal + block1 := unittest.BlockWithParentFixture(head) + err := state.Extend(&block1) + require.NoError(t, err) - segment, err := state.AtBlockID(block2.ID()).SealingSegment() + parent := block1 + // build a large chain of intermediary blocks + for i := 0; i < 100; i++ { + next := unittest.BlockWithParentFixture(parent.Header) + err = state.Extend(&next) require.NoError(t, err) + parent = next + } - // sealing segment should contain B1 and B2 - // B2 is reference of snapshot, B1 is latest sealed - assert.Len(t, segment, 2) - assert.Equal(t, block1.ID(), segment[0].ID()) - assert.Equal(t, block2.ID(), segment[1].ID()) + // build the block sealing block 1 + blockN := unittest.BlockWithParentFixture(parent.Header) + blockN.SetPayload(flow.Payload{ + Seals: []*flow.Seal{ + unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), + }, }) - }) + err = state.Extend(&blockN) + require.NoError(t, err) - // test sealing segment for sealing segment with a large number of blocks - // between the reference block and latest sealed - // ROOT <- B1 <- .... <- BN(S1) - // Expected sealing segment: [B1, ..., BN] - t.Run("long sealing segment", func(t *testing.T) { - util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + segment, err := state.AtBlockID(blockN.ID()).SealingSegment() + require.NoError(t, err) - // build a block to seal - block1 := unittest.BlockWithParentFixture(head) - err := state.Extend(&block1) - require.NoError(t, err) + // sealing segment should cover range [B1, BN] + assert.Len(t, segment, 102) + // first and last blocks should be B1, BN + assert.Equal(t, block1.ID(), segment[0].ID()) + assert.Equal(t, blockN.ID(), segment[101].ID()) + }) + }) - parent := block1 - // build a large chain of intermediary blocks - for i := 0; i < 100; i++ { - next := unittest.BlockWithParentFixture(parent.Header) - err = state.Extend(&next) - require.NoError(t, err) - parent = next - } + // test sealing segment where the segment blocks contain seals for + // ancestor blocks prior to the sealing segment + // ROOT -> B1 -> B2 -> B3(S1) -> B4(S2) + // Expected sealing segment: [B2, B3, B4] + t.Run("overlapping sealing segment", func(t *testing.T) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { - // build the block sealing block 1 - blockN := unittest.BlockWithParentFixture(parent.Header) - blockN.SetPayload(flow.Payload{ - Seals: []*flow.Seal{ - unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), - }, - }) - err = state.Extend(&blockN) - require.NoError(t, err) + block1 := unittest.BlockWithParentFixture(head) + err := state.Extend(&block1) + require.NoError(t, err) - segment, err := state.AtBlockID(blockN.ID()).SealingSegment() - require.NoError(t, err) + block2 := unittest.BlockWithParentFixture(block1.Header) + err = state.Extend(&block2) + require.NoError(t, err) - // sealing segment should cover range [B1, BN] - assert.Len(t, segment, 102) - // first and last blocks should be B1, BN - assert.Equal(t, block1.ID(), segment[0].ID()) - assert.Equal(t, blockN.ID(), segment[101].ID()) + block3 := unittest.BlockWithParentFixture(block2.Header) + block3.SetPayload(flow.Payload{ + Seals: []*flow.Seal{ + unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), + }, }) + err = state.Extend(&block3) + require.NoError(t, err) + + block4 := unittest.BlockWithParentFixture(block3.Header) + block4.SetPayload(flow.Payload{ + Seals: []*flow.Seal{ + unittest.Seal.Fixture(unittest.Seal.WithBlockID(block2.ID())), + }, + }) + err = state.Extend(&block4) + require.NoError(t, err) + + segment, err := state.AtBlockID(block4.ID()).SealingSegment() + require.NoError(t, err) + + // sealing segment should be [B2, B3, B4] + assert.Len(t, segment, 3) + assert.Equal(t, block2.ID(), segment[0].ID()) + assert.Equal(t, block3.ID(), segment[1].ID()) + assert.Equal(t, block4.ID(), segment[2].ID()) }) + }) +} - // test sealing segment where the segment blocks contain seals for - // ancestor blocks prior to the sealing segment - // ROOT -> B1 -> B2 -> B3(S1) -> B4(S2) - // Expected sealing segment: [B2, B3, B4] - t.Run("overlapping sealing segment", func(t *testing.T) { - util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { +func TestLatestResultAndSeal(t *testing.T) { + identities := unittest.CompleteIdentitySet() + rootSnapshot := unittest.RootSnapshotFixture(identities) - block1 := unittest.BlockWithParentFixture(head) - err := state.Extend(&block1) - require.NoError(t, err) + t.Run("root snapshot", func(t *testing.T) { - block2 := unittest.BlockWithParentFixture(block1.Header) - err = state.Extend(&block2) - require.NoError(t, err) + }) - block3 := unittest.BlockWithParentFixture(block2.Header) - block3.SetPayload(flow.Payload{ - Seals: []*flow.Seal{ - unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), - }, - }) - err = state.Extend(&block3) - require.NoError(t, err) + t.Run("non-root snapshot", func(t *testing.T) { + t.Run("reference block contains seal", func(t *testing.T) { - block4 := unittest.BlockWithParentFixture(block3.Header) - block4.SetPayload(flow.Payload{ - Seals: []*flow.Seal{ - unittest.Seal.Fixture(unittest.Seal.WithBlockID(block2.ID())), - }, - }) - err = state.Extend(&block4) - require.NoError(t, err) + }) - segment, err := state.AtBlockID(block4.ID()).SealingSegment() - require.NoError(t, err) + t.Run("reference block contains no seal", func(t *testing.T) { - // sealing segment should be [B2, B3, B4] - assert.Len(t, segment, 3) - assert.Equal(t, block2.ID(), segment[0].ID()) - assert.Equal(t, block3.ID(), segment[1].ID()) - assert.Equal(t, block4.ID(), segment[2].ID()) - }) }) - }) + t.Run("reference block contains multiple seals", func(t *testing.T) { + }) + }) } -func TestLatestResultAndSeal(t *testing.T) {} - // test retrieving quorum certificate and seed func TestQuorumCertificate(t *testing.T) { identities := unittest.IdentityListFixture(5, unittest.WithAllRoles()) From 33dd989b6c6c34a3aeb9d4abb249fe603fe2f293 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 09:00:29 -0800 Subject: [PATCH 095/178] rename validity functions --- state/protocol/badger/mutator.go | 4 ++-- state/protocol/badger/state.go | 12 ++++++------ state/protocol/badger/validity.go | 8 ++++---- state/protocol/badger/validity_test.go | 14 +++++++------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index 23712f70566..277ec36299b 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -771,7 +771,7 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T } // Finally, the epoch setup event must contain all necessary information. - err = validSetup(ev) + err = isValidEpochSetup(ev) if err != nil { return nil, state.NewInvalidExtensionErrorf("invalid epoch setup: %s", err) } @@ -805,7 +805,7 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T if err != nil { return nil, state.NewInvalidExtensionErrorf("could not retrieve next epoch setup: %s", err) } - err = validCommit(ev, setup) + err = isValidEpochCommit(ev, setup) if err != nil { return nil, state.NewInvalidExtensionErrorf("invalid epoch commit: %s", err) } diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 82ff1189912..d2a05627a99 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -210,10 +210,10 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err return fmt.Errorf("could not get previous epoch commit event: %w", err) } - if err := validSetup(setup); err != nil { + if err := isValidEpochSetup(setup); err != nil { return fmt.Errorf("invalid setup: %w", err) } - if err := validCommit(commit, setup); err != nil { + if err := isValidEpochCommit(commit, setup); err != nil { return fmt.Errorf("invalid commit") } @@ -235,10 +235,10 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err return fmt.Errorf("could not get current epoch commit event: %w", err) } - if err := validSetup(setup); err != nil { + if err := isValidEpochSetup(setup); err != nil { return fmt.Errorf("invalid setup: %w", err) } - if err := validCommit(commit, setup); err != nil { + if err := isValidEpochCommit(commit, setup); err != nil { return fmt.Errorf("invalid commit") } @@ -255,7 +255,7 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err if err != nil { return fmt.Errorf("could not get next epoch setup event: %w", err) } - if err := validSetup(setup); err != nil { + if err := isValidEpochSetup(setup); err != nil { return fmt.Errorf("invalid setup: %w", err) } @@ -266,7 +266,7 @@ func (state *State) bootstrapEpoch(root protocol.Snapshot) func(*badger.Txn) err return fmt.Errorf("could not get next epoch commit event: %w", err) } if err == nil { - if err := validCommit(commit, setup); err != nil { + if err := isValidEpochCommit(commit, setup); err != nil { return fmt.Errorf("invalid commit") } commits = append(commits, commit) diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index d8471df4b9e..bb6adf4ce84 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -8,7 +8,7 @@ import ( "github.com/onflow/flow-go/state/protocol" ) -func validSetup(setup *flow.EpochSetup) error { +func isValidEpochSetup(setup *flow.EpochSetup) error { // STEP 1: general sanity checks // the seed needs to be at least minimum length if len(setup.RandomSource) != flow.EpochSetupRandomSourceLength { @@ -87,7 +87,7 @@ func validSetup(setup *flow.EpochSetup) error { return nil } -func validCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error { +func isValidEpochCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error { if len(setup.Assignments) != len(commit.ClusterQCs) { return fmt.Errorf("number of clusters (%d) does not number of QCs (%d)", len(setup.Assignments), len(commit.ClusterQCs)) @@ -120,8 +120,8 @@ func validCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error { return nil } -// validRootSnapshot checks internal consistency of root state snapshot -func validRootSnapshot(snap protocol.Snapshot) error { +// isValidRootSnapshot checks internal consistency of root state snapshot +func isValidRootSnapshot(snap protocol.Snapshot) error { segment, err := snap.SealingSegment() if err != nil { diff --git a/state/protocol/badger/validity_test.go b/state/protocol/badger/validity_test.go index 1787ca0b004..8f08feb18e5 100644 --- a/state/protocol/badger/validity_test.go +++ b/state/protocol/badger/validity_test.go @@ -99,7 +99,7 @@ func TestEpochSetupValidity(t *testing.T) { // set an invalid final view for the first epoch setup.FinalView = setup.FirstView - err := validSetup(setup) + err := isValidEpochSetup(setup) require.Error(t, err) }) @@ -110,7 +110,7 @@ func TestEpochSetupValidity(t *testing.T) { collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] setup.Assignments = append(setup.Assignments, []flow.Identifier{collector.NodeID}) - err := validSetup(setup) + err := isValidEpochSetup(setup) require.Error(t, err) }) @@ -119,7 +119,7 @@ func TestEpochSetupValidity(t *testing.T) { setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) setup.RandomSource = unittest.SeedFixture(crypto.SeedMinLenDKG - 1) - err := validSetup(setup) + err := isValidEpochSetup(setup) require.Error(t, err) }) } @@ -132,7 +132,7 @@ func TestBootstrapInvalidEpochCommit(t *testing.T) { // use a different counter for the commit commit.Counter = setup.Counter + 1 - err := validCommit(commit, setup) + err := isValidEpochCommit(commit, setup) require.Error(t, err) }) @@ -143,7 +143,7 @@ func TestBootstrapInvalidEpochCommit(t *testing.T) { // add an extra QC to commit commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) - err := validCommit(commit, setup) + err := isValidEpochCommit(commit, setup) require.Error(t, err) }) @@ -153,7 +153,7 @@ func TestBootstrapInvalidEpochCommit(t *testing.T) { commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) commit.DKGGroupKey = nil - err := validCommit(commit, setup) + err := isValidEpochCommit(commit, setup) require.Error(t, err) }) @@ -168,7 +168,7 @@ func TestBootstrapInvalidEpochCommit(t *testing.T) { Index: 1, } - err := validCommit(commit, setup) + err := isValidEpochCommit(commit, setup) require.Error(t, err) }) } From a0ee6dc294983d04056dcdeed4042d93f4857f76 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 12:20:54 -0800 Subject: [PATCH 096/178] consolidate Latest seal and result in protocol.Snapshot --- state/protocol/snapshot.go | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/state/protocol/snapshot.go b/state/protocol/snapshot.go index efc4d6b6267..dae4d2abc81 100644 --- a/state/protocol/snapshot.go +++ b/state/protocol/snapshot.go @@ -37,15 +37,11 @@ type Snapshot interface { // selected point of the protocol state history. It will error if it doesn't exist. Identity(nodeID flow.Identifier) (*flow.Identity, error) - // LatestSeal returns the most recent included seal as of this block. The seal - // may have been included in a parent block, if this block is empty. If this - // block contains multiple seals, this returns the seal for the block with - // the greatest height. - LatestSeal() (*flow.Seal, error) - - // LatestResult returns the execution result referenced by the most recent - // included seal as of this block (see LatestSeal). - LatestResult() (*flow.ExecutionResult, error) + // LatestSealed returns the most recent included seal as of this block and + // the corresponding execution result. The seal may have been included in a + // parent block, if this block is empty. If this block contains multiple + // seals, this returns the seal for the block with the greatest height. + LatestSealedResult() (*flow.ExecutionResult, *flow.Seal, error) // Commit returns the state commitment of the most recently included seal // as of this block. It represents the sealed state. From f26d672927583feede5335248b3d0b311f87922f Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 12:25:56 -0800 Subject: [PATCH 097/178] update to SealedResult in Snapshot implementations --- state/protocol/badger/snapshot.go | 18 +++++------------- state/protocol/badger/snapshot_test.go | 2 +- state/protocol/inmem/convert.go | 6 +----- state/protocol/inmem/snapshot.go | 8 ++------ state/protocol/invalid/snapshot.go | 4 ++-- state/protocol/snapshot.go | 4 ++-- 6 files changed, 13 insertions(+), 29 deletions(-) diff --git a/state/protocol/badger/snapshot.go b/state/protocol/badger/snapshot.go index e45b9bae014..0e1c2b76d69 100644 --- a/state/protocol/badger/snapshot.go +++ b/state/protocol/badger/snapshot.go @@ -252,28 +252,20 @@ func (s *Snapshot) Commit() (flow.StateCommitment, error) { return seal.FinalState, nil } -func (s *Snapshot) LatestSeal() (*flow.Seal, error) { +func (s *Snapshot) SealedResult() (*flow.ExecutionResult, *flow.Seal, error) { seal, err := s.state.seals.ByBlockID(s.blockID) if err != nil { - return nil, fmt.Errorf("could not look up latest seal: %w", err) - } - return seal, nil -} - -func (s *Snapshot) LatestResult() (*flow.ExecutionResult, error) { - seal, err := s.LatestSeal() - if err != nil { - return nil, fmt.Errorf("could not get latest seal: %w", err) + return nil, nil, fmt.Errorf("could not look up latest seal: %w", err) } result, err := s.state.results.ByID(seal.ResultID) if err != nil { - return nil, fmt.Errorf("could not get latest result: %w", err) + return nil, nil, fmt.Errorf("could not get latest result: %w", err) } - return result, nil + return result, seal, nil } func (s *Snapshot) SealingSegment() ([]*flow.Block, error) { - seal, err := s.LatestSeal() + _, seal, err := s.SealedResult() if err != nil { return nil, fmt.Errorf("could not get seal for sealing segment: %w", err) } diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index 2c82f4378c4..926663202b0 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -264,7 +264,7 @@ func TestSealingSegment(t *testing.T) { }) } -func TestLatestResultAndSeal(t *testing.T) { +func TestLatestSealedResult(t *testing.T) { identities := unittest.CompleteIdentitySet() rootSnapshot := unittest.RootSnapshotFixture(identities) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index 776ce32af4a..a1ae91365cd 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -30,14 +30,10 @@ func FromSnapshot(from protocol.Snapshot) (*Snapshot, error) { if err != nil { return nil, fmt.Errorf("could not get identities: %w", err) } - snap.LatestSeal, err = from.LatestSeal() + snap.LatestResult, snap.LatestSeal, err = from.SealedResult() if err != nil { return nil, fmt.Errorf("could not get seal: %w", err) } - snap.LatestResult, err = from.LatestResult() - if err != nil { - return nil, fmt.Errorf("could not get result: %w", err) - } snap.SealingSegment, err = from.SealingSegment() if err != nil { return nil, fmt.Errorf("could not get sealing segment: %w", err) diff --git a/state/protocol/inmem/snapshot.go b/state/protocol/inmem/snapshot.go index 4bc40684309..4b445bd7b18 100644 --- a/state/protocol/inmem/snapshot.go +++ b/state/protocol/inmem/snapshot.go @@ -44,12 +44,8 @@ func (s Snapshot) Commit() (flow.StateCommitment, error) { return s.enc.LatestSeal.FinalState, nil } -func (s Snapshot) LatestSeal() (*flow.Seal, error) { - return s.enc.LatestSeal, nil -} - -func (s Snapshot) LatestResult() (*flow.ExecutionResult, error) { - return s.enc.LatestResult, nil +func (s Snapshot) SealedResult() (*flow.ExecutionResult, *flow.Seal, error) { + return s.enc.LatestResult, s.enc.LatestSeal, nil } func (s Snapshot) SealingSegment() ([]*flow.Block, error) { diff --git a/state/protocol/invalid/snapshot.go b/state/protocol/invalid/snapshot.go index b2166887b34..4a230ab7feb 100644 --- a/state/protocol/invalid/snapshot.go +++ b/state/protocol/invalid/snapshot.go @@ -38,8 +38,8 @@ func (u *Snapshot) Commit() (flow.StateCommitment, error) { return nil, u.err } -func (u *Snapshot) LatestSeal() (*flow.Seal, error) { - return nil, u.err +func (u *Snapshot) SealedResult() (*flow.ExecutionResult, *flow.Seal, error) { + return nil, nil, u.err } func (u *Snapshot) LatestResult() (*flow.ExecutionResult, error) { diff --git a/state/protocol/snapshot.go b/state/protocol/snapshot.go index dae4d2abc81..2399d2eaf58 100644 --- a/state/protocol/snapshot.go +++ b/state/protocol/snapshot.go @@ -37,11 +37,11 @@ type Snapshot interface { // selected point of the protocol state history. It will error if it doesn't exist. Identity(nodeID flow.Identifier) (*flow.Identity, error) - // LatestSealed returns the most recent included seal as of this block and + // SealedResult returns the most recent included seal as of this block and // the corresponding execution result. The seal may have been included in a // parent block, if this block is empty. If this block contains multiple // seals, this returns the seal for the block with the greatest height. - LatestSealedResult() (*flow.ExecutionResult, *flow.Seal, error) + SealedResult() (*flow.ExecutionResult, *flow.Seal, error) // Commit returns the state commitment of the most recently included seal // as of this block. It represents the sealed state. From 21e0956a6a62b617e81f9410701da237b58abba6 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 12:27:18 -0800 Subject: [PATCH 098/178] update snapshot mock --- state/protocol/mock/snapshot.go | 76 ++++++++++++++------------------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/state/protocol/mock/snapshot.go b/state/protocol/mock/snapshot.go index bd8db021678..6482d7ac668 100644 --- a/state/protocol/mock/snapshot.go +++ b/state/protocol/mock/snapshot.go @@ -122,16 +122,16 @@ func (_m *Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { return r0, r1 } -// LatestResult provides a mock function with given fields: -func (_m *Snapshot) LatestResult() (*flow.ExecutionResult, error) { +// Pending provides a mock function with given fields: +func (_m *Snapshot) Pending() ([]flow.Identifier, error) { ret := _m.Called() - var r0 *flow.ExecutionResult - if rf, ok := ret.Get(0).(func() *flow.ExecutionResult); ok { + var r0 []flow.Identifier + if rf, ok := ret.Get(0).(func() []flow.Identifier); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.ExecutionResult) + r0 = ret.Get(0).([]flow.Identifier) } } @@ -145,17 +145,15 @@ func (_m *Snapshot) LatestResult() (*flow.ExecutionResult, error) { return r0, r1 } -// LatestSeal provides a mock function with given fields: -func (_m *Snapshot) LatestSeal() (*flow.Seal, error) { +// Phase provides a mock function with given fields: +func (_m *Snapshot) Phase() (flow.EpochPhase, error) { ret := _m.Called() - var r0 *flow.Seal - if rf, ok := ret.Get(0).(func() *flow.Seal); ok { + var r0 flow.EpochPhase + if rf, ok := ret.Get(0).(func() flow.EpochPhase); ok { r0 = rf() } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.Seal) - } + r0 = ret.Get(0).(flow.EpochPhase) } var r1 error @@ -168,16 +166,16 @@ func (_m *Snapshot) LatestSeal() (*flow.Seal, error) { return r0, r1 } -// Pending provides a mock function with given fields: -func (_m *Snapshot) Pending() ([]flow.Identifier, error) { +// QuorumCertificate provides a mock function with given fields: +func (_m *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { ret := _m.Called() - var r0 []flow.Identifier - if rf, ok := ret.Get(0).(func() []flow.Identifier); ok { + var r0 *flow.QuorumCertificate + if rf, ok := ret.Get(0).(func() *flow.QuorumCertificate); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]flow.Identifier) + r0 = ret.Get(0).(*flow.QuorumCertificate) } } @@ -191,48 +189,36 @@ func (_m *Snapshot) Pending() ([]flow.Identifier, error) { return r0, r1 } -// Phase provides a mock function with given fields: -func (_m *Snapshot) Phase() (flow.EpochPhase, error) { +// SealedResult provides a mock function with given fields: +func (_m *Snapshot) SealedResult() (*flow.ExecutionResult, *flow.Seal, error) { ret := _m.Called() - var r0 flow.EpochPhase - if rf, ok := ret.Get(0).(func() flow.EpochPhase); ok { + var r0 *flow.ExecutionResult + if rf, ok := ret.Get(0).(func() *flow.ExecutionResult); ok { r0 = rf() } else { - r0 = ret.Get(0).(flow.EpochPhase) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.ExecutionResult) + } } - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { + var r1 *flow.Seal + if rf, ok := ret.Get(1).(func() *flow.Seal); ok { r1 = rf() } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// QuorumCertificate provides a mock function with given fields: -func (_m *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { - ret := _m.Called() - - var r0 *flow.QuorumCertificate - if rf, ok := ret.Get(0).(func() *flow.QuorumCertificate); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*flow.QuorumCertificate) + if ret.Get(1) != nil { + r1 = ret.Get(1).(*flow.Seal) } } - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() + var r2 error + if rf, ok := ret.Get(2).(func() error); ok { + r2 = rf() } else { - r1 = ret.Error(1) + r2 = ret.Error(2) } - return r0, r1 + return r0, r1, r2 } // SealingSegment provides a mock function with given fields: From 795ced5f65fcfff923ffd2e2707ad3badbfbd3cc Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 14:13:24 -0800 Subject: [PATCH 099/178] use new SealedResult API --- cmd/scaffold.go | 8 ++++---- state/protocol/badger/mutator_test.go | 16 ++++++++-------- state/protocol/badger/snapshot_test.go | 4 ++-- state/protocol/badger/state.go | 18 ++++++------------ state/protocol/badger/validity.go | 8 ++------ state/protocol/invalid/snapshot.go | 4 ---- 6 files changed, 22 insertions(+), 36 deletions(-) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 7b68c795e9f..5a64015f2c1 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -466,10 +466,10 @@ func (fnb *FlowNodeBuilder) initState() { fnb.RootQC, err = loadRootQC(fnb.BaseConfig.BootstrapDir) fnb.MustNot(err).Msg("could not load root QC") - fnb.RootResult, err = state.AtBlockID(rootBlock.ID()).LatestResult() - fnb.MustNot(err).Msg("could not get root result") - fnb.RootSeal, err = state.AtBlockID(rootBlock.ID()).LatestSeal() - fnb.MustNot(err).Msg("could not get root seal") + result, seal, err := state.AtBlockID(rootBlock.ID()).SealedResult() + fnb.MustNot(err).Msg("could not get sealed result") + fnb.RootResult = result + fnb.RootSeal = seal } else { // Bootstrap! diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 60ab6228c9f..b33aaf2a0a3 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -62,7 +62,7 @@ func TestBootstrapValid(t *testing.T) { err = db.View(operation.LookupBlockSeal(genesisID, &sealID)) require.NoError(t, err) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) err = db.View(operation.RetrieveSeal(sealID, seal)) require.NoError(t, err) @@ -123,7 +123,7 @@ func TestExtendSealedBoundary(t *testing.T) { util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) finalCommit, err := state.Final().Commit() require.NoError(t, err) @@ -566,7 +566,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { util.RunWithFullProtocolStateAndConsumer(t, rootSnapshot, consumer, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) // we should begin the epoch in the staking phase @@ -766,7 +766,7 @@ func TestExtendConflictingEpochEvents(t *testing.T) { util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) // add two conflicting blocks for each service event to reference @@ -859,7 +859,7 @@ func TestExtendEpochSetupInvalid(t *testing.T) { util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) // add a block for the first seal to reference @@ -941,7 +941,7 @@ func TestExtendEpochCommitInvalid(t *testing.T) { util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) // add a block for the first seal to reference @@ -1088,7 +1088,7 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) // add a block for the first seal to reference @@ -1223,7 +1223,7 @@ func TestHeaderExtendValid(t *testing.T) { util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { head, err := rootSnapshot.Head() require.NoError(t, err) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) extend := unittest.BlockWithParentFixture(head) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index 926663202b0..698d6d46d87 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -387,7 +387,7 @@ func TestQuorumCertificate(t *testing.T) { func TestSnapshot_EpochQuery(t *testing.T) { identities := unittest.CompleteIdentitySet() rootSnapshot := unittest.RootSnapshotFixture(identities) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.MutableState) { @@ -490,7 +490,7 @@ func TestSnapshot_EpochFirstView(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(identities) head, err := rootSnapshot.Head() require.NoError(t, err) - seal, err := rootSnapshot.LatestSeal() + _, seal, err := rootSnapshot.SealedResult() require.NoError(t, err) util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.MutableState) { diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index d2a05627a99..651c2d9e352 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -99,10 +99,10 @@ func Bootstrap( return fmt.Errorf("could not insert child index for head block (id=%x): %w", head.ID(), err) } - // 2) insert the root execution result into the database and index it - result, err := root.LatestResult() + // 2) insert the root execution result and seal into the database and index it + result, seal, err := root.SealedResult() if err != nil { - return fmt.Errorf("could not get latest result from root snapshot: %w", err) + return fmt.Errorf("could not get sealed result from root snapshot: %w", err) } err = operation.SkipDuplicates(operation.InsertExecutionResult(result))(tx) if err != nil { @@ -112,12 +112,6 @@ func Bootstrap( if err != nil { return fmt.Errorf("could not index root result: %w", err) } - - // 3) insert the root block seal into the database and index it - seal, err := root.LatestSeal() - if err != nil { - return fmt.Errorf("could not get latest seal from root snapshot: %w", err) - } err = operation.SkipDuplicates(operation.InsertSeal(seal.ID(), seal))(tx) if err != nil { return fmt.Errorf("could not insert root seal: %w", err) @@ -127,7 +121,7 @@ func Bootstrap( return fmt.Errorf("could not index root block seal: %w", err) } - // 4) insert the root quorum certificate into the database + // 3) insert the root quorum certificate into the database qc, err := root.QuorumCertificate() if err != nil { return fmt.Errorf("could not get root qc: %w", err) @@ -137,7 +131,7 @@ func Bootstrap( return fmt.Errorf("could not insert root qc: %w", err) } - // 5) initialize the current protocol state values + // 4) initialize the current protocol state values err = operation.InsertStartedView(head.Header.ChainID, head.Header.View)(tx) if err != nil { return fmt.Errorf("could not insert started view: %w", err) @@ -159,7 +153,7 @@ func Bootstrap( return fmt.Errorf("could not insert sealed height: %w", err) } - // 6) initialize values related to the epoch logic + // 5) initialize values related to the epoch logic err = state.bootstrapEpoch(root)(tx) if err != nil { return fmt.Errorf("could not bootstrap epoch values: %w", err) diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index bb6adf4ce84..2757a1e61dc 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -127,13 +127,9 @@ func isValidRootSnapshot(snap protocol.Snapshot) error { if err != nil { return fmt.Errorf("could not get sealing segment: %w", err) } - seal, err := snap.LatestSeal() + result, seal, err := snap.SealedResult() if err != nil { - return fmt.Errorf("could not latest seal: %w", err) - } - result, err := snap.LatestResult() - if err != nil { - return fmt.Errorf("could not get latest result: %w", err) + return fmt.Errorf("could not latest sealed result: %w", err) } if len(segment) == 0 { diff --git a/state/protocol/invalid/snapshot.go b/state/protocol/invalid/snapshot.go index 4a230ab7feb..67d89d96d6f 100644 --- a/state/protocol/invalid/snapshot.go +++ b/state/protocol/invalid/snapshot.go @@ -42,10 +42,6 @@ func (u *Snapshot) SealedResult() (*flow.ExecutionResult, *flow.Seal, error) { return nil, nil, u.err } -func (u *Snapshot) LatestResult() (*flow.ExecutionResult, error) { - return nil, u.err -} - func (u *Snapshot) SealingSegment() ([]*flow.Block, error) { return nil, u.err } From 59a48225d68d575822560945eb0f83246f995235 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 15:39:22 -0800 Subject: [PATCH 100/178] SealedResult tests --- state/protocol/badger/snapshot_test.go | 62 +++++++++++++++++++++++--- storage/badger/payloads_test.go | 2 +- utils/unittest/fixtures.go | 33 +++++++++----- 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index 698d6d46d87..0b8e703469b 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -269,20 +269,72 @@ func TestLatestSealedResult(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(identities) t.Run("root snapshot", func(t *testing.T) { + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + gotResult, gotSeal, err := state.Final().SealedResult() + require.NoError(t, err) + expectedResult, expectedSeal, err := rootSnapshot.SealedResult() + require.NoError(t, err) + assert.Equal(t, expectedResult, gotResult) + assert.Equal(t, expectedSeal, gotSeal) + }) }) t.Run("non-root snapshot", func(t *testing.T) { - t.Run("reference block contains seal", func(t *testing.T) { + head, err := rootSnapshot.Head() + require.NoError(t, err) - }) + util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + block1 := unittest.BlockWithParentFixture(head) + err = state.Extend(&block1) + require.NoError(t, err) - t.Run("reference block contains no seal", func(t *testing.T) { + block2 := unittest.BlockWithParentFixture(block1.Header) + receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1) + block2.SetPayload(unittest.PayloadFixture(unittest.WithSeals(seal1), unittest.WithReceipts(receipt1))) + err = state.Extend(&block2) + require.NoError(t, err) - }) + // B1 <- B2(R1,S1) + // querying B2 should return result R1, seal S1 + t.Run("reference block contains seal", func(t *testing.T) { + gotResult, gotSeal, err := state.AtBlockID(block2.ID()).SealedResult() + require.NoError(t, err) + assert.Equal(t, &block2.Payload.Receipts[0].ExecutionResult, gotResult) + assert.Equal(t, block2.Payload.Seals[0], gotSeal) + }) + + block3 := unittest.BlockWithParentFixture(block2.Header) + err = state.Extend(&block3) + require.NoError(t, err) - t.Run("reference block contains multiple seals", func(t *testing.T) { + // B1 <- B2(R1,S1) <- B3 + // querying B3 should still return (R1,S1) even though they are in parent block + t.Run("reference block contains no seal", func(t *testing.T) { + gotResult, gotSeal, err := state.AtBlockID(block2.ID()).SealedResult() + require.NoError(t, err) + assert.Equal(t, &receipt1.ExecutionResult, gotResult) + assert.Equal(t, seal1, gotSeal) + }) + + // B1 <- B2(R1,S1) <- B3 <- B4(R2,S2,R3,S3) + // There are two seals in B4 - should return latest by height (S3,R3) + t.Run("reference block contains multiple seals", func(t *testing.T) { + receipt2, seal2 := unittest.ReceiptAndSealForBlock(&block2) + receipt3, seal3 := unittest.ReceiptAndSealForBlock(&block3) + block4 := unittest.BlockWithParentFixture(block3.Header) + block4.SetPayload(unittest.PayloadFixture( + unittest.WithReceipts(receipt2, receipt3), + unittest.WithSeals(seal2, seal3), + )) + err = state.Extend(&block4) + require.NoError(t, err) + gotResult, gotSeal, err := state.AtBlockID(block4.ID()).SealedResult() + require.NoError(t, err) + assert.Equal(t, &receipt3.ExecutionResult, gotResult) + assert.Equal(t, seal3, gotSeal) + }) }) }) } diff --git a/storage/badger/payloads_test.go b/storage/badger/payloads_test.go index 8bea5e38d50..5ec93dd6299 100644 --- a/storage/badger/payloads_test.go +++ b/storage/badger/payloads_test.go @@ -32,7 +32,7 @@ func TestPayloadStoreRetrieve(t *testing.T) { expected.Receipts = make([]*flow.ExecutionReceipt, 0) // store payload - err := store.Store(blockID, expected) + err := store.Store(blockID, &expected) require.NoError(t, err) // fetch payload diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 5960125cd16..2c0ad4cc21d 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -166,28 +166,39 @@ func StateDeltaFixture() *messages.ExecutionStateDelta { } } -func PayloadFixture(options ...func(*flow.Payload)) *flow.Payload { - payload := flow.Payload{ - Guarantees: CollectionGuaranteesFixture(16), - Seals: Seal.Fixtures(16), - } +func ReceiptAndSealForBlock(block *flow.Block) (*flow.ExecutionReceipt, *flow.Seal) { + receipt := ReceiptForBlockFixture(block) + seal := Seal.Fixture(Seal.WithBlock(block.Header), Seal.WithResult(&receipt.ExecutionResult)) + return receipt, seal +} + +func PayloadFixture(options ...func(*flow.Payload)) flow.Payload { + payload := flow.EmptyPayload() for _, option := range options { option(&payload) } - return &payload + return payload +} + +func WithSeals(seals ...*flow.Seal) func(*flow.Payload) { + return func(payload *flow.Payload) { + payload.Seals = append(payload.Seals, seals...) + } } -func WithoutSeals(payload *flow.Payload) { - payload.Seals = nil +func WithReceipts(receipts ...*flow.ExecutionReceipt) func(*flow.Payload) { + return func(payload *flow.Payload) { + payload.Receipts = append(payload.Receipts, receipts...) + } } func BlockWithParentFixture(parent *flow.Header) flow.Block { - payload := PayloadFixture(WithoutSeals) + payload := PayloadFixture() header := BlockHeaderWithParentFixture(parent) header.PayloadHash = payload.Hash() return flow.Block{ Header: &header, - Payload: payload, + Payload: &payload, } } @@ -229,7 +240,7 @@ func StateDeltaWithParentFixture(parent *flow.Header) *messages.ExecutionStateDe header.PayloadHash = payload.Hash() block := flow.Block{ Header: &header, - Payload: payload, + Payload: &payload, } var stateInteractions []*delta.Snapshot From 182a0794596f254c3a23da17c9168032d2458411 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 15:51:13 -0800 Subject: [PATCH 101/178] check root snapshot qc block ID --- state/protocol/badger/validity.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index 2757a1e61dc..f434a2930f9 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -121,6 +121,7 @@ func isValidEpochCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error } // isValidRootSnapshot checks internal consistency of root state snapshot +// TODO not used func isValidRootSnapshot(snap protocol.Snapshot) error { segment, err := snap.SealingSegment() @@ -135,14 +136,17 @@ func isValidRootSnapshot(snap protocol.Snapshot) error { if len(segment) == 0 { return fmt.Errorf("invalid empty sealing segment") } + // TAIL <- ... <- HEAD head := segment[len(segment)-1] // reference block of the snapshot tail := segment[0] // last sealed block + headID := head.ID() + tailID := tail.ID() - if result.BlockID != tail.ID() { + if result.BlockID != tailID { return fmt.Errorf("root execution result for wrong block (%x != %x)", result.BlockID, tail.ID()) } - if seal.BlockID != tail.ID() { + if seal.BlockID != tailID { return fmt.Errorf("root block seal for wrong block (%x != %x)", seal.BlockID, tail.ID()) } @@ -150,6 +154,15 @@ func isValidRootSnapshot(snap protocol.Snapshot) error { return fmt.Errorf("root block seal for wrong execution result (%x != %x)", seal.ResultID, result.ID()) } + // root qc must be for reference block of snapshot + qc, err := snap.QuorumCertificate() + if err != nil { + return fmt.Errorf("could not get qc for root snapshot: %w", err) + } + if qc.BlockID != headID { + return fmt.Errorf("qc is for wrong block (got: %x, expected: %x)", qc.BlockID, headID) + } + firstView, err := snap.Epochs().Current().FirstView() if err != nil { return fmt.Errorf("could not get first view: %w", err) From ddb5344fd61c1643dcf3f51eb1eb57e484d86d3d Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 16:13:56 -0800 Subject: [PATCH 102/178] use correct random source in bootstrap/testnet --- cmd/bootstrap/cmd/seal.go | 10 ++++++++-- integration/testnet/network.go | 9 +++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cmd/bootstrap/cmd/seal.go b/cmd/bootstrap/cmd/seal.go index bfa1147a79c..b79d86f878d 100644 --- a/cmd/bootstrap/cmd/seal.go +++ b/cmd/bootstrap/cmd/seal.go @@ -1,6 +1,7 @@ package cmd import ( + "crypto/rand" "encoding/hex" "github.com/onflow/flow-go/cmd/bootstrap/run" @@ -24,7 +25,12 @@ func constructRootResultAndSeal( } participants := model.ToIdentityList(participantNodes) - blockID := block.ID() + + randomSource := make([]byte, flow.EpochSetupRandomSourceLength) + _, err = rand.Read(randomSource) + if err != nil { + log.Fatal().Err(err).Msg("could not generate random source for epoch setup event") + } epochSetup := &flow.EpochSetup{ Counter: flagEpochCounter, @@ -32,7 +38,7 @@ func constructRootResultAndSeal( FinalView: block.Header.View + leader.EstimatedSixMonthOfViews, Participants: participants, Assignments: assignments, - RandomSource: blockID[:], + RandomSource: randomSource, } dkgLookup := model.ToDKGLookup(dkgData, participants) diff --git a/integration/testnet/network.go b/integration/testnet/network.go index 14b65dfe95a..5628bc04053 100644 --- a/integration/testnet/network.go +++ b/integration/testnet/network.go @@ -589,7 +589,6 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo // generate root block root := run.GenerateRootBlock(chainID, parentID, height, timestamp) - rootID := root.Header.ID() // generate QC nodeInfos := bootstrap.FilterByRole(toNodeInfos(confs), flow.RoleConsensus) @@ -608,13 +607,19 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo return nil, nil, nil, err } + randomSource := make([]byte, flow.EpochSetupRandomSourceLength) + _, err = rand.Read(randomSource) + if err != nil { + return nil, nil, nil, err + } + // generate epoch service events epochSetup := &flow.EpochSetup{ Counter: epochCounter, FinalView: root.Header.View + leader.EstimatedSixMonthOfViews, Participants: participants, Assignments: clusterAssignments, - RandomSource: rootID[:], + RandomSource: randomSource, } dkgLookup := bootstrap.ToDKGLookup(dkg, participants) From 0109fc022cbdf7d7810e6fd2e9c0c365c61cd20c Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 17:21:49 -0800 Subject: [PATCH 103/178] include receipts in sealing segment test --- state/protocol/badger/snapshot_test.go | 28 ++++++++------------------ 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index 0b8e703469b..fe4ca0576f2 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -158,11 +158,8 @@ func TestSealingSegment(t *testing.T) { // build a block sealing block1 block2 := unittest.BlockWithParentFixture(block1.Header) - block2.SetPayload(flow.Payload{ - Seals: []*flow.Seal{ - unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), - }, - }) + receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1) + block2.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1), unittest.WithSeals(seal1))) err = state.Extend(&block2) require.NoError(t, err) @@ -200,11 +197,8 @@ func TestSealingSegment(t *testing.T) { // build the block sealing block 1 blockN := unittest.BlockWithParentFixture(parent.Header) - blockN.SetPayload(flow.Payload{ - Seals: []*flow.Seal{ - unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), - }, - }) + receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1) + blockN.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1), unittest.WithSeals(seal1))) err = state.Extend(&blockN) require.NoError(t, err) @@ -235,20 +229,14 @@ func TestSealingSegment(t *testing.T) { require.NoError(t, err) block3 := unittest.BlockWithParentFixture(block2.Header) - block3.SetPayload(flow.Payload{ - Seals: []*flow.Seal{ - unittest.Seal.Fixture(unittest.Seal.WithBlockID(block1.ID())), - }, - }) + receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1) + block3.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt1), unittest.WithSeals(seal1))) err = state.Extend(&block3) require.NoError(t, err) block4 := unittest.BlockWithParentFixture(block3.Header) - block4.SetPayload(flow.Payload{ - Seals: []*flow.Seal{ - unittest.Seal.Fixture(unittest.Seal.WithBlockID(block2.ID())), - }, - }) + receipt2, seal2 := unittest.ReceiptAndSealForBlock(&block2) + block4.SetPayload(unittest.PayloadFixture(unittest.WithReceipts(receipt2), unittest.WithSeals(seal2))) err = state.Extend(&block4) require.NoError(t, err) From d081fddbaff28bf9564c553b7e462fc6fc8b2a40 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 17:22:11 -0800 Subject: [PATCH 104/178] move bootstrap tests to state_test --- state/protocol/badger/state_test.go | 136 ++++++++++++++++++++++--- state/protocol/badger/validity_test.go | 79 -------------- utils/unittest/equals.go | 18 ++++ 3 files changed, 141 insertions(+), 92 deletions(-) diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index 293847bf029..53c221c355c 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -1,6 +1,8 @@ package badger_test import ( + "fmt" + "os" "testing" "github.com/dgraph-io/badger/v2" @@ -9,14 +11,17 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" + "github.com/onflow/flow-go/state/protocol" bprotocol "github.com/onflow/flow-go/state/protocol/badger" - "github.com/onflow/flow-go/state/protocol/util" + "github.com/onflow/flow-go/state/protocol/inmem" + protoutil "github.com/onflow/flow-go/state/protocol/util" storagebadger "github.com/onflow/flow-go/storage/badger" + storutil "github.com/onflow/flow-go/storage/util" "github.com/onflow/flow-go/utils/unittest" ) -// TestBootstrapAndOpen verifies after bootstraping with a state root, -// we should be able to open it and got the same state root +// TestBootstrapAndOpen verifies after bootstrapping with a root snapshot +// we should be able to open it and got the same state. func TestBootstrapAndOpen(t *testing.T) { // create a state root and bootstrap the protocol state with it @@ -25,11 +30,9 @@ func TestBootstrapAndOpen(t *testing.T) { block.Header.ParentID = unittest.IdentifierFixture() }) - util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { - - // protocol state has been bootstrapped, now open a protocol state with - // the database - metrics := &metrics.NoopCollector{} + protoutil.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { + // protocol state has been bootstrapped, now open a protocol state with the database + metrics := new(metrics.NoopCollector) all := storagebadger.InitAll(metrics, db) state, err := bprotocol.OpenState( metrics, @@ -43,12 +46,119 @@ func TestBootstrapAndOpen(t *testing.T) { all.Statuses) require.NoError(t, err) - expected, err := rootSnapshot.Head() - require.NoError(t, err) - actual, err := state.Final().Head() - require.NoError(t, err) + unittest.AssertSnapshotsEqual(t, rootSnapshot, state.Final()) + }) +} + +// TestBootstrapNonRoot tests bootstrapping the protocol state +// from arbitrary states. +func TestBootstrapNonRoot(t *testing.T) { + + // start with a regular post-spork root snapshot + participants := unittest.CompleteIdentitySet() + rootSnapshot := unittest.RootSnapshotFixture(participants) + rootBlock, err := rootSnapshot.Head() + require.NoError(t, err) + + t.Run("with one block built", func(t *testing.T) { + after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) { + block1 := unittest.BlockWithParentFixture(rootBlock) + err = state.Extend(&block1) + require.NoError(t, err) + }) + bootstrap(t, after, func(state *bprotocol.State, err error) { + assert.NoError(t, err) + }) + }) +} + +func TestBootstrapDuplicateID(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, + {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, + {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, // dupe + } + root := unittest.RootSnapshotFixture(participants) + bootstrap(t, root, func(state *bprotocol.State, err error) { + assert.Error(t, err) + }) +} - assert.Equal(t, expected, actual) +func TestBootstrapZeroStake(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 0}, + {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, + {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + } + root := unittest.RootSnapshotFixture(participants) + bootstrap(t, root, func(state *bprotocol.State, err error) { + assert.Error(t, err) }) +} +func TestBootstrapMissingRole(t *testing.T) { + requiredRoles := []flow.Role{ + flow.RoleConsensus, + flow.RoleCollection, + flow.RoleExecution, + flow.RoleVerification, + } + + for _, role := range requiredRoles { + t.Run(fmt.Sprintf("no %s nodes", role.String()), func(t *testing.T) { + participants := unittest.IdentityListFixture(5, unittest.WithAllRolesExcept(role)) + root := unittest.RootSnapshotFixture(participants) + bootstrap(t, root, func(state *bprotocol.State, err error) { + assert.Error(t, err) + }) + }) + } +} + +func TestBootstrapExistingAddress(t *testing.T) { + participants := flow.IdentityList{ + {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, + {NodeID: flow.Identifier{0x02}, Address: "a1", Role: flow.RoleConsensus, Stake: 2}, // dupe address + {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, + {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, + } + + root := unittest.RootSnapshotFixture(participants) + bootstrap(t, root, func(state *bprotocol.State, err error) { + assert.Error(t, err) + }) +} + +// bootstraps protocol state with the given snapshot and invokes the callback +// with the result of the constructor +func bootstrap(t *testing.T, rootSnapshot protocol.Snapshot, f func(*bprotocol.State, error)) { + metrics := metrics.NewNoopCollector() + dir := unittest.TempDir(t) + defer os.RemoveAll(dir) + db := unittest.BadgerDB(t, dir) + defer db.Close() + headers, _, seals, _, _, blocks, setups, commits, statuses, results := storutil.StorageLayer(t, db) + state, err := bprotocol.Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) + f(state, err) +} + +// snapshotAfter returns an in-memory state snapshot of the FINALIZED state after +// bootstrapping the protocol state from the root snapshot, applying the state +// mutating function f, and clearing the on-disk protocol state. +// +// This is used for generating valid snapshots to use when testing bootstrapping +// from non-root states. +func snapshotAfter(t *testing.T, rootSnapshot protocol.Snapshot, f func(*bprotocol.FollowerState)) protocol.Snapshot { + var after protocol.Snapshot + protoutil.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { + f(state) + final := state.Final() + var err error + after, err = inmem.FromSnapshot(final) + require.NoError(t, err) + }) + return after } diff --git a/state/protocol/badger/validity_test.go b/state/protocol/badger/validity_test.go index 8f08feb18e5..cd08bf4b1b7 100644 --- a/state/protocol/badger/validity_test.go +++ b/state/protocol/badger/validity_test.go @@ -1,97 +1,18 @@ package badger import ( - "fmt" - "os" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" - "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/state/protocol" - "github.com/onflow/flow-go/storage/util" "github.com/onflow/flow-go/utils/unittest" ) var participants = unittest.IdentityListFixture(5, unittest.WithAllRoles()) -// bootstraps protocol state with the given snapshot and invokes the callback -// with the result of the constructor -func bootstrap(t *testing.T, rootSnapshot protocol.Snapshot, f func(*State, error)) { - metrics := metrics.NewNoopCollector() - dir := unittest.TempDir(t) - defer os.RemoveAll(dir) - db := unittest.BadgerDB(t, dir) - defer db.Close() - headers, _, seals, _, _, blocks, setups, commits, statuses, results := util.StorageLayer(t, db) - state, err := Bootstrap(metrics, db, headers, seals, results, blocks, setups, commits, statuses, rootSnapshot) - f(state, err) -} - -func TestBootstrapDuplicateID(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, // dupe - } - root := unittest.RootSnapshotFixture(participants) - bootstrap(t, root, func(state *State, err error) { - assert.Error(t, err) - }) -} - -func TestBootstrapZeroStake(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 0}, - {NodeID: flow.Identifier{0x02}, Address: "a2", Role: flow.RoleConsensus, Stake: 2}, - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - root := unittest.RootSnapshotFixture(participants) - bootstrap(t, root, func(state *State, err error) { - assert.Error(t, err) - }) -} - -func TestBootstrapMissingRole(t *testing.T) { - requiredRoles := []flow.Role{ - flow.RoleConsensus, - flow.RoleCollection, - flow.RoleExecution, - flow.RoleVerification, - } - - for _, role := range requiredRoles { - t.Run(fmt.Sprintf("no %s nodes", role.String()), func(t *testing.T) { - participants := unittest.IdentityListFixture(5, unittest.WithAllRolesExcept(role)) - root := unittest.RootSnapshotFixture(participants) - bootstrap(t, root, func(state *State, err error) { - assert.Error(t, err) - }) - }) - } -} - -func TestBootstrapExistingAddress(t *testing.T) { - participants := flow.IdentityList{ - {NodeID: flow.Identifier{0x01}, Address: "a1", Role: flow.RoleCollection, Stake: 1}, - {NodeID: flow.Identifier{0x02}, Address: "a1", Role: flow.RoleConsensus, Stake: 2}, // dupe address - {NodeID: flow.Identifier{0x03}, Address: "a3", Role: flow.RoleExecution, Stake: 3}, - {NodeID: flow.Identifier{0x04}, Address: "a4", Role: flow.RoleVerification, Stake: 4}, - } - - root := unittest.RootSnapshotFixture(participants) - bootstrap(t, root, func(state *State, err error) { - assert.Error(t, err) - }) -} - func TestEpochSetupValidity(t *testing.T) { t.Run("invalid first/final view", func(t *testing.T) { _, _, seal := unittest.BootstrapFixture(participants) diff --git a/utils/unittest/equals.go b/utils/unittest/equals.go index dc02844e891..28f2118a73f 100644 --- a/utils/unittest/equals.go +++ b/utils/unittest/equals.go @@ -1,11 +1,15 @@ package unittest import ( + "encoding/json" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/inmem" ) func toHex(ids []flow.Identifier) []string { @@ -23,3 +27,17 @@ func IDEqual(t *testing.T, id1, id2 flow.Identifier) { func IDsEqual(t *testing.T, id1, id2 []flow.Identifier) { require.Equal(t, toHex(id1), toHex(id2)) } + +func AssertSnapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) { + inmem1, err := inmem.FromSnapshot(snap1) + require.NoError(t, err) + inmem2, err := inmem.FromSnapshot(snap2) + require.NoError(t, err) + + encoded1, err := json.Marshal(inmem1) + require.NoError(t, err) + encoded2, err := json.Marshal(inmem2) + require.NoError(t, err) + + assert.Equal(t, encoded1, encoded2) +} From b51930da7184be1929d75901d8b28e0563730db4 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 17:43:02 -0800 Subject: [PATCH 105/178] begin adding non-root bootstrapping --- state/protocol/badger/state_test.go | 65 +++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index 53c221c355c..27ebebe6559 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -50,8 +50,11 @@ func TestBootstrapAndOpen(t *testing.T) { }) } -// TestBootstrapNonRoot tests bootstrapping the protocol state -// from arbitrary states. +// TestBootstrapNonRoot tests bootstrapping the protocol state from arbitrary states. +// +// NOTE: for all these cases, we build a final child block (CHILD). This is +// needed otherwise the parent block would not have a valid QC, since the QC +// is stored in the child. func TestBootstrapNonRoot(t *testing.T) { // start with a regular post-spork root snapshot @@ -60,14 +63,45 @@ func TestBootstrapNonRoot(t *testing.T) { rootBlock, err := rootSnapshot.Head() require.NoError(t, err) + // should be able to bootstrap from snapshot after building one block + // ROOT <- B1 <- CHILD t.Run("with one block built", func(t *testing.T) { - after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) { + after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { + block1 := unittest.BlockWithParentFixture(rootBlock) + buildBlock(t, state, &block1) + child := unittest.BlockWithParentFixture(block1.Header) + buildBlock(t, state, &child) + + return state.AtBlockID(block1.ID()) + }) + + bootstrap(t, after, func(state *bprotocol.State, err error) { + assert.NoError(t, err) + unittest.AssertSnapshotsEqual(t, after, state.Final()) + }) + }) + + // should be able to bootstrap from snapshot after sealing a non-root block + // ROOT <- B1 <- B2(S1) <- CHILD + t.Run("with sealed block", func(t *testing.T) { + after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { block1 := unittest.BlockWithParentFixture(rootBlock) - err = state.Extend(&block1) - require.NoError(t, err) + buildBlock(t, state, &block1) + + receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1) + block2 := unittest.BlockWithParentFixture(block1.Header) + block2.SetPayload(unittest.PayloadFixture(unittest.WithSeals(seal1), unittest.WithReceipts(receipt1))) + buildBlock(t, state, &block2) + + child := unittest.BlockWithParentFixture(block2.Header) + buildBlock(t, state, &child) + + return state.AtBlockID(block2.ID()) }) + bootstrap(t, after, func(state *bprotocol.State, err error) { assert.NoError(t, err) + unittest.AssertSnapshotsEqual(t, after, state.Final()) }) }) } @@ -145,20 +179,27 @@ func bootstrap(t *testing.T, rootSnapshot protocol.Snapshot, f func(*bprotocol.S f(state, err) } -// snapshotAfter returns an in-memory state snapshot of the FINALIZED state after -// bootstrapping the protocol state from the root snapshot, applying the state -// mutating function f, and clearing the on-disk protocol state. +// snapshotAfter bootstraps the protocol state from the root snapshot, applies +// the state-changing function f, clears the on-disk state, and returns a +// memory-backed snapshot corresponding to that returned by f. // // This is used for generating valid snapshots to use when testing bootstrapping // from non-root states. -func snapshotAfter(t *testing.T, rootSnapshot protocol.Snapshot, f func(*bprotocol.FollowerState)) protocol.Snapshot { +func snapshotAfter(t *testing.T, rootSnapshot protocol.Snapshot, f func(*bprotocol.FollowerState) protocol.Snapshot) protocol.Snapshot { var after protocol.Snapshot protoutil.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { - f(state) - final := state.Final() + snap := f(state) var err error - after, err = inmem.FromSnapshot(final) + after, err = inmem.FromSnapshot(snap) require.NoError(t, err) }) return after } + +// buildBlock builds and marks valid the given block +func buildBlock(t *testing.T, state protocol.MutableState, block *flow.Block) { + err := state.Extend(block) + require.NoError(t, err) + err = state.MarkValid(block.ID()) + require.NoError(t, err) +} From cdd40c26bceee0758326597a6b0bda194dd510bd Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Feb 2021 19:58:30 -0800 Subject: [PATCH 106/178] fix bootstrapping use head when indexing latest seal use head as root block - necessary because the qc included in a snapshot is for the root block -- but may need re-thinking because now we have blocks in storage below the root block --- state/protocol/badger/state.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 651c2d9e352..b878efbef86 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -116,7 +116,7 @@ func Bootstrap( if err != nil { return fmt.Errorf("could not insert root seal: %w", err) } - err = operation.IndexBlockSeal(seal.BlockID, seal.ID())(tx) + err = operation.IndexBlockSeal(head.ID(), seal.ID())(tx) if err != nil { return fmt.Errorf("could not index root block seal: %w", err) } @@ -140,7 +140,7 @@ func Bootstrap( if err != nil { return fmt.Errorf("could not insert started view: %w", err) } - err = operation.InsertRootHeight(tail.Header.Height)(tx) + err = operation.InsertRootHeight(head.Header.Height)(tx) if err != nil { return fmt.Errorf("could not insert root height: %w", err) } From da89d0b0fdfb86debdc001975157cdfdf1fa42c8 Mon Sep 17 00:00:00 2001 From: danuio Date: Tue, 9 Feb 2021 11:27:18 +0000 Subject: [PATCH 107/178] Add tests and move methods to convert --- access/api.go | 3 +- access/handler.go | 15 +----- engine/access/mock/access_api_client.go | 30 ++++++++++++ engine/access/rpc/backend/backend.go | 11 +++-- engine/access/rpc/backend/backend_test.go | 57 +++++++++++++++++++++++ engine/common/rpc/convert/convert.go | 18 +++++++ go.mod | 2 +- go.sum | 6 +-- 8 files changed, 118 insertions(+), 24 deletions(-) diff --git a/access/api.go b/access/api.go index 55906604d19..46092fce9b6 100644 --- a/access/api.go +++ b/access/api.go @@ -8,7 +8,6 @@ import ( "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/state/protocol" ) // API provides all public-facing functionality of the Flow Access API. @@ -41,7 +40,7 @@ type API interface { GetEventsForHeightRange(ctx context.Context, eventType string, startHeight, endHeight uint64) ([]flow.BlockEvents, error) GetEventsForBlockIDs(ctx context.Context, eventType string, blockIDs []flow.Identifier) ([]flow.BlockEvents, error) - GetLatestProtocolStateSnapshot(ctx context.Context) (protocol.Snapshot, error) + GetLatestProtocolStateSnapshot(ctx context.Context) ([]byte, error) } // TODO: Combine this with flow.TransactionResult? diff --git a/access/handler.go b/access/handler.go index 74bf483fbfc..f4f498472e1 100644 --- a/access/handler.go +++ b/access/handler.go @@ -2,7 +2,6 @@ package access import ( "context" - "encoding/json" "github.com/golang/protobuf/ptypes" "github.com/onflow/flow/protobuf/go/flow/access" @@ -12,8 +11,6 @@ import ( "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" - - "github.com/onflow/flow-go/state/protocol/inmem" ) type Handler struct { @@ -415,18 +412,8 @@ func (h *Handler) GetLatestProtocolStateSnapshot(ctx context.Context, req *acces return nil, err } - serializable, err := inmem.FromSnapshot(snapshot) - if err != nil { - return nil, err - } - - data, err := json.Marshal(serializable) - if err != nil { - return nil, err - } - return &access.ProtocolStateSnapshotResponse{ - SerializedSnapshot: data, + SerializedSnapshot: snapshot, }, nil } diff --git a/engine/access/mock/access_api_client.go b/engine/access/mock/access_api_client.go index 179c89646b5..d38e98fdadf 100644 --- a/engine/access/mock/access_api_client.go +++ b/engine/access/mock/access_api_client.go @@ -467,6 +467,36 @@ func (_m *AccessAPIClient) GetLatestBlockHeader(ctx context.Context, in *access. return r0, r1 } +// GetLatestProtocolStateSnapshot provides a mock function with given fields: ctx, in, opts +func (_m *AccessAPIClient) GetLatestProtocolStateSnapshot(ctx context.Context, in *access.GetLatestProtocolStateSnapshotRequest, opts ...grpc.CallOption) (*access.ProtocolStateSnapshotResponse, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *access.ProtocolStateSnapshotResponse + if rf, ok := ret.Get(0).(func(context.Context, *access.GetLatestProtocolStateSnapshotRequest, ...grpc.CallOption) *access.ProtocolStateSnapshotResponse); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*access.ProtocolStateSnapshotResponse) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *access.GetLatestProtocolStateSnapshotRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetNetworkParameters provides a mock function with given fields: ctx, in, opts func (_m *AccessAPIClient) GetNetworkParameters(ctx context.Context, in *access.GetNetworkParametersRequest, opts ...grpc.CallOption) (*access.GetNetworkParametersResponse, error) { _va := make([]interface{}, len(opts)) diff --git a/engine/access/rpc/backend/backend.go b/engine/access/rpc/backend/backend.go index 72d9ebd5fb1..d4afb1c2d90 100644 --- a/engine/access/rpc/backend/backend.go +++ b/engine/access/rpc/backend/backend.go @@ -12,6 +12,7 @@ import ( "google.golang.org/grpc/status" "github.com/onflow/flow-go/access" + "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/module" @@ -185,9 +186,13 @@ func (b *Backend) GetNetworkParameters(_ context.Context) access.NetworkParamete } } -func (b *Backend) GetLatestProtocolStateSnapshot(_ context.Context) (protocol.Snapshot, error) { - latestSealed := b.state.Sealed() - return latestSealed, nil +func (b *Backend) GetLatestProtocolStateSnapshot(_ context.Context) ([]byte, error) { + data, err := convert.SnapshotToBytes(b.state.Sealed()) + if err != nil { + return nil, err + } + + return data, nil } func convertStorageError(err error) error { diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 69670cb035b..22814bda1d7 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -122,6 +122,63 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { suite.Require().Equal(block.ParentID, header.ParentID) suite.assertAllExpectations() + +} + +func (suite *Suite) TestGetLatestProtocolStateSnapshot() { + // setup the snapshot mock + block := unittest.BlockHeaderFixture() + suite.snapshot.On("Head").Return(&block, nil).Once() + + identities := unittest.IdentityListFixture(10) + suite.snapshot.On("Identities", mock.Anything).Return(identities, nil).Once() + + seal := unittest.Seal.Fixture() + suite.snapshot.On("LatestSeal").Return(seal, nil).Once() + + result := unittest.ExecutionResultFixture() + suite.snapshot.On("LatestResult").Return(result, nil).Once() + + blocks := unittest.BlockFixtures(5) + suite.snapshot.On("SealingSegment").Return(blocks, nil).Once() + + qc := unittest.QuorumCertificateFixture() + suite.snapshot.On("QuorumCertificate").Return(qc, nil).Once() + + phase := flow.EpochPhaseSetup + suite.snapshot.On("Phase").Return(phase, nil).Once() + + epoch := protocol.EpochQuery{} + epoch.On("Current").Return(unittest.EpochSetupFixture(), nil) + epoch.On("Next").Return(unittest.EpochCommitFixture(), nil) + epoch.On("Preview").Return(unittest.EpochSetupFixture(), nil) + + suite.snapshot.On("Epochs").Return(epoch, nil).Once() + + // set up the state mock + suite.state.On("Sealed").Return(suite.snapshot, nil).Once() + + backend := New( + suite.state, + nil, nil, nil, nil, nil, + nil, nil, nil, + suite.chainID, + metrics.NewNoopCollector(), + nil, + false, + suite.log, + ) + + // query the handler for the latest sealed snapshot + bytes, err := backend.GetLatestProtocolStateSnapshot(context.Background()) + suite.Require().NoError(err) + + // make sure the returned bytes is equal to the serialized snapshot + convertedSnapshot, err := convert.SnapshotToBytes(suite.snapshot) + suite.Require().NoError(err) + suite.Require().Equal(bytes, convertedSnapshot) + + // suite.assertAllExpectations() } func (suite *Suite) TestGetLatestSealedBlockHeader() { diff --git a/engine/common/rpc/convert/convert.go b/engine/common/rpc/convert/convert.go index e3d28b6b821..d30b920b7a8 100644 --- a/engine/common/rpc/convert/convert.go +++ b/engine/common/rpc/convert/convert.go @@ -1,6 +1,7 @@ package convert import ( + "encoding/json" "errors" "fmt" @@ -10,6 +11,8 @@ import ( "github.com/onflow/flow-go/crypto" "github.com/onflow/flow-go/crypto/hash" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol" + "github.com/onflow/flow-go/state/protocol/inmem" ) var ErrEmptyMessage = errors.New("protobuf message is empty") @@ -359,3 +362,18 @@ func MessagesToIdentifiers(l [][]byte) []flow.Identifier { } return results } + +// SnapshotToBytes converts a `protocol.Snapshot` to bytes +func SnapshotToBytes(snapshot protocol.Snapshot) ([]byte, error) { + serializable, err := inmem.FromSnapshot(snapshot) + if err != nil { + return nil, err + } + + data, err := json.Marshal(serializable) + if err != nil { + return nil, err + } + + return data, nil +} diff --git a/go.mod b/go.mod index cebeaa60290..d59602f21d5 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/multiformats/go-multiaddr v0.3.1 github.com/onflow/cadence v0.12.6 github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1 - github.com/onflow/flow-go-sdk v0.14.3 + github.com/onflow/flow-go-sdk v0.14.4 github.com/onflow/flow-go/crypto v0.12.0 github.com/onflow/flow/protobuf/go/flow v0.1.9 github.com/opentracing/opentracing-go v1.2.0 diff --git a/go.sum b/go.sum index 3c21191b4bb..0bb1d9db44c 100644 --- a/go.sum +++ b/go.sum @@ -827,10 +827,8 @@ github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1 h1:nmIDPf94F9Ecx6e github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1/go.mod h1:4zE/4A+5zyahxSFccQmcBqzp4ONXIwvGHaOKN8h8CRM= github.com/onflow/flow-ft/lib/go/contracts v0.4.0 h1:M1I4z027GHOLcjkj9lL2UUUpZpEAF90hY5gRqvFGAPg= github.com/onflow/flow-ft/lib/go/contracts v0.4.0/go.mod h1:1zoTjp1KzNnOPkyqKmWKerUyf0gciw+e6tAEt0Ks3JE= -github.com/onflow/flow-go-sdk v0.14.3 h1:6t1ycWJSPpgz7LeMDaZ3cIbiKa24JNxUEFyAu3Vvi2U= -github.com/onflow/flow-go-sdk v0.14.3/go.mod h1:VAXKnZQlRRPfIkm8nw71B6bwhXNnmTfqV4wNrP3fK9I= -github.com/onflow/flow/protobuf/go/flow v0.1.8 h1:jBR8aXEL0MOh3gVJmCr0KYXmtG3JUBhzADonKkYE6oI= -github.com/onflow/flow/protobuf/go/flow v0.1.8/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM= +github.com/onflow/flow-go-sdk v0.14.4 h1:VnUyqdEnGy7lV66XAMkeygeqNYht+rOVh1bfdIjxNKU= +github.com/onflow/flow-go-sdk v0.14.4/go.mod h1:proD2hHrttTZMXUKR7nnDE5eO/EqEX9HE3i+eVOEzpE= github.com/onflow/flow/protobuf/go/flow v0.1.9 h1:ugK6/9K4AkMxqPbCvQzbbV24AH50Ozze43nqpukQoOM= github.com/onflow/flow/protobuf/go/flow v0.1.9/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= From fa567c6bf3b659cf1c294edb8d045fa1c360b783 Mon Sep 17 00:00:00 2001 From: danuio Date: Tue, 9 Feb 2021 12:00:53 +0000 Subject: [PATCH 108/178] Revert bump of flow-go-sdk --- go.mod | 2 +- go.sum | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d59602f21d5..cebeaa60290 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/multiformats/go-multiaddr v0.3.1 github.com/onflow/cadence v0.12.6 github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1 - github.com/onflow/flow-go-sdk v0.14.4 + github.com/onflow/flow-go-sdk v0.14.3 github.com/onflow/flow-go/crypto v0.12.0 github.com/onflow/flow/protobuf/go/flow v0.1.9 github.com/opentracing/opentracing-go v1.2.0 diff --git a/go.sum b/go.sum index 0bb1d9db44c..491063a4ea7 100644 --- a/go.sum +++ b/go.sum @@ -827,8 +827,9 @@ github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1 h1:nmIDPf94F9Ecx6e github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1/go.mod h1:4zE/4A+5zyahxSFccQmcBqzp4ONXIwvGHaOKN8h8CRM= github.com/onflow/flow-ft/lib/go/contracts v0.4.0 h1:M1I4z027GHOLcjkj9lL2UUUpZpEAF90hY5gRqvFGAPg= github.com/onflow/flow-ft/lib/go/contracts v0.4.0/go.mod h1:1zoTjp1KzNnOPkyqKmWKerUyf0gciw+e6tAEt0Ks3JE= -github.com/onflow/flow-go-sdk v0.14.4 h1:VnUyqdEnGy7lV66XAMkeygeqNYht+rOVh1bfdIjxNKU= -github.com/onflow/flow-go-sdk v0.14.4/go.mod h1:proD2hHrttTZMXUKR7nnDE5eO/EqEX9HE3i+eVOEzpE= +github.com/onflow/flow-go-sdk v0.14.3 h1:6t1ycWJSPpgz7LeMDaZ3cIbiKa24JNxUEFyAu3Vvi2U= +github.com/onflow/flow-go-sdk v0.14.3/go.mod h1:VAXKnZQlRRPfIkm8nw71B6bwhXNnmTfqV4wNrP3fK9I= +github.com/onflow/flow/protobuf/go/flow v0.1.8/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM= github.com/onflow/flow/protobuf/go/flow v0.1.9 h1:ugK6/9K4AkMxqPbCvQzbbV24AH50Ozze43nqpukQoOM= github.com/onflow/flow/protobuf/go/flow v0.1.9/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= From c9322ca1ca2e8d7f110729b35e085f6ea0e914b3 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 10:28:50 -0800 Subject: [PATCH 109/178] generate clusters based on final participants --- utils/unittest/fixtures.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 2c0ad4cc21d..2deac6b36c9 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -1055,17 +1055,16 @@ func WithFirstView(view uint64) func(*flow.EpochSetup) { func EpochSetupFixture(opts ...func(setup *flow.EpochSetup)) *flow.EpochSetup { participants := IdentityListFixture(5, WithAllRoles()) - assignments := ClusterAssignment(1, participants) setup := &flow.EpochSetup{ Counter: uint64(rand.Uint32()), FinalView: uint64(rand.Uint32() + 1000), Participants: participants, - Assignments: assignments, RandomSource: SeedFixture(flow.EpochSetupRandomSourceLength), } for _, apply := range opts { apply(setup) } + setup.Assignments = ClusterAssignment(1, setup.Participants) return setup } From 34b4e2f4647607c94d2d919534c55a231b69632b Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 10:43:21 -0800 Subject: [PATCH 110/178] bootstrapping tests --- state/protocol/badger/state_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index 27ebebe6559..00c58e722bb 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -1,6 +1,7 @@ package badger_test import ( + "errors" "fmt" "os" "testing" @@ -104,6 +105,26 @@ func TestBootstrapNonRoot(t *testing.T) { unittest.AssertSnapshotsEqual(t, after, state.Final()) }) }) + + t.Run("with next epoch", func(t *testing.T) { + after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { + unittest.NewEpochBuilder(t, state).BuildEpoch() + + // find the point where we transition to the epoch setup phase + for height := rootBlock.Height + 1; ; height++ { + _, err := state.AtHeight(height).Epochs().Next().Counter() + if errors.Is(err, protocol.ErrNextEpochNotSetup) { + continue + } + return state.AtHeight(height) + } + }) + + bootstrap(t, after, func(state *bprotocol.State, err error) { + assert.NoError(t, err) + unittest.AssertSnapshotsEqual(t, after, state.Final()) + }) + }) } func TestBootstrapDuplicateID(t *testing.T) { From ddaa8447e810841feaa3b74673cec66b89ae3f22 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 11:17:08 -0800 Subject: [PATCH 111/178] fix bug with converting clustering in setup epochs --- state/protocol/inmem/convert.go | 10 ++++++---- state/protocol/inmem/convert_test.go | 3 --- state/protocol/inmem/encodable.go | 1 + state/protocol/inmem/epoch.go | 6 +----- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index a1ae91365cd..d6fb324e427 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -107,6 +107,12 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { return nil, fmt.Errorf("could not get random source: %w", err) } + clustering, err := from.Clustering() + if err != nil { + return nil, fmt.Errorf("could not get clustering: %w", err) + } + epoch.Clustering = clustering + // convert dkg dkg, err := from.DKG() // if this epoch hasn't been committed yet, return the epoch as-is @@ -123,10 +129,6 @@ func FromEpoch(from protocol.Epoch) (*Epoch, error) { epoch.DKG = &convertedDKG.enc // convert clusters - clustering, err := from.Clustering() - if err != nil { - return nil, fmt.Errorf("could not get clustering: %w", err) - } for index := range clustering { cluster, err := from.Cluster(uint(index)) if err != nil { diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index d6e05def875..81e8ec4cacd 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -47,9 +47,6 @@ func TestFromSnapshot(t *testing.T) { // test that we are able retrieve an in-memory version of root snapshot t.Run("root snapshot", func(t *testing.T) { - // TODO need root QC stored for this test to pass - t.SkipNow() - expected := state.AtHeight(0) actual, err := inmem.FromSnapshot(expected) require.NoError(t, err) diff --git a/state/protocol/inmem/encodable.go b/state/protocol/inmem/encodable.go index ce5d06b7b4b..1849f389b57 100644 --- a/state/protocol/inmem/encodable.go +++ b/state/protocol/inmem/encodable.go @@ -32,6 +32,7 @@ type EncodableEpoch struct { FinalView uint64 RandomSource []byte InitialIdentities flow.IdentityList + Clustering flow.ClusterList Clusters []EncodableCluster DKG *EncodableDKG } diff --git a/state/protocol/inmem/epoch.go b/state/protocol/inmem/epoch.go index 8431c8b10fa..fa1225ec484 100644 --- a/state/protocol/inmem/epoch.go +++ b/state/protocol/inmem/epoch.go @@ -31,11 +31,7 @@ func (e Epoch) Seed(indices ...uint32) ([]byte, error) { } func (e Epoch) Clustering() (flow.ClusterList, error) { - var clusters flow.ClusterList - for _, cluster := range e.enc.Clusters { - clusters = append(clusters, cluster.Members) - } - return clusters, nil + return e.enc.Clustering, nil } func (e Epoch) DKG() (protocol.DKG, error) { From 8f4fe269cb313fd212343bb2953795edec985a88 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 15:47:00 -0800 Subject: [PATCH 112/178] fix access/ingestion tests these tests assumed block fixtures always contained guarantees -- updated to explicitly add them each time --- engine/access/ingestion/engine_test.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/engine/access/ingestion/engine_test.go b/engine/access/ingestion/engine_test.go index 3d5af121984..f70fb5fe027 100644 --- a/engine/access/ingestion/engine_test.go +++ b/engine/access/ingestion/engine_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/onflow/flow-go/consensus/hotstuff/model" + hotmodel "github.com/onflow/flow-go/consensus/hotstuff/model" "github.com/onflow/flow-go/engine" "github.com/onflow/flow-go/engine/access/rpc" "github.com/onflow/flow-go/model/flow" @@ -105,7 +105,10 @@ func (suite *Suite) SetupTest() { func (suite *Suite) TestOnFinalizedBlock() { block := unittest.BlockFixture() - modelBlock := model.Block{ + block.SetPayload(unittest.PayloadFixture( + unittest.WithGuarantees(unittest.CollectionGuaranteesFixture(4)...), + )) + hotstuffBlock := hotmodel.Block{ BlockID: block.ID(), } @@ -135,7 +138,7 @@ func (suite *Suite) TestOnFinalizedBlock() { ) // process the block through the finalized callback - suite.eng.OnFinalizedBlock(&modelBlock) + suite.eng.OnFinalizedBlock(&hotstuffBlock) // wait for engine shutdown done := suite.eng.unit.Done() @@ -250,6 +253,9 @@ func (suite *Suite) TestRequestMissingCollections() { var collIDs []flow.Identifier for i := 0; i < blkCnt; i++ { block := unittest.BlockFixture() + block.SetPayload(unittest.PayloadFixture( + unittest.WithGuarantees(unittest.CollectionGuaranteesFixture(4)...), + )) // some blocks may not be present hence add a gap height := startHeight + uint64(i) block.Header.Height = height @@ -365,17 +371,17 @@ func (suite *Suite) TestUpdateLastFullBlockReceivedIndex() { // generate the test blocks, cgs and collections for i := 0; i < blkCnt; i++ { - cgs := make([]*flow.CollectionGuarantee, collPerBlk) + guarantees := make([]*flow.CollectionGuarantee, collPerBlk) for j := 0; j < collPerBlk; j++ { coll := unittest.CollectionFixture(2).Light() collMap[coll.ID()] = &coll cg := unittest.CollectionGuaranteeFixture(func(cg *flow.CollectionGuarantee) { cg.CollectionID = coll.ID() }) - cgs[j] = cg + guarantees[j] = cg } block := unittest.BlockFixture() - block.Payload.Guarantees = cgs + block.SetPayload(unittest.PayloadFixture(unittest.WithGuarantees(guarantees...))) // set the height height := startHeight + uint64(i) block.Header.Height = height From bccdd6d64428e8eca971036c88acc17fcb483524 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 15:47:44 -0800 Subject: [PATCH 113/178] fix payload test to compare literal --- model/flow/payload_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/model/flow/payload_test.go b/model/flow/payload_test.go index 605d8e04219..379f673a0ad 100644 --- a/model/flow/payload_test.go +++ b/model/flow/payload_test.go @@ -22,7 +22,7 @@ func TestPayloadEncodeJSON(t *testing.T) { require.NoError(t, err) decodedHash := decoded.Hash() assert.Equal(t, payloadHash, decodedHash) - assert.Equal(t, payload, &decoded) + assert.Equal(t, payload, decoded) } func TestPayloadEncodingMsgpack(t *testing.T) { @@ -35,5 +35,5 @@ func TestPayloadEncodingMsgpack(t *testing.T) { require.NoError(t, err) decodedHash := decoded.Hash() assert.Equal(t, payloadHash, decodedHash) - assert.Equal(t, payload, &decoded) + assert.Equal(t, payload, decoded) } From e2a9396a213f431a1ed14a3a781bd588e8956f75 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 15:49:40 -0800 Subject: [PATCH 114/178] return EpochBuilder for easier call chaining --- utils/unittest/epoch_builder.go | 4 +++- utils/unittest/fixtures.go | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/utils/unittest/epoch_builder.go b/utils/unittest/epoch_builder.go index 7e4b83a0813..bf1dbe4db81 100644 --- a/utils/unittest/epoch_builder.go +++ b/utils/unittest/epoch_builder.go @@ -207,7 +207,7 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { // CompleteEpoch caps off the current epoch by building the first block of the next // epoch. We must be in the Committed phase to call CompleteEpoch. Once the epoch // has been capped off, we can build the next epoch with BuildEpoch. -func (builder *EpochBuilder) CompleteEpoch() { +func (builder *EpochBuilder) CompleteEpoch() *EpochBuilder { phase, err := builder.state.Final().Phase() require.Nil(builder.t, err) @@ -236,6 +236,8 @@ func (builder *EpochBuilder) CompleteEpoch() { }, }) builder.addBlock(&A) + + return builder } // addBlock adds the given block to the state by: extending the state, diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 2deac6b36c9..6862afcb27b 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -186,6 +186,12 @@ func WithSeals(seals ...*flow.Seal) func(*flow.Payload) { } } +func WithGuarantees(guarantees ...*flow.CollectionGuarantee) func(*flow.Payload) { + return func(payload *flow.Payload) { + payload.Guarantees = append(payload.Guarantees, guarantees...) + } +} + func WithReceipts(receipts ...*flow.ExecutionReceipt) func(*flow.Payload) { return func(payload *flow.Payload) { payload.Receipts = append(payload.Receipts, receipts...) From 02aaecce64c6def7ffb70e0573b6e07e8304c2e4 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 16:04:59 -0800 Subject: [PATCH 115/178] avoid empty slices in encode/decode tests --- storage/badger/blocks_test.go | 1 + storage/badger/payloads_test.go | 9 +++------ utils/unittest/fixtures.go | 10 ++++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/storage/badger/blocks_test.go b/storage/badger/blocks_test.go index 382d89e448b..d459f00751d 100644 --- a/storage/badger/blocks_test.go +++ b/storage/badger/blocks_test.go @@ -51,6 +51,7 @@ func TestBlockStoreAndRetrieve(t *testing.T) { // verify after storing a block should be able to retrieve it back blocks := badgerstorage.InitAll(cacheMetrics, db).Blocks block := unittest.FullBlockFixture() + block.SetPayload(unittest.PayloadFixture(unittest.WithAllTheFixins)) err := blocks.Store(&block) require.NoError(t, err) diff --git a/storage/badger/payloads_test.go b/storage/badger/payloads_test.go index 5ec93dd6299..feb19e14b3b 100644 --- a/storage/badger/payloads_test.go +++ b/storage/badger/payloads_test.go @@ -2,13 +2,11 @@ package badger_test import ( "errors" - "testing" "github.com/dgraph-io/badger/v2" "github.com/stretchr/testify/require" - "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module/metrics" "github.com/onflow/flow-go/storage" "github.com/onflow/flow-go/utils/unittest" @@ -28,8 +26,7 @@ func TestPayloadStoreRetrieve(t *testing.T) { store := badgerstorage.NewPayloads(db, index, guarantees, seals, receipts) blockID := unittest.IdentifierFixture() - expected := unittest.PayloadFixture() - expected.Receipts = make([]*flow.ExecutionReceipt, 0) + expected := unittest.PayloadFixture(unittest.WithAllTheFixins) // store payload err := store.Store(blockID, &expected) @@ -38,11 +35,11 @@ func TestPayloadStoreRetrieve(t *testing.T) { // fetch payload payload, err := store.ByBlockID(blockID) require.NoError(t, err) - require.Equal(t, expected, payload) + require.Equal(t, expected.Hash(), payload.Hash()) }) } -func TestPayloadRetreiveWithoutStore(t *testing.T) { +func TestPayloadRetrieveWithoutStore(t *testing.T) { unittest.RunWithBadgerDB(t, func(db *badger.DB) { metrics := metrics.NewNoopCollector() diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 6862afcb27b..8b8d0aaeb8b 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -180,6 +180,16 @@ func PayloadFixture(options ...func(*flow.Payload)) flow.Payload { return payload } +// WithAllTheFixins ensures a payload contains no empty slice fields. When +// encoding and decoding, nil vs empty slices are not preserved, which can +// result in two models that are semantically equal being considered non-equal +// by our testing framework. +func WithAllTheFixins(payload *flow.Payload) { + payload.Seals = Seal.Fixtures(3) + payload.Guarantees = CollectionGuaranteesFixture(4) + payload.Receipts = []*flow.ExecutionReceipt{ExecutionReceiptFixture()} +} + func WithSeals(seals ...*flow.Seal) func(*flow.Payload) { return func(payload *flow.Payload) { payload.Seals = append(payload.Seals, seals...) From a512cd2fe15e77f38e56c8fa12f675031e29a6d8 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 16:13:48 -0800 Subject: [PATCH 116/178] explicitly add guarantees to mocked block --- utils/unittest/chain_suite.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/unittest/chain_suite.go b/utils/unittest/chain_suite.go index 227612c2b6b..0211d498f5b 100644 --- a/utils/unittest/chain_suite.go +++ b/utils/unittest/chain_suite.go @@ -497,7 +497,9 @@ type subgraphFixture struct { func (bc *BaseChainSuite) ValidSubgraphFixture() subgraphFixture { // BLOCKS: <- previousBlock <- block parentBlock := BlockFixture() + parentBlock.SetPayload(PayloadFixture(WithGuarantees(CollectionGuaranteesFixture(12)...))) block := BlockWithParentFixture(parentBlock.Header) + block.SetPayload(PayloadFixture(WithGuarantees(CollectionGuaranteesFixture(12)...))) // RESULTS for Blocks: previousResult := ExecutionResultFixture(WithBlock(&parentBlock)) From a583118b6f94847cc3c08bcf8902847fc785e301 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 17:30:58 -0800 Subject: [PATCH 117/178] fix QC fixture it was ignoring functional modifiers --- utils/unittest/fixtures.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 8b8d0aaeb8b..e92dd3bba76 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -1021,12 +1021,16 @@ func KeyFixture(algo crypto.SigningAlgorithm) crypto.PrivateKey { } func QuorumCertificateFixture(opts ...func(*flow.QuorumCertificate)) *flow.QuorumCertificate { - return &flow.QuorumCertificate{ + qc := flow.QuorumCertificate{ View: uint64(rand.Uint32()), BlockID: IdentifierFixture(), SignerIDs: IdentifierListFixture(3), SigData: CombinedSignatureFixture(2), } + for _, apply := range opts { + apply(&qc) + } + return &qc } func QCWithBlockID(blockID flow.Identifier) func(*flow.QuorumCertificate) { From 88f87097b07a669169caf17eb42964180356f2fc Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 17:31:49 -0800 Subject: [PATCH 118/178] add note on order to SealingSegment doc --- state/protocol/snapshot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state/protocol/snapshot.go b/state/protocol/snapshot.go index 2399d2eaf58..8135e10e41c 100644 --- a/state/protocol/snapshot.go +++ b/state/protocol/snapshot.go @@ -50,7 +50,7 @@ type Snapshot interface { // SealingSegment returns the chain segment such that the head (greatest // height) is this snapshot's reference block and the tail (least height) // is the most recently sealed block as of this snapshot (ie. the block - // referenced by LatestSeal). + // referenced by LatestSeal). The segment is in ascending height order. // // TAIL <- B1 <- ... <- BN <- HEAD // From a83b9a1145ed93e0d897625674c15e9d9a30865f Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 17:32:14 -0800 Subject: [PATCH 119/178] use valid qc in bootstrapping tests --- state/protocol/badger/snapshot_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index fe4ca0576f2..2458da8baa1 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -95,6 +95,7 @@ func TestClusters(t *testing.T) { identities := append(unittest.IdentityListFixture(4, unittest.WithAllRolesExcept(flow.RoleCollection)), collectors...) root, result, seal := unittest.BootstrapFixture(identities) + qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(root.ID())) setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) setup.Assignments = unittest.ClusterAssignment(uint(nClusters), collectors) @@ -103,7 +104,7 @@ func TestClusters(t *testing.T) { commit.ClusterQCs[i] = unittest.QuorumCertificateFixture() } - rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, unittest.QuorumCertificateFixture()) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc) require.NoError(t, err) util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { @@ -759,8 +760,9 @@ func TestSnapshot_PostSporkIdentities(t *testing.T) { root, result, seal := unittest.BootstrapFixture(expected, func(block *flow.Block) { block.Header.ParentID = unittest.IdentifierFixture() }) + qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(root.ID())) - rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, unittest.QuorumCertificateFixture()) + rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc) require.NoError(t, err) util.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { From 73d83bacb4e0334bb5bef6064c238fc80b0eeef4 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 17:33:20 -0800 Subject: [PATCH 120/178] refactor Bootstrap to use helper functions --- state/protocol/badger/state.go | 126 ++++++++++++++++++++++-------- state/protocol/badger/validity.go | 3 +- 2 files changed, 94 insertions(+), 35 deletions(-) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index b878efbef86..548e8ed39cf 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -51,27 +51,87 @@ func Bootstrap( } state := newState(metrics, db, headers, seals, results, blocks, setups, commits, statuses) + if err := isValidRootSnapshot(root); err != nil { + return nil, fmt.Errorf("cannot bootstrap invalid root snapshot: %w", err) + } + err = operation.RetryOnConflict(db.Update, func(tx *badger.Txn) error { // 1) insert each block in the root chain segment + err = state.bootstrapSealingSegment(root)(tx) + if err != nil { + return fmt.Errorf("could not bootstrap sealing chain segment: %w", err) + } + segment, err := root.SealingSegment() if err != nil { return fmt.Errorf("could not get sealing segment: %w", err) } - if len(segment) == 0 { - return fmt.Errorf("root sealing segment must contain at least one block") - } - // sealing segment is in ascending height order, so the tail is the // oldest ancestor and head is the newest child in the segment - tail := segment[0] + // TAIL <- ... <- HEAD + head := segment[len(segment)-1] // reference block of the snapshot + tail := segment[0] // last sealed block + + // 2) insert the root execution result and seal into the database and index it + err = state.bootstrapSealedResult(root)(tx) + if err != nil { + return fmt.Errorf("could not bootstrap sealed result: %w", err) + } + + // 3) insert the root quorum certificate into the database + qc, err := root.QuorumCertificate() + if err != nil { + return fmt.Errorf("could not get root qc: %w", err) + } + err = operation.InsertRootQuorumCertificate(qc)(tx) + if err != nil { + return fmt.Errorf("could not insert root qc: %w", err) + } + + // 4) initialize the current protocol state height/view pointers + err = state.bootstrapStatePointers(root)(tx) + if err != nil { + return fmt.Errorf("could not bootstrap height/view pointers: %w", err) + } + + // 5) initialize values related to the epoch logic + err = state.bootstrapEpoch(root)(tx) + if err != nil { + return fmt.Errorf("could not bootstrap epoch values: %w", err) + } + + state.metrics.BlockSealed(tail) + state.metrics.SealedHeight(tail.Header.Height) + state.metrics.FinalizedHeight(head.Header.Height) + for _, block := range segment { + state.metrics.BlockFinalized(block) + } + + return nil + }) + if err != nil { + return nil, fmt.Errorf("bootstrapping failed: %w", err) + } + + return state, nil +} + +// bootstrapSealingSegment inserts all blocks and associated metadata for the +// protocol state root snapshot to disk. +func (state *State) bootstrapSealingSegment(root protocol.Snapshot) func(*badger.Txn) error { + return func(tx *badger.Txn) error { + segment, err := root.SealingSegment() + if err != nil { + return fmt.Errorf("could not get sealing segment: %w", err) + } head := segment[len(segment)-1] for i, block := range segment { blockID := block.ID() height := block.Header.Height - err = state.blocks.StoreTx(block)(tx) + err := state.blocks.StoreTx(block)(tx) if err != nil { return fmt.Errorf("could not insert root block: %w", err) } @@ -99,11 +159,21 @@ func Bootstrap( return fmt.Errorf("could not insert child index for head block (id=%x): %w", head.ID(), err) } - // 2) insert the root execution result and seal into the database and index it + return nil + } +} + +func (state *State) bootstrapSealedResult(root protocol.Snapshot) func(*badger.Txn) error { + return func(tx *badger.Txn) error { + head, err := root.Head() + if err != nil { + return fmt.Errorf("could not get head from root snapshot: %w", err) + } result, seal, err := root.SealedResult() if err != nil { return fmt.Errorf("could not get sealed result from root snapshot: %w", err) } + err = operation.SkipDuplicates(operation.InsertExecutionResult(result))(tx) if err != nil { return fmt.Errorf("could not insert root result: %w", err) @@ -112,6 +182,7 @@ func Bootstrap( if err != nil { return fmt.Errorf("could not index root result: %w", err) } + err = operation.SkipDuplicates(operation.InsertSeal(seal.ID(), seal))(tx) if err != nil { return fmt.Errorf("could not insert root seal: %w", err) @@ -121,17 +192,22 @@ func Bootstrap( return fmt.Errorf("could not index root block seal: %w", err) } - // 3) insert the root quorum certificate into the database - qc, err := root.QuorumCertificate() - if err != nil { - return fmt.Errorf("could not get root qc: %w", err) - } - err = operation.InsertRootQuorumCertificate(qc)(tx) + return nil + } +} + +// bootstrapStatePointers instantiates special pointers used to by the protocol +// state to keep track of special block heights and views. +func (state *State) bootstrapStatePointers(root protocol.Snapshot) func(*badger.Txn) error { + return func(tx *badger.Txn) error { + segment, err := root.SealingSegment() if err != nil { - return fmt.Errorf("could not insert root qc: %w", err) + return fmt.Errorf("could not get sealing segment: %w", err) } + head := segment[len(segment)-1] + tail := segment[0] - // 4) initialize the current protocol state values + // insert initial views for HotStuff err = operation.InsertStartedView(head.Header.ChainID, head.Header.View)(tx) if err != nil { return fmt.Errorf("could not insert started view: %w", err) @@ -140,6 +216,8 @@ func Bootstrap( if err != nil { return fmt.Errorf("could not insert started view: %w", err) } + + // insert height pointers err = operation.InsertRootHeight(head.Header.Height)(tx) if err != nil { return fmt.Errorf("could not insert root height: %w", err) @@ -153,26 +231,8 @@ func Bootstrap( return fmt.Errorf("could not insert sealed height: %w", err) } - // 5) initialize values related to the epoch logic - err = state.bootstrapEpoch(root)(tx) - if err != nil { - return fmt.Errorf("could not bootstrap epoch values: %w", err) - } - - state.metrics.BlockSealed(tail) - state.metrics.SealedHeight(tail.Header.Height) - state.metrics.FinalizedHeight(head.Header.Height) - for _, block := range segment { - state.metrics.BlockFinalized(block) - } - return nil - }) - if err != nil { - return nil, fmt.Errorf("bootstrapping failed: %w", err) } - - return state, nil } // bootstrapEpoch bootstraps the protocol state database with information about diff --git a/state/protocol/badger/validity.go b/state/protocol/badger/validity.go index f434a2930f9..d970c926bdd 100644 --- a/state/protocol/badger/validity.go +++ b/state/protocol/badger/validity.go @@ -121,7 +121,6 @@ func isValidEpochCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error } // isValidRootSnapshot checks internal consistency of root state snapshot -// TODO not used func isValidRootSnapshot(snap protocol.Snapshot) error { segment, err := snap.SealingSegment() @@ -174,7 +173,7 @@ func isValidRootSnapshot(snap protocol.Snapshot) error { // the segment must be fully within the current epoch if firstView > tail.Header.View { - return fmt.Errorf("root block has lower view than first view of epoch") + return fmt.Errorf("tail block of sealing segment has lower view than first view of epoch") } if head.Header.View >= finalView { return fmt.Errorf("final view of epoch less than first block view") From 5d2beee74d6e812fe86ef8b827222a03c20578eb Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 17:34:18 -0800 Subject: [PATCH 121/178] tests for non-root snapshots --- state/protocol/badger/state_test.go | 75 ++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index 00c58e722bb..3df69db3cda 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -1,7 +1,6 @@ package badger_test import ( - "errors" "fmt" "os" "testing" @@ -77,7 +76,7 @@ func TestBootstrapNonRoot(t *testing.T) { }) bootstrap(t, after, func(state *bprotocol.State, err error) { - assert.NoError(t, err) + require.NoError(t, err) unittest.AssertSnapshotsEqual(t, after, state.Final()) }) }) @@ -101,26 +100,74 @@ func TestBootstrapNonRoot(t *testing.T) { }) bootstrap(t, after, func(state *bprotocol.State, err error) { - assert.NoError(t, err) + require.NoError(t, err) unittest.AssertSnapshotsEqual(t, after, state.Final()) }) }) - t.Run("with next epoch", func(t *testing.T) { + t.Run("with setup next epoch", func(t *testing.T) { after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { unittest.NewEpochBuilder(t, state).BuildEpoch() // find the point where we transition to the epoch setup phase for height := rootBlock.Height + 1; ; height++ { - _, err := state.AtHeight(height).Epochs().Next().Counter() - if errors.Is(err, protocol.ErrNextEpochNotSetup) { - continue + phase, err := state.AtHeight(height).Phase() + require.NoError(t, err) + if phase == flow.EpochPhaseSetup { + return state.AtHeight(height) } - return state.AtHeight(height) } }) bootstrap(t, after, func(state *bprotocol.State, err error) { + require.NoError(t, err) + unittest.AssertSnapshotsEqual(t, after, state.Final()) + }) + }) + + t.Run("with committed next epoch", func(t *testing.T) { + after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { + unittest.NewEpochBuilder(t, state).BuildEpoch().CompleteEpoch() + + // find the point where we transition to the epoch committed phase + for height := rootBlock.Height + 1; ; height++ { + phase, err := state.AtHeight(height).Phase() + require.NoError(t, err) + if phase == flow.EpochPhaseCommitted { + return state.AtHeight(height) + } + } + }) + + bootstrap(t, after, func(state *bprotocol.State, err error) { + require.NoError(t, err) + unittest.AssertSnapshotsEqual(t, after, state.Final()) + }) + }) + + t.Run("with previous and next epoch", func(t *testing.T) { + after := snapshotAfter(t, rootSnapshot, func(state *bprotocol.FollowerState) protocol.Snapshot { + unittest.NewEpochBuilder(t, state). + BuildEpoch().CompleteEpoch(). // build epoch 2 + BuildEpoch() // build epoch 3 + + // find a snapshot from epoch setup phase in epoch 2 + epoch1Counter, err := rootSnapshot.Epochs().Current().Counter() + require.NoError(t, err) + for height := rootBlock.Height + 1; ; height++ { + snap := state.AtHeight(height) + counter, err := snap.Epochs().Current().Counter() + require.NoError(t, err) + phase, err := snap.Phase() + require.NoError(t, err) + if phase == flow.EpochPhaseSetup && counter == epoch1Counter+1 { + return snap + } + } + }) + + bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { + require.NoError(t, err) assert.NoError(t, err) unittest.AssertSnapshotsEqual(t, after, state.Final()) }) @@ -187,6 +234,18 @@ func TestBootstrapExistingAddress(t *testing.T) { }) } +func TestBootstrap_DisconnectedSealingSegment(t *testing.T) { + // disconnected + // seal isn't for tail +} + +func TestBootstrap_InvalidQuorumCertificate(t *testing.T) {} + +func TestBootstrap_SealMismatch(t *testing.T) { + // seal isn't for tail + // seal doesn't match result +} + // bootstraps protocol state with the given snapshot and invokes the callback // with the result of the constructor func bootstrap(t *testing.T, rootSnapshot protocol.Snapshot, f func(*bprotocol.State, error)) { From c7738bf7e6d69f575860e343375c5b0feb664fe0 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Feb 2021 18:00:57 -0800 Subject: [PATCH 122/178] bad sealing segment, qc, seal/result test cases --- state/protocol/badger/state_test.go | 59 ++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index 3df69db3cda..4784bfdc1fd 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -235,15 +235,64 @@ func TestBootstrapExistingAddress(t *testing.T) { } func TestBootstrap_DisconnectedSealingSegment(t *testing.T) { - // disconnected - // seal isn't for tail + rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) + // convert to encodable to easily modify snapshot + encodable := rootSnapshot.Encodable() + // add an un-connected tail block to the sealing segment + tail := unittest.BlockFixture() + encodable.SealingSegment = append([]*flow.Block{&tail}, encodable.SealingSegment...) + rootSnapshot = inmem.SnapshotFromEncodable(encodable) + + bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { + assert.Error(t, err) + }) } -func TestBootstrap_InvalidQuorumCertificate(t *testing.T) {} +func TestBootstrap_InvalidQuorumCertificate(t *testing.T) { + rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) + // convert to encodable to easily modify snapshot + encodable := rootSnapshot.Encodable() + encodable.QuorumCertificate.BlockID = unittest.IdentifierFixture() + rootSnapshot = inmem.SnapshotFromEncodable(encodable) + + bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { + assert.Error(t, err) + }) +} func TestBootstrap_SealMismatch(t *testing.T) { - // seal isn't for tail - // seal doesn't match result + t.Run("seal doesn't match tail block", func(t *testing.T) { + rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) + // convert to encodable to easily modify snapshot + encodable := rootSnapshot.Encodable() + encodable.LatestSeal.BlockID = unittest.IdentifierFixture() + + bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { + assert.Error(t, err) + }) + }) + + t.Run("result doesn't match tail block", func(t *testing.T) { + rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) + // convert to encodable to easily modify snapshot + encodable := rootSnapshot.Encodable() + encodable.LatestResult.BlockID = unittest.IdentifierFixture() + + bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { + assert.Error(t, err) + }) + }) + + t.Run("seal doesn't match result", func(t *testing.T) { + rootSnapshot := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) + // convert to encodable to easily modify snapshot + encodable := rootSnapshot.Encodable() + encodable.LatestSeal.ResultID = unittest.IdentifierFixture() + + bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { + assert.Error(t, err) + }) + }) } // bootstraps protocol state with the given snapshot and invokes the callback From 9d23d1ff8b0f262afc676bb56058aeb2ce032d9e Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 10 Feb 2021 12:02:29 -0800 Subject: [PATCH 123/178] update service event -related models Add stub method for converting cadence->flow-go service event representations --- model/flow/execution_result.go | 2 +- model/flow/seal.go | 4 ++++ model/flow/service_event.go | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/model/flow/execution_result.go b/model/flow/execution_result.go index b454336fa6b..55d67f926ad 100644 --- a/model/flow/execution_result.go +++ b/model/flow/execution_result.go @@ -5,7 +5,7 @@ type ExecutionResult struct { PreviousResultID Identifier // commit of the previous ER BlockID Identifier // commit of the current block Chunks ChunkList - ServiceEvents []Event + ServiceEvents []*ServiceEvent } // ID returns the hash of the execution result body diff --git a/model/flow/seal.go b/model/flow/seal.go index 8c43a7b5ea6..f5465e3b4f2 100644 --- a/model/flow/seal.go +++ b/model/flow/seal.go @@ -17,6 +17,7 @@ package flow // assignment cannot be computed) and its integrity is not protected by a // signature of a node that is authorized to generate it. A seal should only // be processed in the context of the block, which contains it. +// // (2) Even though seals are not strictly entities, they still implement the // Entity interface. This allows us to store and retrieve seals individually. // CAUTION: As seals are part of the block payload, their _exact_ content must @@ -24,13 +25,16 @@ package flow // signatures (incl. order). While it is possible to construct different valid // seals for the same result (using different subsets of assigned verifiers), // they cannot be treated as equivalent for the following reason: +// // * Swapping a seal in a block with a different once changes the binary // representation of the block payload containing the seal. // * Changing the binary block representation would invalidate the block // proposer's signature. +// // Therefore, to retrieve valid blocks from storage, it is required that // the Seal.ID includes all fields with independent degrees of freedom // (such as AggregatedApprovalSigs). +// type Seal struct { BlockID Identifier ResultID Identifier diff --git a/model/flow/service_event.go b/model/flow/service_event.go index 8df9979fe85..31b27cb4450 100644 --- a/model/flow/service_event.go +++ b/model/flow/service_event.go @@ -12,6 +12,16 @@ const ( ServiceEventCommit = "commit" ) +// ConvertServiceEvent converts a service event encoded as the generic +// flow.Event type to a ServiceEvent type for use within protocol software +// and protocol state. This acts as the conversion from the Cadence type to +// the flow-go type. +// +// TODO implement once Cadence types are defined +func ConvertServiceEvent(event Event) (*ServiceEvent, error) { + return nil, fmt.Errorf("ConvertServiceEvent not implemented") +} + // ServiceEvent represents a service event, which is a special event that when // emitted from a service account smart contract, is propagated to the protocol // and included in blocks. Service events typically cause changes to the From 98efc082267a5532e1b86fe950c35f1509452171 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Wed, 10 Feb 2021 14:19:27 -0800 Subject: [PATCH 124/178] convert service events upon execution --- engine/execution/ingestion/engine.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/engine/execution/ingestion/engine.go b/engine/execution/ingestion/engine.go index 10063cfc6da..f366532e036 100644 --- a/engine/execution/ingestion/engine.go +++ b/engine/execution/ingestion/engine.go @@ -1261,11 +1261,21 @@ func (e *Engine) generateExecutionResultForBlock( block.Header.ParentID, err) } + // convert Cadence service event representation to flow-go representation + convertedServiceEvents := make([]*flow.ServiceEvent, 0, len(serviceEvents)) + for _, event := range serviceEvents { + converted, err := flow.ConvertServiceEvent(event) + if err != nil { + return nil, fmt.Errorf("could not convert service event: %w", err) + } + convertedServiceEvents = append(convertedServiceEvents, converted) + } + er := &flow.ExecutionResult{ PreviousResultID: previousErID, BlockID: block.ID(), Chunks: chunks, - ServiceEvents: serviceEvents, + ServiceEvents: convertedServiceEvents, } return er, nil From 49fdba647f0d454148e490248abfac7d20820c27 Mon Sep 17 00:00:00 2001 From: danuio Date: Thu, 11 Feb 2021 10:42:49 +0000 Subject: [PATCH 125/178] temp --- engine/access/rpc/backend/backend_test.go | 32 ++--------------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 22814bda1d7..9a18648474a 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -127,36 +127,8 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { func (suite *Suite) TestGetLatestProtocolStateSnapshot() { // setup the snapshot mock - block := unittest.BlockHeaderFixture() - suite.snapshot.On("Head").Return(&block, nil).Once() - - identities := unittest.IdentityListFixture(10) - suite.snapshot.On("Identities", mock.Anything).Return(identities, nil).Once() - - seal := unittest.Seal.Fixture() - suite.snapshot.On("LatestSeal").Return(seal, nil).Once() - - result := unittest.ExecutionResultFixture() - suite.snapshot.On("LatestResult").Return(result, nil).Once() - - blocks := unittest.BlockFixtures(5) - suite.snapshot.On("SealingSegment").Return(blocks, nil).Once() - - qc := unittest.QuorumCertificateFixture() - suite.snapshot.On("QuorumCertificate").Return(qc, nil).Once() - - phase := flow.EpochPhaseSetup - suite.snapshot.On("Phase").Return(phase, nil).Once() - - epoch := protocol.EpochQuery{} - epoch.On("Current").Return(unittest.EpochSetupFixture(), nil) - epoch.On("Next").Return(unittest.EpochCommitFixture(), nil) - epoch.On("Preview").Return(unittest.EpochSetupFixture(), nil) - - suite.snapshot.On("Epochs").Return(epoch, nil).Once() - - // set up the state mock - suite.state.On("Sealed").Return(suite.snapshot, nil).Once() + snap := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) + suite.state.On("Sealed").Return(snap, nil).Once() backend := New( suite.state, From d669734786cbd61c61ab1f8832cd26ab5ffbb72a Mon Sep 17 00:00:00 2001 From: Danu Date: Thu, 11 Feb 2021 11:08:56 +0000 Subject: [PATCH 126/178] Update engine/common/rpc/convert/convert.go Co-authored-by: Jordan Schalm --- engine/common/rpc/convert/convert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/rpc/convert/convert.go b/engine/common/rpc/convert/convert.go index d30b920b7a8..09121ec1151 100644 --- a/engine/common/rpc/convert/convert.go +++ b/engine/common/rpc/convert/convert.go @@ -363,7 +363,7 @@ func MessagesToIdentifiers(l [][]byte) []flow.Identifier { return results } -// SnapshotToBytes converts a `protocol.Snapshot` to bytes +// SnapshotToBytes converts a `protocol.Snapshot` to bytes, encoded as JSON func SnapshotToBytes(snapshot protocol.Snapshot) ([]byte, error) { serializable, err := inmem.FromSnapshot(snapshot) if err != nil { From 791f768eac601752348ef180b0bb4b43f5ece34a Mon Sep 17 00:00:00 2001 From: danuio Date: Thu, 11 Feb 2021 17:07:31 +0000 Subject: [PATCH 127/178] Fix tests --- engine/access/rpc/backend/backend_test.go | 25 ++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/engine/access/rpc/backend/backend_test.go b/engine/access/rpc/backend/backend_test.go index 62115c44461..3bdbaafb868 100644 --- a/engine/access/rpc/backend/backend_test.go +++ b/engine/access/rpc/backend/backend_test.go @@ -55,7 +55,6 @@ func (suite *Suite) SetupTest() { suite.log = zerolog.Logger{} suite.state = new(protocol.State) suite.snapshot = new(protocol.Snapshot) - suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() suite.state.On("Final").Return(suite.snapshot, nil).Maybe() suite.blocks = new(storagemock.Blocks) suite.headers = new(storagemock.Headers) @@ -128,7 +127,7 @@ func (suite *Suite) TestGetLatestFinalizedBlockHeader() { func (suite *Suite) TestGetLatestProtocolStateSnapshot() { // setup the snapshot mock snap := unittest.RootSnapshotFixture(unittest.CompleteIdentitySet()) - suite.state.On("Sealed").Return(snap, nil).Once() + suite.state.On("Sealed").Return(snap).Once() backend := New( suite.state, @@ -146,7 +145,7 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot() { suite.Require().NoError(err) // make sure the returned bytes is equal to the serialized snapshot - convertedSnapshot, err := convert.SnapshotToBytes(suite.snapshot) + convertedSnapshot, err := convert.SnapshotToBytes(snap) suite.Require().NoError(err) suite.Require().Equal(bytes, convertedSnapshot) @@ -155,6 +154,8 @@ func (suite *Suite) TestGetLatestProtocolStateSnapshot() { func (suite *Suite) TestGetLatestSealedBlockHeader() { // setup the mocks + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() + block := unittest.BlockHeaderFixture() suite.snapshot.On("Head").Return(&block, nil).Once() @@ -182,6 +183,8 @@ func (suite *Suite) TestGetLatestSealedBlockHeader() { } func (suite *Suite) TestGetTransaction() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() + transaction := unittest.TransactionFixture() expected := transaction.TransactionBody @@ -211,6 +214,8 @@ func (suite *Suite) TestGetTransaction() { } func (suite *Suite) TestGetCollection() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() + expected := unittest.CollectionFixture(1).Light() suite.collections. @@ -242,6 +247,7 @@ func (suite *Suite) TestGetCollection() { // TestTransactionStatusTransition tests that the status of transaction changes from Finalized to Sealed // when the protocol state is updated func (suite *Suite) TestTransactionStatusTransition() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() ctx := context.Background() collection := unittest.CollectionFixture(1) @@ -365,6 +371,7 @@ func (suite *Suite) TestTransactionStatusTransition() { // TestTransactionExpiredStatusTransition tests that the status of transaction changes from Unknown to Expired // when enough blocks pass func (suite *Suite) TestTransactionExpiredStatusTransition() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() ctx := context.Background() collection := unittest.CollectionFixture(1) @@ -476,6 +483,8 @@ func (suite *Suite) TestTransactionExpiredStatusTransition() { } func (suite *Suite) TestGetLatestFinalizedBlock() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() + // setup the mocks expected := unittest.BlockFixture() header := expected.Header @@ -517,6 +526,8 @@ type mockCloser struct{} func (mc *mockCloser) Close() error { return nil } func (suite *Suite) TestGetEventsForBlockIDs() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() + events := getEvents(10) setupStorage := func(n int) []*flow.Header { @@ -670,6 +681,8 @@ func (suite *Suite) TestGetEventsForBlockIDs() { } func (suite *Suite) TestGetEventsForHeightRange() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() + ctx := context.Background() var minHeight uint64 = 5 var maxHeight uint64 = 10 @@ -829,6 +842,7 @@ func (suite *Suite) TestGetEventsForHeightRange() { } func (suite *Suite) TestGetAccount() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() address, err := suite.chainID.Chain().NewAddressGenerator().NextAddress() suite.Require().NoError(err) @@ -905,6 +919,8 @@ func (suite *Suite) TestGetAccount() { } func (suite *Suite) TestGetAccountAtBlockHeight() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() + height := uint64(5) address := unittest.AddressFixture() account := &entitiesproto.Account{ @@ -969,6 +985,8 @@ func (suite *Suite) TestGetAccountAtBlockHeight() { } func (suite *Suite) TestGetNetworkParameters() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() + expectedChainID := flow.Mainnet backend := New( @@ -989,6 +1007,7 @@ func (suite *Suite) TestGetNetworkParameters() { // TestExecutionNodesForBlockID tests the common method backend.executionNodesForBlockID used for serving all API calls // that need to talk to an execution node. func (suite *Suite) TestExecutionNodesForBlockID() { + suite.state.On("Sealed").Return(suite.snapshot, nil).Maybe() totalBlocks := 3 receiptPerBlock := 2 From d3c2af2d76c31d8492bcadd5a62cbf74eb7c50ec Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 25 Feb 2021 15:33:24 -0800 Subject: [PATCH 128/178] Apply suggestions from code review Co-authored-by: Leo Zhang --- state/protocol/badger/state_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index 4784bfdc1fd..bc28ba0bb09 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -56,7 +56,7 @@ func TestBootstrapAndOpen(t *testing.T) { // needed otherwise the parent block would not have a valid QC, since the QC // is stored in the child. func TestBootstrapNonRoot(t *testing.T) { - + t.Parallel() // start with a regular post-spork root snapshot participants := unittest.CompleteIdentitySet() rootSnapshot := unittest.RootSnapshotFixture(participants) From 301d10b72f6dca63c1d9c068a097e5961c3ce015 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 25 Feb 2021 15:36:24 -0800 Subject: [PATCH 129/178] remove unneeded error check --- state/protocol/badger/state_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index bc28ba0bb09..f85399d5bd8 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -168,7 +168,6 @@ func TestBootstrapNonRoot(t *testing.T) { bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { require.NoError(t, err) - assert.NoError(t, err) unittest.AssertSnapshotsEqual(t, after, state.Final()) }) }) From d9728d94509e91ffd3c1a68550dea00d64164361 Mon Sep 17 00:00:00 2001 From: Danu Date: Fri, 26 Feb 2021 09:52:19 +0000 Subject: [PATCH 130/178] [Transit] Download Protocol State Snapshot Command (#392) * Add initial command file * Bump flow-go-sdk to v0.14.4 * Set up barebones command and link up all deps * temp * Add new file name and fix any errors in CMD * Move protocol snapshot command to transit and PR comments * Add access address flag * Move write file to utils/io * Update utils/io/write.go Co-authored-by: Jordan Schalm Co-authored-by: Jordan Schalm --- cmd/bootstrap/transit/main.go | 131 +++++++++++++++++++++++++++------- go.mod | 4 +- go.sum | 70 ++++++------------ model/bootstrap/filenames.go | 23 +++--- utils/io/write.go | 24 +++++++ 5 files changed, 165 insertions(+), 87 deletions(-) create mode 100644 utils/io/write.go diff --git a/cmd/bootstrap/transit/main.go b/cmd/bootstrap/transit/main.go index 2b6a896ef0d..b68596f65af 100644 --- a/cmd/bootstrap/transit/main.go +++ b/cmd/bootstrap/transit/main.go @@ -4,6 +4,7 @@ import ( "context" "crypto/md5" "crypto/rand" + "encoding/json" "flag" "fmt" "io" @@ -17,11 +18,14 @@ import ( "golang.org/x/crypto/nacl/box" "google.golang.org/api/iterator" "google.golang.org/api/option" + "google.golang.org/grpc" + "github.com/onflow/flow-go-sdk/client" "github.com/onflow/flow-go/cmd/bootstrap/build" "github.com/onflow/flow-go/ledger/complete/wal" "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol/inmem" utilsio "github.com/onflow/flow-go/utils/io" ) @@ -55,18 +59,24 @@ var ( func main() { - var bootDir, keyDir, wrapID, role string - var version, pull, push, prepare bool + var bootDir, keyDir, wrapID, role, accessAddress string + var version, pull, push, prepare, downloadSnapshot bool flag.BoolVar(&version, "v", false, "View version and commit information") flag.StringVar(&bootDir, "d", "~/bootstrap", "The bootstrap directory containing your node-info files") flag.StringVar(&keyDir, "t", "", "Token provided by the Flow team to access the transit server") flag.BoolVar(&pull, "pull", false, "Fetch keys and metadata from the transit server") + flag.BoolVar(&downloadSnapshot, "download-snapshot", false, "Download the latest protocol state snapshot from an access node and write to disk") flag.BoolVar(&push, "push", false, "Upload public keys to the transit server") flag.BoolVar(&prepare, "prepare", false, "Generate transit keys for push step") flag.StringVar(&role, "role", "", `node role (can be "collection", "consensus", "execution", "verification" or "access")`) flag.StringVar(&wrapID, "x-server-wrap", "", "(Flow Team Use), wrap response keys for consensus node") + flag.StringVar(&accessAddress, "access-address", "", "The address of an access node") flag.StringVar(&flowBucket, "flow-bucket", "flow-genesis-bootstrap", "Storage for the transit server") + + // + flag.StringVar(&flowBucket, "flow-bucket", "flow-genesis-bootstrap", "Storage for the transit server") + flag.Parse() // always print version information @@ -96,16 +106,21 @@ func main() { return } - if optionsSelected(pull, push, prepare) != 1 { + if optionsSelected(pull, push, prepare, downloadSnapshot) != 1 { flag.Usage() - log.Fatal("Exactly one of -pull, -push, or -prepare must be specified\n") + log.Fatal("Exactly one of -pull, -push, -download-snapshot, or -prepare must be specified\n") } - if !prepare && keyDir == "" { + if (push || pull) && keyDir == "" { flag.Usage() log.Fatal("Access key, '-t', required for push and pull commands") } + if downloadSnapshot && accessAddress == "" { + flag.Usage() + log.Fatal("Access address, '-access-address', required to download latest protocl snapshot") + } + nodeID, err := fetchNodeID(bootDir) if err != nil { log.Fatalf("Could not determine node ID: %s\n", err) @@ -130,6 +145,11 @@ func main() { runPrepare(bootDir, nodeID, flowRole) return } + + if prepare { + runDownloadSnapshot(ctx, bootDir, nodeID, "") + return + } } // Read the NodeID file to build other paths from @@ -161,11 +181,11 @@ func printVersion() { // Run the push process // - create transit keypair (if the role type is Consensus) // - upload files to GCS bucket -func runPush(ctx context.Context, bootDir, token, nodeId string, role flow.Role) { +func runPush(ctx context.Context, bootDir, token, nodeID string, role flow.Role) { log.Println("Running push") if role == flow.RoleConsensus { - err := generateKeys(bootDir, nodeId) + err := generateKeys(bootDir, nodeID) if err != nil { log.Fatalf("Failed to push: %s", err) } @@ -174,18 +194,18 @@ func runPush(ctx context.Context, bootDir, token, nodeId string, role flow.Role) files := getFilesToUpload(role) for _, file := range files { - err := bucketUpload(ctx, bootDir, fmt.Sprintf(file, nodeId), token) + err := bucketUpload(ctx, bootDir, fmt.Sprintf(file, nodeID), token) if err != nil { log.Fatalf("Failed to push: %s", err) } } } -func runPull(ctx context.Context, bootDir, token, nodeId string, role flow.Role) { +func runPull(ctx context.Context, bootDir, token, nodeID string, role flow.Role) { log.Println("Running pull") - extraFiles := getAdditionalFilesToDownload(role, nodeId) + extraFiles := getAdditionalFilesToDownload(role, nodeID) var err error @@ -213,7 +233,7 @@ func runPull(ctx context.Context, bootDir, token, nodeId string, role flow.Role) } if role == flow.RoleConsensus { - err = unwrapFile(bootDir, nodeId) + err = unwrapFile(bootDir, nodeID) if err != nil { log.Fatalf("Failed to pull: %s", err) } @@ -229,10 +249,10 @@ func runPull(ctx context.Context, bootDir, token, nodeId string, role flow.Role) // Run the prepare process // - create transit keypair (if the role type is Consensus) -func runPrepare(bootdir, nodeId string, role flow.Role) { +func runPrepare(bootdir, nodeID string, role flow.Role) { if role == flow.RoleConsensus { log.Println("creating transit-keys") - err := generateKeys(bootdir, nodeId) + err := generateKeys(bootdir, nodeID) if err != nil { log.Fatalf("Failed to prepare: %s", err) } @@ -241,11 +261,68 @@ func runPrepare(bootdir, nodeId string, role flow.Role) { log.Printf("no preparation needed for role: %s", role.String()) } +// runDownloadSnapshot fetches the latest protocol snapshot from an access node and +// writes it to the `root-protocol-snapshot.json` file +func runDownloadSnapshot(ctx context.Context, bootDir, nodeIDHex, accessAddress string) { + + // convert the nodeID string to `flow.Identifier` + nodeID, err := flow.HexStringToIdentifier(nodeIDHex) + if err != nil { + log.Fatal("could not convert node id to identifier") + } + + // create a flow client with given access address + flowClient, err := client.New(accessAddress, grpc.WithInsecure()) + if err != nil { + log.Fatalf("could not create flow client: %v", err) + } + + // get latest snapshot bytes encoded as JSON + bytes, err := flowClient.GetLatestProtocolStateSnapshot(ctx) + if err != nil { + log.Fatal("could not get latest protocol snapshot from access node") + } + + // unmarshal bytes to snapshot object + var snapshot inmem.Snapshot + err = json.Unmarshal(bytes, &snapshot) + if err != nil { + log.Fatal("could not unmarshal snapshot data retreived from access node") + } + + // check if given NodeID is part of the current or next epoch + currentIdentities, err := snapshot.Epochs().Current().InitialIdentities() + if err != nil { + log.Fatal("could not get initial identities from current epoch") + } + if _, exists := currentIdentities.ByNodeID(nodeID); exists { + err := utilsio.WriteText(filepath.Join(bootDir, bootstrap.PathRootProtocolStateSnapshot), bytes) + if err != nil { + log.Fatalf("could not write snapshot to disk: %v", err) + } + return + } + + nextIdentities, err := snapshot.Epochs().Next().InitialIdentities() + if err != nil { + log.Fatal("could not get initial identities from next epoch") + } + if _, exists := nextIdentities.ByNodeID(nodeID); exists { + err := utilsio.WriteText(filepath.Join(bootDir, bootstrap.PathRootProtocolStateSnapshot), bytes) + if err != nil { + log.Fatalf("could not write snapshot to disk: %v", err) + } + return + } + + log.Fatalf("could not write snapshot, given node ID does not belong to current or next epoch: %v", nodeID) +} + // generateKeys creates the transit keypair and writes them to disk for later -func generateKeys(bootDir, nodeId string) error { +func generateKeys(bootDir, nodeID string) error { - privPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPriv, nodeId)) - pubPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPub, nodeId)) + privPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPriv, nodeID)) + pubPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPub, nodeID)) if utilsio.FileExists(privPath) && utilsio.FileExists(pubPath) { log.Print("transit-key-path priv & pub both exist, exiting") @@ -275,14 +352,14 @@ func generateKeys(bootDir, nodeId string) error { return nil } -func unwrapFile(bootDir, nodeId string) error { +func unwrapFile(bootDir, nodeID string) error { log.Print("Decrypting Random Beacon key") - pubKeyPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPub, nodeId)) - privKeyPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPriv, nodeId)) - ciphertextPath := filepath.Join(bootDir, fmt.Sprintf(FilenameRandomBeaconCipher, nodeId)) - plaintextPath := filepath.Join(bootDir, fmt.Sprintf(bootstrap.PathRandomBeaconPriv, nodeId)) + pubKeyPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPub, nodeID)) + privKeyPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPriv, nodeID)) + ciphertextPath := filepath.Join(bootDir, fmt.Sprintf(FilenameRandomBeaconCipher, nodeID)) + plaintextPath := filepath.Join(bootDir, fmt.Sprintf(bootstrap.PathRandomBeaconPriv, nodeID)) ciphertext, err := utilsio.ReadFile(ciphertextPath) if err != nil { @@ -318,10 +395,10 @@ func unwrapFile(bootDir, nodeId string) error { return nil } -func wrapFile(bootDir, nodeId string) error { - pubKeyPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPub, nodeId)) - plaintextPath := filepath.Join(bootDir, fmt.Sprintf(bootstrap.PathRandomBeaconPriv, nodeId)) - ciphertextPath := filepath.Join(bootDir, fmt.Sprintf(FilenameRandomBeaconCipher, nodeId)) +func wrapFile(bootDir, nodeID string) error { + pubKeyPath := filepath.Join(bootDir, fmt.Sprintf(FilenameTransitKeyPub, nodeID)) + plaintextPath := filepath.Join(bootDir, fmt.Sprintf(bootstrap.PathRandomBeaconPriv, nodeID)) + ciphertextPath := filepath.Join(bootDir, fmt.Sprintf(FilenameRandomBeaconCipher, nodeID)) plaintext, err := utilsio.ReadFile(plaintextPath) if err != nil { @@ -461,10 +538,10 @@ func getFilesToUpload(role flow.Role) []string { } } -func getAdditionalFilesToDownload(role flow.Role, nodeId string) []string { +func getAdditionalFilesToDownload(role flow.Role, nodeID string) []string { switch role { case flow.RoleConsensus: - return []string{fmt.Sprintf(filesToDownloadConsensus, nodeId)} + return []string{fmt.Sprintf(filesToDownloadConsensus, nodeID)} } return make([]string, 0) } diff --git a/go.mod b/go.mod index f9116157855..826f2b82516 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/bsipos/thist v1.0.0 github.com/btcsuite/btcd v0.20.1-beta github.com/codahale/hdrhistogram v0.9.0 // indirect + github.com/coreos/etcd v3.3.10+incompatible github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.0.3 github.com/ethereum/go-ethereum v1.9.13 @@ -35,7 +36,7 @@ require ( github.com/multiformats/go-multiaddr v0.3.1 github.com/onflow/cadence v0.12.11 github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1 - github.com/onflow/flow-go-sdk v0.14.3 + github.com/onflow/flow-go-sdk v0.14.4 github.com/onflow/flow-go/crypto v0.12.0 github.com/onflow/flow/protobuf/go/flow v0.1.9 github.com/opentracing/opentracing-go v1.2.0 @@ -56,6 +57,7 @@ require ( golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 google.golang.org/api v0.31.0 google.golang.org/grpc v1.31.1 + google.golang.org/grpc/examples v0.0.0-20210217184607-1b75f7144df2 // indirect gotest.tools v2.2.0+incompatible ) diff --git a/go.sum b/go.sum index ce838683afa..c6d4489f8e7 100644 --- a/go.sum +++ b/go.sum @@ -144,15 +144,19 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.9.0 h1:9GjrtRI+mLEFPtTfR/AZhcxp+Ii8NZYWq5104FbZQY0= github.com/codahale/hdrhistogram v0.9.0/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -178,6 +182,7 @@ github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3 h1:MQLRM35Pp github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po= github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= @@ -220,6 +225,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fxamacker/cbor/v2 v2.2.1-0.20201006223149-25f67fca9803 h1:CS/w4nHgzo/lk+H/b5BRnfGRCKw/0DBdRjIRULZWLsg= github.com/fxamacker/cbor/v2 v2.2.1-0.20201006223149-25f67fca9803/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -291,6 +297,7 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 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= @@ -335,10 +342,12 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= @@ -439,6 +448,7 @@ github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/bitset v1.0.0 h1:Ws0PXV3PwXqWK2n7Vz6idCdrV/9OrBXgHEJi27ZB9Dw= @@ -829,15 +839,13 @@ github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1 h1:nmIDPf94F9Ecx6e github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1/go.mod h1:4zE/4A+5zyahxSFccQmcBqzp4ONXIwvGHaOKN8h8CRM= github.com/onflow/flow-ft/lib/go/contracts v0.4.0 h1:M1I4z027GHOLcjkj9lL2UUUpZpEAF90hY5gRqvFGAPg= github.com/onflow/flow-ft/lib/go/contracts v0.4.0/go.mod h1:1zoTjp1KzNnOPkyqKmWKerUyf0gciw+e6tAEt0Ks3JE= -github.com/onflow/flow-go-sdk v0.14.3 h1:6t1ycWJSPpgz7LeMDaZ3cIbiKa24JNxUEFyAu3Vvi2U= -github.com/onflow/flow-go-sdk v0.14.3/go.mod h1:VAXKnZQlRRPfIkm8nw71B6bwhXNnmTfqV4wNrP3fK9I= -github.com/onflow/flow/protobuf/go/flow v0.1.8/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM= +github.com/onflow/flow-go-sdk v0.14.4 h1:VnUyqdEnGy7lV66XAMkeygeqNYht+rOVh1bfdIjxNKU= +github.com/onflow/flow-go-sdk v0.14.4/go.mod h1:proD2hHrttTZMXUKR7nnDE5eO/EqEX9HE3i+eVOEzpE= github.com/onflow/flow/protobuf/go/flow v0.1.9 h1:ugK6/9K4AkMxqPbCvQzbbV24AH50Ozze43nqpukQoOM= github.com/onflow/flow/protobuf/go/flow v0.1.9/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -850,7 +858,6 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWEr github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -879,24 +886,20 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= @@ -905,16 +908,12 @@ github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzk github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150 h1:ZeU+auZj1iNzN8iVhff6M38Mfu73FQiJve/GEXYJBjE= github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/raviqqe/hamt v0.0.0-20210114072021-37930cf9f7d8 h1:JyknRyD8lJLefdzQGSYuBNZPLeQU/jl3u8ed2aaDfLk= github.com/raviqqe/hamt v0.0.0-20210114072021-37930cf9f7d8/go.mod h1:0/epCjol3v+s4bEPHiO4Y2mTdFUSwycMMpX3rOfWQ0A= @@ -941,13 +940,13 @@ github.com/segmentio/fasthash v1.0.2 h1:86fGDl2hB+iSHYlccB/FP9qRGvLNuH/fhEEFn6gn github.com/segmentio/fasthash v1.0.2/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= @@ -962,7 +961,6 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= @@ -971,44 +969,40 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= -github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM= github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.3.0+incompatible h1:B/kUIXcj6kIU3WSXgeJ7/uYj94I/r0LDa//JKgN/Sf0= github.com/uber/jaeger-lib v2.3.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -1031,6 +1025,7 @@ github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+m github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1038,6 +1033,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -1046,7 +1042,6 @@ 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.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1055,7 +1050,6 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= @@ -1094,7 +1088,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2 h1:y102fOLFqhV41b+4GPiJoa0k/x+pJcEi2/HB1Y5T6fU= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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= @@ -1116,10 +1109,8 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk 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 h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= 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 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -1129,7 +1120,6 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG 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 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -1162,7 +1152,6 @@ golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLL 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 h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= 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= @@ -1173,7 +1162,6 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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= @@ -1185,9 +1173,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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 h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1196,7 +1182,6 @@ golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7w 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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1231,7 +1216,6 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/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= @@ -1248,7 +1232,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK 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 h1:L/G4KZvrQn7FWLN/LlulBtBzrLUhqjiGfTWWDmrh+IQ= 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= @@ -1261,13 +1244,11 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY 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-20200828161849-5deb26317202 h1:DrWbY9UUFi/sl/3HkNVoBjDbGfIPZZfgoGsGxOL1EU8= golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca h1:pvScuB+UnCGDas2naNKUOXruM08MjwVcEdaweeynIqQ= golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= 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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1287,7 +1268,6 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb 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 h1:TgDr+1inK2XVUKZx3BYAqQg/GwucGdBkzZjWaTg/I+A= 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= @@ -1303,7 +1283,6 @@ google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 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 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -1315,7 +1294,6 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= 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= @@ -1326,7 +1304,6 @@ google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvx 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 h1:YzfoEYWbODU5Fbt37+h7X16BWQbad7Q4S6gclTKFXM8= 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= @@ -1339,6 +1316,7 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY 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-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200831141814-d751682dd103 h1:z46CEPU+LlO0kGGwrH8h5epkkJhRZbAHYWOWD9JhLPI= google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1355,7 +1333,6 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ 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 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= @@ -1363,14 +1340,15 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= +google.golang.org/grpc/examples v0.0.0-20210217184607-1b75f7144df2 h1:1cKjpk2kD02zsadTfPyHzxHEjHdnvvZCttQVhEEXkOQ= +google.golang.org/grpc/examples v0.0.0-20210217184607-1b75f7144df2/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= 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 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= 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 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= 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= @@ -1383,7 +1361,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= @@ -1401,11 +1378,9 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl 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 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1417,7 +1392,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh 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 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= 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 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= diff --git a/model/bootstrap/filenames.go b/model/bootstrap/filenames.go index 221f65a98ac..0f3b8680bcc 100644 --- a/model/bootstrap/filenames.go +++ b/model/bootstrap/filenames.go @@ -16,17 +16,18 @@ var ( DirnameExecutionState = "execution-state" // public genesis information - DirnamePublicBootstrap = "public-root-information" - PathInternalNodeInfosPub = filepath.Join(DirnamePublicBootstrap, "node-internal-infos.pub.json") - PathFinallist = filepath.Join(DirnamePublicBootstrap, "finallist.pub.json") - PathNodeInfosPub = filepath.Join(DirnamePublicBootstrap, "node-infos.pub.json") - PathPartnerNodeInfoPrefix = filepath.Join(DirnamePublicBootstrap, "node-info.pub.") - PathNodeInfoPub = filepath.Join(DirnamePublicBootstrap, "node-info.pub.%v.json") // %v will be replaced by NodeID - PathRootBlock = filepath.Join(DirnamePublicBootstrap, "root-block.json") - PathRootQC = filepath.Join(DirnamePublicBootstrap, "root-qc.json") - PathRootResult = filepath.Join(DirnamePublicBootstrap, "root-execution-result.json") - PathRootSeal = filepath.Join(DirnamePublicBootstrap, "root-block-seal.json") - PathRootCheckpoint = filepath.Join(DirnameExecutionState, wal.RootCheckpointFilename) // only available on an execution node + DirnamePublicBootstrap = "public-root-information" + PathInternalNodeInfosPub = filepath.Join(DirnamePublicBootstrap, "node-internal-infos.pub.json") + PathFinallist = filepath.Join(DirnamePublicBootstrap, "finallist.pub.json") + PathNodeInfosPub = filepath.Join(DirnamePublicBootstrap, "node-infos.pub.json") + PathPartnerNodeInfoPrefix = filepath.Join(DirnamePublicBootstrap, "node-info.pub.") + PathNodeInfoPub = filepath.Join(DirnamePublicBootstrap, "node-info.pub.%v.json") // %v will be replaced by NodeID + PathRootBlock = filepath.Join(DirnamePublicBootstrap, "root-block.json") + PathRootQC = filepath.Join(DirnamePublicBootstrap, "root-qc.json") + PathRootProtocolStateSnapshot = filepath.Join(DirnamePublicBootstrap, "root-protocol-state-snapshot.json") + PathRootResult = filepath.Join(DirnamePublicBootstrap, "root-execution-result.json") + PathRootSeal = filepath.Join(DirnamePublicBootstrap, "root-block-seal.json") + PathRootCheckpoint = filepath.Join(DirnameExecutionState, wal.RootCheckpointFilename) // only available on an execution node // private genesis information DirPrivateRoot = "private-root-information" diff --git a/utils/io/write.go b/utils/io/write.go new file mode 100644 index 00000000000..e5aad8806c8 --- /dev/null +++ b/utils/io/write.go @@ -0,0 +1,24 @@ +package io + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +// WriteFile writes a byte array to the file at the given path. +// This method will also create the directory and file as needed. +func WriteFile(path string, data []byte) error { + err := os.MkdirAll(filepath.Dir(path), 0755) + if err != nil { + return fmt.Errorf("could not create output dir: %v", err) + } + + err = ioutil.WriteFile(path, data, 0644) + if err != nil { + return fmt.Errorf("could not write file: %v", err) + } + + return nil +} From 76d8c41fd7be8751bc973f3f7af87982deb9e15f Mon Sep 17 00:00:00 2001 From: Danu Date: Thu, 4 Mar 2021 10:06:51 +0000 Subject: [PATCH 131/178] [Transit] Pull and Push Root Snapshot (#474) * Pull and Push updated to push and pull root snapshot * Update engine/common/rpc/convert/convert.go Co-authored-by: Vishal <1117327+vishalchangrani@users.noreply.github.com> * Update cmd/bootstrap/transit/main.go Co-authored-by: Vishal <1117327+vishalchangrani@users.noreply.github.com> Co-authored-by: Vishal <1117327+vishalchangrani@users.noreply.github.com> --- cmd/bootstrap/transit/main.go | 22 +++++++++------------- engine/common/rpc/convert/convert.go | 13 ++++++++++++- utils/unittest/equals.go | 4 ++-- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cmd/bootstrap/transit/main.go b/cmd/bootstrap/transit/main.go index b68596f65af..4169d5c1fe1 100644 --- a/cmd/bootstrap/transit/main.go +++ b/cmd/bootstrap/transit/main.go @@ -4,7 +4,6 @@ import ( "context" "crypto/md5" "crypto/rand" - "encoding/json" "flag" "fmt" "io" @@ -22,10 +21,10 @@ import ( "github.com/onflow/flow-go-sdk/client" "github.com/onflow/flow-go/cmd/bootstrap/build" + "github.com/onflow/flow-go/engine/common/rpc/convert" "github.com/onflow/flow-go/ledger/complete/wal" "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/state/protocol/inmem" utilsio "github.com/onflow/flow-go/utils/io" ) @@ -45,6 +44,7 @@ var ( // default files to upload for all role type filesToUpload = []string{ bootstrap.PathNodeInfoPub, + bootstrap.PathRootProtocolStateSnapshot, // push root protocol state snapshot } // consensus node additionally will need the transit key (to securely transport DKG in phase 2) @@ -74,9 +74,6 @@ func main() { flag.StringVar(&accessAddress, "access-address", "", "The address of an access node") flag.StringVar(&flowBucket, "flow-bucket", "flow-genesis-bootstrap", "Storage for the transit server") - // - flag.StringVar(&flowBucket, "flow-bucket", "flow-genesis-bootstrap", "Storage for the transit server") - flag.Parse() // always print version information @@ -239,12 +236,12 @@ func runPull(ctx context.Context, bootDir, token, nodeID string, role flow.Role) } } - rootFile := filepath.Join(bootDir, bootstrap.PathRootBlock) + rootFile := filepath.Join(bootDir, bootstrap.PathRootProtocolStateSnapshot) rootMD5, err := getFileMD5(rootFile) if err != nil { log.Fatalf("Failed to calculate md5 of %s: %v", rootFile, err) } - log.Printf("MD5 of the root block is: %s\n", rootMD5) + log.Printf("MD5 of the root protocol snapshot is: %s\n", rootMD5) } // Run the prepare process @@ -283,11 +280,10 @@ func runDownloadSnapshot(ctx context.Context, bootDir, nodeIDHex, accessAddress log.Fatal("could not get latest protocol snapshot from access node") } - // unmarshal bytes to snapshot object - var snapshot inmem.Snapshot - err = json.Unmarshal(bytes, &snapshot) + // unmarshal bytes to snapshot + snapshot, err := convert.BytesToInmemSnapshot(bytes) if err != nil { - log.Fatal("could not unmarshal snapshot data retreived from access node") + log.Fatalf("could not convert array of bytes to snapshot: %v", err) } // check if given NodeID is part of the current or next epoch @@ -296,7 +292,7 @@ func runDownloadSnapshot(ctx context.Context, bootDir, nodeIDHex, accessAddress log.Fatal("could not get initial identities from current epoch") } if _, exists := currentIdentities.ByNodeID(nodeID); exists { - err := utilsio.WriteText(filepath.Join(bootDir, bootstrap.PathRootProtocolStateSnapshot), bytes) + err := utilsio.WriteFile(filepath.Join(bootDir, bootstrap.PathRootProtocolStateSnapshot), bytes) if err != nil { log.Fatalf("could not write snapshot to disk: %v", err) } @@ -308,7 +304,7 @@ func runDownloadSnapshot(ctx context.Context, bootDir, nodeIDHex, accessAddress log.Fatal("could not get initial identities from next epoch") } if _, exists := nextIdentities.ByNodeID(nodeID); exists { - err := utilsio.WriteText(filepath.Join(bootDir, bootstrap.PathRootProtocolStateSnapshot), bytes) + err := utilsio.WriteFile(filepath.Join(bootDir, bootstrap.PathRootProtocolStateSnapshot), bytes) if err != nil { log.Fatalf("could not write snapshot to disk: %v", err) } diff --git a/engine/common/rpc/convert/convert.go b/engine/common/rpc/convert/convert.go index 09121ec1151..3e219407f38 100644 --- a/engine/common/rpc/convert/convert.go +++ b/engine/common/rpc/convert/convert.go @@ -370,10 +370,21 @@ func SnapshotToBytes(snapshot protocol.Snapshot) ([]byte, error) { return nil, err } - data, err := json.Marshal(serializable) + data, err := json.Marshal(serializable.Encodable()) if err != nil { return nil, err } return data, nil } + +// BytesToInmemSnapshot converts an array of bytes to `inmem.Snapshot` +func BytesToInmemSnapshot(bytes []byte) (*inmem.Snapshot, error) { + var snapshot inmem.Snapshot + err := json.Unmarshal(bytes, &snapshot) + if err != nil { + return nil, fmt.Errorf("could not unmarshal snapshot data retreived from access node: %w", err) + } + + return &snapshot, nil +} diff --git a/utils/unittest/equals.go b/utils/unittest/equals.go index 28f2118a73f..58917c2bc8d 100644 --- a/utils/unittest/equals.go +++ b/utils/unittest/equals.go @@ -34,9 +34,9 @@ func AssertSnapshotsEqual(t *testing.T, snap1, snap2 protocol.Snapshot) { inmem2, err := inmem.FromSnapshot(snap2) require.NoError(t, err) - encoded1, err := json.Marshal(inmem1) + encoded1, err := json.Marshal(inmem1.Encodable()) require.NoError(t, err) - encoded2, err := json.Marshal(inmem2) + encoded2, err := json.Marshal(inmem2.Encodable()) require.NoError(t, err) assert.Equal(t, encoded1, encoded2) From 9c8a9ce83e6ad45ccbe40d6904a79e182b81c8f3 Mon Sep 17 00:00:00 2001 From: danuio Date: Thu, 4 Mar 2021 10:10:13 +0000 Subject: [PATCH 132/178] Remove Transit push RootSnapshotFile --- cmd/bootstrap/transit/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/bootstrap/transit/main.go b/cmd/bootstrap/transit/main.go index 4169d5c1fe1..f36f7d5c5f7 100644 --- a/cmd/bootstrap/transit/main.go +++ b/cmd/bootstrap/transit/main.go @@ -44,7 +44,6 @@ var ( // default files to upload for all role type filesToUpload = []string{ bootstrap.PathNodeInfoPub, - bootstrap.PathRootProtocolStateSnapshot, // push root protocol state snapshot } // consensus node additionally will need the transit key (to securely transport DKG in phase 2) From ec4ed9f312c30750bd0a19dd4664cdc122db93ae Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Mar 2021 16:55:49 -0800 Subject: [PATCH 133/178] remove dupe method from merge --- access/handler.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/access/handler.go b/access/handler.go index 700d4f945e6..f4f498472e1 100644 --- a/access/handler.go +++ b/access/handler.go @@ -2,7 +2,6 @@ package access import ( "context" - "fmt" "github.com/golang/protobuf/ptypes" "github.com/onflow/flow/protobuf/go/flow/access" @@ -36,11 +35,6 @@ func (h *Handler) Ping(ctx context.Context, _ *access.PingRequest) (*access.Ping return &access.PingResponse{}, nil } -func (h *Handler) GetLatestProtocolStateSnapshot(_ context.Context, - _ *access.GetLatestProtocolStateSnapshotRequest) (*access.ProtocolStateSnapshotResponse, error) { - return nil, fmt.Errorf("unimplemented method") -} - func (h *Handler) GetNetworkParameters( ctx context.Context, _ *access.GetNetworkParametersRequest, From 5f72d3687ff4306c4d8a230375f0a033aa80399f Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Mar 2021 17:48:57 -0800 Subject: [PATCH 134/178] enforce consistent json marshalling for block payload --- model/flow/payload.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/model/flow/payload.go b/model/flow/payload.go index 303a5f247d9..06ecde8591e 100644 --- a/model/flow/payload.go +++ b/model/flow/payload.go @@ -1,5 +1,9 @@ package flow +import ( + "encoding/json" +) + // Payload is the actual content of each block. type Payload struct { Guarantees []*CollectionGuarantee @@ -12,6 +16,24 @@ func EmptyPayload() Payload { return Payload{} } +// JSONMarshal defines the JSON marshalling for block payloads. Enforce a +// consistent representation for empty slices. +func (p *Payload) MarshalJSON() ([]byte, error) { + dup := *p // copy p + + if len(dup.Guarantees) == 0 { + dup.Guarantees = nil + } + if len(dup.Receipts) == 0 { + dup.Receipts = nil + } + if len(dup.Seals) == 0 { + dup.Seals = nil + } + + return json.Marshal(dup) +} + // Hash returns the root hash of the payload. func (p Payload) Hash() Identifier { collHash := MerkleRoot(GetIDs(p.Guarantees)...) From 250055d7498beb6c6a33bbf4b2acb305700cd0d2 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Mar 2021 17:50:12 -0800 Subject: [PATCH 135/178] use the canonical ordering in unit test util --- utils/unittest/fixtures.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index cb15e67e7ab..574328398b3 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -9,6 +9,7 @@ import ( sdk "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go/model/chunks" + "github.com/onflow/flow-go/model/flow/order" "github.com/onflow/flow-go/module/signature" "github.com/onflow/flow-go/state/protocol/inmem" @@ -1070,7 +1071,7 @@ func VoteFixture() *hotstuff.Vote { func WithParticipants(participants flow.IdentityList) func(*flow.EpochSetup) { return func(setup *flow.EpochSetup) { - setup.Participants = participants + setup.Participants = participants.Order(order.ByNodeIDAsc) setup.Assignments = ClusterAssignment(1, participants) } } From 5997ca5f533300bfc5c12990cfdea8f02e715fd4 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Mar 2021 17:52:11 -0800 Subject: [PATCH 136/178] fix bootstrap test (was comparing the wrong snapshot) --- state/protocol/badger/state_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index f85399d5bd8..4064e4f2b67 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -166,7 +166,7 @@ func TestBootstrapNonRoot(t *testing.T) { } }) - bootstrap(t, rootSnapshot, func(state *bprotocol.State, err error) { + bootstrap(t, after, func(state *bprotocol.State, err error) { require.NoError(t, err) unittest.AssertSnapshotsEqual(t, after, state.Final()) }) From 26e3ee685a34f0621b86502f884a56891b8e775c Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Mar 2021 18:01:16 -0800 Subject: [PATCH 137/178] lint --- state/protocol/badger/state_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state/protocol/badger/state_test.go b/state/protocol/badger/state_test.go index 4064e4f2b67..4a0de073856 100644 --- a/state/protocol/badger/state_test.go +++ b/state/protocol/badger/state_test.go @@ -30,7 +30,7 @@ func TestBootstrapAndOpen(t *testing.T) { block.Header.ParentID = unittest.IdentifierFixture() }) - protoutil.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.State) { + protoutil.RunWithBootstrapState(t, rootSnapshot, func(db *badger.DB, _ *bprotocol.State) { // protocol state has been bootstrapped, now open a protocol state with the database metrics := new(metrics.NoopCollector) all := storagebadger.InitAll(metrics, db) From 5b61f8c5768d99baae2921be2e2080e095a22772 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 4 Mar 2021 18:01:27 -0800 Subject: [PATCH 138/178] fix conversion function caught be linter as well - this was using the wrong structure for decoding --- engine/common/rpc/convert/convert.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/common/rpc/convert/convert.go b/engine/common/rpc/convert/convert.go index 3e219407f38..85cd131ed91 100644 --- a/engine/common/rpc/convert/convert.go +++ b/engine/common/rpc/convert/convert.go @@ -380,11 +380,11 @@ func SnapshotToBytes(snapshot protocol.Snapshot) ([]byte, error) { // BytesToInmemSnapshot converts an array of bytes to `inmem.Snapshot` func BytesToInmemSnapshot(bytes []byte) (*inmem.Snapshot, error) { - var snapshot inmem.Snapshot - err := json.Unmarshal(bytes, &snapshot) + var encodable inmem.EncodableSnapshot + err := json.Unmarshal(bytes, &encodable) if err != nil { return nil, fmt.Errorf("could not unmarshal snapshot data retreived from access node: %w", err) } - return &snapshot, nil + return inmem.SnapshotFromEncodable(encodable), nil } From 8d41059fc939309ee1a1160bd58c5dabf3000614 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Mar 2021 09:23:23 -0800 Subject: [PATCH 139/178] go mod tidy --- go.mod | 2 -- go.sum | 6 ------ 2 files changed, 8 deletions(-) diff --git a/go.mod b/go.mod index 4639bf1da3f..2c3f685f3a5 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/bsipos/thist v1.0.0 github.com/btcsuite/btcd v0.20.1-beta github.com/codahale/hdrhistogram v0.9.0 // indirect - github.com/coreos/etcd v3.3.10+incompatible github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.0.3 github.com/ef-ds/deque v1.0.4 @@ -58,7 +57,6 @@ require ( golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 google.golang.org/api v0.31.0 google.golang.org/grpc v1.31.1 - google.golang.org/grpc/examples v0.0.0-20210217184607-1b75f7144df2 // indirect gotest.tools v2.2.0+incompatible ) diff --git a/go.sum b/go.sum index efde40280e7..40d8e0cb1d1 100644 --- a/go.sum +++ b/go.sum @@ -844,8 +844,6 @@ github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1 h1:nmIDPf94F9Ecx6e github.com/onflow/flow-core-contracts/lib/go/contracts v0.7.1/go.mod h1:4zE/4A+5zyahxSFccQmcBqzp4ONXIwvGHaOKN8h8CRM= github.com/onflow/flow-ft/lib/go/contracts v0.4.0 h1:M1I4z027GHOLcjkj9lL2UUUpZpEAF90hY5gRqvFGAPg= github.com/onflow/flow-ft/lib/go/contracts v0.4.0/go.mod h1:1zoTjp1KzNnOPkyqKmWKerUyf0gciw+e6tAEt0Ks3JE= -github.com/onflow/flow-go-sdk v0.14.4 h1:VnUyqdEnGy7lV66XAMkeygeqNYht+rOVh1bfdIjxNKU= -github.com/onflow/flow-go-sdk v0.14.4/go.mod h1:proD2hHrttTZMXUKR7nnDE5eO/EqEX9HE3i+eVOEzpE= github.com/onflow/flow-go-sdk v0.15.0 h1:h2FD/d1p/VRIWEcAYcVOT2rm4qKppIn4sVog+/eXKrw= github.com/onflow/flow-go-sdk v0.15.0/go.mod h1:Dkcd1xQta8EPQL5XKE9vi2OTa6CoMsbX0jIQ4ozhUUs= github.com/onflow/flow/protobuf/go/flow v0.1.9 h1:ugK6/9K4AkMxqPbCvQzbbV24AH50Ozze43nqpukQoOM= @@ -1320,7 +1318,6 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY 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-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200831141814-d751682dd103 h1:z46CEPU+LlO0kGGwrH8h5epkkJhRZbAHYWOWD9JhLPI= google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1344,9 +1341,6 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= -google.golang.org/grpc/examples v0.0.0-20210217184607-1b75f7144df2 h1:1cKjpk2kD02zsadTfPyHzxHEjHdnvvZCttQVhEEXkOQ= -google.golang.org/grpc/examples v0.0.0-20210217184607-1b75f7144df2/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= 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= From f3dfffd5d74ff463a1f1ed7c59f0c08fb05bec15 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Mar 2021 14:58:32 -0800 Subject: [PATCH 140/178] go mod tidy --- go.sum | 1 + 1 file changed, 1 insertion(+) diff --git a/go.sum b/go.sum index 40d8e0cb1d1..975aba266ac 100644 --- a/go.sum +++ b/go.sum @@ -1191,6 +1191,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 0f2caf398689c0fe23097c5b53d4b13a25b5fef8 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Mar 2021 14:58:56 -0800 Subject: [PATCH 141/178] generate service events in root results in bootstrapping --- cmd/bootstrap/cmd/seal.go | 4 ++-- cmd/bootstrap/run/result.go | 9 ++++++++- cmd/bootstrap/run/seal.go | 9 ++++----- engine/execution/ingestion/engine.go | 4 ++-- integration/testnet/network.go | 4 ++-- model/flow/execution_result.go | 2 +- 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/cmd/bootstrap/cmd/seal.go b/cmd/bootstrap/cmd/seal.go index b79d86f878d..d78a31ecd35 100644 --- a/cmd/bootstrap/cmd/seal.go +++ b/cmd/bootstrap/cmd/seal.go @@ -50,8 +50,8 @@ func constructRootResultAndSeal( DKGParticipants: dkgLookup, } - result := run.GenerateRootResult(block, stateCommit) - seal := run.GenerateRootSeal(result, epochSetup, epochCommit) + result := run.GenerateRootResult(block, stateCommit, epochSetup, epochCommit) + seal := run.GenerateRootSeal(result) writeJSON(model.PathRootResult, result) writeJSON(model.PathRootSeal, seal) diff --git a/cmd/bootstrap/run/result.go b/cmd/bootstrap/run/result.go index 21a84a98f7d..3fc364fdeb4 100644 --- a/cmd/bootstrap/run/result.go +++ b/cmd/bootstrap/run/result.go @@ -5,11 +5,18 @@ import ( "github.com/onflow/flow-go/model/flow" ) -func GenerateRootResult(block *flow.Block, commit flow.StateCommitment) *flow.ExecutionResult { +func GenerateRootResult( + block *flow.Block, + commit flow.StateCommitment, + epochSetup *flow.EpochSetup, + epochCommit *flow.EpochCommit, +) *flow.ExecutionResult { + result := &flow.ExecutionResult{ PreviousResultID: flow.ZeroID, BlockID: block.ID(), Chunks: chunks.ChunkListFromCommit(commit), + ServiceEvents: []flow.ServiceEvent{epochSetup.ServiceEvent(), epochCommit.ServiceEvent()}, } return result } diff --git a/cmd/bootstrap/run/seal.go b/cmd/bootstrap/run/seal.go index bf769ac2a44..b79a13430a0 100644 --- a/cmd/bootstrap/run/seal.go +++ b/cmd/bootstrap/run/seal.go @@ -4,13 +4,12 @@ import ( "github.com/onflow/flow-go/model/flow" ) -func GenerateRootSeal(result *flow.ExecutionResult, setup *flow.EpochSetup, commit *flow.EpochCommit) *flow.Seal { +func GenerateRootSeal(result *flow.ExecutionResult) *flow.Seal { finalState, _ := result.FinalStateCommitment() seal := &flow.Seal{ - BlockID: result.BlockID, - ResultID: result.ID(), - FinalState: finalState, - ServiceEvents: []flow.ServiceEvent{setup.ServiceEvent(), commit.ServiceEvent()}, + BlockID: result.BlockID, + ResultID: result.ID(), + FinalState: finalState, } return seal } diff --git a/engine/execution/ingestion/engine.go b/engine/execution/ingestion/engine.go index 4f125706d25..234dabb3a7e 100644 --- a/engine/execution/ingestion/engine.go +++ b/engine/execution/ingestion/engine.go @@ -1192,13 +1192,13 @@ func (e *Engine) generateExecutionResultForBlock( } // convert Cadence service event representation to flow-go representation - convertedServiceEvents := make([]*flow.ServiceEvent, 0, len(serviceEvents)) + convertedServiceEvents := make([]flow.ServiceEvent, 0, len(serviceEvents)) for _, event := range serviceEvents { converted, err := flow.ConvertServiceEvent(event) if err != nil { return nil, fmt.Errorf("could not convert service event: %w", err) } - convertedServiceEvents = append(convertedServiceEvents, converted) + convertedServiceEvents = append(convertedServiceEvents, *converted) } er := &flow.ExecutionResult{ diff --git a/integration/testnet/network.go b/integration/testnet/network.go index 5628bc04053..8bd250712ce 100644 --- a/integration/testnet/network.go +++ b/integration/testnet/network.go @@ -631,8 +631,8 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo } // generate execution result and block seal - result := run.GenerateRootResult(root, commit) - seal := run.GenerateRootSeal(result, epochSetup, epochCommit) + result := run.GenerateRootResult(root, commit, epochSetup, epochCommit) + seal := run.GenerateRootSeal(result) err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootBlock), root) if err != nil { diff --git a/model/flow/execution_result.go b/model/flow/execution_result.go index 55d67f926ad..8bb6f2f3612 100644 --- a/model/flow/execution_result.go +++ b/model/flow/execution_result.go @@ -5,7 +5,7 @@ type ExecutionResult struct { PreviousResultID Identifier // commit of the previous ER BlockID Identifier // commit of the current block Chunks ChunkList - ServiceEvents []*ServiceEvent + ServiceEvents []ServiceEvent } // ID returns the hash of the execution result body From 308b090baea0fdc34dcddf5fa4fca7a1770686f2 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Mar 2021 15:20:00 -0800 Subject: [PATCH 142/178] update usage of service events in test utils --- utils/unittest/epoch_builder.go | 46 ++++++++++++++++++--------------- utils/unittest/fixtures.go | 16 ++++++++---- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/utils/unittest/epoch_builder.go b/utils/unittest/epoch_builder.go index bf1dbe4db81..cd2bed7d3bd 100644 --- a/utils/unittest/epoch_builder.go +++ b/utils/unittest/epoch_builder.go @@ -1,5 +1,6 @@ package unittest +import "C" import ( "math/rand" "testing" @@ -114,6 +115,15 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { } } + // defaults for the EpochSetup event + setupDefaults := []func(*flow.EpochSetup){ + WithParticipants(identities), + SetupWithCounter(counter + 1), + WithFirstView(finalView + 1), + WithFinalView(finalView + 1000), + } + setup := EpochSetupFixture(append(setupDefaults, builder.setupOpts...)...) + // build block B, sealing up to and including block A B := BlockWithParentFixture(A) B.SetPayload(flow.Payload{ @@ -121,8 +131,11 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { Seals: sealsForPrev, }) builder.addBlock(&B) + // create a receipt for block B, to be included in block C + // the receipt for B contains the EpochSetup event receiptB := ReceiptForBlockFixture(&B) + receiptB.ExecutionResult.ServiceEvents = []flow.ServiceEvent{setup.ServiceEvent()} // insert block C with a receipt for block B, and a seal for the receipt in // block B if there was one @@ -141,30 +154,29 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { // create a receipt for block C, to be included in block D receiptC := ReceiptForBlockFixture(&C) - // defaults for the EpochSetup event - setupDefaults := []func(*flow.EpochSetup){ - WithParticipants(identities), - SetupWithCounter(counter + 1), - WithFirstView(finalView + 1), - WithFinalView(finalView + 1000), - } - // build block D - // D contains a seal for block B and the EpochSetup event, as well as a - // receipt for block C - setup := EpochSetupFixture(append(setupDefaults, builder.setupOpts...)...) + // D contains a seal for block B and a receipt for block C D := BlockWithParentFixture(C.Header) sealForB := Seal.Fixture( Seal.WithResult(&receiptB.ExecutionResult), - Seal.WithServiceEvents(setup.ServiceEvent()), ) D.SetPayload(flow.Payload{ Receipts: []*flow.ExecutionReceipt{receiptC}, Seals: []*flow.Seal{sealForB}, }) builder.addBlock(&D) - // create receipt for block D + + // defaults for the EpochCommit event + commitDefaults := []func(*flow.EpochCommit){ + CommitWithCounter(counter + 1), + WithDKGFromParticipants(setup.Participants), + } + commit := EpochCommitFixture(append(commitDefaults, builder.commitOpts...)...) + + // create receipt for block D, to be included in block E + // the receipt for block D contains the EpochCommit event receiptD := ReceiptForBlockFixture(&D) + receiptD.ExecutionResult.ServiceEvents = []flow.ServiceEvent{commit.ServiceEvent()} // build block E // E contains a seal for C and a receipt for D @@ -180,20 +192,12 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { // create receipt for block E receiptE := ReceiptForBlockFixture(&E) - // defaults for the EpochCommit event - commitDefaults := []func(*flow.EpochCommit){ - CommitWithCounter(counter + 1), - WithDKGFromParticipants(setup.Participants), - } - // build block F // F contains a seal for block D and the EpochCommit event, as well as a // receipt for block E - commit := EpochCommitFixture(append(commitDefaults, builder.commitOpts...)...) F := BlockWithParentFixture(E.Header) sealForD := Seal.Fixture( Seal.WithResult(&receiptD.ExecutionResult), - Seal.WithServiceEvents(commit.ServiceEvent()), ) F.SetPayload(flow.Payload{ Receipts: []*flow.ExecutionReceipt{receiptE}, diff --git a/utils/unittest/fixtures.go b/utils/unittest/fixtures.go index 574328398b3..5c4624bf3b2 100644 --- a/utils/unittest/fixtures.go +++ b/utils/unittest/fixtures.go @@ -562,6 +562,12 @@ func WithChunk(chunkIdx uint64) func(*flow.ResultApproval) { } } +func WithServiveEvents(events ...flow.ServiceEvent) func(*flow.ExecutionResult) { + return func(result *flow.ExecutionResult) { + result.ServiceEvents = events + } +} + func ResultApprovalFixture(opts ...func(*flow.ResultApproval)) *flow.ResultApproval { attestation := flow.Attestation{ BlockID: IdentifierFixture(), @@ -1174,7 +1180,6 @@ func BootstrapFixture(participants flow.IdentityList, opts ...func(*flow.Block)) for _, apply := range opts { apply(root) } - result := BootstrapExecutionResultFixture(root, GenesisStateCommitment) counter := uint64(1) setup := EpochSetupFixture( @@ -1184,10 +1189,11 @@ func BootstrapFixture(participants flow.IdentityList, opts ...func(*flow.Block)) WithFinalView(root.Header.View+1000), ) commit := EpochCommitFixture(WithDKGFromParticipants(participants), CommitWithCounter(counter)) - seal := Seal.Fixture( - Seal.WithResult(result), - Seal.WithServiceEvents(setup.ServiceEvent(), commit.ServiceEvent()), - ) + + result := BootstrapExecutionResultFixture(root, GenesisStateCommitment) + result.ServiceEvents = []flow.ServiceEvent{setup.ServiceEvent(), commit.ServiceEvent()} + + seal := Seal.Fixture(Seal.WithResult(result)) return root, result, seal } From e7e9a77c40819142785413a7a5a45deb5e85ae6b Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Mar 2021 16:50:21 -0800 Subject: [PATCH 143/178] update tests using Seal.WithServiceEvents --- state/protocol/badger/mutator_test.go | 298 ++++++++++++-------------- utils/unittest/seals.go | 6 - 2 files changed, 141 insertions(+), 163 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index b33aaf2a0a3..2c6d62975fa 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -558,6 +558,9 @@ func TestExtendReceiptsValid(t *testing.T) { // Tests the full flow of transitioning between epochs by finalizing a setup // event, then a commit event, then finalizing the first block of the next epoch. // Also tests that appropriate epoch transition events are fired. +// +// ROOT <- B1 <- B2(R1) <- B3(S1) <- B4(R2) <- B5(S2) <- B6 <- B7 +// TODO revisit func TestExtendEpochTransitionValid(t *testing.T) { // create a event consumer to test epoch transition events consumer := new(mockprotocol.Consumer) @@ -582,19 +585,6 @@ func TestExtendEpochTransitionValid(t *testing.T) { err = state.Finalize(block1.ID()) require.Nil(t, err) - // create a receipt for block 1 - block1Receipt := unittest.ReceiptForBlockFixture(&block1) - - // add a second block with a receipt committing to the first block - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{block1Receipt}, - }) - err = state.Extend(&block2) - require.Nil(t, err) - err = state.Finalize(block2.ID()) - require.Nil(t, err) - epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) epoch1FinalView := epoch1Setup.FinalView @@ -610,24 +600,27 @@ func TestExtendEpochTransitionValid(t *testing.T) { unittest.WithFirstView(epoch1FinalView+1), ) - // create the seal referencing block1 and including the setup event - seal1 := unittest.Seal.Fixture( - unittest.Seal.WithResult(&block1Receipt.ExecutionResult), - unittest.Seal.WithServiceEvents(epoch2Setup.ServiceEvent()), - ) + // create a receipt for block 1 containing the EpochSetup event + receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1) + receipt1.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Setup.ServiceEvent()} - // create a receipt for block2 - block2Receipt := unittest.ReceiptForBlockFixture(&block2) + // add a second block with the receipt for block 1 + block2 := unittest.BlockWithParentFixture(block1.Header) + block2.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{receipt1}, + }) + err = state.Extend(&block2) + require.Nil(t, err) + err = state.Finalize(block2.ID()) + require.Nil(t, err) - // block 3 contains the epoch setup service event, as well as a receipt - // for block 2 + // block 3 contains the seal for block 1 block3 := unittest.BlockWithParentFixture(block2.Header) block3.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{block2Receipt}, - Seals: []*flow.Seal{seal1}, + Seals: []*flow.Seal{seal1}, }) - // insert the block containing the seal containing the setup event + // insert the block sealing the EpochSetup event err = state.Extend(&block3) require.Nil(t, err) @@ -663,31 +656,32 @@ func TestExtendEpochTransitionValid(t *testing.T) { unittest.WithDKGFromParticipants(epoch2Participants), ) - // create a seal for block 2 with epoch2 service event - seal2 := unittest.Seal.Fixture( - unittest.Seal.WithResult(&block2Receipt.ExecutionResult), - unittest.Seal.WithServiceEvents(epoch2Commit.ServiceEvent()), - ) - - // create a receipt for block 3 - block3Receipt := unittest.ReceiptForBlockFixture(&block3) + // create receipt and seal for block 2 + // the receipt for block 2 contains the EpochCommit event + receipt2, seal2 := unittest.ReceiptAndSealForBlock(&block2) + receipt2.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Commit.ServiceEvent()} - // block 4 contains the epoch commit service event, as well as a receipt - // for block 3 + // block 4 contains the receipt for block 2 block4 := unittest.BlockWithParentFixture(block3.Header) block4.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{block3Receipt}, - Seals: []*flow.Seal{seal2}, + Receipts: []*flow.ExecutionReceipt{receipt2}, }) err = state.Extend(&block4) require.Nil(t, err) + err = state.Finalize(block4.ID()) + require.Nil(t, err) + + block5 := unittest.BlockWithParentFixture(block4.Header) + block5.SetPayload(flow.Payload{ + Seals: []*flow.Seal{seal2}, + }) // we should NOT be able to query epoch 2 commit info wrt block 3 _, err = state.AtBlockID(block3.ID()).Epochs().Next().DKG() require.Error(t, err) - // now epoch 2 is fully ready, we can query anything we want about it wrt block 4 (or later) + // now epoch 2 is fully ready, we can query anything we want about it wrt block 5 (or later) _, err = state.AtBlockID(block4.ID()).Epochs().Next().InitialIdentities() require.Nil(t, err) _, err = state.AtBlockID(block4.ID()).Epochs().Next().Clustering() @@ -712,54 +706,54 @@ func TestExtendEpochTransitionValid(t *testing.T) { require.Equal(t, epoch1Setup.Counter, epochCounter) // block 5 has the final view of the epoch - block5 := unittest.BlockWithParentFixture(block4.Header) - block5.SetPayload(flow.EmptyPayload()) - block5.Header.View = epoch1FinalView + block6 := unittest.BlockWithParentFixture(block5.Header) + block6.SetPayload(flow.EmptyPayload()) + block6.Header.View = epoch1FinalView - err = state.Extend(&block5) + err = state.Extend(&block6) require.Nil(t, err) // we should still be in epoch 1, since epochs are inclusive of final view - epochCounter, err = state.AtBlockID(block5.ID()).Epochs().Current().Counter() + epochCounter, err = state.AtBlockID(block6.ID()).Epochs().Current().Counter() require.Nil(t, err) require.Equal(t, epoch1Setup.Counter, epochCounter) // block 6 has a view > final view of epoch 1, it will be considered the first block of epoch 2 - block6 := unittest.BlockWithParentFixture(block5.Header) - block6.SetPayload(flow.EmptyPayload()) + block7 := unittest.BlockWithParentFixture(block6.Header) + block7.SetPayload(flow.EmptyPayload()) // we should handle view that aren't exactly the first valid view of the epoch - block6.Header.View = epoch1FinalView + uint64(1+rand.Intn(10)) + block7.Header.View = epoch1FinalView + uint64(1+rand.Intn(10)) - err = state.Extend(&block6) + err = state.Extend(&block7) require.Nil(t, err) // now, at long last, we are in epoch 2 - epochCounter, err = state.AtBlockID(block6.ID()).Epochs().Current().Counter() + epochCounter, err = state.AtBlockID(block7.ID()).Epochs().Current().Counter() require.Nil(t, err) require.Equal(t, epoch2Setup.Counter, epochCounter) // we should begin epoch 2 in staking phase // how that the commit event has been emitted, we should be in the committed phase - phase, err = state.AtBlockID(block6.ID()).Phase() + phase, err = state.AtBlockID(block7.ID()).Phase() assert.Nil(t, err) require.Equal(t, flow.EpochPhaseStaking, phase) // expect epoch transition once we finalize block 6 consumer.On("EpochTransition", epoch2Setup.Counter, block6.Header).Once() - err = state.Finalize(block5.ID()) - require.Nil(t, err) err = state.Finalize(block6.ID()) require.Nil(t, err) - consumer.AssertCalled(t, "EpochTransition", epoch2Setup.Counter, block6.Header) + err = state.Finalize(block7.ID()) + require.Nil(t, err) + consumer.AssertCalled(t, "EpochTransition", epoch2Setup.Counter, block7.Header) }) } // we should be able to have conflicting forks with two different instances of // the same service event for the same epoch // -// /-->BLOCK1-->BLOCK3-->BLOCK5 +// /-->B1-->B3(R1)-->B5(S1) // ROOT --+ -// \-->BLOCK2-->BLOCK4-->BLOCK6 +// \-->B2-->B4(R2)-->B6(S2) // func TestExtendConflictingEpochEvents(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(participants) @@ -780,24 +774,6 @@ func TestExtendConflictingEpochEvents(t *testing.T) { err = state.Extend(&block2) require.Nil(t, err) - // add blocks containing receipts for block1 and block2 (necessary for - // sealing) - block1Receipt := unittest.ReceiptForBlockFixture(&block1) - block3 := unittest.BlockWithParentFixture(block1.Header) - block3.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{block1Receipt}, - }) - err = state.Extend(&block3) - require.Nil(t, err) - - block2Receipt := unittest.ReceiptForBlockFixture(&block2) - block4 := unittest.BlockWithParentFixture(block2.Header) - block4.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{block2Receipt}, - }) - err = state.Extend(&block4) - require.Nil(t, err) - rootSetup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) // create two conflicting epoch setup events for the next epoch (final view differs) @@ -814,19 +790,38 @@ func TestExtendConflictingEpochEvents(t *testing.T) { unittest.WithFirstView(rootSetup.FinalView+1), ) - // create one seal containing the first setup event - seal1 := unittest.Seal.Fixture( - unittest.Seal.WithResult(&block1Receipt.ExecutionResult), - unittest.Seal.WithServiceEvents(nextEpochSetup1.ServiceEvent()), - ) + // add blocks containing receipts for block1 and block2 (necessary for sealing) + // block 1 receipt contains nextEpochSetup1 + block1Receipt := unittest.ReceiptForBlockFixture(&block1) + block1Receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{nextEpochSetup1.ServiceEvent()} - // create another seal containing the second setup event - seal2 := unittest.Seal.Fixture( - unittest.Seal.WithResult(&block2Receipt.ExecutionResult), - unittest.Seal.WithServiceEvents(nextEpochSetup2.ServiceEvent()), - ) + // add block 1 receipt to block 3 payload + block3 := unittest.BlockWithParentFixture(block1.Header) + block3.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{block1Receipt}, + }) + err = state.Extend(&block3) + require.Nil(t, err) + + // block 2 receipt contains nextEpochSetup2 + block2Receipt := unittest.ReceiptForBlockFixture(&block2) + block2Receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{nextEpochSetup2.ServiceEvent()} + + // add block 2 receipt to block 4 payload + block4 := unittest.BlockWithParentFixture(block2.Header) + block4.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{block2Receipt}, + }) + err = state.Extend(&block4) + require.Nil(t, err) + + // seal for block 1 + seal1 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block1Receipt.ExecutionResult)) - // block 5 builds on block 3, contains setup event 1 + // seal for block 2 + seal2 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block2Receipt.ExecutionResult)) + + // block 5 builds on block 3, contains seal for block 1 block5 := unittest.BlockWithParentFixture(block3.Header) block5.SetPayload(flow.Payload{ Seals: []*flow.Seal{seal1}, @@ -834,7 +829,7 @@ func TestExtendConflictingEpochEvents(t *testing.T) { err = state.Extend(&block5) require.Nil(t, err) - // block 6 builds on block 4, contains setup event 2 + // block 6 builds on block 4, contains seal for block 2 block6 := unittest.BlockWithParentFixture(block4.Header) block6.SetPayload(flow.Payload{ Seals: []*flow.Seal{seal2}, @@ -878,27 +873,25 @@ func TestExtendEpochSetupInvalid(t *testing.T) { // this function will return a VALID setup event and seal, we will modify // in different ways in each test case - createSetup := func() (*flow.EpochSetup, *flow.Seal) { + createSetup := func() (*flow.EpochSetup, *flow.ExecutionReceipt, *flow.Seal) { setup := unittest.EpochSetupFixture( unittest.WithParticipants(epoch2Participants), unittest.SetupWithCounter(epoch1Setup.Counter+1), unittest.WithFinalView(epoch1Setup.FinalView+1000), unittest.WithFirstView(epoch1Setup.FinalView+1), ) - seal := unittest.Seal.Fixture( - unittest.Seal.WithBlockID(block1.ID()), - unittest.Seal.WithServiceEvents(setup.ServiceEvent()), - ) - return setup, seal + receipt, seal := unittest.ReceiptAndSealForBlock(&block1) + receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{setup.ServiceEvent()} + return setup, receipt, seal } t.Run("wrong counter", func(t *testing.T) { - setup, seal := createSetup() + setup, receipt, _ := createSetup() setup.Counter = epoch1Setup.Counter block := unittest.BlockWithParentFixture(block1.Header) block.SetPayload(flow.Payload{ - Seals: []*flow.Seal{seal}, + Receipts: []*flow.ExecutionReceipt{receipt}, }) err = state.Extend(&block) @@ -907,12 +900,12 @@ func TestExtendEpochSetupInvalid(t *testing.T) { }) t.Run("invalid final view", func(t *testing.T) { - setup, seal := createSetup() + setup, receipt, _ := createSetup() block := unittest.BlockWithParentFixture(block1.Header) setup.FinalView = block.Header.View block.SetPayload(flow.Payload{ - Seals: []*flow.Seal{seal}, + Receipts: []*flow.ExecutionReceipt{receipt}, }) err = state.Extend(&block) require.Error(t, err) @@ -920,12 +913,12 @@ func TestExtendEpochSetupInvalid(t *testing.T) { }) t.Run("empty seed", func(t *testing.T) { - setup, seal := createSetup() + setup, receipt, _ := createSetup() setup.RandomSource = nil block := unittest.BlockWithParentFixture(block1.Header) block.SetPayload(flow.Payload{ - Seals: []*flow.Seal{seal}, + Receipts: []*flow.ExecutionReceipt{receipt}, }) err = state.Extend(&block) @@ -939,9 +932,10 @@ func TestExtendEpochSetupInvalid(t *testing.T) { func TestExtendEpochCommitInvalid(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(participants) util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() require.NoError(t, err) - _, seal, err := rootSnapshot.SealedResult() + result, _, err := rootSnapshot.SealedResult() require.NoError(t, err) // add a block for the first seal to reference @@ -952,18 +946,7 @@ func TestExtendEpochCommitInvalid(t *testing.T) { err = state.Finalize(block1.ID()) require.Nil(t, err) - // add a block with a receipt for block1 - block1Receipt := unittest.ReceiptForBlockFixture(&block1) - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{block1Receipt}, - }) - err = state.Extend(&block2) - require.Nil(t, err) - err = state.Finalize(block2.ID()) - require.Nil(t, err) - - epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) // swap consensus node for a new one for epoch 2 epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleConsensus)) @@ -972,65 +955,67 @@ func TestExtendEpochCommitInvalid(t *testing.T) { epoch2NewParticipant, ).Order(order.ByNodeIDAsc) - createSetup := func(sealedResult *flow.ExecutionResult) (*flow.EpochSetup, *flow.Seal) { + createSetup := func(block *flow.Block) (*flow.EpochSetup, *flow.ExecutionReceipt, *flow.Seal) { setup := unittest.EpochSetupFixture( unittest.WithParticipants(epoch2Participants), unittest.SetupWithCounter(epoch1Setup.Counter+1), unittest.WithFinalView(epoch1Setup.FinalView+1000), unittest.WithFirstView(epoch1Setup.FinalView+1), ) - seal := unittest.Seal.Fixture( - unittest.Seal.WithResult(sealedResult), - unittest.Seal.WithServiceEvents(setup.ServiceEvent()), - ) - return setup, seal + receipt, seal := unittest.ReceiptAndSealForBlock(block) + receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{setup.ServiceEvent()} + return setup, receipt, seal } - createCommit := func(sealedResult *flow.ExecutionResult) (*flow.EpochCommit, *flow.Seal) { + createCommit := func(block *flow.Block) (*flow.EpochCommit, *flow.ExecutionReceipt, *flow.Seal) { commit := unittest.EpochCommitFixture( unittest.CommitWithCounter(epoch1Setup.Counter+1), unittest.WithDKGFromParticipants(epoch2Participants), ) - seal := unittest.Seal.Fixture( - unittest.Seal.WithResult(sealedResult), - unittest.Seal.WithServiceEvents(commit.ServiceEvent()), - ) - return commit, seal + receipt, seal := unittest.ReceiptAndSealForBlock(block) + receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{commit.ServiceEvent()} + return commit, receipt, seal } t.Run("without setup", func(t *testing.T) { - _, seal := createCommit(&block1Receipt.ExecutionResult) + _, receipt, _ := createCommit(&block1) - block := unittest.BlockWithParentFixture(block2.Header) + block := unittest.BlockWithParentFixture(block1.Header) block.SetPayload(flow.Payload{ - Seals: []*flow.Seal{seal}, + Receipts: []*flow.ExecutionReceipt{receipt}, }) err = state.Extend(&block) require.Error(t, err) require.True(t, st.IsInvalidExtensionError(err), err) }) - // insert the epoch setup - epoch2Setup, setupSeal := createSetup(&block1Receipt.ExecutionResult) - block2Receipt := unittest.ReceiptForBlockFixture(&block2) + // insert the receipt containing the EpochSetup event and its seal + epoch2Setup, setupReceipt, setupSeal := createSetup(&block1) + block2 := unittest.BlockWithParentFixture(block1.Header) + block2.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{setupReceipt}, + }) + err = state.Extend(&block2) + require.Nil(t, err) + err = state.Finalize(block2.ID()) + require.Nil(t, err) + block3 := unittest.BlockWithParentFixture(block2.Header) block3.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{block2Receipt}, - Seals: []*flow.Seal{setupSeal}, + Seals: []*flow.Seal{setupSeal}, }) err = state.Extend(&block3) require.Nil(t, err) err = state.Finalize(block3.ID()) require.Nil(t, err) - _ = epoch2Setup t.Run("inconsistent counter", func(t *testing.T) { - commit, seal := createCommit(&block2Receipt.ExecutionResult) + commit, receipt, _ := createCommit(&block3) commit.Counter = epoch2Setup.Counter + 1 block := unittest.BlockWithParentFixture(block3.Header) block.SetPayload(flow.Payload{ - Seals: []*flow.Seal{seal}, + Receipts: []*flow.ExecutionReceipt{receipt}, }) err := state.Extend(&block) require.Error(t, err) @@ -1038,31 +1023,33 @@ func TestExtendEpochCommitInvalid(t *testing.T) { }) t.Run("inconsistent cluster QCs", func(t *testing.T) { - commit, seal := createCommit(&block2Receipt.ExecutionResult) + commit, receipt, _ := createCommit(&block3) commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) block := unittest.BlockWithParentFixture(block3.Header) block.SetPayload(flow.Payload{ - Seals: []*flow.Seal{seal}, + Receipts: []*flow.ExecutionReceipt{receipt}, }) err := state.Extend(&block) require.Error(t, err) + require.True(t, st.IsInvalidExtensionError(err), err) }) t.Run("missing dkg group key", func(t *testing.T) { - commit, seal := createCommit(&block2Receipt.ExecutionResult) + commit, receipt, _ := createCommit(&block3) commit.DKGGroupKey = nil block := unittest.BlockWithParentFixture(block3.Header) block.SetPayload(flow.Payload{ - Seals: []*flow.Seal{seal}, + Receipts: []*flow.ExecutionReceipt{receipt}, }) err := state.Extend(&block) require.Error(t, err) + require.True(t, st.IsInvalidExtensionError(err), err) }) t.Run("inconsistent DKG participants", func(t *testing.T) { - commit, seal := createCommit(&block2Receipt.ExecutionResult) + commit, receipt, _ := createCommit(&block3) // add the consensus node from epoch *1*, which was removed for epoch 2 epoch1CONNode := participants.Filter(filter.HasRole(flow.RoleConsensus))[0] @@ -1073,16 +1060,19 @@ func TestExtendEpochCommitInvalid(t *testing.T) { block := unittest.BlockWithParentFixture(block3.Header) block.SetPayload(flow.Payload{ - Seals: []*flow.Seal{seal}, + Receipts: []*flow.ExecutionReceipt{receipt}, }) err := state.Extend(&block) require.Error(t, err) + require.True(t, st.IsInvalidExtensionError(err), err) }) }) } // if we reach the first block of the next epoch before both setup and commit // service events are finalized, the chain should halt +// +// ROOT <- B1 <- B2(R1) <- B3(S1) <- B4 func TestExtendEpochTransitionWithoutCommit(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(participants) util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { @@ -1099,17 +1089,6 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { err = state.Finalize(block1.ID()) require.Nil(t, err) - // add a block containing a receipt for block1 - block1Receipt := unittest.ReceiptForBlockFixture(&block1) - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{block1Receipt}, - }) - err = state.Extend(&block2) - require.Nil(t, err) - err = state.Finalize(block2.ID()) - require.Nil(t, err) - epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) epoch1FinalView := epoch1Setup.FinalView @@ -1125,19 +1104,24 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { unittest.WithFirstView(epoch1FinalView+1), ) - // create the seal referencing block1 and including the setup event - seal1 := unittest.Seal.Fixture( - unittest.Seal.WithResult(&block1Receipt.ExecutionResult), - unittest.Seal.WithServiceEvents(epoch2Setup.ServiceEvent()), - ) + receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1) + receipt1.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Setup.ServiceEvent()} - // block 3 contains the epoch setup service event + // add a block containing a receipt for block 1 + block2 := unittest.BlockWithParentFixture(block1.Header) + block2.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{receipt1}, + }) + err = state.Extend(&block2) + require.Nil(t, err) + err = state.Finalize(block2.ID()) + require.Nil(t, err) + + // block 3 seals block 1 block3 := unittest.BlockWithParentFixture(block2.Header) block3.SetPayload(flow.Payload{ Seals: []*flow.Seal{seal1}, }) - - // insert the block containing the seal containing the setup event err = state.Extend(&block3) require.Nil(t, err) diff --git a/utils/unittest/seals.go b/utils/unittest/seals.go index 7053f7ba80f..01323ee1011 100644 --- a/utils/unittest/seals.go +++ b/utils/unittest/seals.go @@ -53,12 +53,6 @@ func (f *sealFactory) WithBlock(block *flow.Header) func(*flow.Seal) { } } -func (f *sealFactory) WithServiceEvents(events ...flow.ServiceEvent) func(*flow.Seal) { - return func(seal *flow.Seal) { - seal.ServiceEvents = events - } -} - func (f *sealFactory) AggregatedSignatureFixtures(number int) []flow.AggregatedSignature { sigs := make([]flow.AggregatedSignature, 0, number) for ; number > 0; number-- { From 85e1ca34767bcf907225389cf1c873d5cab8bfc7 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Mar 2021 17:26:53 -0800 Subject: [PATCH 144/178] update protocol/cluster tests --- module/builder/collection/builder_test.go | 2 +- state/cluster/badger/mutator_test.go | 2 +- state/protocol/badger/mutator_test.go | 16 +++++----- state/protocol/badger/snapshot_test.go | 12 ++++---- state/protocol/badger/validity_test.go | 36 +++++++++++------------ state/protocol/inmem/convert.go | 8 ++--- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/module/builder/collection/builder_test.go b/module/builder/collection/builder_test.go index 389b7cb37e1..7e5b78278e6 100644 --- a/module/builder/collection/builder_test.go +++ b/module/builder/collection/builder_test.go @@ -88,7 +88,7 @@ func (suite *BuilderSuite) SetupTest() { root, result, seal := unittest.BootstrapFixture(participants) qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(root.ID())) // ensure we don't enter a new epoch for tests that build many blocks - seal.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = root.Header.View + 100000 + result.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = root.Header.View + 100000 rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc) require.NoError(suite.T(), err) diff --git a/state/cluster/badger/mutator_test.go b/state/cluster/badger/mutator_test.go index 5d27cff459f..7d41bf1c16f 100644 --- a/state/cluster/badger/mutator_test.go +++ b/state/cluster/badger/mutator_test.go @@ -74,7 +74,7 @@ func (suite *MutatorSuite) SetupTest() { genesis, result, seal := unittest.BootstrapFixture(participants) qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(genesis.ID())) // ensure we don't enter a new epoch for tests that build many blocks - seal.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = genesis.Header.View + 100000 + result.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = genesis.Header.View + 100000 rootSnapshot, err := inmem.SnapshotFromBootstrapState(genesis, result, seal, qc) require.NoError(suite.T(), err) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 2c6d62975fa..a535499ec57 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -569,7 +569,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { util.RunWithFullProtocolStateAndConsumer(t, rootSnapshot, consumer, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - _, seal, err := rootSnapshot.SealedResult() + result, _, err := rootSnapshot.SealedResult() require.NoError(t, err) // we should begin the epoch in the staking phase @@ -585,7 +585,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { err = state.Finalize(block1.ID()) require.Nil(t, err) - epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) epoch1FinalView := epoch1Setup.FinalView // add a participant for the next epoch @@ -760,7 +760,7 @@ func TestExtendConflictingEpochEvents(t *testing.T) { util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - _, seal, err := rootSnapshot.SealedResult() + result, _, err := rootSnapshot.SealedResult() require.NoError(t, err) // add two conflicting blocks for each service event to reference @@ -774,7 +774,7 @@ func TestExtendConflictingEpochEvents(t *testing.T) { err = state.Extend(&block2) require.Nil(t, err) - rootSetup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + rootSetup := result.ServiceEvents[0].Event.(*flow.EpochSetup) // create two conflicting epoch setup events for the next epoch (final view differs) nextEpochSetup1 := unittest.EpochSetupFixture( @@ -854,7 +854,7 @@ func TestExtendEpochSetupInvalid(t *testing.T) { util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - _, seal, err := rootSnapshot.SealedResult() + result, _, err := rootSnapshot.SealedResult() require.NoError(t, err) // add a block for the first seal to reference @@ -865,7 +865,7 @@ func TestExtendEpochSetupInvalid(t *testing.T) { err = state.Finalize(block1.ID()) require.Nil(t, err) - epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) // add a participant for the next epoch epoch2NewParticipant := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) @@ -1078,7 +1078,7 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) - _, seal, err := rootSnapshot.SealedResult() + result, _, err := rootSnapshot.SealedResult() require.NoError(t, err) // add a block for the first seal to reference @@ -1089,7 +1089,7 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { err = state.Finalize(block1.ID()) require.Nil(t, err) - epoch1Setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) epoch1FinalView := epoch1Setup.FinalView // add a participant for the next epoch diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index 2458da8baa1..6d7951bd345 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -96,8 +96,8 @@ func TestClusters(t *testing.T) { root, result, seal := unittest.BootstrapFixture(identities) qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(root.ID())) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := result.ServiceEvents[1].Event.(*flow.EpochCommit) setup.Assignments = unittest.ClusterAssignment(uint(nClusters), collectors) commit.ClusterQCs = make([]*flow.QuorumCertificate, nClusters) for i := 0; i < nClusters; i++ { @@ -428,11 +428,11 @@ func TestQuorumCertificate(t *testing.T) { func TestSnapshot_EpochQuery(t *testing.T) { identities := unittest.CompleteIdentitySet() rootSnapshot := unittest.RootSnapshotFixture(identities) - _, seal, err := rootSnapshot.SealedResult() + result, _, err := rootSnapshot.SealedResult() require.NoError(t, err) util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.MutableState) { - epoch1Counter := seal.ServiceEvents[0].Event.(*flow.EpochSetup).Counter + epoch1Counter := result.ServiceEvents[0].Event.(*flow.EpochSetup).Counter epoch2Counter := epoch1Counter + 1 // Prepare an epoch builder, which builds epochs with 6 blocks, A,B,C,D,E,F @@ -531,7 +531,7 @@ func TestSnapshot_EpochFirstView(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(identities) head, err := rootSnapshot.Head() require.NoError(t, err) - _, seal, err := rootSnapshot.SealedResult() + result, _, err := rootSnapshot.SealedResult() require.NoError(t, err) util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.MutableState) { @@ -562,7 +562,7 @@ func TestSnapshot_EpochFirstView(t *testing.T) { // figure out the expected first views of the epochs epoch1FirstView := head.View - epoch2FirstView := seal.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView + 1 + epoch2FirstView := result.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView + 1 epoch1Heights := []uint64{0, 1, 2, 3, 4, 5} epoch2Heights := []uint64{6, 7, 8, 9, 10, 11} diff --git a/state/protocol/badger/validity_test.go b/state/protocol/badger/validity_test.go index cd08bf4b1b7..36b243d8b56 100644 --- a/state/protocol/badger/validity_test.go +++ b/state/protocol/badger/validity_test.go @@ -15,8 +15,8 @@ var participants = unittest.IdentityListFixture(5, unittest.WithAllRoles()) func TestEpochSetupValidity(t *testing.T) { t.Run("invalid first/final view", func(t *testing.T) { - _, _, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + _, result, _ := unittest.BootstrapFixture(participants) + setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) // set an invalid final view for the first epoch setup.FinalView = setup.FirstView @@ -25,8 +25,8 @@ func TestEpochSetupValidity(t *testing.T) { }) t.Run("invalid cluster assignments", func(t *testing.T) { - _, _, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + _, result, _ := unittest.BootstrapFixture(participants) + setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) // create an invalid cluster assignment (node appears in multiple clusters) collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] setup.Assignments = append(setup.Assignments, []flow.Identifier{collector.NodeID}) @@ -36,8 +36,8 @@ func TestEpochSetupValidity(t *testing.T) { }) t.Run("short seed", func(t *testing.T) { - _, _, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + _, result, _ := unittest.BootstrapFixture(participants) + setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) setup.RandomSource = unittest.SeedFixture(crypto.SeedMinLenDKG - 1) err := isValidEpochSetup(setup) @@ -47,9 +47,9 @@ func TestEpochSetupValidity(t *testing.T) { func TestBootstrapInvalidEpochCommit(t *testing.T) { t.Run("inconsistent counter", func(t *testing.T) { - _, _, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + _, result, _ := unittest.BootstrapFixture(participants) + setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := result.ServiceEvents[1].Event.(*flow.EpochCommit) // use a different counter for the commit commit.Counter = setup.Counter + 1 @@ -58,9 +58,9 @@ func TestBootstrapInvalidEpochCommit(t *testing.T) { }) t.Run("inconsistent cluster QCs", func(t *testing.T) { - _, _, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + _, result, _ := unittest.BootstrapFixture(participants) + setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := result.ServiceEvents[1].Event.(*flow.EpochCommit) // add an extra QC to commit commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) @@ -69,9 +69,9 @@ func TestBootstrapInvalidEpochCommit(t *testing.T) { }) t.Run("missing dkg group key", func(t *testing.T) { - _, _, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + _, result, _ := unittest.BootstrapFixture(participants) + setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := result.ServiceEvents[1].Event.(*flow.EpochCommit) commit.DKGGroupKey = nil err := isValidEpochCommit(commit, setup) @@ -79,9 +79,9 @@ func TestBootstrapInvalidEpochCommit(t *testing.T) { }) t.Run("inconsistent DKG participants", func(t *testing.T) { - _, _, seal := unittest.BootstrapFixture(participants) - setup := seal.ServiceEvents[0].Event.(*flow.EpochSetup) - commit := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + _, result, _ := unittest.BootstrapFixture(participants) + setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) + commit := result.ServiceEvents[1].Event.(*flow.EpochCommit) // add an invalid DKG participant collector := participants.Filter(filter.HasRole(flow.RoleCollection))[0] commit.DKGParticipants[collector.NodeID] = flow.DKGParticipant{ diff --git a/state/protocol/inmem/convert.go b/state/protocol/inmem/convert.go index d6fb324e427..27a8ae6421c 100644 --- a/state/protocol/inmem/convert.go +++ b/state/protocol/inmem/convert.go @@ -187,13 +187,13 @@ func ClusterFromEncodable(enc EncodableCluster) (*Cluster, error) { // genesis or post-spork states. func SnapshotFromBootstrapState(root *flow.Block, result *flow.ExecutionResult, seal *flow.Seal, qc *flow.QuorumCertificate) (*Snapshot, error) { - setup, ok := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + setup, ok := result.ServiceEvents[0].Event.(*flow.EpochSetup) if !ok { - return nil, fmt.Errorf("invalid setup event type (%T)", seal.ServiceEvents[0].Event) + return nil, fmt.Errorf("invalid setup event type (%T)", result.ServiceEvents[0].Event) } - commit, ok := seal.ServiceEvents[1].Event.(*flow.EpochCommit) + commit, ok := result.ServiceEvents[1].Event.(*flow.EpochCommit) if !ok { - return nil, fmt.Errorf("invalid commit event type (%T)", seal.ServiceEvents[1].Event) + return nil, fmt.Errorf("invalid commit event type (%T)", result.ServiceEvents[1].Event) } current, err := NewCommittedEpoch(setup, commit) From 19bbd5796241edf8c894f393829c40c3547b8564 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 5 Mar 2021 17:35:46 -0800 Subject: [PATCH 145/178] update integration tests --- integration/localnet/bootstrap.go | 2 +- integration/testnet/network.go | 43 +++++++++++++--------- integration/tests/collection/suite_test.go | 23 ++++-------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/integration/localnet/bootstrap.go b/integration/localnet/bootstrap.go index 70c876aed4a..39cada707b9 100644 --- a/integration/localnet/bootstrap.go +++ b/integration/localnet/bootstrap.go @@ -118,7 +118,7 @@ func main() { panic(err) } - _, _, containers, err := testnet.BootstrapNetwork(conf, BootstrapDir) + _, _, _, containers, err := testnet.BootstrapNetwork(conf, BootstrapDir) if err != nil { panic(err) } diff --git a/integration/testnet/network.go b/integration/testnet/network.go index 8bd250712ce..4b8e7f9f554 100644 --- a/integration/testnet/network.go +++ b/integration/testnet/network.go @@ -74,6 +74,7 @@ type FlowNetwork struct { Containers map[string]*Container AccessPorts map[string]string root *flow.Block + result *flow.ExecutionResult seal *flow.Seal } @@ -96,6 +97,11 @@ func (net *FlowNetwork) Seal() *flow.Seal { return net.seal } +// Result returns the root block result generated for the network. +func (net *FlowNetwork) Result() *flow.ExecutionResult { + return net.result +} + // Start starts the network. func (net *FlowNetwork) Start(ctx context.Context) { // makes it easier to see logs for a specific test case @@ -334,7 +340,7 @@ func PrepareFlowNetwork(t *testing.T, networkConf NetworkConfig) *FlowNetwork { bootstrapDir, err := ioutil.TempDir(TmpRoot, "flow-integration-bootstrap") require.Nil(t, err) - root, seal, confs, err := BootstrapNetwork(networkConf, bootstrapDir) + root, result, seal, confs, err := BootstrapNetwork(networkConf, bootstrapDir) require.Nil(t, err) flowNetwork := &FlowNetwork{ @@ -347,6 +353,7 @@ func PrepareFlowNetwork(t *testing.T, networkConf NetworkConfig) *FlowNetwork { AccessPorts: make(map[string]string), root: root, seal: seal, + result: result, } // add each node to the network @@ -514,7 +521,7 @@ func (net *FlowNetwork) AddNode(t *testing.T, bootstrapDir string, nodeConf Cont return nil } -func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Block, *flow.Seal, []ContainerConfig, error) { +func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Block, *flow.ExecutionResult, *flow.Seal, []ContainerConfig, error) { // Setup as Testnet chainID := flow.Testnet chain := chainID.Chain() @@ -522,7 +529,7 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo // number of nodes nNodes := len(networkConf.Nodes) if nNodes == 0 { - return nil, nil, nil, fmt.Errorf("must specify at least one node") + return nil, nil, nil, nil, fmt.Errorf("must specify at least one node") } // Sort so that access nodes start up last @@ -531,13 +538,13 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo // generate staking and networking keys for each configured node confs, err := setupKeys(networkConf) if err != nil { - return nil, nil, nil, fmt.Errorf("failed to setup keys: %w", err) + return nil, nil, nil, nil, fmt.Errorf("failed to setup keys: %w", err) } // run DKG for all consensus nodes dkg, err := runDKG(confs) if err != nil { - return nil, nil, nil, fmt.Errorf("failed to run DKG: %w", err) + return nil, nil, nil, nil, fmt.Errorf("failed to run DKG: %w", err) } // write private key files for each DKG participant @@ -553,7 +560,7 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo path := fmt.Sprintf(bootstrap.PathRandomBeaconPriv, nodeID) err = writeJSON(filepath.Join(bootstrapDir, path), privParticpant) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } } @@ -564,12 +571,12 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo // retrieve private representation of the node private, err := nodeConfig.NodeInfo.Private() if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } err = writeJSON(path, private) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } } @@ -577,7 +584,7 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo trieDir := filepath.Join(bootstrapDir, bootstrap.DirnameExecutionState) commit, err := run.GenerateExecutionState(trieDir, unittest.ServiceAccountPublicKey, unittest.GenesisTokenSupply, chain) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } // define root block parameters @@ -594,23 +601,23 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo nodeInfos := bootstrap.FilterByRole(toNodeInfos(confs), flow.RoleConsensus) signerData, err := run.GenerateQCParticipantData(nodeInfos, nodeInfos, dkg) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } qc, err := run.GenerateRootQC(root, signerData) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } // generate root blocks for each collector cluster clusterAssignments, clusterQCs, err := setupClusterGenesisBlockQCs(networkConf.NClusters, epochCounter, confs) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } randomSource := make([]byte, flow.EpochSetupRandomSourceLength) _, err = rand.Read(randomSource) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } // generate epoch service events @@ -636,25 +643,25 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootBlock), root) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootQC), qc) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootResult), result) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootSeal), seal) if err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } - return root, seal, confs, nil + return root, result, seal, confs, nil } // setupKeys generates private staking and networking keys for each configured diff --git a/integration/tests/collection/suite_test.go b/integration/tests/collection/suite_test.go index 38f32ff012a..c18b5a6db36 100644 --- a/integration/tests/collection/suite_test.go +++ b/integration/tests/collection/suite_test.go @@ -19,11 +19,8 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" "github.com/onflow/flow-go/model/messages" - "github.com/onflow/flow-go/module/metrics" - "github.com/onflow/flow-go/module/trace" clusterstate "github.com/onflow/flow-go/state/cluster" clusterstateimpl "github.com/onflow/flow-go/state/cluster/badger" - storage "github.com/onflow/flow-go/storage/badger" "github.com/onflow/flow-go/utils/unittest" ) @@ -123,8 +120,8 @@ func (suite *CollectorSuite) Ghost() *ghostclient.GhostClient { } func (suite *CollectorSuite) Clusters() flow.ClusterList { - seal := suite.net.Seal() - setup, ok := seal.ServiceEvents[0].Event.(*flow.EpochSetup) + result := suite.net.Result() + setup, ok := result.ServiceEvents[0].Event.(*flow.EpochSetup) suite.Require().True(ok) collectors := suite.net.Identities().Filter(filter.HasRole(flow.RoleCollection)) @@ -310,7 +307,7 @@ func (suite *CollectorSuite) ClusterStateFor(id flow.Identifier) *clusterstateim myCluster, _, ok := suite.Clusters().ByNodeID(id) require.True(suite.T(), ok, "could not get node %s in clusters", id) - setup, ok := suite.net.Seal().ServiceEvents[0].Event.(*flow.EpochSetup) + setup, ok := suite.net.Result().ServiceEvents[0].Event.(*flow.EpochSetup) suite.Require().True(ok, "could not get root seal setup") rootBlock := clusterstate.CanonicalRootBlock(setup.Counter, myCluster) node := suite.net.ContainerByID(id) @@ -318,14 +315,10 @@ func (suite *CollectorSuite) ClusterStateFor(id flow.Identifier) *clusterstateim db, err := node.DB() require.Nil(suite.T(), err, "could not get node db") - metrics := metrics.NewNoopCollector() - tracer := trace.NewNoopTracer() + clusterStateRoot, err := clusterstateimpl.NewStateRoot(rootBlock) + suite.NoError(err) + clusterState, err := clusterstateimpl.Bootstrap(db, clusterStateRoot) + require.NoError(suite.T(), err, "could not get cluster state") - headers := storage.NewHeaders(metrics, db) - payloads := storage.NewClusterPayloads(metrics, db) - - state, err := clusterstateimpl.NewState(db, tracer, rootBlock.Header.ChainID, headers, payloads) - require.Nil(suite.T(), err, "could not get cluster state") - - return state + return clusterState } From ba7b27dc699f63bd9dc98581be6b22beca449478 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 8 Mar 2021 17:54:06 -0800 Subject: [PATCH 146/178] check validity when querying protocol state --- state/errors.go | 34 ++++++++++++++++++++++++++------- state/protocol/badger/state.go | 14 +++++++++++++- utils/unittest/epoch_builder.go | 1 - 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/state/errors.go b/state/errors.go index 964121d69a4..62fdc11bbc5 100644 --- a/state/errors.go +++ b/state/errors.go @@ -3,6 +3,8 @@ package state import ( "errors" "fmt" + + "github.com/onflow/flow-go/model/flow" ) // InvalidExtensionError is an error for invalid extension of the state @@ -30,8 +32,7 @@ func (e InvalidExtensionError) Error() string { // IsInvalidExtensionError returns whether the given error is an InvalidExtensionError error func IsInvalidExtensionError(err error) bool { - var errInvalidExtensionError InvalidExtensionError - return errors.As(err, &errInvalidExtensionError) + return errors.As(err, &InvalidExtensionError{}) } // OutdatedExtensionError is an error for the extension of the state being outdated. @@ -61,11 +62,10 @@ func (e OutdatedExtensionError) Error() string { } func IsOutdatedExtensionError(err error) bool { - var errOutdatedExtensionError OutdatedExtensionError - return errors.As(err, &errOutdatedExtensionError) + return errors.As(err, &OutdatedExtensionError{}) } -// NoValidChildBlockError is a sentinal error when the case where a certain block has +// NoValidChildBlockError is a sentinel error when the case where a certain block has // no valid child. type NoValidChildBlockError struct { err error @@ -90,6 +90,26 @@ func (e NoValidChildBlockError) Error() string { } func IsNoValidChildBlockError(err error) bool { - var errNoValidChildBlockError NoValidChildBlockError - return errors.As(err, &errNoValidChildBlockError) + return errors.As(err, &NoValidChildBlockError{}) +} + +// UnvalidatedBlockQueryError is an error returned when we query a block by ID +// which has not been marked valid by HotStuff. This means that we aren't yet +// sure whether the block contains a valid quorum certificate. As a rule, the +// protocol state returns an error for these blocks rather than returning a +// state which may be invalid. +type UnvalidatedBlockQueryError struct { + BlockID flow.Identifier +} + +func (e UnvalidatedBlockQueryError) Error() string { + return fmt.Sprintf("invalid query for unvalidated block (id=%x)", e.BlockID) +} + +func NewUnvalidatedBlockQueryError(blockID flow.Identifier) error { + return UnvalidatedBlockQueryError{BlockID: blockID} +} + +func IsUnvalidatedBlockQueryError(err error) bool { + return errors.As(err, &UnvalidatedBlockQueryError{}) } diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 548e8ed39cf..4affcc08e9b 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -10,6 +10,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" + "github.com/onflow/flow-go/state" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/state/protocol/invalid" "github.com/onflow/flow-go/storage" @@ -422,10 +423,21 @@ func (s *State) AtHeight(height uint64) protocol.Snapshot { if err != nil { return invalid.NewSnapshot(fmt.Errorf("could not look up block by height: %w", err)) } - return s.AtBlockID(blockID) + return NewSnapshot(s, blockID) } func (s *State) AtBlockID(blockID flow.Identifier) protocol.Snapshot { + // check that the block has been marked valid by HotStuff + // we don't need this check for Final/Sealed/AtHeight queries because all + // blocks which have been indexed must have been marked valid + var valid bool + err := s.db.View(operation.RetrieveBlockValidity(blockID, &valid)) + if err != nil { + return invalid.NewSnapshot(fmt.Errorf("could not get validity for block (id=%x): %w", err)) + } + if !valid { + return invalid.NewSnapshot(state.NewUnvalidatedBlockQueryError(blockID)) + } return NewSnapshot(s, blockID) } diff --git a/utils/unittest/epoch_builder.go b/utils/unittest/epoch_builder.go index cd2bed7d3bd..0b5ef7a84db 100644 --- a/utils/unittest/epoch_builder.go +++ b/utils/unittest/epoch_builder.go @@ -1,6 +1,5 @@ package unittest -import "C" import ( "math/rand" "testing" From 83dd6ad8d78dcd653374703f27448b1aed20ed42 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Mar 2021 09:51:02 -0800 Subject: [PATCH 147/178] add feature branch to CI --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ad87d0135e..1de0667e80e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,11 @@ on: push: branches: - master + - feature/serializable-snapshots pull_request: branches: - master + - feature/serializable-snapshots jobs: golangci: From b78c981cdeadd404c3e5576d650af20c74932336 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Mar 2021 14:47:55 -0800 Subject: [PATCH 148/178] use result service events in finalizar --- state/protocol/badger/mutator.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index 277ec36299b..e47641aa054 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -580,7 +580,11 @@ func (m *FollowerState) Finalize(blockID flow.Identifier) error { // track protocol events that should be emitted var events []func() for _, seal := range payload.Seals { - for _, event := range seal.ServiceEvents { + result, err := m.results.ByID(seal.ResultID) + if err != nil { + return fmt.Errorf("could not retrieve result (id=%x) for seal (id=%x): %w", seal.ResultID, seal.ID(), err) + } + for _, event := range result.ServiceEvents { switch ev := event.Event.(type) { case *flow.EpochSetup: events = append(events, func() { m.consumer.EpochSetupPhaseStarted(ev.Counter-1, header) }) From 9c91dc26cded866644c114840d7b7241bb21a446 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Mar 2021 15:10:58 -0800 Subject: [PATCH 149/178] apply service event when processing parent of sealing block --- state/protocol/badger/mutator.go | 47 ++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/state/protocol/badger/mutator.go b/state/protocol/badger/mutator.go index e47641aa054..4301d3cbe5d 100644 --- a/state/protocol/badger/mutator.go +++ b/state/protocol/badger/mutator.go @@ -713,14 +713,32 @@ func (m *FollowerState) epochStatus(block *flow.Header) (*flow.EpochStatus, erro return status, err } -// handleServiceEvents checks the service events within the seals of a block. -// It returns an error if there are any invalid, malformed, or duplicate events, -// in which case this block should be rejected. +// handleServiceEvents handles applying state changes which occur as a result +// of service events being included in a block payload. +// +// Consider a chain where a service event is emitted during execution of block A. +// Block B contains a receipt for A. Block C contains a seal for block A. Block +// D contains a QC for C. +// +// A <- B(RA) <- C(SA) <- D +// +// Service events are included within execution results, which are stored +// opaquely as part of the block payload in block B. We only validate and insert +// the typed service event to storage once we have received a valid QC for the +// block containing the seal for A. This occurs once we mark block D as valid +// with MarkValid. Because of this, any change to the protocol state introduced +// by a service event emitted in A would only become visible when querying D or +// later (D's children). +// +// This method will only apply service-event-induced state changes when the +// input block has the form of block D (ie. has a parent, which contains a seal +// for a block in which a service event was emitted). +// +// If the service events are valid, or there are no service events, this method +// returns a slice of Badger operations to apply while storing the block. This +// includes an operation to index the epoch status for every block, and +// operations to insert service events for blocks that include them. // -// If the service events are valid, or there are no service events, it returns -// a slice of Badger operations to apply while storing the block. This includes -// an operation to index the epoch status for every block, and operations to -// insert service events for blocks that include them. func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.Txn) error, error) { // Determine epoch status for block's CURRENT epoch. @@ -742,14 +760,25 @@ func (m *FollowerState) handleServiceEvents(block *flow.Block) ([]func(*badger.T // keep track of DB operations to apply when inserting this block var ops []func(*badger.Txn) error + // we will apply service events from blocks which are sealed by this block's PARENT + parent, err := m.blocks.ByID(block.Header.ParentID) + if err != nil { + return nil, fmt.Errorf("could not get parent (id=%x): %w", block.Header.ParentID, err) + } + // The payload might contain epoch preparation service events for the next // epoch. In this case, we need to update the tentative protocol state. // We need to validate whether all information is available in the protocol // state to go to the next epoch when needed. In cases where there is a bug // in the smart contract, it could be that this happens too late and the // chain finalization should halt. - for _, seal := range block.Payload.Seals { - for _, event := range seal.ServiceEvents { + for _, seal := range parent.Payload.Seals { + result, err := m.results.ByID(seal.ResultID) + if err != nil { + return nil, fmt.Errorf("could not get result (id=%x) for seal (id=%x): %w", seal.ResultID, seal.ID(), err) + } + + for _, event := range result.ServiceEvents { switch ev := event.Event.(type) { case *flow.EpochSetup: From faeaf1238cb2ae64c31491d5400c39e09d614b7d Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 9 Mar 2021 15:13:39 -0800 Subject: [PATCH 150/178] lint --- state/protocol/badger/state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 4affcc08e9b..6646faaf8e4 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -433,7 +433,7 @@ func (s *State) AtBlockID(blockID flow.Identifier) protocol.Snapshot { var valid bool err := s.db.View(operation.RetrieveBlockValidity(blockID, &valid)) if err != nil { - return invalid.NewSnapshot(fmt.Errorf("could not get validity for block (id=%x): %w", err)) + return invalid.NewSnapshot(fmt.Errorf("could not get validity for block (id=%x): %w", blockID, err)) } if !valid { return invalid.NewSnapshot(state.NewUnvalidatedBlockQueryError(blockID)) From 16e4f13b9396c78970487b9ddf1455a861eead3b Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 14:02:19 -0800 Subject: [PATCH 151/178] revert requirement that blocks be marked valid for querying --- state/protocol/badger/state.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/state/protocol/badger/state.go b/state/protocol/badger/state.go index 6646faaf8e4..354f366e5c7 100644 --- a/state/protocol/badger/state.go +++ b/state/protocol/badger/state.go @@ -10,7 +10,6 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/module" - "github.com/onflow/flow-go/state" "github.com/onflow/flow-go/state/protocol" "github.com/onflow/flow-go/state/protocol/invalid" "github.com/onflow/flow-go/storage" @@ -427,17 +426,6 @@ func (s *State) AtHeight(height uint64) protocol.Snapshot { } func (s *State) AtBlockID(blockID flow.Identifier) protocol.Snapshot { - // check that the block has been marked valid by HotStuff - // we don't need this check for Final/Sealed/AtHeight queries because all - // blocks which have been indexed must have been marked valid - var valid bool - err := s.db.View(operation.RetrieveBlockValidity(blockID, &valid)) - if err != nil { - return invalid.NewSnapshot(fmt.Errorf("could not get validity for block (id=%x): %w", blockID, err)) - } - if !valid { - return invalid.NewSnapshot(state.NewUnvalidatedBlockQueryError(blockID)) - } return NewSnapshot(s, blockID) } From f0cd379b526e6d0b0fc467967d47d3531df6be8b Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 14:04:19 -0800 Subject: [PATCH 152/178] revert addition of unvalidatedquery error --- state/errors.go | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/state/errors.go b/state/errors.go index 62fdc11bbc5..becea097404 100644 --- a/state/errors.go +++ b/state/errors.go @@ -3,8 +3,6 @@ package state import ( "errors" "fmt" - - "github.com/onflow/flow-go/model/flow" ) // InvalidExtensionError is an error for invalid extension of the state @@ -92,24 +90,3 @@ func (e NoValidChildBlockError) Error() string { func IsNoValidChildBlockError(err error) bool { return errors.As(err, &NoValidChildBlockError{}) } - -// UnvalidatedBlockQueryError is an error returned when we query a block by ID -// which has not been marked valid by HotStuff. This means that we aren't yet -// sure whether the block contains a valid quorum certificate. As a rule, the -// protocol state returns an error for these blocks rather than returning a -// state which may be invalid. -type UnvalidatedBlockQueryError struct { - BlockID flow.Identifier -} - -func (e UnvalidatedBlockQueryError) Error() string { - return fmt.Sprintf("invalid query for unvalidated block (id=%x)", e.BlockID) -} - -func NewUnvalidatedBlockQueryError(blockID flow.Identifier) error { - return UnvalidatedBlockQueryError{BlockID: blockID} -} - -func IsUnvalidatedBlockQueryError(err error) bool { - return errors.As(err, &UnvalidatedBlockQueryError{}) -} From 49ac03b2401c76cd7cfc8a2378a8f1f42d96e0c1 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 14:53:47 -0800 Subject: [PATCH 153/178] update epoch builder * builds an extra block so that the EpochCommit event's seal has a QC * exposes an API for querying info about built epochs --- state/protocol/inmem/convert_test.go | 37 +++++++--------- utils/unittest/epoch_builder.go | 66 +++++++++++++++++++++++----- 2 files changed, 73 insertions(+), 30 deletions(-) diff --git a/state/protocol/inmem/convert_test.go b/state/protocol/inmem/convert_test.go index 81e8ec4cacd..72047ac2efc 100644 --- a/state/protocol/inmem/convert_test.go +++ b/state/protocol/inmem/convert_test.go @@ -24,30 +24,27 @@ func TestFromSnapshot(t *testing.T) { util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.FollowerState) { - // Prepare an epoch builder, which builds epochs with 4 blocks, A,B,C,D - // See EpochBuilder documentation for details of these blocks. - // epochBuilder := unittest.NewEpochBuilder(t, state) - // build blocks WITHIN epoch 1 - PREPARING epoch 2 - // A - height 0 (root block) - // B - height 1 - staking phase - // C - height 2 - setup phase - // D - height 3 - committed phase + // build epoch 1 (prepare epoch 2) epochBuilder. BuildEpoch(). CompleteEpoch() - // build blocks WITHIN epoch 2 - PREPARING epoch 3 - // A - height 4 - // B - height 5 - staking phase - // C - height 6 - setup phase - // D - height 7 - committed phase + // build epoch 2 (prepare epoch 3) epochBuilder. BuildEpoch(). CompleteEpoch() + // get heights of each phase in built epochs + epoch1, ok := epochBuilder.EpochHeights(1) + require.True(t, ok) + epoch2, ok := epochBuilder.EpochHeights(2) + require.True(t, ok) + // test that we are able retrieve an in-memory version of root snapshot t.Run("root snapshot", func(t *testing.T) { - expected := state.AtHeight(0) + root, err := state.Params().Root() + require.NoError(t, err) + expected := state.AtHeight(root.Height) actual, err := inmem.FromSnapshot(expected) require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) @@ -57,21 +54,21 @@ func TestFromSnapshot(t *testing.T) { // test getting an in-memory snapshot for all phase of epoch 1 t.Run("epoch 1", func(t *testing.T) { t.Run("staking phase", func(t *testing.T) { - expected := state.AtHeight(1) + expected := state.AtHeight(epoch1.Staking) actual, err := inmem.FromSnapshot(expected) require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("setup phase", func(t *testing.T) { - expected := state.AtHeight(2) + expected := state.AtHeight(epoch1.Setup) actual, err := inmem.FromSnapshot(expected) require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("committed phase", func(t *testing.T) { - expected := state.AtHeight(3) + expected := state.AtHeight(epoch1.Committed) actual, err := inmem.FromSnapshot(expected) require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) @@ -82,21 +79,21 @@ func TestFromSnapshot(t *testing.T) { // test getting an in-memory snapshot for all phase of epoch 2 t.Run("epoch 2", func(t *testing.T) { t.Run("staking phase", func(t *testing.T) { - expected := state.AtHeight(5) + expected := state.AtHeight(epoch2.Staking) actual, err := inmem.FromSnapshot(expected) require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("setup phase", func(t *testing.T) { - expected := state.AtHeight(6) + expected := state.AtHeight(epoch2.Setup) actual, err := inmem.FromSnapshot(expected) require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) testEncodeDecode(t, actual) }) t.Run("committed phase", func(t *testing.T) { - expected := state.AtHeight(7) + expected := state.AtHeight(epoch2.Committed) actual, err := inmem.FromSnapshot(expected) require.NoError(t, err) assertSnapshotsEqual(t, expected, actual) diff --git a/utils/unittest/epoch_builder.go b/utils/unittest/epoch_builder.go index 0b5ef7a84db..33dbfe175cc 100644 --- a/utils/unittest/epoch_builder.go +++ b/utils/unittest/epoch_builder.go @@ -11,11 +11,21 @@ import ( "github.com/onflow/flow-go/state/protocol" ) +// EpochHeights is a structure caching the results of building an epoch with +// EpochBuilder. It contains the first block height for each phase of the epoch. +type EpochHeights struct { + Counter uint64 // which epoch this is + Staking uint64 // first height of staking phase + Setup uint64 // first height of setup phase + Committed uint64 // first height of committed phase +} + // EpochBuilder is a testing utility for building epochs into chain state. type EpochBuilder struct { t *testing.T state protocol.MutableState blocks map[flow.Identifier]*flow.Block + built map[uint64]EpochHeights setupOpts []func(*flow.EpochSetup) // options to apply to the EpochSetup event commitOpts []func(*flow.EpochCommit) // options to apply to the EpochCommit event } @@ -26,6 +36,7 @@ func NewEpochBuilder(t *testing.T, state protocol.MutableState) *EpochBuilder { t: t, state: state, blocks: make(map[flow.Identifier]*flow.Block), + built: make(map[uint64]EpochHeights), } return builder } @@ -46,19 +57,25 @@ func (builder *EpochBuilder) UsingCommitOpts(opts ...func(*flow.EpochCommit)) *E return builder } +// EpochHeights returns heights of each phase within about a built epoch. +func (builder *EpochBuilder) EpochHeights(counter uint64) (EpochHeights, bool) { + epoch, ok := builder.built[counter] + return epoch, ok +} + // Build builds and finalizes a sequence of blocks comprising a minimal full // epoch (epoch N). We assume the latest finalized block is within staking phase // in epoch N. // -// | EPOCH N | -// | | -// P A B C D E F -// +------------+ +------------+ +-----------+ +--------------+ +----------+ +----------+ +----------+ -// | ER(P-1) |->| ER(P) |->| ~~ER(A)~~ |->| ER(B) |->| ER(C) |->| ER(D) |->| ER(E) | -// | S(ER(P-2)) | | S(ER(P-1)) | | S(ER(P)) | | ~~S(ER(A))~~ | | S(ER(B)) | | S(ER(C)) | | S(ER(D)) | -// +------------+ +------------+ +-----------+ +--------------+ +----------+ +----------+ +----------+ -// | | -// Setup Commit +// | EPOCH N | +// | | +// P A B C D E F G +// +------------+ +------------+ +-----------+ +-----------+ +----------+ +----------+ +----------+----------+ +// | ER(P-1) |->| ER(P) |->| ER(A) |->| ER(B) |->| ER(C) |->| ER(D) |->| ER(E) | ER(F) | +// | S(ER(P-2)) | | S(ER(P-1)) | | S(ER(P)) | | S(ER(A)) | | S(ER(B)) | | S(ER(C)) | | S(ER(D)) | S(ER(E)) | +// +------------+ +------------+ +-----------+ +-----------+ +----------+ +----------+ +----------+----------+ +// | | +// Setup Commit // // ER(X) := ExecutionReceipt for block X // S(ER(X)) := Seal for the ExecutionResult contained in ER(X) (seals block X) @@ -69,12 +86,18 @@ func (builder *EpochBuilder) UsingCommitOpts(opts ...func(*flow.EpochCommit)) *E // not contain a receipt for block A, and block C does not contain a seal for // block A. This is because the root block is sealed from genesis and we // can't insert duplicate seals. - +// // D contains a seal for block B containing the EpochSetup service event. +// E contains a QC for D, which causes the EpochSetup to become activated. +// // F contains a seal for block D containing the EpochCommit service event. +// G contains a QC for F, which causes the EpochCommit to become activated. // // To build a sequence of epochs, we call BuildEpoch, then CompleteEpoch, and so on. // +// Upon building an epoch N (preparing epoch N+1), we store some information +// about the heights of blocks in the BUILT epoch (epoch N). These can be +// queried with EpochHeights. func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { // prepare default values for the service events based on the current state @@ -203,6 +226,29 @@ func (builder *EpochBuilder) BuildEpoch() *EpochBuilder { Seals: []*flow.Seal{sealForD}, }) builder.addBlock(&F) + // create receipt for block F + receiptF := ReceiptForBlockFixture(&F) + + // build block G + // G contains a seal for block E and a receipt for block F + G := BlockWithParentFixture(F.Header) + sealForE := Seal.Fixture( + Seal.WithResult(&receiptE.ExecutionResult), + ) + G.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{receiptF}, + Seals: []*flow.Seal{sealForE}, + }) + + builder.addBlock(&G) + + // cache information about the built epoch + builder.built[counter] = EpochHeights{ + Counter: counter, + Staking: A.Height, + Setup: E.Header.Height, + Committed: G.Header.Height, + } return builder } From de930a89ad3e990596b02d2beb4e29402ef95947 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 14:58:17 -0800 Subject: [PATCH 154/178] use correct result ID --- module/builder/collection/builder_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/module/builder/collection/builder_test.go b/module/builder/collection/builder_test.go index 7e5b78278e6..3465d0c1158 100644 --- a/module/builder/collection/builder_test.go +++ b/module/builder/collection/builder_test.go @@ -89,6 +89,7 @@ func (suite *BuilderSuite) SetupTest() { qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(root.ID())) // ensure we don't enter a new epoch for tests that build many blocks result.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = root.Header.View + 100000 + seal.ResultID = result.ID() rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc) require.NoError(suite.T(), err) From da1ae76615ba15b9dd1876c4513d3f403d27e87a Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 15:08:26 -0800 Subject: [PATCH 155/178] use correct result id in cluster mutator test --- state/cluster/badger/mutator_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/state/cluster/badger/mutator_test.go b/state/cluster/badger/mutator_test.go index 7d41bf1c16f..9abd3f157dd 100644 --- a/state/cluster/badger/mutator_test.go +++ b/state/cluster/badger/mutator_test.go @@ -75,6 +75,7 @@ func (suite *MutatorSuite) SetupTest() { qc := unittest.QuorumCertificateFixture(unittest.QCWithBlockID(genesis.ID())) // ensure we don't enter a new epoch for tests that build many blocks result.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView = genesis.Header.View + 100000 + seal.ResultID = result.ID() rootSnapshot, err := inmem.SnapshotFromBootstrapState(genesis, result, seal, qc) require.NoError(suite.T(), err) From 764aa05971205f2ab396b7888d3357d3e2a6c4c9 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 15:43:12 -0800 Subject: [PATCH 156/178] update full epoch transition test --- state/protocol/badger/mutator_test.go | 149 +++++++++++++++++--------- 1 file changed, 97 insertions(+), 52 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index a535499ec57..647df387163 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -559,13 +559,30 @@ func TestExtendReceiptsValid(t *testing.T) { // event, then a commit event, then finalizing the first block of the next epoch. // Also tests that appropriate epoch transition events are fired. // -// ROOT <- B1 <- B2(R1) <- B3(S1) <- B4(R2) <- B5(S2) <- B6 <- B7 -// TODO revisit +// Epoch information becomes available in the protocol state in the block AFTER +// the block sealing the relevant service event. This is because the block after +// the sealing block contains a QC certifying validity of the payload of the +// sealing block. +// +// ROOT <- B1 <- B2(R1) <- B3(S1) <- B4 <- B5(R2) <- B6(S2) <- B7 <- B8 <-|- B9 +// +// B4 contains a QC for B3, which seals B1, in which EpochSetup is emitted. +// * we can query the EpochSetup beginning with B4 +// * EpochSetupPhaseStarted triggered when B3 is finalized +// +// B7 contains a QC for B6, which seals B2, in which EpochCommitted is emitted. +// * we can query the EpochCommit beginning with B7 +// * EpochSetupPhaseStarted triggered when B6 is finalized +// +// B8 is the final block of the epoch. +// B9 is the first block of the NEXT epoch. +// func TestExtendEpochTransitionValid(t *testing.T) { // create a event consumer to test epoch transition events consumer := new(mockprotocol.Consumer) consumer.On("BlockFinalized", mock.Anything) rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolStateAndConsumer(t, rootSnapshot, consumer, func(db *badger.DB, state *protocol.MutableState) { head, err := rootSnapshot.Head() require.NoError(t, err) @@ -603,6 +620,7 @@ func TestExtendEpochTransitionValid(t *testing.T) { // create a receipt for block 1 containing the EpochSetup event receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1) receipt1.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Setup.ServiceEvent()} + seal1.ResultID = receipt1.ExecutionResult.ID() // add a second block with the receipt for block 1 block2 := unittest.BlockWithParentFixture(block1.Header) @@ -624,33 +642,44 @@ func TestExtendEpochTransitionValid(t *testing.T) { err = state.Extend(&block3) require.Nil(t, err) + // insert a block with a QC pointing to block 3 + block4 := unittest.BlockWithParentFixture(block3.Header) + err = state.Extend(&block4) + require.Nil(t, err) + // now that the setup event has been emitted, we should be in the setup phase - phase, err = state.AtBlockID(block3.ID()).Phase() + phase, err = state.AtBlockID(block4.ID()).Phase() assert.Nil(t, err) require.Equal(t, flow.EpochPhaseSetup, phase) - // we should NOT be able to query epoch 2 wrt block 1 - _, err = state.AtBlockID(block1.ID()).Epochs().Next().InitialIdentities() - require.Error(t, err) - _, err = state.AtBlockID(block1.ID()).Epochs().Next().Clustering() - require.Error(t, err) + // we should NOT be able to query epoch 2 wrt blocks before 4 + for _, blockID := range []flow.Identifier{block1.ID(), block2.ID(), block3.ID()} { + _, err = state.AtBlockID(blockID).Epochs().Next().InitialIdentities() + require.Error(t, err) + _, err = state.AtBlockID(blockID).Epochs().Next().Clustering() + require.Error(t, err) + } - // we should be able to query epoch 2 wrt block 3 - _, err = state.AtBlockID(block3.ID()).Epochs().Next().InitialIdentities() + // we should be able to query epoch 2 wrt block 4 + _, err = state.AtBlockID(block4.ID()).Epochs().Next().InitialIdentities() assert.Nil(t, err) - _, err = state.AtBlockID(block3.ID()).Epochs().Next().Clustering() + _, err = state.AtBlockID(block4.ID()).Epochs().Next().Clustering() assert.Nil(t, err) // only setup event is finalized, not commit, so shouldn't be able to get certain info - _, err = state.AtBlockID(block3.ID()).Epochs().Next().DKG() + _, err = state.AtBlockID(block4.ID()).Epochs().Next().DKG() require.Error(t, err) - // ensure an epoch phase transition when we finalize the event + // ensure an epoch phase transition when we finalize block 3 consumer.On("EpochSetupPhaseStarted", epoch2Setup.Counter-1, block3.Header).Once() err = state.Finalize(block3.ID()) require.Nil(t, err) consumer.AssertCalled(t, "EpochSetupPhaseStarted", epoch2Setup.Counter-1, block3.Header) + // finalize block 4 so we can finalize subsequent blocks + err = state.Finalize(block4.ID()) + require.Nil(t, err) + epoch2Commit := unittest.EpochCommitFixture( unittest.CommitWithCounter(epoch2Setup.Counter), unittest.WithDKGFromParticipants(epoch2Participants), @@ -660,91 +689,107 @@ func TestExtendEpochTransitionValid(t *testing.T) { // the receipt for block 2 contains the EpochCommit event receipt2, seal2 := unittest.ReceiptAndSealForBlock(&block2) receipt2.ExecutionResult.ServiceEvents = []flow.ServiceEvent{epoch2Commit.ServiceEvent()} + seal2.ResultID = receipt2.ExecutionResult.ID() - // block 4 contains the receipt for block 2 - block4 := unittest.BlockWithParentFixture(block3.Header) - block4.SetPayload(flow.Payload{ + // block 5 contains the receipt for block 2 + block5 := unittest.BlockWithParentFixture(block4.Header) + block5.SetPayload(flow.Payload{ Receipts: []*flow.ExecutionReceipt{receipt2}, }) - err = state.Extend(&block4) + err = state.Extend(&block5) require.Nil(t, err) - err = state.Finalize(block4.ID()) + err = state.Finalize(block5.ID()) require.Nil(t, err) - block5 := unittest.BlockWithParentFixture(block4.Header) - block5.SetPayload(flow.Payload{ + // block 6 contains the seal for block 2 + block6 := unittest.BlockWithParentFixture(block5.Header) + block6.SetPayload(flow.Payload{ Seals: []*flow.Seal{seal2}, }) - // we should NOT be able to query epoch 2 commit info wrt block 3 - _, err = state.AtBlockID(block3.ID()).Epochs().Next().DKG() - require.Error(t, err) + err = state.Extend(&block6) + require.Nil(t, err) - // now epoch 2 is fully ready, we can query anything we want about it wrt block 5 (or later) - _, err = state.AtBlockID(block4.ID()).Epochs().Next().InitialIdentities() + // insert a block with a QC pointing to block 6 + block7 := unittest.BlockWithParentFixture(block6.Header) + err = state.Extend(&block7) require.Nil(t, err) - _, err = state.AtBlockID(block4.ID()).Epochs().Next().Clustering() + + // we should NOT be able to query epoch 2 commit info wrt blocks before 7 + for _, blockID := range []flow.Identifier{block4.ID(), block5.ID(), block6.ID()} { + _, err = state.AtBlockID(blockID).Epochs().Next().DKG() + require.Error(t, err) + } + + // now epoch 2 is fully ready, we can query anything we want about it wrt block 7 (or later) + _, err = state.AtBlockID(block7.ID()).Epochs().Next().InitialIdentities() require.Nil(t, err) - _, err = state.AtBlockID(block4.ID()).Epochs().Next().DKG() + _, err = state.AtBlockID(block7.ID()).Epochs().Next().Clustering() + require.Nil(t, err) + _, err = state.AtBlockID(block7.ID()).Epochs().Next().DKG() assert.Nil(t, err) - // how that the commit event has been emitted, we should be in the committed phase - phase, err = state.AtBlockID(block4.ID()).Phase() + // now that the commit event has been emitted, we should be in the committed phase + phase, err = state.AtBlockID(block7.ID()).Phase() assert.Nil(t, err) require.Equal(t, flow.EpochPhaseCommitted, phase) - // expect epoch phase transition once we finalize block 4 - consumer.On("EpochCommittedPhaseStarted", epoch2Setup.Counter-1, block4.Header) - err = state.Finalize(block4.ID()) + // expect epoch phase transition once we finalize block 6 + consumer.On("EpochCommittedPhaseStarted", epoch2Setup.Counter-1, block6.Header) + err = state.Finalize(block6.ID()) + require.Nil(t, err) + consumer.AssertCalled(t, "EpochCommittedPhaseStarted", epoch2Setup.Counter-1, block6.Header) + + // finalize block 7 so we can finalize subsequent blocks + err = state.Finalize(block7.ID()) require.Nil(t, err) - consumer.AssertCalled(t, "EpochCommittedPhaseStarted", epoch2Setup.Counter-1, block4.Header) // we should still be in epoch 1 epochCounter, err := state.AtBlockID(block4.ID()).Epochs().Current().Counter() require.Nil(t, err) require.Equal(t, epoch1Setup.Counter, epochCounter) - // block 5 has the final view of the epoch - block6 := unittest.BlockWithParentFixture(block5.Header) - block6.SetPayload(flow.EmptyPayload()) - block6.Header.View = epoch1FinalView + // block 8 has the final view of the epoch + block8 := unittest.BlockWithParentFixture(block7.Header) + block8.SetPayload(flow.EmptyPayload()) + block8.Header.View = epoch1FinalView - err = state.Extend(&block6) + err = state.Extend(&block8) require.Nil(t, err) // we should still be in epoch 1, since epochs are inclusive of final view - epochCounter, err = state.AtBlockID(block6.ID()).Epochs().Current().Counter() + epochCounter, err = state.AtBlockID(block8.ID()).Epochs().Current().Counter() require.Nil(t, err) require.Equal(t, epoch1Setup.Counter, epochCounter) - // block 6 has a view > final view of epoch 1, it will be considered the first block of epoch 2 - block7 := unittest.BlockWithParentFixture(block6.Header) - block7.SetPayload(flow.EmptyPayload()) - // we should handle view that aren't exactly the first valid view of the epoch - block7.Header.View = epoch1FinalView + uint64(1+rand.Intn(10)) + // block 9 has a view > final view of epoch 1, it will be considered the first block of epoch 2 + block9 := unittest.BlockWithParentFixture(block8.Header) + block9.SetPayload(flow.EmptyPayload()) + // we should handle views that aren't exactly the first valid view of the epoch + block9.Header.View = epoch1FinalView + uint64(1+rand.Intn(10)) - err = state.Extend(&block7) + err = state.Extend(&block9) require.Nil(t, err) // now, at long last, we are in epoch 2 - epochCounter, err = state.AtBlockID(block7.ID()).Epochs().Current().Counter() + epochCounter, err = state.AtBlockID(block9.ID()).Epochs().Current().Counter() require.Nil(t, err) require.Equal(t, epoch2Setup.Counter, epochCounter) // we should begin epoch 2 in staking phase // how that the commit event has been emitted, we should be in the committed phase - phase, err = state.AtBlockID(block7.ID()).Phase() + phase, err = state.AtBlockID(block9.ID()).Phase() assert.Nil(t, err) require.Equal(t, flow.EpochPhaseStaking, phase) - // expect epoch transition once we finalize block 6 - consumer.On("EpochTransition", epoch2Setup.Counter, block6.Header).Once() - err = state.Finalize(block6.ID()) + // expect epoch transition once we finalize block 9 + consumer.On("EpochTransition", epoch2Setup.Counter, block9.Header).Once() + err = state.Finalize(block8.ID()) require.Nil(t, err) - err = state.Finalize(block7.ID()) + err = state.Finalize(block9.ID()) require.Nil(t, err) - consumer.AssertCalled(t, "EpochTransition", epoch2Setup.Counter, block7.Header) + consumer.AssertCalled(t, "EpochTransition", epoch2Setup.Counter, block9.Header) }) } From aba506e4d076761d4cebefc743e96e540c7499b0 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 15:50:17 -0800 Subject: [PATCH 157/178] fix conflicting epoch tests --- state/protocol/badger/mutator_test.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 647df387163..74cf2adfee4 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -796,13 +796,14 @@ func TestExtendEpochTransitionValid(t *testing.T) { // we should be able to have conflicting forks with two different instances of // the same service event for the same epoch // -// /-->B1-->B3(R1)-->B5(S1) -// ROOT --+ -// \-->B2-->B4(R2)-->B6(S2) +// /--B1<--B3(R1)<--B5(S1)<--B7 +// ROOT <--+ +// \--B2<--B4(R2)<--B6(S2)<--B8 // func TestExtendConflictingEpochEvents(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(participants) util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + head, err := rootSnapshot.Head() require.NoError(t, err) result, _, err := rootSnapshot.SealedResult() @@ -882,12 +883,22 @@ func TestExtendConflictingEpochEvents(t *testing.T) { err = state.Extend(&block6) require.Nil(t, err) + // block 7 builds on block 5, contains QC for block 7 + block7 := unittest.BlockWithParentFixture(block5.Header) + err = state.Extend(&block7) + require.Nil(t, err) + + // block 8 builds on block 6, contains QC for block 6 + block8 := unittest.BlockWithParentFixture(block6.Header) + err = state.Extend(&block8) + require.Nil(t, err) + // should be able query each epoch from the appropriate reference block - setup1FinalView, err := state.AtBlockID(block5.ID()).Epochs().Next().FinalView() + setup1FinalView, err := state.AtBlockID(block7.ID()).Epochs().Next().FinalView() assert.Nil(t, err) require.Equal(t, nextEpochSetup1.FinalView, setup1FinalView) - setup2FinalView, err := state.AtBlockID(block6.ID()).Epochs().Next().FinalView() + setup2FinalView, err := state.AtBlockID(block8.ID()).Epochs().Next().FinalView() assert.Nil(t, err) require.Equal(t, nextEpochSetup2.FinalView, setup2FinalView) }) From 22ee465d51b26eb46aa045d87f0e84ffe2c011f9 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 15:51:33 -0800 Subject: [PATCH 158/178] use NoError over Nil --- state/protocol/badger/mutator_test.go | 148 +++++++++++++------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 74cf2adfee4..5bd7ecba760 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -113,7 +113,7 @@ func TestExtendValid(t *testing.T) { consumer.On("BlockFinalized", extend.Header).Once() err = fullState.Finalize(extend.ID()) - require.Nil(t, err) + require.NoError(t, err) consumer.AssertExpectations(t) }) } @@ -311,7 +311,7 @@ func TestExtendReceiptsDuplicate(t *testing.T) { block2 := unittest.BlockWithParentFixture(head) block2.SetPayload(flow.EmptyPayload()) err = state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) receipt := unittest.ReceiptForBlockFixture(&block2) @@ -322,7 +322,7 @@ func TestExtendReceiptsDuplicate(t *testing.T) { Receipts: []*flow.ExecutionReceipt{receipt}, }) err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) block4 := unittest.BlockWithParentFixture(block3.Header) block4.SetPayload(flow.Payload{ @@ -363,7 +363,7 @@ func TestExtendReceiptsSealedBlock(t *testing.T) { block2 := unittest.BlockWithParentFixture(head) block2.SetPayload(flow.EmptyPayload()) err = state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) block2Receipt := unittest.ReceiptForBlockFixture(&block2) @@ -375,7 +375,7 @@ func TestExtendReceiptsSealedBlock(t *testing.T) { Receipts: []*flow.ExecutionReceipt{block2Receipt}, }) err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) // create a seal for block2 seal2 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block2Receipt.ExecutionResult)) @@ -386,7 +386,7 @@ func TestExtendReceiptsSealedBlock(t *testing.T) { Seals: []*flow.Seal{seal2}, }) err = state.Extend(&block4) - require.Nil(t, err) + require.NoError(t, err) // insert another receipt for block 2, which is now the highest sealed // block, and ensure that the receipt is rejected @@ -411,7 +411,7 @@ func TestExtendReceiptsSealedBlock(t *testing.T) { Receipts: []*flow.ExecutionReceipt{receipt}, }) err = state.Extend(&block6) - require.Nil(t, err) + require.NoError(t, err) }) } @@ -431,13 +431,13 @@ func TestExtendReceiptsBlockNotOnFork(t *testing.T) { block2.Payload.Guarantees = nil block2.Header.PayloadHash = block2.Payload.Hash() err := state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) // create block3 block3 := unittest.BlockWithParentFixture(block2.Header) block3.SetPayload(flow.EmptyPayload()) err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) block3Receipt := unittest.ReceiptForBlockFixture(&block3) @@ -467,13 +467,13 @@ func TestExtendReceiptsNotSorted(t *testing.T) { block2.Payload.Guarantees = nil block2.Header.PayloadHash = block2.Payload.Hash() err := state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) block3 := unittest.BlockWithParentFixture(block2.Header) block3.Payload.Guarantees = nil block3.Header.PayloadHash = block3.Payload.Hash() err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) // insert a block with payload receipts not sorted by block height. block4 := unittest.BlockWithParentFixture(block3.Header) @@ -501,7 +501,7 @@ func TestExtendReceiptsInvalid(t *testing.T) { block2 := unittest.BlockWithParentFixture(head) block2.SetPayload(flow.EmptyPayload()) err = state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) // Add a receipt for block 2 receipt := unittest.ExecutionReceiptFixture() @@ -527,17 +527,17 @@ func TestExtendReceiptsValid(t *testing.T) { block2 := unittest.BlockWithParentFixture(head) block2.SetPayload(flow.EmptyPayload()) err = state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) block3 := unittest.BlockWithParentFixture(block2.Header) block3.SetPayload(flow.EmptyPayload()) err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) block4 := unittest.BlockWithParentFixture(block3.Header) block4.SetPayload(flow.EmptyPayload()) err = state.Extend(&block4) - require.Nil(t, err) + require.NoError(t, err) receipt3a := unittest.ReceiptForBlockFixture(&block3) receipt3b := unittest.ReceiptForBlockFixture(&block3) @@ -551,7 +551,7 @@ func TestExtendReceiptsValid(t *testing.T) { }, }) err = state.Extend(&block5) - require.Nil(t, err) + require.NoError(t, err) }) } @@ -591,16 +591,16 @@ func TestExtendEpochTransitionValid(t *testing.T) { // we should begin the epoch in the staking phase phase, err := state.AtBlockID(head.ID()).Phase() - assert.Nil(t, err) + assert.NoError(t, err) require.Equal(t, flow.EpochPhaseStaking, phase) // add a block for the first seal to reference block1 := unittest.BlockWithParentFixture(head) block1.SetPayload(flow.EmptyPayload()) err = state.Extend(&block1) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block1.ID()) - require.Nil(t, err) + require.NoError(t, err) epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) epoch1FinalView := epoch1Setup.FinalView @@ -628,9 +628,9 @@ func TestExtendEpochTransitionValid(t *testing.T) { Receipts: []*flow.ExecutionReceipt{receipt1}, }) err = state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block2.ID()) - require.Nil(t, err) + require.NoError(t, err) // block 3 contains the seal for block 1 block3 := unittest.BlockWithParentFixture(block2.Header) @@ -640,16 +640,16 @@ func TestExtendEpochTransitionValid(t *testing.T) { // insert the block sealing the EpochSetup event err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) // insert a block with a QC pointing to block 3 block4 := unittest.BlockWithParentFixture(block3.Header) err = state.Extend(&block4) - require.Nil(t, err) + require.NoError(t, err) // now that the setup event has been emitted, we should be in the setup phase phase, err = state.AtBlockID(block4.ID()).Phase() - assert.Nil(t, err) + assert.NoError(t, err) require.Equal(t, flow.EpochPhaseSetup, phase) // we should NOT be able to query epoch 2 wrt blocks before 4 @@ -662,9 +662,9 @@ func TestExtendEpochTransitionValid(t *testing.T) { // we should be able to query epoch 2 wrt block 4 _, err = state.AtBlockID(block4.ID()).Epochs().Next().InitialIdentities() - assert.Nil(t, err) + assert.NoError(t, err) _, err = state.AtBlockID(block4.ID()).Epochs().Next().Clustering() - assert.Nil(t, err) + assert.NoError(t, err) // only setup event is finalized, not commit, so shouldn't be able to get certain info _, err = state.AtBlockID(block4.ID()).Epochs().Next().DKG() @@ -673,12 +673,12 @@ func TestExtendEpochTransitionValid(t *testing.T) { // ensure an epoch phase transition when we finalize block 3 consumer.On("EpochSetupPhaseStarted", epoch2Setup.Counter-1, block3.Header).Once() err = state.Finalize(block3.ID()) - require.Nil(t, err) + require.NoError(t, err) consumer.AssertCalled(t, "EpochSetupPhaseStarted", epoch2Setup.Counter-1, block3.Header) // finalize block 4 so we can finalize subsequent blocks err = state.Finalize(block4.ID()) - require.Nil(t, err) + require.NoError(t, err) epoch2Commit := unittest.EpochCommitFixture( unittest.CommitWithCounter(epoch2Setup.Counter), @@ -698,9 +698,9 @@ func TestExtendEpochTransitionValid(t *testing.T) { }) err = state.Extend(&block5) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block5.ID()) - require.Nil(t, err) + require.NoError(t, err) // block 6 contains the seal for block 2 block6 := unittest.BlockWithParentFixture(block5.Header) @@ -709,12 +709,12 @@ func TestExtendEpochTransitionValid(t *testing.T) { }) err = state.Extend(&block6) - require.Nil(t, err) + require.NoError(t, err) // insert a block with a QC pointing to block 6 block7 := unittest.BlockWithParentFixture(block6.Header) err = state.Extend(&block7) - require.Nil(t, err) + require.NoError(t, err) // we should NOT be able to query epoch 2 commit info wrt blocks before 7 for _, blockID := range []flow.Identifier{block4.ID(), block5.ID(), block6.ID()} { @@ -724,30 +724,30 @@ func TestExtendEpochTransitionValid(t *testing.T) { // now epoch 2 is fully ready, we can query anything we want about it wrt block 7 (or later) _, err = state.AtBlockID(block7.ID()).Epochs().Next().InitialIdentities() - require.Nil(t, err) + require.NoError(t, err) _, err = state.AtBlockID(block7.ID()).Epochs().Next().Clustering() - require.Nil(t, err) + require.NoError(t, err) _, err = state.AtBlockID(block7.ID()).Epochs().Next().DKG() - assert.Nil(t, err) + assert.NoError(t, err) // now that the commit event has been emitted, we should be in the committed phase phase, err = state.AtBlockID(block7.ID()).Phase() - assert.Nil(t, err) + assert.NoError(t, err) require.Equal(t, flow.EpochPhaseCommitted, phase) // expect epoch phase transition once we finalize block 6 consumer.On("EpochCommittedPhaseStarted", epoch2Setup.Counter-1, block6.Header) err = state.Finalize(block6.ID()) - require.Nil(t, err) + require.NoError(t, err) consumer.AssertCalled(t, "EpochCommittedPhaseStarted", epoch2Setup.Counter-1, block6.Header) // finalize block 7 so we can finalize subsequent blocks err = state.Finalize(block7.ID()) - require.Nil(t, err) + require.NoError(t, err) // we should still be in epoch 1 epochCounter, err := state.AtBlockID(block4.ID()).Epochs().Current().Counter() - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, epoch1Setup.Counter, epochCounter) // block 8 has the final view of the epoch @@ -756,11 +756,11 @@ func TestExtendEpochTransitionValid(t *testing.T) { block8.Header.View = epoch1FinalView err = state.Extend(&block8) - require.Nil(t, err) + require.NoError(t, err) // we should still be in epoch 1, since epochs are inclusive of final view epochCounter, err = state.AtBlockID(block8.ID()).Epochs().Current().Counter() - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, epoch1Setup.Counter, epochCounter) // block 9 has a view > final view of epoch 1, it will be considered the first block of epoch 2 @@ -770,25 +770,25 @@ func TestExtendEpochTransitionValid(t *testing.T) { block9.Header.View = epoch1FinalView + uint64(1+rand.Intn(10)) err = state.Extend(&block9) - require.Nil(t, err) + require.NoError(t, err) // now, at long last, we are in epoch 2 epochCounter, err = state.AtBlockID(block9.ID()).Epochs().Current().Counter() - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, epoch2Setup.Counter, epochCounter) // we should begin epoch 2 in staking phase // how that the commit event has been emitted, we should be in the committed phase phase, err = state.AtBlockID(block9.ID()).Phase() - assert.Nil(t, err) + assert.NoError(t, err) require.Equal(t, flow.EpochPhaseStaking, phase) // expect epoch transition once we finalize block 9 consumer.On("EpochTransition", epoch2Setup.Counter, block9.Header).Once() err = state.Finalize(block8.ID()) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block9.ID()) - require.Nil(t, err) + require.NoError(t, err) consumer.AssertCalled(t, "EpochTransition", epoch2Setup.Counter, block9.Header) }) } @@ -813,12 +813,12 @@ func TestExtendConflictingEpochEvents(t *testing.T) { block1 := unittest.BlockWithParentFixture(head) block1.SetPayload(flow.EmptyPayload()) err = state.Extend(&block1) - require.Nil(t, err) + require.NoError(t, err) block2 := unittest.BlockWithParentFixture(head) block2.SetPayload(flow.EmptyPayload()) err = state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) rootSetup := result.ServiceEvents[0].Event.(*flow.EpochSetup) @@ -847,7 +847,7 @@ func TestExtendConflictingEpochEvents(t *testing.T) { Receipts: []*flow.ExecutionReceipt{block1Receipt}, }) err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) // block 2 receipt contains nextEpochSetup2 block2Receipt := unittest.ReceiptForBlockFixture(&block2) @@ -859,7 +859,7 @@ func TestExtendConflictingEpochEvents(t *testing.T) { Receipts: []*flow.ExecutionReceipt{block2Receipt}, }) err = state.Extend(&block4) - require.Nil(t, err) + require.NoError(t, err) // seal for block 1 seal1 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block1Receipt.ExecutionResult)) @@ -873,7 +873,7 @@ func TestExtendConflictingEpochEvents(t *testing.T) { Seals: []*flow.Seal{seal1}, }) err = state.Extend(&block5) - require.Nil(t, err) + require.NoError(t, err) // block 6 builds on block 4, contains seal for block 2 block6 := unittest.BlockWithParentFixture(block4.Header) @@ -881,25 +881,25 @@ func TestExtendConflictingEpochEvents(t *testing.T) { Seals: []*flow.Seal{seal2}, }) err = state.Extend(&block6) - require.Nil(t, err) + require.NoError(t, err) // block 7 builds on block 5, contains QC for block 7 block7 := unittest.BlockWithParentFixture(block5.Header) err = state.Extend(&block7) - require.Nil(t, err) + require.NoError(t, err) // block 8 builds on block 6, contains QC for block 6 block8 := unittest.BlockWithParentFixture(block6.Header) err = state.Extend(&block8) - require.Nil(t, err) + require.NoError(t, err) // should be able query each epoch from the appropriate reference block setup1FinalView, err := state.AtBlockID(block7.ID()).Epochs().Next().FinalView() - assert.Nil(t, err) + assert.NoError(t, err) require.Equal(t, nextEpochSetup1.FinalView, setup1FinalView) setup2FinalView, err := state.AtBlockID(block8.ID()).Epochs().Next().FinalView() - assert.Nil(t, err) + assert.NoError(t, err) require.Equal(t, nextEpochSetup2.FinalView, setup2FinalView) }) } @@ -917,9 +917,9 @@ func TestExtendEpochSetupInvalid(t *testing.T) { block1 := unittest.BlockWithParentFixture(head) block1.SetPayload(flow.EmptyPayload()) err = state.Extend(&block1) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block1.ID()) - require.Nil(t, err) + require.NoError(t, err) epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) @@ -998,9 +998,9 @@ func TestExtendEpochCommitInvalid(t *testing.T) { block1 := unittest.BlockWithParentFixture(head) block1.SetPayload(flow.EmptyPayload()) err = state.Extend(&block1) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block1.ID()) - require.Nil(t, err) + require.NoError(t, err) epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) @@ -1052,18 +1052,18 @@ func TestExtendEpochCommitInvalid(t *testing.T) { Receipts: []*flow.ExecutionReceipt{setupReceipt}, }) err = state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block2.ID()) - require.Nil(t, err) + require.NoError(t, err) block3 := unittest.BlockWithParentFixture(block2.Header) block3.SetPayload(flow.Payload{ Seals: []*flow.Seal{setupSeal}, }) err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block3.ID()) - require.Nil(t, err) + require.NoError(t, err) t.Run("inconsistent counter", func(t *testing.T) { commit, receipt, _ := createCommit(&block3) @@ -1141,9 +1141,9 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { block1 := unittest.BlockWithParentFixture(head) block1.SetPayload(flow.EmptyPayload()) err = state.Extend(&block1) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block1.ID()) - require.Nil(t, err) + require.NoError(t, err) epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) epoch1FinalView := epoch1Setup.FinalView @@ -1169,9 +1169,9 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { Receipts: []*flow.ExecutionReceipt{receipt1}, }) err = state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) err = state.Finalize(block2.ID()) - require.Nil(t, err) + require.NoError(t, err) // block 3 seals block 1 block3 := unittest.BlockWithParentFixture(block2.Header) @@ -1179,7 +1179,7 @@ func TestExtendEpochTransitionWithoutCommit(t *testing.T) { Seals: []*flow.Seal{seal1}, }) err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) // block 4 will be the first block for epoch 2 block4 := unittest.BlockWithParentFixture(block3.Header) @@ -1385,12 +1385,12 @@ func TestHeaderExtendHighestSeal(t *testing.T) { block2 := unittest.BlockWithParentFixture(head) block2.SetPayload(flow.EmptyPayload()) err := state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) block3 := unittest.BlockWithParentFixture(block2.Header) block3.SetPayload(flow.EmptyPayload()) err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) // create seals for block2 and block3 seal2 := unittest.Seal.Fixture( @@ -1409,7 +1409,7 @@ func TestHeaderExtendHighestSeal(t *testing.T) { Guarantees: nil, }) err = state.Extend(&block4) - require.Nil(t, err) + require.NoError(t, err) finalCommit, err := state.AtBlockID(block4.ID()).Commit() require.NoError(t, err) @@ -1428,12 +1428,12 @@ func TestMakeValid(t *testing.T) { block2 := unittest.BlockWithParentFixture(head) block2.SetPayload(flow.EmptyPayload()) err := state.Extend(&block2) - require.Nil(t, err) + require.NoError(t, err) block3 := unittest.BlockWithParentFixture(block2.Header) block3.SetPayload(flow.EmptyPayload()) err = state.Extend(&block3) - require.Nil(t, err) + require.NoError(t, err) consumer.On("BlockProcessable", mock.Anything).Return() From e0977e33e23dcbfb741ca4f076fa97ec061d475c Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 17:02:26 -0800 Subject: [PATCH 159/178] fix invalid epoch setup tests --- state/protocol/badger/mutator_test.go | 51 ++++++++++++++------------- utils/unittest/protocol_state.go | 45 ++++++++++++++++++----- 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 5bd7ecba760..a00dca0e055 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -918,8 +918,6 @@ func TestExtendEpochSetupInvalid(t *testing.T) { block1.SetPayload(flow.EmptyPayload()) err = state.Extend(&block1) require.NoError(t, err) - err = state.Finalize(block1.ID()) - require.NoError(t, err) epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) @@ -929,55 +927,60 @@ func TestExtendEpochSetupInvalid(t *testing.T) { // this function will return a VALID setup event and seal, we will modify // in different ways in each test case - createSetup := func() (*flow.EpochSetup, *flow.ExecutionReceipt, *flow.Seal) { + createSetup := func(opts ...func(*flow.EpochSetup)) (*flow.EpochSetup, *flow.ExecutionReceipt, *flow.Seal) { setup := unittest.EpochSetupFixture( unittest.WithParticipants(epoch2Participants), unittest.SetupWithCounter(epoch1Setup.Counter+1), unittest.WithFinalView(epoch1Setup.FinalView+1000), unittest.WithFirstView(epoch1Setup.FinalView+1), ) + for _, apply := range opts { + apply(setup) + } receipt, seal := unittest.ReceiptAndSealForBlock(&block1) receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{setup.ServiceEvent()} + seal.ResultID = receipt.ExecutionResult.ID() return setup, receipt, seal } t.Run("wrong counter", func(t *testing.T) { - setup, receipt, _ := createSetup() - setup.Counter = epoch1Setup.Counter - - block := unittest.BlockWithParentFixture(block1.Header) - block.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{receipt}, + _, receipt, seal := createSetup(func(setup *flow.EpochSetup) { + setup.Counter = epoch1Setup.Counter }) - err = state.Extend(&block) + block2 := unittest.BlockWithParentFixture(block1.Header) + sealingBlock := unittest.SealBlock(t, state, &block2, receipt, seal) + + qcBlock := unittest.BlockWithParentFixture(sealingBlock) + err = state.Extend(&qcBlock) require.Error(t, err) require.True(t, st.IsInvalidExtensionError(err), err) }) t.Run("invalid final view", func(t *testing.T) { - setup, receipt, _ := createSetup() - - block := unittest.BlockWithParentFixture(block1.Header) - setup.FinalView = block.Header.View - block.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{receipt}, + _, receipt, seal := createSetup(func(setup *flow.EpochSetup) { + setup.FinalView = block1.Header.View }) - err = state.Extend(&block) + + block2 := unittest.BlockWithParentFixture(block1.Header) + sealingBlock := unittest.SealBlock(t, state, &block2, receipt, seal) + + qcBlock := unittest.BlockWithParentFixture(sealingBlock) + err = state.Extend(&qcBlock) require.Error(t, err) require.True(t, st.IsInvalidExtensionError(err), err) }) t.Run("empty seed", func(t *testing.T) { - setup, receipt, _ := createSetup() - setup.RandomSource = nil - - block := unittest.BlockWithParentFixture(block1.Header) - block.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{receipt}, + _, receipt, seal := createSetup(func(setup *flow.EpochSetup) { + setup.RandomSource = nil }) - err = state.Extend(&block) + block2 := unittest.BlockWithParentFixture(block1.Header) + sealingBlock := unittest.SealBlock(t, state, &block2, receipt, seal) + + qcBlock := unittest.BlockWithParentFixture(sealingBlock) + err = state.Extend(&qcBlock) require.Error(t, err) require.True(t, st.IsInvalidExtensionError(err), err) }) diff --git a/utils/unittest/protocol_state.go b/utils/unittest/protocol_state.go index d24b01e2d88..ebd04b6245c 100644 --- a/utils/unittest/protocol_state.go +++ b/utils/unittest/protocol_state.go @@ -1,22 +1,25 @@ package unittest import ( + "testing" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" "github.com/onflow/flow-go/model/flow" - protint "github.com/onflow/flow-go/state/protocol" - protocol "github.com/onflow/flow-go/state/protocol/mock" + "github.com/onflow/flow-go/state/protocol" + mockprotocol "github.com/onflow/flow-go/state/protocol/mock" ) // FinalizedProtocolStateWithParticipants returns a protocol state with finalized participants func FinalizedProtocolStateWithParticipants(participants flow.IdentityList) ( - *flow.Block, *protocol.Snapshot, *protocol.State, *protocol.Snapshot) { + *flow.Block, *mockprotocol.Snapshot, *mockprotocol.State, *mockprotocol.Snapshot) { sealed := BlockFixture() block := BlockWithParentFixture(sealed.Header) head := block.Header // set up protocol snapshot mock - snapshot := &protocol.Snapshot{} + snapshot := &mockprotocol.Snapshot{} snapshot.On("Identities", mock.Anything).Return( func(filter flow.IdentityFilter) flow.IdentityList { return participants.Filter(filter) @@ -38,7 +41,7 @@ func FinalizedProtocolStateWithParticipants(participants flow.IdentityList) ( nil, ) - sealedSnapshot := &protocol.Snapshot{} + sealedSnapshot := &mockprotocol.Snapshot{} sealedSnapshot.On("Head").Return( func() *flow.Header { return sealed.Header @@ -47,20 +50,44 @@ func FinalizedProtocolStateWithParticipants(participants flow.IdentityList) ( ) // set up protocol state mock - state := &protocol.State{} + state := &mockprotocol.State{} state.On("Final").Return( - func() protint.Snapshot { + func() protocol.Snapshot { return snapshot }, ) - state.On("Sealed").Return(func() protint.Snapshot { + state.On("Sealed").Return(func() protocol.Snapshot { return sealedSnapshot }, ) state.On("AtBlockID", mock.Anything).Return( - func(blockID flow.Identifier) protint.Snapshot { + func(blockID flow.Identifier) protocol.Snapshot { return snapshot }, ) return &block, snapshot, state, sealedSnapshot } + +// SealBlock inserts and seals the given block with the given receipt and seal. +// Returns the block sealing the input block. +func SealBlock(t *testing.T, st protocol.MutableState, block *flow.Block, receipt *flow.ExecutionReceipt, seal *flow.Seal) *flow.Header { + + err := st.Extend(block) + require.NoError(t, err) + + block2 := BlockWithParentFixture(block.Header) + block2.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{receipt}, + }) + err = st.Extend(&block2) + require.NoError(t, err) + + block3 := BlockWithParentFixture(block2.Header) + block3.SetPayload(flow.Payload{ + Seals: []*flow.Seal{seal}, + }) + err = st.Extend(&block3) + require.NoError(t, err) + + return block3.Header +} From 93ea3f4c524833ee9932dd5dbd750bd2d33fe62b Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 17:34:46 -0800 Subject: [PATCH 160/178] fix invalid epoch commit tests --- state/protocol/badger/mutator_test.go | 108 ++++++++++---------------- utils/unittest/protocol_state.go | 10 +-- 2 files changed, 45 insertions(+), 73 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index a00dca0e055..317aca878da 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -948,8 +948,7 @@ func TestExtendEpochSetupInvalid(t *testing.T) { setup.Counter = epoch1Setup.Counter }) - block2 := unittest.BlockWithParentFixture(block1.Header) - sealingBlock := unittest.SealBlock(t, state, &block2, receipt, seal) + sealingBlock := unittest.SealBlock(t, state, &block1, receipt, seal) qcBlock := unittest.BlockWithParentFixture(sealingBlock) err = state.Extend(&qcBlock) @@ -962,8 +961,7 @@ func TestExtendEpochSetupInvalid(t *testing.T) { setup.FinalView = block1.Header.View }) - block2 := unittest.BlockWithParentFixture(block1.Header) - sealingBlock := unittest.SealBlock(t, state, &block2, receipt, seal) + sealingBlock := unittest.SealBlock(t, state, &block1, receipt, seal) qcBlock := unittest.BlockWithParentFixture(sealingBlock) err = state.Extend(&qcBlock) @@ -976,8 +974,7 @@ func TestExtendEpochSetupInvalid(t *testing.T) { setup.RandomSource = nil }) - block2 := unittest.BlockWithParentFixture(block1.Header) - sealingBlock := unittest.SealBlock(t, state, &block2, receipt, seal) + sealingBlock := unittest.SealBlock(t, state, &block1, receipt, seal) qcBlock := unittest.BlockWithParentFixture(sealingBlock) err = state.Extend(&qcBlock) @@ -1002,8 +999,6 @@ func TestExtendEpochCommitInvalid(t *testing.T) { block1.SetPayload(flow.EmptyPayload()) err = state.Extend(&block1) require.NoError(t, err) - err = state.Finalize(block1.ID()) - require.NoError(t, err) epoch1Setup := result.ServiceEvents[0].Event.(*flow.EpochSetup) @@ -1023,105 +1018,84 @@ func TestExtendEpochCommitInvalid(t *testing.T) { ) receipt, seal := unittest.ReceiptAndSealForBlock(block) receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{setup.ServiceEvent()} + seal.ResultID = receipt.ExecutionResult.ID() return setup, receipt, seal } - createCommit := func(block *flow.Block) (*flow.EpochCommit, *flow.ExecutionReceipt, *flow.Seal) { + createCommit := func(block *flow.Block, opts ...func(*flow.EpochCommit)) (*flow.EpochCommit, *flow.ExecutionReceipt, *flow.Seal) { commit := unittest.EpochCommitFixture( unittest.CommitWithCounter(epoch1Setup.Counter+1), unittest.WithDKGFromParticipants(epoch2Participants), ) + for _, apply := range opts { + apply(commit) + } receipt, seal := unittest.ReceiptAndSealForBlock(block) receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{commit.ServiceEvent()} + seal.ResultID = receipt.ExecutionResult.ID() return commit, receipt, seal } t.Run("without setup", func(t *testing.T) { - _, receipt, _ := createCommit(&block1) + _, receipt, seal := createCommit(&block1) - block := unittest.BlockWithParentFixture(block1.Header) - block.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{receipt}, - }) - err = state.Extend(&block) + sealingBlock := unittest.SealBlock(t, state, &block1, receipt, seal) + + qcBlock := unittest.BlockWithParentFixture(sealingBlock) + err = state.Extend(&qcBlock) require.Error(t, err) require.True(t, st.IsInvalidExtensionError(err), err) }) - // insert the receipt containing the EpochSetup event and its seal + // seal block 1, in which EpochSetup was emitted epoch2Setup, setupReceipt, setupSeal := createSetup(&block1) - block2 := unittest.BlockWithParentFixture(block1.Header) - block2.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{setupReceipt}, - }) - err = state.Extend(&block2) - require.NoError(t, err) - err = state.Finalize(block2.ID()) - require.NoError(t, err) + block2 := unittest.SealBlock(t, state, &block1, setupReceipt, setupSeal) - block3 := unittest.BlockWithParentFixture(block2.Header) - block3.SetPayload(flow.Payload{ - Seals: []*flow.Seal{setupSeal}, - }) + // insert a block with a QC for block 2 + block3 := unittest.BlockWithParentFixture(block2) err = state.Extend(&block3) require.NoError(t, err) - err = state.Finalize(block3.ID()) - require.NoError(t, err) t.Run("inconsistent counter", func(t *testing.T) { - commit, receipt, _ := createCommit(&block3) - commit.Counter = epoch2Setup.Counter + 1 - - block := unittest.BlockWithParentFixture(block3.Header) - block.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{receipt}, + _, receipt, seal := createCommit(&block3, func(commit *flow.EpochCommit) { + commit.Counter = epoch2Setup.Counter + 1 }) - err := state.Extend(&block) + + sealingBlock := unittest.SealBlock(t, state, &block3, receipt, seal) + + qcBlock := unittest.BlockWithParentFixture(sealingBlock) + err = state.Extend(&qcBlock) require.Error(t, err) require.True(t, st.IsInvalidExtensionError(err), err) }) t.Run("inconsistent cluster QCs", func(t *testing.T) { - commit, receipt, _ := createCommit(&block3) - commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) - - block := unittest.BlockWithParentFixture(block3.Header) - block.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{receipt}, + _, receipt, seal := createCommit(&block3, func(commit *flow.EpochCommit) { + commit.ClusterQCs = append(commit.ClusterQCs, unittest.QuorumCertificateFixture()) }) - err := state.Extend(&block) - require.Error(t, err) - require.True(t, st.IsInvalidExtensionError(err), err) - }) - t.Run("missing dkg group key", func(t *testing.T) { - commit, receipt, _ := createCommit(&block3) - commit.DKGGroupKey = nil + sealingBlock := unittest.SealBlock(t, state, &block3, receipt, seal) - block := unittest.BlockWithParentFixture(block3.Header) - block.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{receipt}, - }) - err := state.Extend(&block) + qcBlock := unittest.BlockWithParentFixture(sealingBlock) + err = state.Extend(&qcBlock) require.Error(t, err) require.True(t, st.IsInvalidExtensionError(err), err) }) t.Run("inconsistent DKG participants", func(t *testing.T) { - commit, receipt, _ := createCommit(&block3) + _, receipt, seal := createCommit(&block3, func(commit *flow.EpochCommit) { + // add the consensus node from epoch *1*, which was removed for epoch 2 + epoch1CONNode := participants.Filter(filter.HasRole(flow.RoleConsensus))[0] + commit.DKGParticipants[epoch1CONNode.NodeID] = flow.DKGParticipant{ + KeyShare: unittest.KeyFixture(crypto.BLSBLS12381).PublicKey(), + Index: 1, + } + }) - // add the consensus node from epoch *1*, which was removed for epoch 2 - epoch1CONNode := participants.Filter(filter.HasRole(flow.RoleConsensus))[0] - commit.DKGParticipants[epoch1CONNode.NodeID] = flow.DKGParticipant{ - KeyShare: unittest.KeyFixture(crypto.BLSBLS12381).PublicKey(), - Index: 1, - } + sealingBlock := unittest.SealBlock(t, state, &block3, receipt, seal) - block := unittest.BlockWithParentFixture(block3.Header) - block.SetPayload(flow.Payload{ - Receipts: []*flow.ExecutionReceipt{receipt}, - }) - err := state.Extend(&block) + qcBlock := unittest.BlockWithParentFixture(sealingBlock) + err = state.Extend(&qcBlock) require.Error(t, err) require.True(t, st.IsInvalidExtensionError(err), err) }) diff --git a/utils/unittest/protocol_state.go b/utils/unittest/protocol_state.go index ebd04b6245c..23eb98a185b 100644 --- a/utils/unittest/protocol_state.go +++ b/utils/unittest/protocol_state.go @@ -68,18 +68,16 @@ func FinalizedProtocolStateWithParticipants(participants flow.IdentityList) ( return &block, snapshot, state, sealedSnapshot } -// SealBlock inserts and seals the given block with the given receipt and seal. -// Returns the block sealing the input block. +// SealBlock seals a block by building two blocks on it, the first containing +// a receipt for the block, the second containing a seal for the block. +// Returns the block containing the seal. func SealBlock(t *testing.T, st protocol.MutableState, block *flow.Block, receipt *flow.ExecutionReceipt, seal *flow.Seal) *flow.Header { - err := st.Extend(block) - require.NoError(t, err) - block2 := BlockWithParentFixture(block.Header) block2.SetPayload(flow.Payload{ Receipts: []*flow.ExecutionReceipt{receipt}, }) - err = st.Extend(&block2) + err := st.Extend(&block2) require.NoError(t, err) block3 := BlockWithParentFixture(block2.Header) From 9454b46e2ce6a6e8cfd5283d674a2bf499f8c974 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 17:56:09 -0800 Subject: [PATCH 161/178] fix TestClusters --- state/protocol/badger/snapshot_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index 6d7951bd345..abdaa8bdec0 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -103,6 +103,7 @@ func TestClusters(t *testing.T) { for i := 0; i < nClusters; i++ { commit.ClusterQCs[i] = unittest.QuorumCertificateFixture() } + seal.ResultID = result.ID() rootSnapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc) require.NoError(t, err) From a9d2b0e2aa4ca357aef1641b0323de698176ce8c Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 18:09:19 -0800 Subject: [PATCH 162/178] fix epoch query tests --- state/protocol/badger/snapshot_test.go | 38 +++++++++----------------- utils/unittest/epoch_builder.go | 32 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index abdaa8bdec0..4d50392edf5 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -436,38 +436,26 @@ func TestSnapshot_EpochQuery(t *testing.T) { epoch1Counter := result.ServiceEvents[0].Event.(*flow.EpochSetup).Counter epoch2Counter := epoch1Counter + 1 - // Prepare an epoch builder, which builds epochs with 6 blocks, A,B,C,D,E,F - // See EpochBuilder documentation for details of these blocks. - // epochBuilder := unittest.NewEpochBuilder(t, state) - // build blocks WITHIN epoch 1 - PREPARING epoch 2 - // A - height 0 (root block) - // B - height 1 - staking phase - // C - height 2 - - // D - height 3 - setup phase - // E - height 4 - - // F - height 5 - committed phase + // build epoch 1 (prepare epoch 2) epochBuilder. BuildEpoch(). CompleteEpoch() - // build blocks WITHIN epoch 2 - PREPARING epoch 3 - // A - height 6 - first block of epoch 2 - // B - height 7 - staking phase - // C - height 8 - - // D - height 9 - setup phase - // D - height 10 - - // D - height 11 - committed phase + // build epoch 2 (prepare epoch 3) epochBuilder. BuildEpoch(). CompleteEpoch() - epoch1Heights := []uint64{0, 1, 2, 3, 4, 5} - epoch2Heights := []uint64{6, 7, 8, 9, 10, 11} + // get heights of each phase in built epochs + epoch1, ok := epochBuilder.EpochHeights(1) + require.True(t, ok) + epoch2, ok := epochBuilder.EpochHeights(2) + require.True(t, ok) // we should be able to query the current epoch from any block t.Run("Current", func(t *testing.T) { t.Run("epoch 1", func(t *testing.T) { - for _, height := range epoch1Heights { + for _, height := range epoch1.Range() { counter, err := state.AtHeight(height).Epochs().Current().Counter() require.Nil(t, err) assert.Equal(t, epoch1Counter, counter) @@ -475,7 +463,7 @@ func TestSnapshot_EpochQuery(t *testing.T) { }) t.Run("epoch 2", func(t *testing.T) { - for _, height := range epoch2Heights { + for _, height := range epoch2.Range() { counter, err := state.AtHeight(height).Epochs().Current().Counter() require.Nil(t, err) assert.Equal(t, epoch2Counter, counter) @@ -487,7 +475,7 @@ func TestSnapshot_EpochQuery(t *testing.T) { // event, afterward we should be able to query next epoch t.Run("Next", func(t *testing.T) { t.Run("epoch 1: before next epoch available", func(t *testing.T) { - for _, height := range epoch1Heights[:3] { + for _, height := range epoch1.StakingRange() { _, err := state.AtHeight(height).Epochs().Next().Counter() assert.Error(t, err) assert.True(t, errors.Is(err, protocol.ErrNextEpochNotSetup)) @@ -495,7 +483,7 @@ func TestSnapshot_EpochQuery(t *testing.T) { }) t.Run("epoch 2: after next epoch available", func(t *testing.T) { - for _, height := range epoch1Heights[3:] { + for _, height := range append(epoch1.SetupRange(), epoch1.CommittedRange()...) { counter, err := state.AtHeight(height).Epochs().Next().Counter() require.Nil(t, err) assert.Equal(t, epoch2Counter, counter) @@ -508,7 +496,7 @@ func TestSnapshot_EpochQuery(t *testing.T) { // to query previous epoch t.Run("Previous", func(t *testing.T) { t.Run("epoch 1", func(t *testing.T) { - for _, height := range epoch1Heights { + for _, height := range epoch1.Range() { _, err := state.AtHeight(height).Epochs().Previous().Counter() assert.Error(t, err) assert.True(t, errors.Is(err, protocol.ErrNoPreviousEpoch)) @@ -516,7 +504,7 @@ func TestSnapshot_EpochQuery(t *testing.T) { }) t.Run("epoch 2", func(t *testing.T) { - for _, height := range epoch2Heights { + for _, height := range epoch2.Range() { counter, err := state.AtHeight(height).Epochs().Previous().Counter() require.Nil(t, err) assert.Equal(t, epoch1Counter, counter) diff --git a/utils/unittest/epoch_builder.go b/utils/unittest/epoch_builder.go index 33dbfe175cc..f19fdff1ad0 100644 --- a/utils/unittest/epoch_builder.go +++ b/utils/unittest/epoch_builder.go @@ -20,6 +20,38 @@ type EpochHeights struct { Committed uint64 // first height of committed phase } +// Range returns the range of all heights that are in this epoch. +func (epoch EpochHeights) Range() []uint64 { + var heights []uint64 + for height := epoch.Staking; height <= epoch.Committed; height++ { + heights = append(heights, height) + } + return heights +} + +// StakingRange returns the range of all heights in the staking phase. +func (epoch EpochHeights) StakingRange() []uint64 { + var heights []uint64 + for height := epoch.Staking; height < epoch.Setup; height++ { + heights = append(heights, height) + } + return heights +} + +// SetupRange returns the range of all heights in the setup phase. +func (epoch EpochHeights) SetupRange() []uint64 { + var heights []uint64 + for height := epoch.Setup; height < epoch.Committed; height++ { + heights = append(heights, height) + } + return heights +} + +// CommittedRange returns the range of all heights in the committed phase. +func (epoch EpochHeights) CommittedRange() []uint64 { + return []uint64{epoch.Committed} +} + // EpochBuilder is a testing utility for building epochs into chain state. type EpochBuilder struct { t *testing.T From 63fb60c277c53052d5ec4927dc6261767a584028 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 18:12:15 -0800 Subject: [PATCH 163/178] fix first view test --- state/protocol/badger/snapshot_test.go | 35 +++++++++----------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index 4d50392edf5..c90f18a7809 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -525,44 +525,33 @@ func TestSnapshot_EpochFirstView(t *testing.T) { util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.MutableState) { - // Prepare an epoch builder, which builds epochs with 6 blocks, A,B,C,D,E,F - // See EpochBuilder documentation for details of these blocks. epochBuilder := unittest.NewEpochBuilder(t, state) - // build blocks WITHIN epoch 1 - PREPARING epoch 2 - // A - height 0 - (root block) - // B - height 1 - staking phase - // C - height 2 - // D - height 3 - setup phase - // E - height 4 - // F - height 5 - committed phase + // build epoch 1 (prepare epoch 2) epochBuilder. BuildEpoch(). CompleteEpoch() - // build blocks WITHIN epoch 2 - PREPARING epoch 3 - // A - height 6 - first block of epoch 2 - // B - height 7 - staking phase - // C - height 8 - // D - height 9 - setup phase - // E - height 10 - // F - height 11 - committed phase + // build epoch 2 (prepare epoch 3) epochBuilder. BuildEpoch(). CompleteEpoch() + // get heights of each phase in built epochs + epoch1, ok := epochBuilder.EpochHeights(1) + require.True(t, ok) + epoch2, ok := epochBuilder.EpochHeights(2) + require.True(t, ok) + // figure out the expected first views of the epochs epoch1FirstView := head.View epoch2FirstView := result.ServiceEvents[0].Event.(*flow.EpochSetup).FinalView + 1 - epoch1Heights := []uint64{0, 1, 2, 3, 4, 5} - epoch2Heights := []uint64{6, 7, 8, 9, 10, 11} - // check first view for snapshots within epoch 1, with respect to a // snapshot in either epoch 1 or epoch 2 (testing Current and Previous) t.Run("epoch 1", func(t *testing.T) { // test w.r.t. epoch 1 snapshot t.Run("Current", func(t *testing.T) { - for _, height := range epoch1Heights { + for _, height := range epoch1.Range() { actualFirstView, err := state.AtHeight(height).Epochs().Current().FirstView() require.Nil(t, err) assert.Equal(t, epoch1FirstView, actualFirstView) @@ -571,7 +560,7 @@ func TestSnapshot_EpochFirstView(t *testing.T) { // test w.r.t. epoch 2 snapshot t.Run("Previous", func(t *testing.T) { - for _, height := range epoch2Heights { + for _, height := range epoch2.Range() { actualFirstView, err := state.AtHeight(height).Epochs().Previous().FirstView() require.Nil(t, err) assert.Equal(t, epoch1FirstView, actualFirstView) @@ -585,7 +574,7 @@ func TestSnapshot_EpochFirstView(t *testing.T) { // test w.r.t. epoch 1 snapshot t.Run("Next", func(t *testing.T) { - for _, height := range epoch1Heights[3:] { + for _, height := range append(epoch1.SetupRange(), epoch1.CommittedRange()...) { actualFirstView, err := state.AtHeight(height).Epochs().Next().FirstView() require.Nil(t, err) assert.Equal(t, epoch2FirstView, actualFirstView) @@ -594,7 +583,7 @@ func TestSnapshot_EpochFirstView(t *testing.T) { // test w.r.t. epoch 2 snapshot t.Run("Current", func(t *testing.T) { - for _, height := range epoch2Heights { + for _, height := range epoch2.Range() { actualFirstView, err := state.AtHeight(height).Epochs().Current().FirstView() require.Nil(t, err) assert.Equal(t, epoch2FirstView, actualFirstView) From 0c0b206d9b05bc7575eff1933d1c7878d3df6322 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 18:17:18 -0800 Subject: [PATCH 164/178] fix cross-epoch identities test --- state/protocol/badger/snapshot_test.go | 34 +++++++++++--------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/state/protocol/badger/snapshot_test.go b/state/protocol/badger/snapshot_test.go index c90f18a7809..79c95251ae6 100644 --- a/state/protocol/badger/snapshot_test.go +++ b/state/protocol/badger/snapshot_test.go @@ -616,34 +616,28 @@ func TestSnapshot_CrossEpochIdentities(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(epoch1Identities) util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *bprotocol.MutableState) { - // Prepare an epoch builder, which builds epochs with 6 blocks, A,B,C,D,E,F - // See EpochBuilder documentation for details of these blocks. epochBuilder := unittest.NewEpochBuilder(t, state) - // build blocks WITHIN epoch 1 - PREPARING epoch 2 - // A - height 0 - (root block) - // B - height 1 - staking phase - // C - height 2 - // D - height 3 - setup phase - // E - height 4 - // F - height 5 - committed phase + // build epoch 1 (prepare epoch 2) epochBuilder. UsingSetupOpts(unittest.WithParticipants(epoch2Identities)). BuildEpoch(). CompleteEpoch() - // build blocks WITHIN epoch 2 - PREPARING epoch 3 - // A - height 6 - first block of epoch 2 - // B - height 7 - staking phase - // C - height 8 - // D - height 9 - setup phase - // E - height 10 - // F - height 11 - committed phase + // build epoch 2 (prepare epoch 3) epochBuilder. UsingSetupOpts(unittest.WithParticipants(epoch3Identities)). BuildEpoch(). CompleteEpoch() + // get heights of each phase in built epochs + epoch1, ok := epochBuilder.EpochHeights(1) + require.True(t, ok) + epoch2, ok := epochBuilder.EpochHeights(2) + require.True(t, ok) + t.Run("should be able to query at root block", func(t *testing.T) { - snapshot := state.AtHeight(0) + root, err := state.Params().Root() + require.NoError(t, err) + snapshot := state.AtHeight(root.Height) identities, err := snapshot.Identities(filter.Any) require.Nil(t, err) @@ -656,7 +650,7 @@ func TestSnapshot_CrossEpochIdentities(t *testing.T) { t.Run("should include next epoch after staking phase", func(t *testing.T) { // get a snapshot from setup phase and commit phase of epoch 1 - snapshots := []protocol.Snapshot{state.AtHeight(3), state.AtHeight(5)} + snapshots := []protocol.Snapshot{state.AtHeight(epoch1.Setup), state.AtHeight(epoch1.Committed)} for _, snapshot := range snapshots { phase, err := snapshot.Phase() @@ -683,7 +677,7 @@ func TestSnapshot_CrossEpochIdentities(t *testing.T) { t.Run("should include previous epoch in staking phase", func(t *testing.T) { // get a snapshot from staking phase of epoch 2 - snapshot := state.AtHeight(7) + snapshot := state.AtHeight(epoch2.Staking) identities, err := snapshot.Identities(filter.Any) require.Nil(t, err) @@ -702,7 +696,7 @@ func TestSnapshot_CrossEpochIdentities(t *testing.T) { t.Run("should not include previous epoch after staking phase", func(t *testing.T) { // get a snapshot from setup phase and commit phase of epoch 2 - snapshots := []protocol.Snapshot{state.AtHeight(9), state.AtHeight(11)} + snapshots := []protocol.Snapshot{state.AtHeight(epoch2.Setup), state.AtHeight(epoch2.Committed)} for _, snapshot := range snapshots { phase, err := snapshot.Phase() From 6be784d12db9cd36f69a2c5520d2bc768f071feb Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Thu, 11 Mar 2021 18:25:24 -0800 Subject: [PATCH 165/178] fix Sealed test --- state/protocol/badger/mutator_test.go | 61 +++++++++++++-------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 317aca878da..d1b2c0b199a 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -1432,47 +1432,44 @@ func TestMakeValid(t *testing.T) { }) } -// If block A is finalized and contains a seal to block B, then B is the last sealed block +// If block B is finalized and contains a seal for block A, then A is the last sealed block func TestSealed(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(participants) util.RunWithFollowerProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.FollowerState) { head, err := rootSnapshot.Head() require.NoError(t, err) - // A <- B <- C <- D <- E <- F <- G - blockA := unittest.BlockWithParentAndSeal(head, nil) - blockB := unittest.BlockWithParentAndSeal(blockA.Header, nil) - blockC := unittest.BlockWithParentAndSeal(blockB.Header, blockA.Header) - blockD := unittest.BlockWithParentAndSeal(blockC.Header, blockB.Header) - blockE := unittest.BlockWithParentAndSeal(blockD.Header, nil) - blockF := unittest.BlockWithParentAndSeal(blockE.Header, nil) - blockG := unittest.BlockWithParentAndSeal(blockF.Header, nil) - blockH := unittest.BlockWithParentAndSeal(blockG.Header, nil) - - saveBlock(t, blockA, nil, state) - saveBlock(t, blockB, nil, state) - saveBlock(t, blockC, nil, state) - saveBlock(t, blockD, blockA, state) - saveBlock(t, blockE, blockB, state) - saveBlock(t, blockF, blockC, state) - saveBlock(t, blockG, blockD, state) - saveBlock(t, blockH, blockE, state) - - sealed, err := state.Sealed().Head() + // block 1 will be sealed + block1 := unittest.BlockWithParentFixture(head) + err = state.Extend(&block1) + require.NoError(t, err) + err = state.Finalize(block1.ID()) require.NoError(t, err) - require.Equal(t, blockB.Header.Height, sealed.Height) - }) -} -func saveBlock(t *testing.T, block *flow.Block, finalizes *flow.Block, state *protocol.FollowerState) { - err := state.Extend(block) - require.NoError(t, err) + receipt1, seal1 := unittest.ReceiptAndSealForBlock(&block1) + + // block 2 contains receipt for block 1 + block2 := unittest.BlockWithParentFixture(block1.Header) + block2.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{receipt1}, + }) + err = state.Extend(&block2) + require.NoError(t, err) + err = state.Finalize(block2.ID()) + require.NoError(t, err) - if finalizes != nil { - err = state.Finalize(finalizes.ID()) + // block 3 contains seal for block 1 + block3 := unittest.BlockWithParentFixture(block2.Header) + block3.SetPayload(flow.Payload{ + Seals: []*flow.Seal{seal1}, + }) + err = state.Extend(&block3) + require.NoError(t, err) + err = state.Finalize(block3.ID()) require.NoError(t, err) - } - err = state.MarkValid(block.Header.ID()) - require.NoError(t, err) + sealed, err := state.Sealed().Head() + require.NoError(t, err) + require.Equal(t, block1.ID(), sealed.ID()) + }) } From 132e243ab527aa4bb3eccf0377cdcee6001fc408 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 12 Mar 2021 14:42:19 -0800 Subject: [PATCH 166/178] allow duplicate service events --- storage/badger/epoch_commits.go | 2 +- storage/badger/epoch_commits_test.go | 2 +- storage/badger/epoch_setups.go | 2 +- storage/badger/epoch_setups_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/badger/epoch_commits.go b/storage/badger/epoch_commits.go index 8b01104af5b..068e3a9852f 100644 --- a/storage/badger/epoch_commits.go +++ b/storage/badger/epoch_commits.go @@ -19,7 +19,7 @@ func NewEpochCommits(collector module.CacheMetrics, db *badger.DB) *EpochCommits store := func(key interface{}, val interface{}) func(*badger.Txn) error { id := key.(flow.Identifier) commit := val.(*flow.EpochCommit) - return operation.InsertEpochCommit(id, commit) + return operation.SkipDuplicates(operation.InsertEpochCommit(id, commit)) } retrieve := func(key interface{}) func(*badger.Txn) (interface{}, error) { diff --git a/storage/badger/epoch_commits_test.go b/storage/badger/epoch_commits_test.go index aba2169b2fc..aacbf81f7b9 100644 --- a/storage/badger/epoch_commits_test.go +++ b/storage/badger/epoch_commits_test.go @@ -37,6 +37,6 @@ func TestEpochCommitStoreAndRetrieve(t *testing.T) { // test storing same epoch commit err = store.Store(expected) - require.True(t, errors.Is(err, storage.ErrAlreadyExists)) + require.NoError(t, err) }) } diff --git a/storage/badger/epoch_setups.go b/storage/badger/epoch_setups.go index 1e7674fe037..c775f049999 100644 --- a/storage/badger/epoch_setups.go +++ b/storage/badger/epoch_setups.go @@ -19,7 +19,7 @@ func NewEpochSetups(collector module.CacheMetrics, db *badger.DB) *EpochSetups { store := func(key interface{}, val interface{}) func(*badger.Txn) error { id := key.(flow.Identifier) setup := val.(*flow.EpochSetup) - return operation.InsertEpochSetup(id, setup) + return operation.SkipDuplicates(operation.InsertEpochSetup(id, setup)) } retrieve := func(key interface{}) func(*badger.Txn) (interface{}, error) { diff --git a/storage/badger/epoch_setups_test.go b/storage/badger/epoch_setups_test.go index 8d8b9dc836c..7fc626ca481 100644 --- a/storage/badger/epoch_setups_test.go +++ b/storage/badger/epoch_setups_test.go @@ -38,6 +38,6 @@ func TestEpochSetupStoreAndRetrieve(t *testing.T) { // test storing same epoch setup err = operation.RetryOnConflict(db.Update, store.StoreTx(expected)) - require.True(t, errors.Is(err, storage.ErrAlreadyExists)) + require.NoError(t, err) }) } From 9e5bdaa0f1f8e76a41c4badbdb10812fd6d6b365 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Fri, 12 Mar 2021 15:26:06 -0800 Subject: [PATCH 167/178] add test for conflicting, duplicate service events --- state/protocol/badger/mutator_test.go | 106 ++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index d1b2c0b199a..95ed24c736b 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -904,6 +904,112 @@ func TestExtendConflictingEpochEvents(t *testing.T) { }) } +// we should be able to have conflicting forks with two duplicate instances of +// the same service event for the same epoch +// +// /--B1<--B3(R1)<--B5(S1)<--B7 +// ROOT <--+ +// \--B2<--B4(R2)<--B6(S2)<--B8 +// +func TestExtendDuplicateEpochEvents(t *testing.T) { + rootSnapshot := unittest.RootSnapshotFixture(participants) + util.RunWithFullProtocolState(t, rootSnapshot, func(db *badger.DB, state *protocol.MutableState) { + + head, err := rootSnapshot.Head() + require.NoError(t, err) + result, _, err := rootSnapshot.SealedResult() + require.NoError(t, err) + + // add two conflicting blocks for each service event to reference + block1 := unittest.BlockWithParentFixture(head) + block1.SetPayload(flow.EmptyPayload()) + err = state.Extend(&block1) + require.NoError(t, err) + + block2 := unittest.BlockWithParentFixture(head) + block2.SetPayload(flow.EmptyPayload()) + err = state.Extend(&block2) + require.NoError(t, err) + + rootSetup := result.ServiceEvents[0].Event.(*flow.EpochSetup) + + // create two conflicting epoch setup events for the next epoch (final view differs) + nextEpochSetup := unittest.EpochSetupFixture( + unittest.WithParticipants(rootSetup.Participants), + unittest.SetupWithCounter(rootSetup.Counter+1), + unittest.WithFinalView(rootSetup.FinalView+1000), + unittest.WithFirstView(rootSetup.FinalView+1), + ) + + // add blocks containing receipts for block1 and block2 (necessary for sealing) + // block 1 receipt contains nextEpochSetup1 + block1Receipt := unittest.ReceiptForBlockFixture(&block1) + block1Receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{nextEpochSetup.ServiceEvent()} + + // add block 1 receipt to block 3 payload + block3 := unittest.BlockWithParentFixture(block1.Header) + block3.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{block1Receipt}, + }) + err = state.Extend(&block3) + require.NoError(t, err) + + // block 2 receipt contains nextEpochSetup2 + block2Receipt := unittest.ReceiptForBlockFixture(&block2) + block2Receipt.ExecutionResult.ServiceEvents = []flow.ServiceEvent{nextEpochSetup.ServiceEvent()} + + // add block 2 receipt to block 4 payload + block4 := unittest.BlockWithParentFixture(block2.Header) + block4.SetPayload(flow.Payload{ + Receipts: []*flow.ExecutionReceipt{block2Receipt}, + }) + err = state.Extend(&block4) + require.NoError(t, err) + + // seal for block 1 + seal1 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block1Receipt.ExecutionResult)) + + // seal for block 2 + seal2 := unittest.Seal.Fixture(unittest.Seal.WithResult(&block2Receipt.ExecutionResult)) + + // block 5 builds on block 3, contains seal for block 1 + block5 := unittest.BlockWithParentFixture(block3.Header) + block5.SetPayload(flow.Payload{ + Seals: []*flow.Seal{seal1}, + }) + err = state.Extend(&block5) + require.NoError(t, err) + + // block 6 builds on block 4, contains seal for block 2 + block6 := unittest.BlockWithParentFixture(block4.Header) + block6.SetPayload(flow.Payload{ + Seals: []*flow.Seal{seal2}, + }) + err = state.Extend(&block6) + require.NoError(t, err) + + // block 7 builds on block 5, contains QC for block 7 + block7 := unittest.BlockWithParentFixture(block5.Header) + err = state.Extend(&block7) + require.NoError(t, err) + + // block 8 builds on block 6, contains QC for block 6 + // at this point we are inserting the duplicate EpochSetup, should not error + block8 := unittest.BlockWithParentFixture(block6.Header) + err = state.Extend(&block8) + require.NoError(t, err) + + // should be able query each epoch from the appropriate reference block + finalView, err := state.AtBlockID(block7.ID()).Epochs().Next().FinalView() + assert.NoError(t, err) + require.Equal(t, nextEpochSetup.FinalView, finalView) + + finalView, err = state.AtBlockID(block8.ID()).Epochs().Next().FinalView() + assert.NoError(t, err) + require.Equal(t, nextEpochSetup.FinalView, finalView) + }) +} + // extending protocol state with an invalid epoch setup service event should cause an error func TestExtendEpochSetupInvalid(t *testing.T) { rootSnapshot := unittest.RootSnapshotFixture(participants) From e7ea4fd18c23f91bdf4201bc7ae565e2a23f2d48 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Mon, 15 Mar 2021 11:50:05 -0700 Subject: [PATCH 168/178] fix comment --- state/protocol/badger/mutator_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/state/protocol/badger/mutator_test.go b/state/protocol/badger/mutator_test.go index 95ed24c736b..33cd2dbc526 100644 --- a/state/protocol/badger/mutator_test.go +++ b/state/protocol/badger/mutator_test.go @@ -904,7 +904,7 @@ func TestExtendConflictingEpochEvents(t *testing.T) { }) } -// we should be able to have conflicting forks with two duplicate instances of +// we should be able to have conflicting forks with two DUPLICATE instances of // the same service event for the same epoch // // /--B1<--B3(R1)<--B5(S1)<--B7 @@ -933,7 +933,7 @@ func TestExtendDuplicateEpochEvents(t *testing.T) { rootSetup := result.ServiceEvents[0].Event.(*flow.EpochSetup) - // create two conflicting epoch setup events for the next epoch (final view differs) + // create an epoch setup event to insert to BOTH forks nextEpochSetup := unittest.EpochSetupFixture( unittest.WithParticipants(rootSetup.Participants), unittest.SetupWithCounter(rootSetup.Counter+1), From 684642b5da9272812a76e2efa7d82019a011e706 Mon Sep 17 00:00:00 2001 From: danuio Date: Thu, 18 Feb 2021 12:07:20 +0000 Subject: [PATCH 169/178] Finalize command now writes only the root protocol state snapshot --- cmd/bootstrap/cmd/block.go | 3 +-- cmd/bootstrap/cmd/finalize.go | 15 +++++++++++++-- cmd/bootstrap/cmd/qc.go | 4 ++-- cmd/bootstrap/cmd/seal.go | 8 +++++--- cmd/bootstrap/run/snapshot.go | 14 ++++++++++++++ model/bootstrap/filenames.go | 18 ++++++++++-------- 6 files changed, 45 insertions(+), 17 deletions(-) create mode 100644 cmd/bootstrap/run/snapshot.go diff --git a/cmd/bootstrap/cmd/block.go b/cmd/bootstrap/cmd/block.go index 0b3208045b3..0585fbd87a2 100644 --- a/cmd/bootstrap/cmd/block.go +++ b/cmd/bootstrap/cmd/block.go @@ -5,7 +5,6 @@ import ( "time" "github.com/onflow/flow-go/cmd/bootstrap/run" - model "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/flow" ) @@ -18,7 +17,7 @@ func constructRootBlock(rootChain string, rootParent string, rootHeight uint64, block := run.GenerateRootBlock(chainID, parentID, height, timestamp) - writeJSON(model.PathRootBlock, block) + // writeJSON(model.PathRootBlock, block) return block } diff --git a/cmd/bootstrap/cmd/finalize.go b/cmd/bootstrap/cmd/finalize.go index 72660296d81..ced5dfa5939 100644 --- a/cmd/bootstrap/cmd/finalize.go +++ b/cmd/bootstrap/cmd/finalize.go @@ -15,6 +15,7 @@ import ( model "github.com/onflow/flow-go/model/bootstrap" "github.com/onflow/flow-go/model/encodable" "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol/inmem" ) var ( @@ -146,7 +147,7 @@ func finalize(cmd *cobra.Command, args []string) { log.Info().Msg("") log.Info().Msg("constructing root QC") - constructRootQC( + rootQC := constructRootQC( block, model.FilterByRole(stakingNodes, flow.RoleConsensus), model.FilterByRole(internalNodes, flow.RoleConsensus), @@ -167,7 +168,17 @@ func finalize(cmd *cobra.Command, args []string) { log.Info().Msg("") log.Info().Msg("constructing root execution result and block seal") - constructRootResultAndSeal(flagRootCommit, block, stakingNodes, assignments, clusterQCs, dkgData) + result, seal := constructRootResultAndSeal(flagRootCommit, block, stakingNodes, assignments, clusterQCs, dkgData) + log.Info().Msg("") + + // construct serializable root protocol snapshot + log.Info().Msg("constructing root procotol snapshot") + snapshot, err := inmem.SnapshotFromBootstrapState(block, result, seal, rootQC) + if err != nil { + log.Fatal().Err(err).Msg("unable to generate root protocol snapshot") + } + // write snapshot to disk + writeJSON(model.PathRootProtocolStateSnapshot, snapshot) log.Info().Msg("") // copy files only if the directories differ diff --git a/cmd/bootstrap/cmd/qc.go b/cmd/bootstrap/cmd/qc.go index 0ae123a4c33..51884729d75 100644 --- a/cmd/bootstrap/cmd/qc.go +++ b/cmd/bootstrap/cmd/qc.go @@ -6,7 +6,7 @@ import ( "github.com/onflow/flow-go/model/flow" ) -func constructRootQC(block *flow.Block, allNodes, internalNodes []model.NodeInfo, dkgData model.DKGData) { +func constructRootQC(block *flow.Block, allNodes, internalNodes []model.NodeInfo, dkgData model.DKGData) *flow.QuorumCertificate { participantData, err := run.GenerateQCParticipantData(allNodes, internalNodes, dkgData) if err != nil { log.Fatal().Err(err).Msg("failed to generate QC participant data") @@ -17,5 +17,5 @@ func constructRootQC(block *flow.Block, allNodes, internalNodes []model.NodeInfo log.Fatal().Err(err).Msg("generating root QC failed") } - writeJSON(model.PathRootQC, qc) + return qc } diff --git a/cmd/bootstrap/cmd/seal.go b/cmd/bootstrap/cmd/seal.go index d78a31ecd35..20d74bb101a 100644 --- a/cmd/bootstrap/cmd/seal.go +++ b/cmd/bootstrap/cmd/seal.go @@ -17,7 +17,7 @@ func constructRootResultAndSeal( assignments flow.AssignmentList, clusterQCs []*flow.QuorumCertificate, dkgData model.DKGData, -) { +) (*flow.ExecutionResult, *flow.Seal) { stateCommit, err := hex.DecodeString(rootCommit) if err != nil { @@ -53,6 +53,8 @@ func constructRootResultAndSeal( result := run.GenerateRootResult(block, stateCommit, epochSetup, epochCommit) seal := run.GenerateRootSeal(result) - writeJSON(model.PathRootResult, result) - writeJSON(model.PathRootSeal, seal) + // writeJSON(model.PathRootResult, result) + // writeJSON(model.PathRootSeal, seal) + + return result, seal } diff --git a/cmd/bootstrap/run/snapshot.go b/cmd/bootstrap/run/snapshot.go new file mode 100644 index 00000000000..ca951d20ddc --- /dev/null +++ b/cmd/bootstrap/run/snapshot.go @@ -0,0 +1,14 @@ +package run + +import ( + "github.com/onflow/flow-go/model/flow" + "github.com/onflow/flow-go/state/protocol/inmem" +) + +// GenerateRootProtocolSnapshot generates the root protocol snapshot to be used +// for bootstrapping a network +func GenerateRootProtocolSnapshot(block *flow.Block, rootQC *flow.QuorumCertificate, + result *flow.ExecutionResult, seal *flow.Seal) (*inmem.Snapshot, error) { + + return nil, nil +} diff --git a/model/bootstrap/filenames.go b/model/bootstrap/filenames.go index 0f3b8680bcc..23339c36717 100644 --- a/model/bootstrap/filenames.go +++ b/model/bootstrap/filenames.go @@ -16,18 +16,20 @@ var ( DirnameExecutionState = "execution-state" // public genesis information - DirnamePublicBootstrap = "public-root-information" - PathInternalNodeInfosPub = filepath.Join(DirnamePublicBootstrap, "node-internal-infos.pub.json") - PathFinallist = filepath.Join(DirnamePublicBootstrap, "finallist.pub.json") - PathNodeInfosPub = filepath.Join(DirnamePublicBootstrap, "node-infos.pub.json") - PathPartnerNodeInfoPrefix = filepath.Join(DirnamePublicBootstrap, "node-info.pub.") - PathNodeInfoPub = filepath.Join(DirnamePublicBootstrap, "node-info.pub.%v.json") // %v will be replaced by NodeID + DirnamePublicBootstrap = "public-root-information" + PathInternalNodeInfosPub = filepath.Join(DirnamePublicBootstrap, "node-internal-infos.pub.json") + PathFinallist = filepath.Join(DirnamePublicBootstrap, "finallist.pub.json") + PathNodeInfosPub = filepath.Join(DirnamePublicBootstrap, "node-infos.pub.json") + PathPartnerNodeInfoPrefix = filepath.Join(DirnamePublicBootstrap, "node-info.pub.") + PathNodeInfoPub = filepath.Join(DirnamePublicBootstrap, "node-info.pub.%v.json") // %v will be replaced by NodeID + PathRootBlock = filepath.Join(DirnamePublicBootstrap, "root-block.json") PathRootQC = filepath.Join(DirnamePublicBootstrap, "root-qc.json") - PathRootProtocolStateSnapshot = filepath.Join(DirnamePublicBootstrap, "root-protocol-state-snapshot.json") PathRootResult = filepath.Join(DirnamePublicBootstrap, "root-execution-result.json") PathRootSeal = filepath.Join(DirnamePublicBootstrap, "root-block-seal.json") - PathRootCheckpoint = filepath.Join(DirnameExecutionState, wal.RootCheckpointFilename) // only available on an execution node + PathRootProtocolStateSnapshot = filepath.Join(DirnamePublicBootstrap, "root-protocol-state-snapshot.json") + + PathRootCheckpoint = filepath.Join(DirnameExecutionState, wal.RootCheckpointFilename) // only available on an execution node // private genesis information DirPrivateRoot = "private-root-information" From 0ddcddbc5a32c04222fc0c85b00c02d93c7fde5e Mon Sep 17 00:00:00 2001 From: danuio Date: Thu, 25 Feb 2021 10:42:51 +0000 Subject: [PATCH 170/178] Change scaffold to read directly from snapshot --- cmd/bootstrap/cmd/block.go | 2 -- cmd/bootstrap/cmd/seal.go | 3 --- cmd/bootstrap/run/snapshot.go | 14 ----------- cmd/scaffold.go | 46 +++++++++-------------------------- 4 files changed, 11 insertions(+), 54 deletions(-) delete mode 100644 cmd/bootstrap/run/snapshot.go diff --git a/cmd/bootstrap/cmd/block.go b/cmd/bootstrap/cmd/block.go index 0585fbd87a2..7064be9b1a1 100644 --- a/cmd/bootstrap/cmd/block.go +++ b/cmd/bootstrap/cmd/block.go @@ -17,8 +17,6 @@ func constructRootBlock(rootChain string, rootParent string, rootHeight uint64, block := run.GenerateRootBlock(chainID, parentID, height, timestamp) - // writeJSON(model.PathRootBlock, block) - return block } diff --git a/cmd/bootstrap/cmd/seal.go b/cmd/bootstrap/cmd/seal.go index 20d74bb101a..7f1224988af 100644 --- a/cmd/bootstrap/cmd/seal.go +++ b/cmd/bootstrap/cmd/seal.go @@ -53,8 +53,5 @@ func constructRootResultAndSeal( result := run.GenerateRootResult(block, stateCommit, epochSetup, epochCommit) seal := run.GenerateRootSeal(result) - // writeJSON(model.PathRootResult, result) - // writeJSON(model.PathRootSeal, seal) - return result, seal } diff --git a/cmd/bootstrap/run/snapshot.go b/cmd/bootstrap/run/snapshot.go deleted file mode 100644 index ca951d20ddc..00000000000 --- a/cmd/bootstrap/run/snapshot.go +++ /dev/null @@ -1,14 +0,0 @@ -package run - -import ( - "github.com/onflow/flow-go/model/flow" - "github.com/onflow/flow-go/state/protocol/inmem" -) - -// GenerateRootProtocolSnapshot generates the root protocol snapshot to be used -// for bootstrapping a network -func GenerateRootProtocolSnapshot(block *flow.Block, rootQC *flow.QuorumCertificate, - result *flow.ExecutionResult, seal *flow.Seal) (*inmem.Snapshot, error) { - - return nil, nil -} diff --git a/cmd/scaffold.go b/cmd/scaffold.go index a1329246678..23051d5d12a 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -470,7 +470,7 @@ func (fnb *FlowNodeBuilder) initState() { fnb.RootChainID = rootBlock.ChainID // load the root QC data from bootstrap files - // TODO get from protocol state + // TODO: get from protocol state fnb.RootQC, err = loadRootQC(fnb.BaseConfig.BootstrapDir) fnb.MustNot(err).Msg("could not load root QC") @@ -480,31 +480,11 @@ func (fnb *FlowNodeBuilder) initState() { fnb.RootSeal = seal } else { // Bootstrap! - fnb.Logger.Info().Msg("bootstrapping empty protocol state") - // load the root block from bootstrap files and set the chain ID based on it - fnb.RootBlock, err = loadRootBlock(fnb.BaseConfig.BootstrapDir) - fnb.MustNot(err).Msg("could not load root block") - - // set the root chain ID based on the root block - fnb.RootChainID = fnb.RootBlock.Header.ChainID - - // load the root QC data from bootstrap files - fnb.RootQC, err = loadRootQC(fnb.BaseConfig.BootstrapDir) - fnb.MustNot(err).Msg("could not load root QC") - - // load the root execution result from bootstrap files - fnb.RootResult, err = loadRootResult(fnb.BaseConfig.BootstrapDir) - fnb.MustNot(err).Msg("could not load root execution result") - - // load the root block seal from bootstrap files - fnb.RootSeal, err = loadRootSeal(fnb.BaseConfig.BootstrapDir) - fnb.MustNot(err).Msg("could not load root seal") - - // bootstrap the protocol state with the loaded data - rootSnapshot, err := inmem.SnapshotFromBootstrapState(fnb.RootBlock, fnb.RootResult, fnb.RootSeal, fnb.RootQC) - fnb.MustNot(err).Msg("failed to construct state root") + // load the root protocol state snapshot from disk + rootSnapshot, err := loadRootProtocolSnapshot(fnb.BaseConfig.BootstrapDir) + fnb.MustNot(err).Msg("failed to read protocol snapshot from disk") fnb.State, err = badgerState.Bootstrap( fnb.Metrics.Compliance, @@ -805,24 +785,20 @@ func loadRootQC(dir string) (*flow.QuorumCertificate, error) { return &qc, err } -func loadRootResult(dir string) (*flow.ExecutionResult, error) { - data, err := io.ReadFile(filepath.Join(dir, bootstrap.PathRootResult)) +// loadRootProtocolSnapshot loads the root protocol snapshot from disk +func loadRootProtocolSnapshot(dir string) (*inmem.Snapshot, error) { + data, err := io.ReadFile(filepath.Join(dir, bootstrap.PathRootProtocolStateSnapshot)) if err != nil { return nil, err } - var result flow.ExecutionResult - err = json.Unmarshal(data, &result) - return &result, err -} -func loadRootSeal(dir string) (*flow.Seal, error) { - data, err := io.ReadFile(filepath.Join(dir, bootstrap.PathRootSeal)) + var snapshot inmem.Snapshot + err = json.Unmarshal(data, &snapshot) if err != nil { return nil, err } - var seal flow.Seal - err = json.Unmarshal(data, &seal) - return &seal, err + + return &snapshot, nil } // Loads the private info for this node from disk (eg. private staking/network keys). From d11a181bee3e94064eb3ecf6fb7b52513fcb8253 Mon Sep 17 00:00:00 2001 From: danuio Date: Thu, 25 Feb 2021 10:57:55 +0000 Subject: [PATCH 171/178] Fix linting error and update localnet/integration to use root procotol snapshot --- integration/localnet/bootstrap.go | 6 ++++++ integration/testnet/network.go | 17 ++++------------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/integration/localnet/bootstrap.go b/integration/localnet/bootstrap.go index 39cada707b9..448a718d8a5 100644 --- a/integration/localnet/bootstrap.go +++ b/integration/localnet/bootstrap.go @@ -173,13 +173,16 @@ func prepareNodes() []testnet.NodeConfig { return nodes } +// Network ... type Network struct { Version string Services Services } +// Services ... type Services map[string]Service +// Service ... type Service struct { Build Build `yaml:"build,omitempty"` Image string @@ -190,6 +193,7 @@ type Service struct { Ports []string `yaml:"ports,omitempty"` } +// Build ... type Build struct { Context string Dockerfile string @@ -402,8 +406,10 @@ func writeDockerComposeConfig(services Services) error { return nil } +// PrometheusServiceDiscovery ... type PrometheusServiceDiscovery []PromtheusTargetList +// PromtheusTargetList ... type PromtheusTargetList struct { Targets []string `json:"targets"` Labels map[string]string `json:"labels"` diff --git a/integration/testnet/network.go b/integration/testnet/network.go index 4b8e7f9f554..dd492db504c 100644 --- a/integration/testnet/network.go +++ b/integration/testnet/network.go @@ -27,6 +27,7 @@ import ( "github.com/onflow/flow-go/model/flow" "github.com/onflow/flow-go/model/flow/filter" clusterstate "github.com/onflow/flow-go/state/cluster" + "github.com/onflow/flow-go/state/protocol/inmem" "github.com/onflow/flow-go/utils/unittest" ) @@ -641,22 +642,12 @@ func BootstrapNetwork(networkConf NetworkConfig, bootstrapDir string) (*flow.Blo result := run.GenerateRootResult(root, commit, epochSetup, epochCommit) seal := run.GenerateRootSeal(result) - err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootBlock), root) + snapshot, err := inmem.SnapshotFromBootstrapState(root, result, seal, qc) if err != nil { - return nil, nil, nil, nil, err - } - - err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootQC), qc) - if err != nil { - return nil, nil, nil, nil, err - } - - err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootResult), result) - if err != nil { - return nil, nil, nil, nil, err + return nil, nil, nil, nil, fmt.Errorf("could not create bootstrap state snapshot") } - err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootSeal), seal) + err = writeJSON(filepath.Join(bootstrapDir, bootstrap.PathRootProtocolStateSnapshot), snapshot.Encodable()) if err != nil { return nil, nil, nil, nil, err } From 9b6bc74bf83fb435c14114cdd322f00bbb733829 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 16 Mar 2021 09:35:30 -0700 Subject: [PATCH 172/178] write encodable struct to file --- cmd/bootstrap/cmd/finalize.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/bootstrap/cmd/finalize.go b/cmd/bootstrap/cmd/finalize.go index ced5dfa5939..bc50a59382c 100644 --- a/cmd/bootstrap/cmd/finalize.go +++ b/cmd/bootstrap/cmd/finalize.go @@ -178,7 +178,7 @@ func finalize(cmd *cobra.Command, args []string) { log.Fatal().Err(err).Msg("unable to generate root protocol snapshot") } // write snapshot to disk - writeJSON(model.PathRootProtocolStateSnapshot, snapshot) + writeJSON(model.PathRootProtocolStateSnapshot, snapshot.Encodable()) log.Info().Msg("") // copy files only if the directories differ From 681dd734f6edfc66388e5ded4f30dedb06082265 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 16 Mar 2021 09:48:55 -0700 Subject: [PATCH 173/178] populate NodeBuilder from root snapshot --- cmd/scaffold.go | 81 ++++++++++++++----------------------------------- 1 file changed, 23 insertions(+), 58 deletions(-) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 23051d5d12a..7572b0deae8 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -421,6 +421,24 @@ func (fnb *FlowNodeBuilder) initStorage() { func (fnb *FlowNodeBuilder) initState() { fnb.ProtocolEvents = events.NewDistributor() + // load the root protocol state snapshot from disk + rootSnapshot, err := loadRootProtocolSnapshot(fnb.BaseConfig.BootstrapDir) + fnb.MustNot(err).Msg("failed to read protocol snapshot from disk") + + fnb.RootResult, fnb.RootSeal, err = rootSnapshot.SealedResult() + fnb.MustNot(err).Msg("failed to read root sealed result") + sealingSegment, err := rootSnapshot.SealingSegment() + fnb.MustNot(err).Msg("failed to read root sealing segment") + fnb.RootBlock = sealingSegment[0] + fnb.RootQC, err = rootSnapshot.QuorumCertificate() + fnb.MustNot(err).Msg("failed to read root qc") + // set the chain ID based on the root header + // TODO: as the root header can now be loaded from protocol state, we should + // not use a global variable for chain ID anymore, but rely on the protocol + // state as final authority on what the chain ID is + // => https://github.com/dapperlabs/flow-go/issues/4167 + fnb.RootChainID = fnb.RootBlock.Header.ChainID + isBootStrapped, err := badgerState.IsBootstrapped(fnb.DB) fnb.MustNot(err).Msg("failed to determine whether database contains bootstrapped state") if isBootStrapped { @@ -443,49 +461,17 @@ func (fnb *FlowNodeBuilder) initState() { // but the protocol state is not updated, so they don't match // when this happens during a spork, we could try deleting the protocol state database. // TODO: revisit this check when implementing Epoch - rootBlockFromBootstrap, err := loadRootBlock(fnb.BaseConfig.BootstrapDir) - fnb.MustNot(err).Msg("could not load root block from disk") - - rootBlock, err := state.Params().Root() + rootBlockFromState, err := state.Params().Root() fnb.MustNot(err).Msg("could not load root block from protocol state") - if rootBlockFromBootstrap.ID() != rootBlock.ID() { + if fnb.RootBlock.ID() != rootBlockFromState.ID() { fnb.Logger.Fatal().Msgf("mismatching root block ID, protocol state block ID: %v, bootstrap root block ID: %v", - rootBlockFromBootstrap.ID(), + fnb.RootBlock.ID(), fnb.RootBlock.ID()) } - fnb.RootBlock = rootBlockFromBootstrap - - // TODO: we shouldn't have to load any files again after bootstrapping; in - // order to make it unnecessary, we need to changes: - // 1) persist the root QC along the root block so it can be loaded from DB - // => https://github.com/dapperlabs/flow-go/issues/4166 - // 2) bootstrap and persist DKG state in a similar fashion to protocol state - // => https://github.com/dapperlabs/flow-go/issues/4165 - - // set the chain ID based on the root header - // TODO: as the root header can now be loaded from protocol state, we should - // not use a global variable for chain ID anymore, but rely on the protocol - // state as final authority on what the chain ID is - // => https://github.com/dapperlabs/flow-go/issues/4167 - fnb.RootChainID = rootBlock.ChainID - - // load the root QC data from bootstrap files - // TODO: get from protocol state - fnb.RootQC, err = loadRootQC(fnb.BaseConfig.BootstrapDir) - fnb.MustNot(err).Msg("could not load root QC") - - result, seal, err := state.AtBlockID(rootBlock.ID()).SealedResult() - fnb.MustNot(err).Msg("could not get sealed result") - fnb.RootResult = result - fnb.RootSeal = seal } else { // Bootstrap! fnb.Logger.Info().Msg("bootstrapping empty protocol state") - // load the root protocol state snapshot from disk - rootSnapshot, err := loadRootProtocolSnapshot(fnb.BaseConfig.BootstrapDir) - fnb.MustNot(err).Msg("failed to read protocol snapshot from disk") - fnb.State, err = badgerState.Bootstrap( fnb.Metrics.Compliance, fnb.DB, @@ -764,27 +750,6 @@ func (fnb *FlowNodeBuilder) closeDatabase() { } } -func loadRootBlock(dir string) (*flow.Block, error) { - data, err := io.ReadFile(filepath.Join(dir, bootstrap.PathRootBlock)) - if err != nil { - return nil, err - } - var block flow.Block - err = json.Unmarshal(data, &block) - return &block, err - -} - -func loadRootQC(dir string) (*flow.QuorumCertificate, error) { - data, err := io.ReadFile(filepath.Join(dir, bootstrap.PathRootQC)) - if err != nil { - return nil, err - } - var qc flow.QuorumCertificate - err = json.Unmarshal(data, &qc) - return &qc, err -} - // loadRootProtocolSnapshot loads the root protocol snapshot from disk func loadRootProtocolSnapshot(dir string) (*inmem.Snapshot, error) { data, err := io.ReadFile(filepath.Join(dir, bootstrap.PathRootProtocolStateSnapshot)) @@ -792,13 +757,13 @@ func loadRootProtocolSnapshot(dir string) (*inmem.Snapshot, error) { return nil, err } - var snapshot inmem.Snapshot + var snapshot inmem.EncodableSnapshot err = json.Unmarshal(data, &snapshot) if err != nil { return nil, err } - return &snapshot, nil + return inmem.SnapshotFromEncodable(snapshot), nil } // Loads the private info for this node from disk (eg. private staking/network keys). From e0ad31452bfe5fabe5fde94824da4c02630c8128 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 16 Mar 2021 09:49:48 -0700 Subject: [PATCH 174/178] go mod tidy --- go.mod | 3 ++ go.sum | 69 ++++++++++++++++++++++++++++++++++++++++++++++ integration/go.sum | 5 ++++ 3 files changed, 77 insertions(+) diff --git a/go.mod b/go.mod index f57934a458d..c9fd003358b 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,10 @@ require ( github.com/bsipos/thist v1.0.0 github.com/btcsuite/btcd v0.20.1-beta github.com/codahale/hdrhistogram v0.9.0 // indirect + github.com/dapperlabs/testingdock v0.4.2 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.0.3 + github.com/docker/distribution v2.7.1+incompatible // indirect github.com/ef-ds/deque v1.0.4 github.com/ethereum/go-ethereum v1.9.13 github.com/fxamacker/cbor/v2 v2.2.1-0.20201006223149-25f67fca9803 @@ -41,6 +43,7 @@ require ( github.com/onflow/flow-go-sdk v0.15.0 github.com/onflow/flow-go/crypto v0.12.0 github.com/onflow/flow/protobuf/go/flow v0.1.9 + github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.7.1 diff --git a/go.sum b/go.sum index f19dccd0611..d356756612d 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -45,6 +46,7 @@ github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOv github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= @@ -64,6 +66,8 @@ github.com/HdrHistogram/hdrhistogram-go v0.9.0 h1:dpujRju0R4M/QZzcnR1LH1qm+TVG3U github.com/HdrHistogram/hdrhistogram-go v0.9.0/go.mod h1:nxrse8/Tzg2tg3DZcZjm6qEclQKK70g0KxO61gFFZD4= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= @@ -107,6 +111,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bsipos/thist v1.0.0 h1:vZ3W5/ZnT54s4LHeonTCbnzCb20ERlJUnhiwXoGpsbY= github.com/bsipos/thist v1.0.0/go.mod h1:7i0xwRua1/bmUxcxi2xAxaFL895rLtOpKUwnw3NrT8I= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= @@ -147,6 +152,16 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.9.0 h1:9GjrtRI+mLEFPtTfR/AZhcxp+Ii8NZYWq5104FbZQY0= github.com/codahale/hdrhistogram v0.9.0/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= @@ -165,6 +180,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/dapperlabs/testingdock v0.4.2 h1:9xlcsGRw4Jfyvz2eO8EH1T1wlLfOFTv1WXKf9sxBDwk= +github.com/dapperlabs/testingdock v0.4.2/go.mod h1:S45YfB1J1mbOeLHhJROx3dFZfMCVSxTgSU9vZ15Oq18= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -192,8 +209,23 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d h1:SknEFm9d070Wn2GeX8dyl7bMrX07cp3UMXuZ2Ct02Kw= +github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.6.0-rc.1.0.20171207180435-f4118485915a+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20190513124817-8c8457b0f2f8 h1:vyqIlE9fpJ+cdE95qkW9ihHas6QT87AFLE72W5bGUEY= +github.com/docker/docker v1.4.2-0.20190513124817-8c8457b0f2f8/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -253,6 +285,7 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -362,11 +395,13 @@ github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfm github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= @@ -500,6 +535,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v0.0.0-20170810061220-e42267488fe3/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= @@ -757,6 +793,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ 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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -855,15 +893,28 @@ github.com/onflow/flow/protobuf/go/flow v0.1.9/go.mod h1:kRugbzZjwQqvevJhrnnCFMJ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -885,6 +936,7 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssy github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/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= @@ -897,6 +949,7 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -911,6 +964,7 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= @@ -919,6 +973,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -946,7 +1002,9 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/segmentio/fasthash v1.0.2 h1:86fGDl2hB+iSHYlccB/FP9qRGvLNuH/fhEEFn6gnQUs= github.com/segmentio/fasthash v1.0.2/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -967,12 +1025,14 @@ github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -997,6 +1057,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -1011,6 +1072,7 @@ github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= @@ -1032,6 +1094,9 @@ github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+m github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -1072,6 +1137,7 @@ go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1357,6 +1423,7 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= @@ -1366,6 +1433,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS 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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= @@ -1398,6 +1466,7 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt 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 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/integration/go.sum b/integration/go.sum index ce0f6d1911d..3382233be55 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -172,6 +172,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/dapperlabs/testingdock v0.4.2/go.mod h1:S45YfB1J1mbOeLHhJROx3dFZfMCVSxTgSU9vZ15Oq18= github.com/dapperlabs/testingdock v0.4.3-0.20200626075145-ea23fc16bb90 h1:RYSKhK13V8pZq+AqjWnH1vrENL/ZMyWqj2W2rGPDmYo= github.com/dapperlabs/testingdock v0.4.3-0.20200626075145-ea23fc16bb90/go.mod h1:HeTbuHG1J4yt4n7NlZSyuk5c5fmyz6hECbyV+36Ku7Q= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -207,6 +208,8 @@ github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d h1:SknEFm9d070Wn2GeX8dy github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.6.0-rc.1.0.20171207180435-f4118485915a+incompatible h1:2YJcZ66ScSWjLY7lifaPjEav51u0EThWBHpfveH6p0g= github.com/docker/distribution v2.6.0-rc.1.0.20171207180435-f4118485915a+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20190513124817-8c8457b0f2f8 h1:vyqIlE9fpJ+cdE95qkW9ihHas6QT87AFLE72W5bGUEY= github.com/docker/docker v1.4.2-0.20190513124817-8c8457b0f2f8/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -917,6 +920,8 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWEr github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= From 7e70fa41cef52e25c37f18034455eb32cbefd356 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 16 Mar 2021 10:00:21 -0700 Subject: [PATCH 175/178] go mod tidy --- go.mod | 3 --- go.sum | 69 ---------------------------------------------------------- 2 files changed, 72 deletions(-) diff --git a/go.mod b/go.mod index c9fd003358b..f57934a458d 100644 --- a/go.mod +++ b/go.mod @@ -8,10 +8,8 @@ require ( github.com/bsipos/thist v1.0.0 github.com/btcsuite/btcd v0.20.1-beta github.com/codahale/hdrhistogram v0.9.0 // indirect - github.com/dapperlabs/testingdock v0.4.2 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v2 v2.0.3 - github.com/docker/distribution v2.7.1+incompatible // indirect github.com/ef-ds/deque v1.0.4 github.com/ethereum/go-ethereum v1.9.13 github.com/fxamacker/cbor/v2 v2.2.1-0.20201006223149-25f67fca9803 @@ -43,7 +41,6 @@ require ( github.com/onflow/flow-go-sdk v0.15.0 github.com/onflow/flow-go/crypto v0.12.0 github.com/onflow/flow/protobuf/go/flow v0.1.9 - github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.7.1 diff --git a/go.sum b/go.sum index d356756612d..f19dccd0611 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -46,7 +45,6 @@ github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOv github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= @@ -66,8 +64,6 @@ github.com/HdrHistogram/hdrhistogram-go v0.9.0 h1:dpujRju0R4M/QZzcnR1LH1qm+TVG3U github.com/HdrHistogram/hdrhistogram-go v0.9.0/go.mod h1:nxrse8/Tzg2tg3DZcZjm6qEclQKK70g0KxO61gFFZD4= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= @@ -111,7 +107,6 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bsipos/thist v1.0.0 h1:vZ3W5/ZnT54s4LHeonTCbnzCb20ERlJUnhiwXoGpsbY= github.com/bsipos/thist v1.0.0/go.mod h1:7i0xwRua1/bmUxcxi2xAxaFL895rLtOpKUwnw3NrT8I= github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= @@ -152,16 +147,6 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codahale/hdrhistogram v0.9.0 h1:9GjrtRI+mLEFPtTfR/AZhcxp+Ii8NZYWq5104FbZQY0= github.com/codahale/hdrhistogram v0.9.0/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= @@ -180,8 +165,6 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/dapperlabs/testingdock v0.4.2 h1:9xlcsGRw4Jfyvz2eO8EH1T1wlLfOFTv1WXKf9sxBDwk= -github.com/dapperlabs/testingdock v0.4.2/go.mod h1:S45YfB1J1mbOeLHhJROx3dFZfMCVSxTgSU9vZ15Oq18= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -209,23 +192,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d h1:SknEFm9d070Wn2GeX8dyl7bMrX07cp3UMXuZ2Ct02Kw= -github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.6.0-rc.1.0.20171207180435-f4118485915a+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v1.4.2-0.20190513124817-8c8457b0f2f8 h1:vyqIlE9fpJ+cdE95qkW9ihHas6QT87AFLE72W5bGUEY= -github.com/docker/docker v1.4.2-0.20190513124817-8c8457b0f2f8/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -285,7 +253,6 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc= github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -395,13 +362,11 @@ github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfm github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= @@ -535,7 +500,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lib/pq v0.0.0-20170810061220-e42267488fe3/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= @@ -793,8 +757,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ 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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -893,28 +855,15 @@ github.com/onflow/flow/protobuf/go/flow v0.1.9/go.mod h1:kRugbzZjwQqvevJhrnnCFMJ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -936,7 +885,6 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssy github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/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= @@ -949,7 +897,6 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -964,7 +911,6 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= @@ -973,8 +919,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -1002,9 +946,7 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/segmentio/fasthash v1.0.2 h1:86fGDl2hB+iSHYlccB/FP9qRGvLNuH/fhEEFn6gnQUs= github.com/segmentio/fasthash v1.0.2/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -1025,14 +967,12 @@ github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -1057,7 +997,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -1072,7 +1011,6 @@ github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= @@ -1094,9 +1032,6 @@ github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+m github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= @@ -1137,7 +1072,6 @@ go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1423,7 +1357,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= @@ -1433,7 +1366,6 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS 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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= @@ -1466,7 +1398,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt 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 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From 1844ba813b4286814b245669d81dd998169a773a Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 16 Mar 2021 10:04:13 -0700 Subject: [PATCH 176/178] go mod tidy --- integration/go.sum | 5 ----- 1 file changed, 5 deletions(-) diff --git a/integration/go.sum b/integration/go.sum index 3382233be55..ce0f6d1911d 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -172,7 +172,6 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/dapperlabs/testingdock v0.4.2/go.mod h1:S45YfB1J1mbOeLHhJROx3dFZfMCVSxTgSU9vZ15Oq18= github.com/dapperlabs/testingdock v0.4.3-0.20200626075145-ea23fc16bb90 h1:RYSKhK13V8pZq+AqjWnH1vrENL/ZMyWqj2W2rGPDmYo= github.com/dapperlabs/testingdock v0.4.3-0.20200626075145-ea23fc16bb90/go.mod h1:HeTbuHG1J4yt4n7NlZSyuk5c5fmyz6hECbyV+36Ku7Q= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -208,8 +207,6 @@ github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d h1:SknEFm9d070Wn2GeX8dy github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.6.0-rc.1.0.20171207180435-f4118485915a+incompatible h1:2YJcZ66ScSWjLY7lifaPjEav51u0EThWBHpfveH6p0g= github.com/docker/distribution v2.6.0-rc.1.0.20171207180435-f4118485915a+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20190513124817-8c8457b0f2f8 h1:vyqIlE9fpJ+cdE95qkW9ihHas6QT87AFLE72W5bGUEY= github.com/docker/docker v1.4.2-0.20190513124817-8c8457b0f2f8/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= @@ -920,8 +917,6 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWEr github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= From c13cb55c9a4a5c384d437cc6ca6f1462063f44db Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 16 Mar 2021 10:44:33 -0700 Subject: [PATCH 177/178] use last block of sealing segment as root consistent with protocol state bootstrap --- cmd/scaffold.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/scaffold.go b/cmd/scaffold.go index 7572b0deae8..08e0f6beba2 100644 --- a/cmd/scaffold.go +++ b/cmd/scaffold.go @@ -429,7 +429,7 @@ func (fnb *FlowNodeBuilder) initState() { fnb.MustNot(err).Msg("failed to read root sealed result") sealingSegment, err := rootSnapshot.SealingSegment() fnb.MustNot(err).Msg("failed to read root sealing segment") - fnb.RootBlock = sealingSegment[0] + fnb.RootBlock = sealingSegment[len(sealingSegment)-1] fnb.RootQC, err = rootSnapshot.QuorumCertificate() fnb.MustNot(err).Msg("failed to read root qc") // set the chain ID based on the root header From 89eae5daa2251fd32c97c708cda0d3ff5f2b2606 Mon Sep 17 00:00:00 2001 From: Jordan Schalm Date: Tue, 16 Mar 2021 14:42:03 -0700 Subject: [PATCH 178/178] remove this feature branch from CI prior to merge --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1de0667e80e..7ad87d0135e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,9 @@ on: push: branches: - master - - feature/serializable-snapshots pull_request: branches: - master - - feature/serializable-snapshots jobs: golangci: