Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
anorth committed May 29, 2019
1 parent daf715e commit f4e44a3
Show file tree
Hide file tree
Showing 25 changed files with 363 additions and 484 deletions.
28 changes: 14 additions & 14 deletions chain/default_store.go
Expand Up @@ -95,20 +95,20 @@ func (store *DefaultStore) Load(ctx context.Context) (err error) {
if err != nil {
return err
}
headTs := types.TipSet{}
var blocks []*types.Block
// traverse starting from head to begin loading the chain
var startHeight types.Uint64
for it := tipCids.Iter(); !it.Complete(); it.Next() {
blk, err := store.GetBlock(ctx, it.Value())
if err != nil {
return errors.Wrap(err, "failed to load block in head TipSet")
}
err = headTs.AddBlock(blk)
if err != nil {
return errors.Wrap(err, "failed to add validated block to TipSet")
}
startHeight = blk.Height
blocks = append(blocks, blk)
}
headTs, err := types.NewTipSet(blocks...)
if err != nil {
return errors.Wrap(err, "failed to add validated block to TipSet")
}
startHeight := headTs.At(0).Height
logStore.Infof("start loading chain at tipset: %s, height: %d", tipCids.String(), startHeight)
// esnures we only produce 10 log messages regardless of the chain height
logStatusEvery := startHeight / 10
Expand Down Expand Up @@ -141,11 +141,11 @@ func (store *DefaultStore) Load(ctx context.Context) (err error) {
genesii = iterator.Value()
}
// Check genesis here.
if len(genesii) != 1 {
return errors.Errorf("genesis tip set must be a single block, got %d blocks", len(genesii))
if !genesii.IsSolo() {
return errors.Errorf("genesis tip set must be a single block, got %d blocks", genesii.Len())
}

loadCid := genesii.ToSlice()[0].Cid()
loadCid := genesii.At(0).Cid()
if !loadCid.Equals(store.genesis) {
return errors.Errorf("expected genesis cid: %s, loaded genesis cid: %s", store.genesis, loadCid)
}
Expand Down Expand Up @@ -202,8 +202,8 @@ func (store *DefaultStore) putBlk(ctx context.Context, block *types.Block) error
// PutTipSetAndState persists the blocks of a tipset and the tipset index.
func (store *DefaultStore) PutTipSetAndState(ctx context.Context, tsas *TipSetAndState) error {
// Persist blocks.
for _, blk := range tsas.TipSet {
if err := store.putBlk(ctx, blk); err != nil {
for i := 0; i < tsas.TipSet.Len(); i++ {
if err := store.putBlk(ctx, tsas.TipSet.At(i)); err != nil {
return err
}
}
Expand Down Expand Up @@ -309,7 +309,7 @@ func (store *DefaultStore) SetHead(ctx context.Context, ts types.TipSet) error {
logStore.Debugf("SetHead %s", ts.String())

// Add logging to debug sporadic test failure.
if len(ts) < 1 {
if !ts.Defined() {
logStore.Error("publishing empty tipset")
logStore.Error(debug.Stack())
}
Expand Down Expand Up @@ -371,7 +371,7 @@ func (store *DefaultStore) GetHead() types.SortedCidSet {
store.mu.RLock()
defer store.mu.RUnlock()

if store.head == nil {
if !store.head.Defined() {
return types.SortedCidSet{}
}

Expand Down
2 changes: 1 addition & 1 deletion chain/default_store_test.go
Expand Up @@ -208,7 +208,7 @@ func TestGetMultipleByParent(t *testing.T) {
gotNew1 := requireGetTsasByParentAndHeight(t, chainStore, pk1, uint64(1))
require.Equal(t, 2, len(gotNew1))
for _, tsas := range gotNew1 {
if len(tsas.TipSet) == 1 {
if tsas.TipSet.IsSolo() {
assert.Equal(t, newRoot, tsas.TipSetStateRoot)
} else {
assert.Equal(t, dstP.link1State, tsas.TipSetStateRoot)
Expand Down
43 changes: 26 additions & 17 deletions chain/default_syncer.go
Expand Up @@ -302,42 +302,51 @@ func (syncer *DefaultSyncer) widen(ctx context.Context, ts types.TipSet) (types.
// Lookup tipsets with the same parents from the store.
parentSet, err := ts.Parents()
if err != nil {
return nil, err
return types.NoTipSet, err
}
height, err := ts.Height()
if err != nil {
return nil, err
return types.NoTipSet, err
}
if !syncer.chainStore.HasTipSetAndStatesWithParentsAndHeight(parentSet.String(), height) {
return nil, nil
return types.NoTipSet, nil
}
candidates, err := syncer.chainStore.GetTipSetAndStatesByParentsAndHeight(parentSet.String(), height)
if err != nil {
return nil, err
return types.NoTipSet, err
}
if len(candidates) == 0 {
return nil, nil
return types.NoTipSet, nil
}

// Only take the tipset with the most blocks (this is EC specific logic)
max := candidates[0]
max := candidates[0].TipSet
for _, candidate := range candidates[0:] {
if len(candidate.TipSet) > len(max.TipSet) {
max = candidate
if candidate.TipSet.Len() > max.Len() {
max = candidate.TipSet
}
}

// Add blocks of the biggest tipset in the store to a copy of ts
wts := ts.Clone()
for _, blk := range max.TipSet {
if err = wts.AddBlock(blk); err != nil {
return nil, err
}
// Form a new tipset from the union of ts and the largest in the store, de-duped.
blocks := make(map[cid.Cid]*types.Block)
for i := 0; i < ts.Len(); i++ {
blocks[ts.At(i).Cid()] = ts.At(i)
}
for i := 0; i < max.Len(); i++ {
blocks[max.At(i).Cid()] = max.At(i)
}
var blockSlice []*types.Block
for _, b := range blocks {
blockSlice = append(blockSlice, b)
}
wts, err := types.NewTipSet(blockSlice...)
if err != nil {
return types.NoTipSet, err
}

// check that the tipset is distinct from the input and tipsets from the store.
if wts.String() == ts.String() || wts.String() == max.TipSet.String() {
return nil, nil
if wts.String() == ts.String() || wts.String() == max.String() {
return types.NoTipSet, nil
}

return wts, nil
Expand Down Expand Up @@ -391,7 +400,7 @@ func (syncer *DefaultSyncer) HandleNewTipset(ctx context.Context, tipsetCids typ
if err != nil {
return err
}
if wts != nil {
if wts.Defined() {
logSyncer.Debug("attempt to sync after widen")
err = syncer.syncOne(ctx, parent, wts)
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions chain/default_syncer_test.go
Expand Up @@ -298,8 +298,8 @@ func requireTsAdded(t *testing.T, chain chain.Store, ts types.TipSet) {
require.True(t, containsTipSet(childTsasSlice, ts))

// Blocks exist in store
for _, blk := range ts {
require.True(t, chain.HasBlock(ctx, blk.Cid()))
for i := 0; i < ts.Len(); i++ {
require.True(t, chain.HasBlock(ctx, ts.At(i).Cid()))
}
}

Expand All @@ -318,8 +318,8 @@ func assertTsAdded(t *testing.T, chainStore chain.Store, ts types.TipSet) {
assert.True(t, containsTipSet(childTsasSlice, ts))

// Blocks exist in store
for _, blk := range ts {
assert.True(t, chainStore.HasBlock(ctx, blk.Cid()))
for i := 0; i < ts.Len(); i++ {
require.True(t, chainStore.HasBlock(ctx, ts.At(i).Cid()))
}
}

Expand Down Expand Up @@ -984,7 +984,7 @@ func TestTipSetWeightDeep(t *testing.T) {
con = consensus.NewExpected(cst, bs, th.NewTestProcessor(), &consensus.MarketView{}, calcGenBlk.Cid(), verifier)
syncer := chain.NewDefaultSyncer(cst, con, chainStore, blockSource)
baseTS := requireHeadTipset(t, chainStore) // this is the last block of the bootstrapping chain creating miners
require.Equal(t, 1, len(baseTS))
require.True(t, baseTS.IsSolo())
bootstrapStateRoot := baseTS.ToSlice()[0].StateRoot
pSt, err := state.LoadStateTree(ctx, cst, baseTS.ToSlice()[0].StateRoot, builtin.Actors)
require.NoError(t, err)
Expand Down
10 changes: 5 additions & 5 deletions chain/get_ancestors.go
Expand Up @@ -145,11 +145,11 @@ func FindCommonAncestor(leftIter, rightIter *TipsetIterator) (types.TipSet, erro

leftHeight, err := left.Height()
if err != nil {
return nil, err
return types.NoTipSet, err
}
rightHeight, err := right.Height()
if err != nil {
return nil, err
return types.NoTipSet, err
}

// Found common ancestor.
Expand All @@ -162,15 +162,15 @@ func FindCommonAncestor(leftIter, rightIter *TipsetIterator) (types.TipSet, erro
// other pointer's tipset.
if rightHeight >= leftHeight {
if err := rightIter.Next(); err != nil {
return nil, err
return types.NoTipSet, err
}
}

if leftHeight >= rightHeight {
if err := leftIter.Next(); err != nil {
return nil, err
return types.NoTipSet, err
}
}
}
return nil, ErrNoCommonAncestor
return types.NoTipSet, ErrNoCommonAncestor
}
16 changes: 7 additions & 9 deletions chain/traversal.go
Expand Up @@ -16,21 +16,19 @@ type BlockProvider interface {
// GetParentTipSet returns the parent tipset of a tipset.
// The result is empty if the tipset has no parents (including if it is empty itself)
func GetParentTipSet(ctx context.Context, store BlockProvider, ts types.TipSet) (types.TipSet, error) {
newTipSet := types.TipSet{}
parents, err := ts.Parents()
if err != nil {
return nil, err
if err != nil || parents.Len() == 0 {
return types.NoTipSet, err
}
var newBlocks []*types.Block
for it := parents.Iter(); !it.Complete() && ctx.Err() == nil; it.Next() {
newBlk, err := store.GetBlock(ctx, it.Value())
if err != nil {
return nil, err
}
if err := newTipSet.AddBlock(newBlk); err != nil {
return nil, err
return types.NoTipSet, err
}
newBlocks = append(newBlocks, newBlk)
}
return newTipSet, nil
return types.NewTipSet(newBlocks...)
}

// IterAncestors returns an iterator over tipset ancestors, yielding first the start tipset and
Expand All @@ -53,7 +51,7 @@ func (it *TipsetIterator) Value() types.TipSet {

// Complete tests whether the iterator is exhausted.
func (it *TipsetIterator) Complete() bool {
return len(it.value) == 0
return !it.value.Defined()
}

// Next advances the iterator to the next value.
Expand Down
2 changes: 1 addition & 1 deletion commands/chain.go
Expand Up @@ -66,7 +66,7 @@ var chainLsCmd = &cmds.Command{
if err != nil {
return err
}
if len(iter.Value()) == 0 {
if !iter.Value().Defined() {
panic("tipsets from this iterator should have at least one member")
}
if err := re.Emit(iter.Value().ToSlice()); err != nil {
Expand Down
26 changes: 7 additions & 19 deletions consensus/expected.go
Expand Up @@ -124,7 +124,7 @@ func NewExpected(cs *hamt.CborIpldStore, bs blockstore.Blockstore, processor Pro
func (c *Expected) NewValidTipSet(ctx context.Context, blks []*types.Block) (types.TipSet, error) {
for _, blk := range blks {
if err := c.validateBlockStructure(ctx, blk); err != nil {
return nil, err
return types.NoTipSet, err
}
}
return types.NewTipSet(blks...)
Expand All @@ -149,7 +149,7 @@ func (c *Expected) validateBlockStructure(ctx context.Context, b *types.Block) e
func (c *Expected) Weight(ctx context.Context, ts types.TipSet, pSt state.Tree) (uint64, error) {
ctx = log.Start(ctx, "Expected.Weight")
log.LogKV(ctx, "Weight", ts.String())
if len(ts) == 1 && ts.ToSlice()[0].Cid().Equals(c.genesisCid) {
if ts.IsSolo() && ts.At(0).Cid().Equals(c.genesisCid) {
return uint64(0), nil
}
// Compute parent weight.
Expand Down Expand Up @@ -247,19 +247,6 @@ func (c *Expected) RunStateTransition(ctx context.Context, ts types.TipSet, ance
return nil, err
}

sl := ts.ToSlice()
one := sl[0]
for _, blk := range sl[1:] {
if blk.Parents.String() != one.Parents.String() {
log.Error("invalid parents", blk.Parents.String(), one.Parents.String(), blk)
panic("invalid parents")
}
if blk.Height != one.Height {
log.Error("invalid height", blk.Height, one.Height, blk)
panic("invalid height")
}
}

vms := vm.NewStorageMap(c.bstore)
st, err = c.runMessages(ctx, pSt, vms, ts, ancestors)
if err != nil {
Expand All @@ -280,7 +267,8 @@ func (c *Expected) RunStateTransition(ctx context.Context, ts types.TipSet, ance
// Returns nil if all the above checks pass.
// See https://github.com/filecoin-project/specs/blob/master/mining.md#chain-validation
func (c *Expected) validateMining(ctx context.Context, st state.Tree, ts types.TipSet, parentTs types.TipSet) error {
for _, blk := range ts.ToSlice() {
for i := 0; i < ts.Len(); i++ {
blk := ts.At(i)
// TODO: Also need to validate BlockSig

// TODO: Once we've picked a delay function (see #2119), we need to
Expand Down Expand Up @@ -360,9 +348,9 @@ func CreateChallengeSeed(parents types.TipSet, nullBlkCount uint64) (types.PoStC
func (c *Expected) runMessages(ctx context.Context, st state.Tree, vms vm.StorageMap, ts types.TipSet, ancestors []types.TipSet) (state.Tree, error) {
var cpySt state.Tree

// TODO: order blocks in the tipset by ticket
// TODO: don't process messages twice
for _, blk := range ts.ToSlice() {
for i := 0; i < ts.Len(); i++ {
blk := ts.At(i)
cpyCid, err := st.Flush(ctx)
if err != nil {
return nil, errors.Wrap(err, "error validating block state")
Expand Down Expand Up @@ -390,7 +378,7 @@ func (c *Expected) runMessages(ctx context.Context, st state.Tree, vms vm.Storag
return nil, ErrStateRootMismatch
}
}
if len(ts) == 1 { // block validation state == aggregate parent state
if ts.IsSolo() { // block validation state == aggregate parent state
return cpySt, nil
}
// multiblock tipsets require reapplying messages to get aggregate state
Expand Down
11 changes: 6 additions & 5 deletions consensus/expected_test.go
Expand Up @@ -90,7 +90,7 @@ func TestExpected_NewValidTipSet(t *testing.T) {

tipSet, err := exp.NewValidTipSet(ctx, blocks)
assert.Error(t, err, "Foo")
assert.Nil(t, tipSet)
assert.False(t, tipSet.Defined())
})
}

Expand Down Expand Up @@ -311,13 +311,14 @@ func TestCreateChallenge(t *testing.T) {
decoded, err := hex.DecodeString(c.challenge)
assert.NoError(t, err)

parents := types.TipSet{}
var parents []*types.Block
for _, ticket := range c.parentTickets {
b := types.Block{Ticket: ticket}
err = parents.AddBlock(&b)
assert.NoError(t, err)
parents = append(parents, &b)
}
r, err := consensus.CreateChallengeSeed(parents, c.nullBlockCount)
parentTs, err := types.NewTipSet(parents...)
assert.NoError(t, err)
r, err := consensus.CreateChallengeSeed(parentTs, c.nullBlockCount)
assert.NoError(t, err)
assert.Equal(t, decoded, r[:])
}
Expand Down
6 changes: 2 additions & 4 deletions consensus/processor.go
Expand Up @@ -156,13 +156,11 @@ func (p *DefaultProcessor) ProcessTipSet(ctx context.Context, st state.Tree, vms
bh := types.NewBlockHeight(h)
msgFilter := make(map[string]struct{})

tips := ts.ToSlice()
types.SortBlocks(tips)

// TODO: this can be made slightly more efficient by reusing the validation
// transition of the first validated block (change would reach here and
// consensus functions).
for _, blk := range tips {
for i := 0; i < ts.Len(); i++ {
blk := ts.At(i)
// find miner's owner address
minerOwnerAddr, err := minerOwnerAddress(ctx, st, vms, blk.Miner)
if err != nil {
Expand Down
7 changes: 0 additions & 7 deletions consensus/testing.go
Expand Up @@ -41,13 +41,6 @@ func RequireNewTipSet(require *require.Assertions, blks ...*types.Block) types.T
return ts
}

// RequireTipSetAdd adds a block to the provided tipset and requires that this
// does not error.
func RequireTipSetAdd(require *require.Assertions, blk *types.Block, ts types.TipSet) {
err := ts.AddBlock(blk)
require.NoError(err)
}

// TestPowerTableView is an implementation of the powertable view used for testing mining
// wherein each miner has totalPower/minerPower power.
type TestPowerTableView struct{ minerPower, totalPower uint64 }
Expand Down

0 comments on commit f4e44a3

Please sign in to comment.