WIP: random beacon interface

Signed-off-by: Jakub Sztandera <kubuxu@protocol.ai>
This commit is contained in:
Jeromy 2020-03-25 16:16:17 -07:00
parent 7e96b15091
commit 7075eaba26
18 changed files with 322 additions and 29 deletions

View File

@ -73,7 +73,7 @@ type FullNode interface {
// miner // miner
MinerGetBaseInfo(context.Context, address.Address, types.TipSetKey) (*MiningBaseInfo, error) 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 ? // // UX ?

View File

@ -88,8 +88,8 @@ type FullNodeStruct struct {
MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"` MpoolGetNonce func(context.Context, address.Address) (uint64, error) `perm:"read"`
MpoolSub func(context.Context) (<-chan api.MpoolUpdate, 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"` 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"` 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"` WalletNew func(context.Context, crypto.SigType) (address.Address, error) `perm:"write"`
WalletHas func(context.Context, address.Address) (bool, 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) 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) { 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, msgs, height, ts) return c.Internal.MinerCreateBlock(ctx, addr, base, ticket, eproof, bvals, msgs, height, ts)
} }
func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) { func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) {

125
chain/beacon/beacon.go Normal file
View File

@ -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)

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"sync/atomic" "sync/atomic"
"time"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
commcid "github.com/filecoin-project/go-fil-commcid" commcid "github.com/filecoin-project/go-fil-commcid"
@ -27,6 +28,7 @@ import (
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/beacon"
genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis" genesis2 "github.com/filecoin-project/lotus/chain/gen/genesis"
"github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/store"
@ -51,6 +53,8 @@ type ChainGen struct {
cs *store.ChainStore cs *store.ChainStore
beacon beacon.DrandBeacon
sm *stmgr.StateManager sm *stmgr.StateManager
genesis *types.BlockHeader genesis *types.BlockHeader
@ -211,12 +215,15 @@ func NewGenerator() (*ChainGen, error) {
miners := []address.Address{maddr1, maddr2} miners := []address.Address{maddr1, maddr2}
beac := beacon.NewMockBeacon(time.Second)
gen := &ChainGen{ gen := &ChainGen{
bs: bs, bs: bs,
cs: cs, cs: cs,
sm: sm, sm: sm,
msgsPerBlock: msgsPerBlock, msgsPerBlock: msgsPerBlock,
genesis: genb.Genesis, genesis: genb.Genesis,
beacon: beac,
w: w, w: w,
GetMessages: getRandomMessages, GetMessages: getRandomMessages,
@ -346,7 +353,13 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad
} }
if proof != nil { 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 { if err != nil {
return nil, xerrors.Errorf("making a block for next tipset failed: %w", err) 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 }, 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 var ts uint64
if cg.Timestamper != nil { 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) 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -20,7 +20,7 @@ import (
"github.com/filecoin-project/lotus/chain/wallet" "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) st, recpts, err := sm.TipSetState(ctx, parents)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to load tipset state: %w", err) 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, Miner: miner,
Parents: parents.Cids(), Parents: parents.Cids(),
Ticket: ticket, Ticket: ticket,
BeaconEntries: bvals,
Height: height, Height: height,
Timestamp: timestamp, Timestamp: timestamp,
EPostProof: *proof, EPostProof: *proof,

View File

@ -36,6 +36,7 @@ import (
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "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/blocksync"
"github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/state"
@ -55,6 +56,9 @@ type Syncer struct {
// The interface for accessing and putting tipsets into local storage // The interface for accessing and putting tipsets into local storage
store *store.ChainStore store *store.ChainStore
// handle to the random beacon for verification
beacon beacon.DrandBeacon
// the state manager handles making state queries // the state manager handles making state queries
sm *stmgr.StateManager sm *stmgr.StateManager
@ -78,7 +82,7 @@ type Syncer struct {
receiptTracker *blockReceiptTracker 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() gen, err := sm.ChainStore().GetGenesis()
if err != nil { if err != nil {
return nil, err return nil, err
@ -90,6 +94,7 @@ func NewSyncer(sm *stmgr.StateManager, bsync *blocksync.BlockSync, connmgr connm
} }
s := &Syncer{ s := &Syncer{
beacon: beacon,
bad: NewBadBlockCache(), bad: NewBadBlockCache(),
Genesis: gent, Genesis: gent,
Bsync: bsync, 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) return xerrors.Errorf("load parent tipset failed (%s): %w", h.Parents, err)
} }
nulls := h.Height - (baseTs.Height() + 1)
// fast checks first // fast checks first
if h.BlockSig == nil { if h.BlockSig == nil {
return xerrors.Errorf("block had nil signature") return xerrors.Errorf("block had nil signature")
@ -617,6 +624,13 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
return nil 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 { tktsCheck := async.Err(func() error {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
if err := h.Miner.MarshalCBOR(buf); err != nil { if err := h.Miner.MarshalCBOR(buf); err != nil {
@ -645,6 +659,7 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
minerCheck, minerCheck,
tktsCheck, tktsCheck,
blockSigCheck, blockSigCheck,
beaconValuesCheck,
eproofCheck, eproofCheck,
winnerCheck, winnerCheck,
msgsCheck, msgsCheck,

View File

@ -34,6 +34,11 @@ type EPostProof struct {
Candidates []EPostTicket Candidates []EPostTicket
} }
type BeaconEntry struct {
Index uint64
Signature crypto.Signature
}
type BlockHeader struct { type BlockHeader struct {
Miner address.Address // 0 Miner address.Address // 0
@ -41,6 +46,8 @@ type BlockHeader struct {
EPostProof EPostProof // 2 EPostProof EPostProof // 2
BeaconEntries []*BeaconEntry
Parents []cid.Cid // 3 Parents []cid.Cid // 3
ParentWeight BigInt // 4 ParentWeight BigInt // 4

View File

@ -21,7 +21,7 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error {
_, err := w.Write(cbg.CborNull) _, err := w.Write(cbg.CborNull)
return err return err
} }
if _, err := w.Write([]byte{141}); err != nil { if _, err := w.Write([]byte{142}); err != nil {
return err return err
} }
@ -40,6 +40,20 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error {
return err 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) // t.Parents ([]cid.Cid) (slice)
if len(t.Parents) > cbg.MaxLength { if len(t.Parents) > cbg.MaxLength {
return xerrors.Errorf("Slice value in field t.Parents was too long") 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") 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") 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) // t.Parents ([]cid.Cid) (slice)
maj, extra, err = cbg.CborReadHeader(br) maj, extra, err = cbg.CborReadHeader(br)
@ -1455,3 +1496,66 @@ func (t *ExpTipSet) UnmarshalCBOR(r io.Reader) error {
} }
return nil 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
}

View File

@ -11,6 +11,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"time"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/google/uuid" "github.com/google/uuid"
@ -37,6 +38,7 @@ import (
lapi "github.com/filecoin-project/lotus/api" lapi "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/beacon"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli" lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/genesis" "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)) 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 { if err := m.Register(a); err != nil {
return xerrors.Errorf("failed to start up genesis miner: %w", err) return xerrors.Errorf("failed to start up genesis miner: %w", err)

2
extern/filecoin-ffi vendored

@ -1 +1 @@
Subproject commit 0a1990fdd9a08b122cf97b373413b688d5b429cf Subproject commit e899cc1dd0720e0a4d25b0e751b84e3733cbedc5

View File

@ -27,6 +27,7 @@ func main() {
types.MessageReceipt{}, types.MessageReceipt{},
types.BlockMsg{}, types.BlockMsg{},
types.ExpTipSet{}, types.ExpTipSet{},
types.BeaconEntry{},
) )
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)

View File

@ -14,6 +14,7 @@ import (
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "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/gen"
"github.com/filecoin-project/lotus/chain/types" "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 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) arc, err := lru.NewARC(10000)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return &Miner{ return &Miner{
api: api, api: api,
epp: epp, epp: epp,
beacon: beacon,
waitFunc: func(ctx context.Context, baseTime uint64) error { waitFunc: func(ctx context.Context, baseTime uint64) error {
// Wait around for half the block time in case other parents come in // Wait around for half the block time in case other parents come in
deadline := baseTime + build.PropagationDelay deadline := baseTime + build.PropagationDelay
@ -49,7 +51,8 @@ func NewMiner(api api.FullNode, epp gen.ElectionPoStProver) *Miner {
type Miner struct { type Miner struct {
api api.FullNode api api.FullNode
epp gen.ElectionPoStProver epp gen.ElectionPoStProver
beacon beacon.DrandBeacon
lk sync.Mutex lk sync.Mutex
addresses []address.Address 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())) log.Debugw("attempting to mine a block", "tipset", types.LogCids(base.ts.Cids()))
start := time.Now() 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) hasPower, err := m.hasPower(ctx, addr, base.ts)
if err != nil { if err != nil {
return nil, xerrors.Errorf("checking if miner is slashed: %w", err) 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) 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 { if err != nil {
return nil, xerrors.Errorf("failed to check if we win next round: %w", err) 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) 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 { if err != nil {
return nil, xerrors.Errorf("failed to create block: %w", err) 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 }, 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) msgs, err := SelectMessages(context.TODO(), m.api.StateGetActor, base.ts, pending)
if err != nil { if err != nil {
return nil, xerrors.Errorf("message filtering failed: %w", err) 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 nheight := base.ts.Height() + base.nullRounds + 1
// why even return this? that api call could just submit it for us // 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) type ActorLookup func(context.Context, address.Address, types.TipSetKey) (*types.Actor, error)

View File

@ -5,6 +5,7 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/beacon"
"github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/gen"
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
) )
@ -16,7 +17,9 @@ func NewTestMiner(nextCh <-chan struct{}, addr address.Address) func(api.FullNod
panic(err) panic(err)
} }
beacon := beacon.NewMockBeacon(0)
m := &Miner{ m := &Miner{
beacon: beacon,
api: api, api: api,
waitFunc: chanWaiter(nextCh), waitFunc: chanWaiter(nextCh),
epp: epp, epp: epp,

View File

@ -29,6 +29,7 @@ import (
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain" "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/blocksync"
"github.com/filecoin-project/lotus/chain/gen" "github.com/filecoin-project/lotus/chain/gen"
"github.com/filecoin-project/lotus/chain/market" "github.com/filecoin-project/lotus/chain/market"
@ -255,6 +256,7 @@ func Online() Option {
Override(new(storagemarket.StorageClientNode), storageadapter.NewClientNodeAdapter), Override(new(storagemarket.StorageClientNode), storageadapter.NewClientNodeAdapter),
Override(RegisterClientValidatorKey, modules.RegisterClientValidator), Override(RegisterClientValidatorKey, modules.RegisterClientValidator),
Override(RunDealClientKey, modules.RunDealClient), Override(RunDealClientKey, modules.RunDealClient),
Override(new(beacon.DrandBeacon), modules.RandomBeacon),
Override(new(*paychmgr.Store), paychmgr.NewStore), Override(new(*paychmgr.Store), paychmgr.NewStore),
Override(new(*paychmgr.Manager), paychmgr.NewManager), Override(new(*paychmgr.Manager), paychmgr.NewManager),

View File

@ -269,13 +269,12 @@ func (a *StateAPI) MinerGetBaseInfo(ctx context.Context, maddr address.Address,
return stmgr.MinerGetBaseInfo(ctx, a.StateManager, tsk, maddr) 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, bvals []*types.BeaconEntry, 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, msgs []*types.SignedMessage, height abi.ChainEpoch, ts uint64) (*types.BlockMsg, error) {
parents, err := a.Chain.GetTipSetFromKey(parentsTSK) parents, err := a.Chain.GetTipSetFromKey(parentsTSK)
if err != nil { if err != nil {
return nil, xerrors.Errorf("loading tipset %s: %w", parentsTSK, err) 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 { if err != nil {
return nil, err return nil, err
} }

View File

@ -19,6 +19,7 @@ import (
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/lotus/chain" "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/blocksync"
"github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/stmgr" "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 return netName, err
} }
func NewSyncer(lc fx.Lifecycle, sm *stmgr.StateManager, bsync *blocksync.BlockSync, h host.Host) (*chain.Syncer, error) { 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()) syncer, err := chain.NewSyncer(sm, bsync, h.ConnManager(), h.ID(), beacon)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,6 +2,8 @@ package modules
import ( import (
"context" "context"
"fmt"
"time"
eventbus "github.com/libp2p/go-eventbus" eventbus "github.com/libp2p/go-eventbus"
event "github.com/libp2p/go-libp2p-core/event" 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/go-fil-markets/storagemarket"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain" "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/blocksync"
"github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/sub" "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 { func RetrievalResolver(l *discovery.Local) retrievalmarket.PeerResolver {
return discovery.Multi(l) return discovery.Multi(l)
} }
func RandomBeacon() beacon.DrandBeacon {
fmt.Println("RANDOM BEACON!")
return beacon.NewMockBeacon(build.BlockDelay * time.Second)
}

View File

@ -43,6 +43,7 @@ import (
lapi "github.com/filecoin-project/lotus/api" lapi "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "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/gen"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/markets/retrievaladapter" "github.com/filecoin-project/lotus/markets/retrievaladapter"
@ -257,13 +258,13 @@ func StagingGraphsync(mctx helpers.MetricsCtx, lc fx.Lifecycle, ibs dtypes.Stagi
return gs 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) minerAddr, err := minerAddrFromDS(ds)
if err != nil { if err != nil {
return nil, err return nil, err
} }
m := miner.NewMiner(api, epp) m := miner.NewMiner(api, epp, beacon)
lc.Append(fx.Hook{ lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error { OnStart: func(ctx context.Context) error {