diff --git a/api/api_full.go b/api/api_full.go index 3127e0915..5e997df3c 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -73,7 +73,7 @@ type FullNode interface { // miner MinerGetBaseInfo(context.Context, address.Address, types.TipSetKey) (*MiningBaseInfo, error) - MinerCreateBlock(context.Context, address.Address, types.TipSetKey, *types.Ticket, *types.EPostProof, []*types.SignedMessage, abi.ChainEpoch, uint64) (*types.BlockMsg, error) + MinerCreateBlock(context.Context, address.Address, types.TipSetKey, *types.Ticket, *types.EPostProof, []*types.BeaconEntry, []*types.SignedMessage, abi.ChainEpoch, uint64) (*types.BlockMsg, error) // // UX ? diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index d11a9823c..0665a90ea 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -88,8 +88,8 @@ type FullNodeStruct struct { MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"` MpoolSub func(context.Context) (<-chan api.MpoolUpdate, error) `perm:"read"` - MinerGetBaseInfo func(context.Context, address.Address, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"` - MinerCreateBlock func(context.Context, address.Address, types.TipSetKey, *types.Ticket, *types.EPostProof, []*types.SignedMessage, abi.ChainEpoch, uint64) (*types.BlockMsg, error) `perm:"write"` + MinerGetBaseInfo func(context.Context, address.Address, types.TipSetKey) (*api.MiningBaseInfo, error) `perm:"read"` + MinerCreateBlock func(context.Context, address.Address, types.TipSetKey, *types.Ticket, *types.EPostProof, []*types.BeaconEntry, []*types.SignedMessage, abi.ChainEpoch, uint64) (*types.BlockMsg, error) `perm:"write"` WalletNew func(context.Context, crypto.SigType) (address.Address, error) `perm:"write"` WalletHas func(context.Context, address.Address) (bool, error) `perm:"write"` @@ -330,8 +330,8 @@ func (c *FullNodeStruct) MinerGetBaseInfo(ctx context.Context, maddr address.Add return c.Internal.MinerGetBaseInfo(ctx, maddr, tsk) } -func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base types.TipSetKey, ticket *types.Ticket, eproof *types.EPostProof, msgs []*types.SignedMessage, height abi.ChainEpoch, ts uint64) (*types.BlockMsg, error) { - return c.Internal.MinerCreateBlock(ctx, addr, base, ticket, eproof, msgs, height, ts) +func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base types.TipSetKey, ticket *types.Ticket, eproof *types.EPostProof, bvals []*types.BeaconEntry, msgs []*types.SignedMessage, height abi.ChainEpoch, ts uint64) (*types.BlockMsg, error) { + return c.Internal.MinerCreateBlock(ctx, addr, base, ticket, eproof, bvals, msgs, height, ts) } func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) { diff --git a/chain/beacon/beacon.go b/chain/beacon/beacon.go new file mode 100644 index 000000000..a0c043476 --- /dev/null +++ b/chain/beacon/beacon.go @@ -0,0 +1,125 @@ +package beacon + +import ( + "bytes" + "context" + "encoding/binary" + "time" + + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/crypto" + logging "github.com/ipfs/go-log" + "golang.org/x/xerrors" + + "github.com/minio/blake2b-simd" +) + +var log = logging.Logger("beacon") + +type Response struct { + Entry *types.BeaconEntry + Err error +} + +type DrandBeacon interface { + RoundTime() time.Duration + LastEntry() (*types.BeaconEntry, error) + Entry(context.Context, uint64) <-chan Response + VerifyEntry(*types.BeaconEntry) (bool, error) + BeaconIndexesForEpoch(abi.ChainEpoch, int) []uint64 +} + +func ValidateBlockValues(b DrandBeacon, h *types.BlockHeader, nulls int) error { + indexes := b.BeaconIndexesForEpoch(h.Height, nulls) + + if len(h.BeaconEntries) != len(indexes) { + return xerrors.Errorf("incorrect number of beacon entries, exp:%d got:%d", len(indexes), len(h.BeaconEntries)) + } + + for i, ix := range indexes { + if h.BeaconEntries[i].Index != ix { + return xerrors.Errorf("beacon entry at [%d] had wrong index, exp:%d got:%d", i, ix, h.BeaconEntries[i].Index) + } + if ok, err := b.VerifyEntry(h.BeaconEntries[i]); err != nil { + return xerrors.Errorf("failed to verify beacon entry %d: %w", i, err) + } else if !ok { + return xerrors.Errorf("beacon entry %d was invalid", i) + } + } + + return nil +} + +func BeaconEntriesForBlock(ctx context.Context, beacon DrandBeacon, round abi.ChainEpoch, nulls int) ([]*types.BeaconEntry, error) { + start := time.Now() + + var out []*types.BeaconEntry + for _, ei := range beacon.BeaconIndexesForEpoch(round, nulls) { + rch := beacon.Entry(ctx, ei) + select { + case resp := <-rch: + if resp.Err != nil { + return nil, xerrors.Errorf("beacon entry request returned error: %w", resp.Err) + } + + out = append(out, resp.Entry) + case <-ctx.Done(): + return nil, xerrors.Errorf("context timed out waiting on beacon entry to come back for round %d: %w", round, ctx.Err()) + } + } + + log.Debugw("fetching beacon entries", "took", time.Since(start), "numEntries", len(out)) + return out, nil +} + +type mockBeacon struct { + interval time.Duration +} + +func NewMockBeacon(interval time.Duration) *mockBeacon { + mb := &mockBeacon{interval: interval} + + return mb +} + +func (mb *mockBeacon) RoundTime() time.Duration { + return mb.interval +} + +func (mb *mockBeacon) LastEntry() (*types.BeaconEntry, error) { + panic("NYI") +} + +func (mb *mockBeacon) entryForIndex(index uint64) *types.BeaconEntry { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, index) + rval := blake2b.Sum256(buf) + return &types.BeaconEntry{ + Index: index, + Signature: crypto.Signature{Type: crypto.SigTypeBLS, Data: rval[:]}, + } +} + +func (mb *mockBeacon) Entry(ctx context.Context, index uint64) <-chan Response { + e := mb.entryForIndex(index) + out := make(chan Response, 1) + out <- Response{Entry: e} + return out +} + +func (mb *mockBeacon) VerifyEntry(e *types.BeaconEntry) (bool, error) { + oe := mb.entryForIndex(e.Index) + return bytes.Equal(e.Signature.Data, oe.Signature.Data), nil +} + +func (mb *mockBeacon) BeaconIndexesForEpoch(epoch abi.ChainEpoch, nulls int) []uint64 { + var out []uint64 + for i := nulls; i > 0; i-- { + out = append(out, uint64(epoch)-uint64(i)) + } + out = append(out, uint64(epoch)) + return []uint64{uint64(epoch)} +} + +var _ DrandBeacon = (*mockBeacon)(nil) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 9becb17d4..a7f328b2d 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -6,6 +6,7 @@ import ( "fmt" "io/ioutil" "sync/atomic" + "time" "github.com/filecoin-project/go-address" commcid "github.com/filecoin-project/go-fil-commcid" @@ -27,6 +28,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/beacon" genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" @@ -51,6 +53,8 @@ type ChainGen struct { cs *store.ChainStore + beacon beacon.DrandBeacon + sm *stmgr.StateManager genesis *types.BlockHeader @@ -211,12 +215,15 @@ func NewGenerator() (*ChainGen, error) { miners := []address.Address{maddr1, maddr2} + beac := beacon.NewMockBeacon(time.Second) + gen := &ChainGen{ bs: bs, cs: cs, sm: sm, msgsPerBlock: msgsPerBlock, genesis: genb.Genesis, + beacon: beac, w: w, GetMessages: getRandomMessages, @@ -346,7 +353,13 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad } if proof != nil { - fblk, err := cg.makeBlock(base, m, proof, t, abi.ChainEpoch(round), msgs) + nulls := int(round) - int(base.Height()+1) + bvals, err := beacon.BeaconEntriesForBlock(context.TODO(), cg.beacon, abi.ChainEpoch(round), nulls) + if err != nil { + return nil, xerrors.Errorf("failed to get beacon entries: %w", err) + } + + fblk, err := cg.makeBlock(base, m, proof, t, bvals, abi.ChainEpoch(round), msgs) if err != nil { return nil, xerrors.Errorf("making a block for next tipset failed: %w", err) } @@ -368,7 +381,7 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad }, nil } -func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof *types.EPostProof, ticket *types.Ticket, height abi.ChainEpoch, msgs []*types.SignedMessage) (*types.FullBlock, error) { +func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof *types.EPostProof, ticket *types.Ticket, bvals []*types.BeaconEntry, height abi.ChainEpoch, msgs []*types.SignedMessage) (*types.FullBlock, error) { var ts uint64 if cg.Timestamper != nil { @@ -377,7 +390,7 @@ func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof * ts = parents.MinTimestamp() + uint64((height-parents.Height())*build.BlockDelay) } - fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, ticket, eproof, msgs, height, ts) + fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, ticket, eproof, bvals, msgs, height, ts) if err != nil { return nil, err } diff --git a/chain/gen/mining.go b/chain/gen/mining.go index 083d972c1..232b07439 100644 --- a/chain/gen/mining.go +++ b/chain/gen/mining.go @@ -20,7 +20,7 @@ import ( "github.com/filecoin-project/lotus/chain/wallet" ) -func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wallet, miner address.Address, parents *types.TipSet, ticket *types.Ticket, proof *types.EPostProof, msgs []*types.SignedMessage, height abi.ChainEpoch, timestamp uint64) (*types.FullBlock, error) { +func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wallet, miner address.Address, parents *types.TipSet, ticket *types.Ticket, proof *types.EPostProof, bvals []*types.BeaconEntry, msgs []*types.SignedMessage, height abi.ChainEpoch, timestamp uint64) (*types.FullBlock, error) { st, recpts, err := sm.TipSetState(ctx, parents) if err != nil { return nil, xerrors.Errorf("failed to load tipset state: %w", err) @@ -35,6 +35,7 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal Miner: miner, Parents: parents.Cids(), Ticket: ticket, + BeaconEntries: bvals, Height: height, Timestamp: timestamp, EPostProof: *proof, diff --git a/chain/sync.go b/chain/sync.go index 77222a90a..fc8d83e8f 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -36,6 +36,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/blocksync" "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/state" @@ -55,6 +56,9 @@ type Syncer struct { // The interface for accessing and putting tipsets into local storage store *store.ChainStore + // handle to the random beacon for verification + beacon beacon.DrandBeacon + // the state manager handles making state queries sm *stmgr.StateManager @@ -78,7 +82,7 @@ type Syncer struct { receiptTracker *blockReceiptTracker } -func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connmgr.ConnManager, self peer.ID) (*Syncer, error) { +func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connmgr.ConnManager, self peer.ID, beacon beacon.DrandBeacon) (*Syncer, error) { gen, err := sm.ChainStore().GetGenesis() if err != nil { return nil, err @@ -90,6 +94,7 @@ func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connm } s := &Syncer{ + beacon: beacon, bad: NewBadBlockCache(), Genesis: gent, Bsync: bsync, @@ -506,6 +511,8 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("load parent tipset failed (%s): %w", h.Parents, err) } + nulls := h.Height - (baseTs.Height() + 1) + // fast checks first if h.BlockSig == nil { return xerrors.Errorf("block had nil signature") @@ -617,6 +624,13 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return nil }) + beaconValuesCheck := async.Err(func() error { + if err := beacon.ValidateBlockValues(syncer.beacon, h, int(nulls)); err != nil { + return xerrors.Errorf("failed to validate blocks random beacon values: %w", err) + } + return nil + }) + tktsCheck := async.Err(func() error { buf := new(bytes.Buffer) if err := h.Miner.MarshalCBOR(buf); err != nil { @@ -645,6 +659,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err minerCheck, tktsCheck, blockSigCheck, + beaconValuesCheck, eproofCheck, winnerCheck, msgsCheck, diff --git a/chain/types/blockheader.go b/chain/types/blockheader.go index 1bffe2230..e474a9446 100644 --- a/chain/types/blockheader.go +++ b/chain/types/blockheader.go @@ -34,6 +34,11 @@ type EPostProof struct { Candidates []EPostTicket } +type BeaconEntry struct { + Index uint64 + Signature crypto.Signature +} + type BlockHeader struct { Miner address.Address // 0 @@ -41,6 +46,8 @@ type BlockHeader struct { EPostProof EPostProof // 2 + BeaconEntries []*BeaconEntry + Parents []cid.Cid // 3 ParentWeight BigInt // 4 diff --git a/chain/types/cbor_gen.go b/chain/types/cbor_gen.go index 6a7a3b12e..0147f7264 100644 --- a/chain/types/cbor_gen.go +++ b/chain/types/cbor_gen.go @@ -21,7 +21,7 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error { _, err := w.Write(cbg.CborNull) return err } - if _, err := w.Write([]byte{141}); err != nil { + if _, err := w.Write([]byte{142}); err != nil { return err } @@ -40,6 +40,20 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error { return err } + // t.BeaconEntries ([]*types.BeaconEntry) (slice) + if len(t.BeaconEntries) > cbg.MaxLength { + return xerrors.Errorf("Slice value in field t.BeaconEntries was too long") + } + + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.BeaconEntries)))); err != nil { + return err + } + for _, v := range t.BeaconEntries { + if err := v.MarshalCBOR(w); err != nil { + return err + } + } + // t.Parents ([]cid.Cid) (slice) if len(t.Parents) > cbg.MaxLength { return xerrors.Errorf("Slice value in field t.Parents was too long") @@ -124,7 +138,7 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("cbor input should be of type array") } - if extra != 13 { + if extra != 14 { return fmt.Errorf("cbor input had wrong number of fields") } @@ -167,6 +181,33 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) error { } } + // t.BeaconEntries ([]*types.BeaconEntry) (slice) + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + + if extra > cbg.MaxLength { + return fmt.Errorf("t.BeaconEntries: array too large (%d)", extra) + } + + if maj != cbg.MajArray { + return fmt.Errorf("expected cbor array") + } + if extra > 0 { + t.BeaconEntries = make([]*BeaconEntry, extra) + } + for i := 0; i < int(extra); i++ { + + var v BeaconEntry + if err := v.UnmarshalCBOR(br); err != nil { + return err + } + + t.BeaconEntries[i] = &v + } + // t.Parents ([]cid.Cid) (slice) maj, extra, err = cbg.CborReadHeader(br) @@ -1455,3 +1496,66 @@ func (t *ExpTipSet) UnmarshalCBOR(r io.Reader) error { } return nil } + +func (t *BeaconEntry) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + if _, err := w.Write([]byte{130}); err != nil { + return err + } + + // t.Index (uint64) (uint64) + + if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, uint64(t.Index))); err != nil { + return err + } + + // t.Signature (crypto.Signature) (struct) + if err := t.Signature.MarshalCBOR(w); err != nil { + return err + } + return nil +} + +func (t *BeaconEntry) UnmarshalCBOR(r io.Reader) error { + br := cbg.GetPeeker(r) + + maj, extra, err := cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 2 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.Index (uint64) (uint64) + + { + + maj, extra, err = cbg.CborReadHeader(br) + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.Index = uint64(extra) + + } + // t.Signature (crypto.Signature) (struct) + + { + + if err := t.Signature.UnmarshalCBOR(br); err != nil { + return xerrors.Errorf("unmarshaling t.Signature: %w", err) + } + + } + return nil +} diff --git a/cmd/lotus-storage-miner/init.go b/cmd/lotus-storage-miner/init.go index 0c1c45631..535abf000 100644 --- a/cmd/lotus-storage-miner/init.go +++ b/cmd/lotus-storage-miner/init.go @@ -11,6 +11,7 @@ import ( "os" "path/filepath" "strconv" + "time" "github.com/docker/go-units" "github.com/google/uuid" @@ -37,6 +38,7 @@ import ( lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/genesis" @@ -435,7 +437,9 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api lapi.FullNode, } epp := storage.NewElectionPoStProver(smgr, dtypes.MinerID(mid)) - m := miner.NewMiner(api, epp) + beacon := beacon.NewMockBeacon(build.BlockDelay * time.Second) + + m := miner.NewMiner(api, epp, beacon) { if err := m.Register(a); err != nil { return xerrors.Errorf("failed to start up genesis miner: %w", err) diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 0a1990fdd..e899cc1dd 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 0a1990fdd9a08b122cf97b373413b688d5b429cf +Subproject commit e899cc1dd0720e0a4d25b0e751b84e3733cbedc5 diff --git a/gen/main.go b/gen/main.go index cbc6da91e..d75b53a31 100644 --- a/gen/main.go +++ b/gen/main.go @@ -27,6 +27,7 @@ func main() { types.MessageReceipt{}, types.BlockMsg{}, types.ExpTipSet{}, + types.BeaconEntry{}, ) if err != nil { fmt.Println(err) diff --git a/miner/miner.go b/miner/miner.go index 0cf16e2ee..f33655e9c 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -14,6 +14,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/types" @@ -26,15 +27,16 @@ var log = logging.Logger("miner") type waitFunc func(ctx context.Context, baseTime uint64) error -func NewMiner(api api.FullNode, epp gen.ElectionPoStProver) *Miner { +func NewMiner(api api.FullNode, epp gen.ElectionPoStProver, beacon beacon.DrandBeacon) *Miner { arc, err := lru.NewARC(10000) if err != nil { panic(err) } return &Miner{ - api: api, - epp: epp, + api: api, + epp: epp, + beacon: beacon, waitFunc: func(ctx context.Context, baseTime uint64) error { // Wait around for half the block time in case other parents come in deadline := baseTime + build.PropagationDelay @@ -49,7 +51,8 @@ func NewMiner(api api.FullNode, epp gen.ElectionPoStProver) *Miner { type Miner struct { api api.FullNode - epp gen.ElectionPoStProver + epp gen.ElectionPoStProver + beacon beacon.DrandBeacon lk sync.Mutex addresses []address.Address @@ -291,6 +294,12 @@ func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningB log.Debugw("attempting to mine a block", "tipset", types.LogCids(base.ts.Cids())) start := time.Now() + round := base.ts.Height() + base.nullRounds + 1 + bvals, err := beacon.BeaconEntriesForBlock(ctx, m.beacon, round, int(base.nullRounds)) + if err != nil { + return nil, xerrors.Errorf("get beacon entries failed: %w", err) + } + hasPower, err := m.hasPower(ctx, addr, base.ts) if err != nil { return nil, xerrors.Errorf("checking if miner is slashed: %w", err) @@ -308,7 +317,7 @@ func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningB return nil, xerrors.Errorf("scratching ticket failed: %w", err) } - proofin, err := gen.IsRoundWinner(ctx, base.ts, int64(base.ts.Height()+base.nullRounds+1), addr, m.epp, m.api) + proofin, err := gen.IsRoundWinner(ctx, base.ts, int64(round), addr, m.epp, m.api) if err != nil { return nil, xerrors.Errorf("failed to check if we win next round: %w", err) } @@ -329,7 +338,7 @@ func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningB return nil, xerrors.Errorf("computing election proof: %w", err) } - b, err := m.createBlock(base, addr, ticket, proof, pending) + b, err := m.createBlock(base, addr, ticket, proof, bvals, pending) if err != nil { return nil, xerrors.Errorf("failed to create block: %w", err) } @@ -369,7 +378,7 @@ func (m *Miner) computeTicket(ctx context.Context, addr address.Address, base *M }, nil } -func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *types.Ticket, proof *types.EPostProof, pending []*types.SignedMessage) (*types.BlockMsg, error) { +func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *types.Ticket, proof *types.EPostProof, bvals []*types.BeaconEntry, pending []*types.SignedMessage) (*types.BlockMsg, error) { msgs, err := SelectMessages(context.TODO(), m.api.StateGetActor, base.ts, pending) if err != nil { return nil, xerrors.Errorf("message filtering failed: %w", err) @@ -385,7 +394,7 @@ func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *type nheight := base.ts.Height() + base.nullRounds + 1 // why even return this? that api call could just submit it for us - return m.api.MinerCreateBlock(context.TODO(), addr, base.ts.Key(), ticket, proof, msgs, nheight, uint64(uts)) + return m.api.MinerCreateBlock(context.TODO(), addr, base.ts.Key(), ticket, proof, bvals, msgs, nheight, uint64(uts)) } type ActorLookup func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error) diff --git a/miner/testminer.go b/miner/testminer.go index 0f5666a66..7b7d6f476 100644 --- a/miner/testminer.go +++ b/miner/testminer.go @@ -5,6 +5,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/gen" lru "github.com/hashicorp/golang-lru" ) @@ -16,7 +17,9 @@ func NewTestMiner(nextCh <-chan struct{}, addr address.Address) func(api.FullNod panic(err) } + beacon := beacon.NewMockBeacon(0) m := &Miner{ + beacon: beacon, api: api, waitFunc: chanWaiter(nextCh), epp: epp, diff --git a/node/builder.go b/node/builder.go index 311aa140e..f6f1a83bf 100644 --- a/node/builder.go +++ b/node/builder.go @@ -29,6 +29,7 @@ import ( "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain" + "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/blocksync" "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/market" @@ -255,6 +256,7 @@ func Online() Option { Override(new(storagemarket.StorageClientNode), storageadapter.NewClientNodeAdapter), Override(RegisterClientValidatorKey, modules.RegisterClientValidator), Override(RunDealClientKey, modules.RunDealClient), + Override(new(beacon.DrandBeacon), modules.RandomBeacon), Override(new(*paychmgr.Store), paychmgr.NewStore), Override(new(*paychmgr.Manager), paychmgr.NewManager), diff --git a/node/impl/full/state.go b/node/impl/full/state.go index e3d7f6a21..a16289640 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -269,13 +269,12 @@ func (a *StateAPI) MinerGetBaseInfo(ctx context.Context, maddr address.Address, return stmgr.MinerGetBaseInfo(ctx, a.StateManager, tsk, maddr) } -// This is on StateAPI because miner.Miner requires this, and MinerAPI requires miner.Miner -func (a *StateAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parentsTSK types.TipSetKey, ticket *types.Ticket, proof *types.EPostProof, msgs []*types.SignedMessage, height abi.ChainEpoch, ts uint64) (*types.BlockMsg, error) { +func (a *StateAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parentsTSK types.TipSetKey, ticket *types.Ticket, proof *types.EPostProof, bvals []*types.BeaconEntry, msgs []*types.SignedMessage, height abi.ChainEpoch, ts uint64) (*types.BlockMsg, error) { parents, err := a.Chain.GetTipSetFromKey(parentsTSK) if err != nil { return nil, xerrors.Errorf("loading tipset %s: %w", parentsTSK, err) } - fblk, err := gen.MinerCreateBlock(ctx, a.StateManager, a.Wallet, addr, parents, ticket, proof, msgs, height, ts) + fblk, err := gen.MinerCreateBlock(ctx, a.StateManager, a.Wallet, addr, parents, ticket, proof, bvals, msgs, height, ts) if err != nil { return nil, err } diff --git a/node/modules/chain.go b/node/modules/chain.go index c70f18892..f0d87df37 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -19,6 +19,7 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/lotus/chain" + "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/blocksync" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/stmgr" @@ -141,8 +142,8 @@ func NetworkName(mctx helpers.MetricsCtx, lc fx.Lifecycle, cs *store.ChainStore, return netName, err } -func NewSyncer(lc fx.Lifecycle, sm *stmgr.StateManager, bsync *blocksync.BlockSync, h host.Host) (*chain.Syncer, error) { - syncer, err := chain.NewSyncer(sm, bsync, h.ConnManager(), h.ID()) +func NewSyncer(lc fx.Lifecycle, sm *stmgr.StateManager, bsync *blocksync.BlockSync, h host.Host, beacon beacon.DrandBeacon) (*chain.Syncer, error) { + syncer, err := chain.NewSyncer(sm, bsync, h.ConnManager(), h.ID(), beacon) if err != nil { return nil, err } diff --git a/node/modules/services.go b/node/modules/services.go index fde738df8..2160a3b50 100644 --- a/node/modules/services.go +++ b/node/modules/services.go @@ -2,6 +2,8 @@ package modules import ( "context" + "fmt" + "time" eventbus "github.com/libp2p/go-eventbus" event "github.com/libp2p/go-libp2p-core/event" @@ -16,6 +18,7 @@ import ( "github.com/filecoin-project/go-fil-markets/storagemarket" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain" + "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/blocksync" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/sub" @@ -114,3 +117,8 @@ func NewLocalDiscovery(ds dtypes.MetadataDS) *discovery.Local { func RetrievalResolver(l *discovery.Local) retrievalmarket.PeerResolver { return discovery.Multi(l) } + +func RandomBeacon() beacon.DrandBeacon { + fmt.Println("RANDOM BEACON!") + return beacon.NewMockBeacon(build.BlockDelay * time.Second) +} diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index e59d4fc28..9d3fb7333 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -43,6 +43,7 @@ import ( lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/markets/retrievaladapter" @@ -257,13 +258,13 @@ func StagingGraphsync(mctx helpers.MetricsCtx, lc fx.Lifecycle, ibs dtypes.Stagi return gs } -func SetupBlockProducer(lc fx.Lifecycle, ds dtypes.MetadataDS, api lapi.FullNode, epp gen.ElectionPoStProver) (*miner.Miner, error) { +func SetupBlockProducer(lc fx.Lifecycle, ds dtypes.MetadataDS, api lapi.FullNode, epp gen.ElectionPoStProver, beacon beacon.DrandBeacon) (*miner.Miner, error) { minerAddr, err := minerAddrFromDS(ds) if err != nil { return nil, err } - m := miner.NewMiner(api, epp) + m := miner.NewMiner(api, epp, beacon) lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error {