Refactor mining logic to match specs ticket chain

This commit is contained in:
whyrusleeping 2019-11-19 09:53:00 -06:00
parent d680dab52e
commit f8eabd3db4
26 changed files with 166 additions and 216 deletions

View File

@ -23,7 +23,7 @@ type FullNode interface {
// First message is guaranteed to be of len == 1, and type == 'current' // First message is guaranteed to be of len == 1, and type == 'current'
ChainNotify(context.Context) (<-chan []*store.HeadChange, error) ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
ChainHead(context.Context) (*types.TipSet, error) ChainHead(context.Context) (*types.TipSet, error)
ChainGetRandomness(context.Context, types.TipSetKey, []*types.Ticket, int) ([]byte, error) ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error) ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error)
ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error) ChainGetTipSet(context.Context, types.TipSetKey) (*types.TipSet, error)
ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error) ChainGetBlockMessages(context.Context, cid.Cid) (*BlockMessages, error)
@ -52,7 +52,7 @@ type FullNode interface {
MinerRegister(context.Context, address.Address) error MinerRegister(context.Context, address.Address) error
MinerUnregister(context.Context, address.Address) error MinerUnregister(context.Context, address.Address) error
MinerAddresses(context.Context) ([]address.Address, error) MinerAddresses(context.Context) ([]address.Address, error)
MinerCreateBlock(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage, uint64) (*types.BlockMsg, error) MinerCreateBlock(context.Context, address.Address, *types.TipSet, *types.Ticket, types.ElectionProof, []*types.SignedMessage, uint64, uint64) (*types.BlockMsg, error)
// // UX ? // // UX ?

View File

@ -37,19 +37,19 @@ type FullNodeStruct struct {
CommonStruct CommonStruct
Internal struct { Internal struct {
ChainNotify func(context.Context) (<-chan []*store.HeadChange, error) `perm:"read"` ChainNotify func(context.Context) (<-chan []*store.HeadChange, error) `perm:"read"`
ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"` ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"`
ChainGetRandomness func(context.Context, types.TipSetKey, []*types.Ticket, int) ([]byte, error) `perm:"read"` ChainGetRandomness func(context.Context, types.TipSetKey, int64) ([]byte, error) `perm:"read"`
ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"` ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"`
ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"` ChainGetTipSet func(context.Context, types.TipSetKey) (*types.TipSet, error) `perm:"read"`
ChainGetBlockMessages func(context.Context, cid.Cid) (*BlockMessages, error) `perm:"read"` ChainGetBlockMessages func(context.Context, cid.Cid) (*BlockMessages, error) `perm:"read"`
ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"` ChainGetParentReceipts func(context.Context, cid.Cid) ([]*types.MessageReceipt, error) `perm:"read"`
ChainGetParentMessages func(context.Context, cid.Cid) ([]Message, error) `perm:"read"` ChainGetParentMessages func(context.Context, cid.Cid) ([]Message, error) `perm:"read"`
ChainGetTipSetByHeight func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) `perm:"read"` ChainGetTipSetByHeight func(context.Context, uint64, *types.TipSet) (*types.TipSet, error) `perm:"read"`
ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"` ChainReadObj func(context.Context, cid.Cid) ([]byte, error) `perm:"read"`
ChainSetHead func(context.Context, *types.TipSet) error `perm:"admin"` ChainSetHead func(context.Context, *types.TipSet) error `perm:"admin"`
ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"` ChainGetGenesis func(context.Context) (*types.TipSet, error) `perm:"read"`
ChainTipSetWeight func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"` ChainTipSetWeight func(context.Context, *types.TipSet) (types.BigInt, error) `perm:"read"`
SyncState func(context.Context) (*SyncState, error) `perm:"read"` SyncState func(context.Context) (*SyncState, error) `perm:"read"`
SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"` SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"`
@ -58,10 +58,10 @@ type FullNodeStruct struct {
MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"` MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"`
MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"` MpoolPushMessage func(context.Context, *types.Message) (*types.SignedMessage, error) `perm:"sign"`
MinerRegister func(context.Context, address.Address) error `perm:"admin"` MinerRegister func(context.Context, address.Address) error `perm:"admin"`
MinerUnregister func(context.Context, address.Address) error `perm:"admin"` MinerUnregister func(context.Context, address.Address) error `perm:"admin"`
MinerAddresses func(context.Context) ([]address.Address, error) `perm:"write"` MinerAddresses func(context.Context) ([]address.Address, error) `perm:"write"`
MinerCreateBlock func(context.Context, address.Address, *types.TipSet, []*types.Ticket, types.ElectionProof, []*types.SignedMessage, uint64) (*types.BlockMsg, error) `perm:"write"` MinerCreateBlock func(context.Context, address.Address, *types.TipSet, *types.Ticket, types.ElectionProof, []*types.SignedMessage, uint64, uint64) (*types.BlockMsg, error) `perm:"write"`
WalletNew func(context.Context, string) (address.Address, error) `perm:"write"` WalletNew func(context.Context, string) (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"`
@ -237,16 +237,16 @@ func (c *FullNodeStruct) MinerAddresses(ctx context.Context) ([]address.Address,
return c.Internal.MinerAddresses(ctx) return c.Internal.MinerAddresses(ctx)
} }
func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, tickets []*types.Ticket, eproof types.ElectionProof, msgs []*types.SignedMessage, ts uint64) (*types.BlockMsg, error) { func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, ticket *types.Ticket, eproof types.ElectionProof, msgs []*types.SignedMessage, height, ts uint64) (*types.BlockMsg, error) {
return c.Internal.MinerCreateBlock(ctx, addr, base, tickets, eproof, msgs, ts) return c.Internal.MinerCreateBlock(ctx, addr, base, ticket, eproof, msgs, height, ts)
} }
func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) { func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) {
return c.Internal.ChainHead(ctx) return c.Internal.ChainHead(ctx)
} }
func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, ticks []*types.Ticket, lb int) ([]byte, error) { func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, round int64) ([]byte, error) {
return c.Internal.ChainGetRandomness(ctx, pts, ticks, lb) return c.Internal.ChainGetRandomness(ctx, pts, round)
} }
func (c *FullNodeStruct) ChainGetTipSetByHeight(ctx context.Context, h uint64, ts *types.TipSet) (*types.TipSet, error) { func (c *FullNodeStruct) ChainGetTipSetByHeight(ctx context.Context, h uint64, ts *types.TipSet) (*types.TipSet, error) {

View File

@ -5,7 +5,7 @@ import (
"io" "io"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
cid "github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors" xerrors "golang.org/x/xerrors"
) )

View File

@ -52,7 +52,7 @@ func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
Height: h, Height: h,
Miner: a, Miner: a,
Tickets: []*types.Ticket{{[]byte{byte(h % 2)}}}, Ticket: &types.Ticket{[]byte{byte(h % 2)}},
ParentStateRoot: dummyCid, ParentStateRoot: dummyCid,
Messages: msgcid, Messages: msgcid,
@ -65,7 +65,7 @@ func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet {
Height: h, Height: h,
Miner: b, Miner: b,
Tickets: []*types.Ticket{{[]byte{byte((h + 1) % 2)}}}, Ticket: &types.Ticket{[]byte{byte((h + 1) % 2)}},
ParentStateRoot: dummyCid, ParentStateRoot: dummyCid,
Messages: msgcid, Messages: msgcid,

View File

@ -3,6 +3,8 @@ package gen
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/sha256"
"encoding/binary"
"fmt" "fmt"
"sync/atomic" "sync/atomic"
@ -46,7 +48,7 @@ type ChainGen struct {
genesis *types.BlockHeader genesis *types.BlockHeader
CurTipset *store.FullTipSet CurTipset *store.FullTipSet
Timestamper func(*types.TipSet, int) uint64 Timestamper func(*types.TipSet, uint64) uint64
w *wallet.Wallet w *wallet.Wallet
@ -195,14 +197,9 @@ func (cg *ChainGen) GenesisCar() ([]byte, error) {
return out.Bytes(), nil return out.Bytes(), nil
} }
func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, ticks []*types.Ticket) (types.ElectionProof, *types.Ticket, error) { func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m address.Address, round int64) (types.ElectionProof, *types.Ticket, error) {
var lastTicket *types.Ticket lastTicket := pts.MinTicket()
if len(ticks) == 0 {
lastTicket = pts.MinTicket()
} else {
lastTicket = ticks[len(ticks)-1]
}
st := pts.ParentState() st := pts.ParentState()
@ -211,7 +208,8 @@ func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m add
return nil, nil, xerrors.Errorf("get miner worker: %w", err) return nil, nil, xerrors.Errorf("get miner worker: %w", err)
} }
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, lastTicket.VRFProof) vrfBase := TicketHash(lastTicket, uint64(round))
vrfout, err := ComputeVRF(ctx, cg.w.Sign, worker, vrfBase)
if err != nil { if err != nil {
return nil, nil, xerrors.Errorf("compute VRF: %w", err) return nil, nil, xerrors.Errorf("compute VRF: %w", err)
} }
@ -220,7 +218,7 @@ func (cg *ChainGen) nextBlockProof(ctx context.Context, pts *types.TipSet, m add
VRFProof: vrfout, VRFProof: vrfout,
} }
win, eproof, err := IsRoundWinner(ctx, pts, append(ticks, tick), m, &mca{w: cg.w, sm: cg.sm}) win, eproof, err := IsRoundWinner(ctx, pts, round, m, &mca{w: cg.w, sm: cg.sm})
if err != nil { if err != nil {
return nil, nil, xerrors.Errorf("checking round winner failed: %w", err) return nil, nil, xerrors.Errorf("checking round winner failed: %w", err)
} }
@ -248,23 +246,21 @@ func (cg *ChainGen) NextTipSet() (*MinedTipSet, error) {
func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Address) (*MinedTipSet, error) { func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Address) (*MinedTipSet, error) {
var blks []*types.FullBlock var blks []*types.FullBlock
ticketSets := make([][]*types.Ticket, len(miners))
msgs, err := cg.getRandomMessages() msgs, err := cg.getRandomMessages()
if err != nil { if err != nil {
return nil, xerrors.Errorf("get random messages: %w", err) return nil, xerrors.Errorf("get random messages: %w", err)
} }
for len(blks) == 0 { for round := int64(base.Height() + 1); len(blks) == 0; round++ {
for i, m := range miners { for _, m := range miners {
proof, t, err := cg.nextBlockProof(context.TODO(), base, m, ticketSets[i]) proof, t, err := cg.nextBlockProof(context.TODO(), base, m, round)
if err != nil { if err != nil {
return nil, xerrors.Errorf("next block proof: %w", err) return nil, xerrors.Errorf("next block proof: %w", err)
} }
ticketSets[i] = append(ticketSets[i], t)
if proof != nil { if proof != nil {
fblk, err := cg.makeBlock(base, m, proof, ticketSets[i], msgs) fblk, err := cg.makeBlock(base, m, proof, t, uint64(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)
} }
@ -286,16 +282,16 @@ func (cg *ChainGen) NextTipSetFromMiners(base *types.TipSet, miners []address.Ad
}, nil }, nil
} }
func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof types.ElectionProof, tickets []*types.Ticket, msgs []*types.SignedMessage) (*types.FullBlock, error) { func (cg *ChainGen) makeBlock(parents *types.TipSet, m address.Address, eproof types.ElectionProof, ticket *types.Ticket, height uint64, msgs []*types.SignedMessage) (*types.FullBlock, error) {
var ts uint64 var ts uint64
if cg.Timestamper != nil { if cg.Timestamper != nil {
ts = cg.Timestamper(parents, len(tickets)) ts = cg.Timestamper(parents, height-parents.Height())
} else { } else {
ts = parents.MinTimestamp() + (uint64(len(tickets)) * build.BlockDelay) ts = parents.MinTimestamp() + ((height - parents.Height()) * build.BlockDelay)
} }
fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, tickets, eproof, msgs, ts) fblk, err := MinerCreateBlock(context.TODO(), cg.sm, cg.w, m, parents, ticket, eproof, msgs, height, ts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -354,7 +350,7 @@ func (cg *ChainGen) YieldRepo() (repo.Repo, error) {
} }
type MiningCheckAPI interface { type MiningCheckAPI interface {
ChainGetRandomness(context.Context, types.TipSetKey, []*types.Ticket, int) ([]byte, error) ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
StateMinerPower(context.Context, address.Address, *types.TipSet) (api.MinerPower, error) StateMinerPower(context.Context, address.Address, *types.TipSet) (api.MinerPower, error)
@ -368,8 +364,8 @@ type mca struct {
sm *stmgr.StateManager sm *stmgr.StateManager
} }
func (mca mca) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, ticks []*types.Ticket, lb int) ([]byte, error) { func (mca mca) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, lb int64) ([]byte, error) {
return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), ticks, int64(lb)) return mca.sm.ChainStore().GetRandomness(ctx, pts.Cids(), int64(lb))
} }
func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) { func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) {
@ -392,8 +388,8 @@ func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*ty
return mca.w.Sign(ctx, a, v) return mca.w.Sign(ctx, a, v)
} }
func IsRoundWinner(ctx context.Context, ts *types.TipSet, ticks []*types.Ticket, miner address.Address, a MiningCheckAPI) (bool, types.ElectionProof, error) { func IsRoundWinner(ctx context.Context, ts *types.TipSet, round int64, miner address.Address, a MiningCheckAPI) (bool, types.ElectionProof, error) {
r, err := a.ChainGetRandomness(ctx, ts.Key(), ticks, build.EcRandomnessLookback) r, err := a.ChainGetRandomness(ctx, ts.Key(), round-build.EcRandomnessLookback)
if err != nil { if err != nil {
return false, nil, xerrors.Errorf("chain get randomness: %w", err) return false, nil, xerrors.Errorf("chain get randomness: %w", err)
} }
@ -430,3 +426,12 @@ func ComputeVRF(ctx context.Context, sign SignFunc, w address.Address, input []b
return sig.Data, nil return sig.Data, nil
} }
func TicketHash(t *types.Ticket, round uint64) []byte {
h := sha256.New()
h.Write(t.VRFProof)
var roundbuf [8]byte
binary.LittleEndian.PutUint64(roundbuf[:], round)
h.Write(roundbuf[:])
return h.Sum(nil)
}

View File

@ -12,18 +12,12 @@ func testGeneration(t testing.TB, n int, msgs int) {
g.msgsPerBlock = msgs g.msgsPerBlock = msgs
var height int
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
mts, err := g.NextTipSet() mts, err := g.NextTipSet()
if err != nil { if err != nil {
t.Fatalf("error at H:%d, %s", i, err) t.Fatalf("error at H:%d, %s", i, err)
} }
_ = mts
ts := mts.TipSet.TipSet()
if ts.Height() != uint64(height+len(ts.Blocks()[0].Tickets)) {
t.Fatal("wrong height", ts.Height(), i, len(ts.Blocks()[0].Tickets), len(ts.Blocks()))
}
height += len(ts.Blocks()[0].Tickets)
} }
} }

View File

@ -18,14 +18,12 @@ 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, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, 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.ElectionProof, msgs []*types.SignedMessage, height, 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)
} }
height := parents.Height() + uint64(len(tickets))
worker, err := stmgr.GetMinerWorkerRaw(ctx, sm, st, miner) worker, err := stmgr.GetMinerWorkerRaw(ctx, sm, st, miner)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to get miner worker: %w", err) return nil, xerrors.Errorf("failed to get miner worker: %w", err)
@ -34,7 +32,7 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal
next := &types.BlockHeader{ next := &types.BlockHeader{
Miner: miner, Miner: miner,
Parents: parents.Cids(), Parents: parents.Cids(),
Tickets: tickets, Ticket: ticket,
Height: height, Height: height,
Timestamp: timestamp, Timestamp: timestamp,
ElectionProof: proof, ElectionProof: proof,

View File

@ -384,7 +384,7 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B
b := &types.BlockHeader{ b := &types.BlockHeader{
Miner: actors.InitAddress, Miner: actors.InitAddress,
Tickets: []*types.Ticket{genesisticket}, Ticket: genesisticket,
ElectionProof: []byte("the Genesis block"), ElectionProof: []byte("the Genesis block"),
Parents: []cid.Cid{}, Parents: []cid.Cid{},
Height: 0, Height: 0,

View File

@ -68,7 +68,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
state := ts.ParentState() state := ts.ParentState()
r := store.NewChainRand(sm.cs, ts.Cids(), ts.Height(), nil) r := store.NewChainRand(sm.cs, ts.Cids(), ts.Height())
return sm.CallRaw(ctx, msg, state, r, ts.Height()) return sm.CallRaw(ctx, msg, state, r, ts.Height())
} }

View File

@ -102,7 +102,7 @@ func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.Bl
cids[i] = v.Cid() cids[i] = v.Cid()
} }
r := store.NewChainRand(sm.cs, cids, blks[0].Height, nil) r := store.NewChainRand(sm.cs, cids, blks[0].Height)
vmi, err := vm.NewVM(pstate, blks[0].Height, r, address.Undef, sm.cs.Blockstore()) vmi, err := vm.NewVM(pstate, blks[0].Height, r, address.Undef, sm.cs.Blockstore())
if err != nil { if err != nil {

View File

@ -3,8 +3,8 @@ package store
import ( import (
"context" "context"
"crypto/sha256" "crypto/sha256"
"encoding/binary"
"encoding/json" "encoding/json"
"fmt"
"sync" "sync"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
@ -12,7 +12,6 @@ import (
"github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/vm"
"go.opencensus.io/trace" "go.opencensus.io/trace"
"go.uber.org/zap"
amt "github.com/filecoin-project/go-amt-ipld" amt "github.com/filecoin-project/go-amt-ipld"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
@ -771,24 +770,21 @@ func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) {
return NewFullTipSet(out), nil return NewFullTipSet(out), nil
} }
func (cs *ChainStore) GetRandomness(ctx context.Context, blks []cid.Cid, tickets []*types.Ticket, lb int64) ([]byte, error) { func ticketHash(t *types.Ticket, round int64) []byte {
h := sha256.New()
var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], uint64(round))
h.Write(t.VRFProof)
h.Write(buf[:])
return h.Sum(nil)
}
func (cs *ChainStore) GetRandomness(ctx context.Context, blks []cid.Cid, round int64) ([]byte, error) {
ctx, span := trace.StartSpan(ctx, "store.GetRandomness") ctx, span := trace.StartSpan(ctx, "store.GetRandomness")
defer span.End() defer span.End()
span.AddAttributes(trace.Int64Attribute("lb", lb)) span.AddAttributes(trace.Int64Attribute("round", round))
if lb < 0 {
return nil, fmt.Errorf("negative lookback parameters are not valid (got %d)", lb)
}
lt := int64(len(tickets))
if lb < lt {
log.Desugar().Warn("self sampling randomness. this should be extremely rare, if you see this often it may be a bug", zap.Stack("stacktrace"))
t := tickets[lt-(1+lb)]
return t.VRFProof, nil
}
nv := lb - lt
for { for {
nts, err := cs.LoadTipSet(blks) nts, err := cs.LoadTipSet(blks)
@ -797,26 +793,21 @@ func (cs *ChainStore) GetRandomness(ctx context.Context, blks []cid.Cid, tickets
} }
mtb := nts.MinTicketBlock() mtb := nts.MinTicketBlock()
lt := int64(len(mtb.Tickets))
if nv < lt {
t := mtb.Tickets[lt-(1+nv)]
return t.VRFProof, nil
}
nv -= lt if int64(nts.Height()) <= round {
return ticketHash(nts.MinTicketBlock().Ticket, round), nil
}
// special case for lookback behind genesis block // special case for lookback behind genesis block
// TODO(spec): this is not in the spec, need to sync that // TODO(spec): this is not in the spec, need to sync that
if mtb.Height == 0 { if mtb.Height == 0 {
t := mtb.Tickets[0] // round is negative
thash := ticketHash(mtb.Ticket, round*-1)
rval := t.VRFProof // for negative lookbacks, just use the hash of the positive tickethash value
for i := int64(0); i < nv; i++ { h := sha256.Sum256(thash)
h := sha256.Sum256(rval) return h[:], nil
rval = h[:]
}
return rval, nil
} }
blks = mtb.Parents blks = mtb.Parents
@ -837,36 +828,33 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h uint64, ts *types
} }
for { for {
mtb := ts.MinTicketBlock()
if h >= ts.Height()-uint64(len(mtb.Tickets)) {
return ts, nil
}
pts, err := cs.LoadTipSet(ts.Parents()) pts, err := cs.LoadTipSet(ts.Parents())
if err != nil { if err != nil {
return nil, err return nil, err
} }
if h > pts.Height() {
return ts, nil
}
ts = pts ts = pts
} }
} }
type chainRand struct { type chainRand struct {
cs *ChainStore cs *ChainStore
blks []cid.Cid blks []cid.Cid
bh uint64 bh uint64
tickets []*types.Ticket
} }
func NewChainRand(cs *ChainStore, blks []cid.Cid, bheight uint64, tickets []*types.Ticket) vm.Rand { func NewChainRand(cs *ChainStore, blks []cid.Cid, bheight uint64) vm.Rand {
return &chainRand{ return &chainRand{
cs: cs, cs: cs,
blks: blks, blks: blks,
bh: bheight, bh: bheight,
tickets: tickets,
} }
} }
func (cr *chainRand) GetRandomness(ctx context.Context, h int64) ([]byte, error) { func (cr *chainRand) GetRandomness(ctx context.Context, round int64) ([]byte, error) {
lb := (int64(cr.bh) + int64(len(cr.tickets))) - h return cr.cs.GetRandomness(ctx, cr.blks, round)
return cr.cs.GetRandomness(ctx, cr.blks, cr.tickets, lb)
} }

View File

@ -2,12 +2,13 @@ package store
import ( import (
"context" "context"
"math/big"
"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/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/chain/vm"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"math/big"
) )
var zero = types.NewInt(0) var zero = types.NewInt(0)
@ -57,7 +58,7 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn
func (cs *ChainStore) call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) { func (cs *ChainStore) call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) {
bstate := ts.ParentState() bstate := ts.ParentState()
r := NewChainRand(cs, ts.Cids(), ts.Height(), nil) r := NewChainRand(cs, ts.Cids(), ts.Height())
vmi, err := vm.NewVM(bstate, ts.Height(), r, actors.NetworkAddress, cs.bs) vmi, err := vm.NewVM(bstate, ts.Height(), r, actors.NetworkAddress, cs.bs)
if err != nil { if err != nil {

View File

@ -27,6 +27,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/address" "github.com/filecoin-project/lotus/chain/address"
"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/state" "github.com/filecoin-project/lotus/chain/state"
"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"
@ -466,30 +467,22 @@ func (syncer *Syncer) minerIsValid(ctx context.Context, maddr address.Address, b
return nil return nil
} }
func (syncer *Syncer) validateTickets(ctx context.Context, mworker address.Address, tickets []*types.Ticket, base *types.TipSet) error { func (syncer *Syncer) validateTicket(ctx context.Context, mworker address.Address, ticket *types.Ticket, base *types.TipSet, round uint64) error {
ctx, span := trace.StartSpan(ctx, "validateTickets") ctx, span := trace.StartSpan(ctx, "validateTickets")
defer span.End() defer span.End()
span.AddAttributes(trace.Int64Attribute("tickets", int64(len(tickets))))
if len(tickets) == 0 { sig := &types.Signature{
return xerrors.Errorf("block had no tickets") Type: types.KTBLS,
Data: ticket.VRFProof,
} }
cur := base.MinTicket() vrfBase := gen.TicketHash(base.MinTicket(), round)
for i := 0; i < len(tickets); i++ {
next := tickets[i]
sig := &types.Signature{ log.Infof("about to verify ticket: %x %d", base.MinTicket().VRFProof, round)
Type: types.KTBLS,
Data: next.VRFProof,
}
// TODO: ticket signatures should also include miner address // TODO: ticket signatures should also include miner address
if err := sig.Verify(mworker, cur.VRFProof); err != nil { if err := sig.Verify(mworker, vrfBase); err != nil {
return xerrors.Errorf("invalid ticket, VRFProof invalid: %w", err) return xerrors.Errorf("invalid ticket, VRFProof invalid: %w", err)
}
cur = next
} }
return nil return nil
@ -514,9 +507,9 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
return xerrors.Errorf("block was from the future") return xerrors.Errorf("block was from the future")
} }
if h.Timestamp < baseTs.MinTimestamp()+uint64(build.BlockDelay*len(h.Tickets)) { if h.Timestamp < baseTs.MinTimestamp()+(build.BlockDelay*(h.Height-baseTs.Height())) {
log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), len(h.Tickets)) log.Warn("timestamp funtimes: ", h.Timestamp, baseTs.MinTimestamp(), h.Height, baseTs.Height())
return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * tkts.len:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, len(h.Tickets)) return xerrors.Errorf("block was generated too soon (h.ts:%d < base.mints:%d + BLOCK_DELAY:%d * deltaH:%d)", h.Timestamp, baseTs.MinTimestamp(), build.BlockDelay, h.Height-baseTs.Height())
} }
winnerCheck := async.Err(func() error { winnerCheck := async.Err(func() error {
@ -583,14 +576,14 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err
}) })
tktsCheck := async.Err(func() error { tktsCheck := async.Err(func() error {
if err := syncer.validateTickets(ctx, waddr, h.Tickets, baseTs); err != nil { if err := syncer.validateTicket(ctx, waddr, h.Ticket, baseTs, h.Height); err != nil {
return xerrors.Errorf("validating block tickets failed: %w", err) return xerrors.Errorf("validating block tickets failed: %w", err)
} }
return nil return nil
}) })
eproofCheck := async.Err(func() error { eproofCheck := async.Err(func() error {
rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), h.Tickets, build.EcRandomnessLookback) rand, err := syncer.sm.ChainStore().GetRandomness(ctx, baseTs.Cids(), int64(h.Height-build.EcRandomnessLookback))
if err != nil { if err != nil {
return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err) return xerrors.Errorf("failed to get randomness for verifying election proof: %w", err)
} }

View File

@ -390,7 +390,7 @@ func TestSyncBadTimestamp(t *testing.T) {
tu.waitUntilSync(0, client) tu.waitUntilSync(0, client)
base := tu.g.CurTipset base := tu.g.CurTipset
tu.g.Timestamper = func(pts *types.TipSet, tl int) uint64 { tu.g.Timestamper = func(pts *types.TipSet, tl uint64) uint64 {
return pts.MinTimestamp() + (build.BlockDelay / 2) return pts.MinTimestamp() + (build.BlockDelay / 2)
} }

View File

@ -25,7 +25,7 @@ type ElectionProof []byte
type BlockHeader struct { type BlockHeader struct {
Miner address.Address Miner address.Address
Tickets []*Ticket Ticket *Ticket
ElectionProof []byte ElectionProof []byte
@ -91,7 +91,7 @@ func (blk *BlockHeader) Serialize() ([]byte, error) {
} }
func (blk *BlockHeader) LastTicket() *Ticket { func (blk *BlockHeader) LastTicket() *Ticket {
return blk.Tickets[len(blk.Tickets)-1] return blk.Ticket
} }
func (blk *BlockHeader) SigningBytes() ([]byte, error) { func (blk *BlockHeader) SigningBytes() ([]byte, error) {

View File

@ -25,10 +25,8 @@ func testBlockHeader(t testing.TB) *BlockHeader {
return &BlockHeader{ return &BlockHeader{
Miner: addr, Miner: addr,
ElectionProof: []byte("cats won the election"), ElectionProof: []byte("cats won the election"),
Tickets: []*Ticket{ Ticket: &Ticket{
&Ticket{ VRFProof: []byte("vrf proof0000000vrf proof0000000"),
VRFProof: []byte("vrf proof0000000vrf proof0000000"),
},
}, },
Parents: []cid.Cid{c, c}, Parents: []cid.Cid{c, c},
ParentMessageReceipts: c, ParentMessageReceipts: c,

View File

@ -5,7 +5,7 @@ import (
"io" "io"
"math" "math"
cid "github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
xerrors "golang.org/x/xerrors" xerrors "golang.org/x/xerrors"
) )
@ -28,15 +28,10 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error {
return err return err
} }
// t.t.Tickets ([]*types.Ticket) (slice) // t.t.Ticket (types.Ticket) (struct)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajArray, uint64(len(t.Tickets)))); err != nil { if err := t.Ticket.MarshalCBOR(w); err != nil {
return err return err
} }
for _, v := range t.Tickets {
if err := v.MarshalCBOR(w); err != nil {
return err
}
}
// t.t.ElectionProof ([]uint8) (slice) // t.t.ElectionProof ([]uint8) (slice)
if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.ElectionProof)))); err != nil { if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajByteString, uint64(len(t.ElectionProof)))); err != nil {
@ -125,32 +120,27 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) error {
} }
} }
// t.t.Tickets ([]*types.Ticket) (slice) // t.t.Ticket (types.Ticket) (struct)
maj, extra, err = cbg.CborReadHeader(br) {
if err != nil {
return err
}
if extra > 8192 {
return fmt.Errorf("t.Tickets: array too large (%d)", extra)
}
if maj != cbg.MajArray { pb, err := br.PeekByte()
return fmt.Errorf("expected cbor array") if err != nil {
}
if extra > 0 {
t.Tickets = make([]*Ticket, extra)
}
for i := 0; i < int(extra); i++ {
var v Ticket
if err := v.UnmarshalCBOR(br); err != nil {
return err return err
} }
if pb == cbg.CborNull[0] {
var nbuf [1]byte
if _, err := br.Read(nbuf[:]); err != nil {
return err
}
} else {
t.Ticket = new(Ticket)
if err := t.Ticket.UnmarshalCBOR(br); err != nil {
return err
}
}
t.Tickets[i] = &v
} }
// t.t.ElectionProof ([]uint8) (slice) // t.t.ElectionProof ([]uint8) (slice)
maj, extra, err = cbg.CborReadHeader(br) maj, extra, err = cbg.CborReadHeader(br)

View File

@ -165,8 +165,7 @@ func (t *Ticket) Less(o *Ticket) bool {
} }
func (ts *TipSet) MinTicket() *Ticket { func (ts *TipSet) MinTicket() *Ticket {
b := ts.MinTicketBlock() return ts.MinTicketBlock().Ticket
return b.Tickets[len(b.Tickets)-1]
} }
func (ts *TipSet) MinTimestamp() uint64 { func (ts *TipSet) MinTimestamp() uint64 {

View File

@ -161,8 +161,8 @@ eventLoop:
log.Errorf("failed to get best mining candidate: %s", err) log.Errorf("failed to get best mining candidate: %s", err)
continue continue
} }
if base.ts.Equals(lastBase.ts) && len(lastBase.tickets) == len(base.tickets) { if base.ts.Equals(lastBase.ts) && lastBase.nullRounds == base.nullRounds {
log.Errorf("BestMiningCandidate from the previous round: %s (tkts:%d)", lastBase.ts.Cids(), len(lastBase.tickets)) log.Errorf("BestMiningCandidate from the previous round: %s (nulls:%d)", lastBase.ts.Cids(), lastBase.nullRounds)
time.Sleep(build.BlockDelay * time.Second) time.Sleep(build.BlockDelay * time.Second)
continue continue
} }
@ -205,15 +205,15 @@ eventLoop:
} }
} }
} else { } else {
nextRound := time.Unix(int64(base.ts.MinTimestamp()+uint64(build.BlockDelay*len(base.tickets))), 0) nextRound := time.Unix(int64(base.ts.MinTimestamp()+uint64(build.BlockDelay*base.nullRounds)), 0)
time.Sleep(time.Until(nextRound)) time.Sleep(time.Until(nextRound))
} }
} }
} }
type MiningBase struct { type MiningBase struct {
ts *types.TipSet ts *types.TipSet
tickets []*types.Ticket nullRounds uint64
} }
func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error) { func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error) {
@ -253,13 +253,13 @@ func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningB
return nil, errors.Wrap(err, "scratching ticket failed") return nil, errors.Wrap(err, "scratching ticket failed")
} }
win, proof, err := gen.IsRoundWinner(ctx, base.ts, append(base.tickets, ticket), addr, &m.api) win, proof, err := gen.IsRoundWinner(ctx, base.ts, int64(base.ts.Height()+base.nullRounds+1), addr, &m.api)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to check if we win next round") return nil, errors.Wrap(err, "failed to check if we win next round")
} }
if !win { if !win {
m.submitNullTicket(base, ticket) base.nullRounds++
return nil, nil return nil, nil
} }
@ -272,11 +272,6 @@ func (m *Miner) mineOne(ctx context.Context, addr address.Address, base *MiningB
return b, nil return b, nil
} }
func (m *Miner) submitNullTicket(base *MiningBase, ticket *types.Ticket) {
base.tickets = append(base.tickets, ticket)
m.lastWork = base
}
func (m *Miner) computeVRF(ctx context.Context, addr address.Address, input []byte) ([]byte, error) { func (m *Miner) computeVRF(ctx context.Context, addr address.Address, input []byte) ([]byte, error) {
w, err := m.getMinerWorker(ctx, addr, nil) w, err := m.getMinerWorker(ctx, addr, nil)
if err != nil { if err != nil {
@ -309,14 +304,11 @@ func (m *Miner) getMinerWorker(ctx context.Context, addr address.Address, ts *ty
} }
func (m *Miner) scratchTicket(ctx context.Context, addr address.Address, base *MiningBase) (*types.Ticket, error) { func (m *Miner) scratchTicket(ctx context.Context, addr address.Address, base *MiningBase) (*types.Ticket, error) {
var lastTicket *types.Ticket round := base.ts.Height() + base.nullRounds
if len(base.tickets) > 0 {
lastTicket = base.tickets[len(base.tickets)-1]
} else {
lastTicket = base.ts.MinTicket()
}
vrfOut, err := m.computeVRF(ctx, addr, lastTicket.VRFProof) vrfBase := gen.TicketHash(base.ts.MinTicket(), round)
vrfOut, err := m.computeVRF(ctx, addr, vrfBase)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -338,10 +330,12 @@ func (m *Miner) createBlock(base *MiningBase, addr address.Address, ticket *type
return nil, xerrors.Errorf("message filtering failed: %w", err) return nil, xerrors.Errorf("message filtering failed: %w", err)
} }
uts := base.ts.MinTimestamp() + uint64(build.BlockDelay*(len(base.tickets)+1)) uts := base.ts.MinTimestamp() + uint64(build.BlockDelay*(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, append(base.tickets, ticket), proof, msgs, uint64(uts)) return m.api.MinerCreateBlock(context.TODO(), addr, base.ts, ticket, proof, msgs, nheight, uint64(uts))
} }
type actorLookup func(context.Context, address.Address, *types.TipSet) (*types.Actor, error) type actorLookup func(context.Context, address.Address, *types.TipSet) (*types.Actor, error)

View File

@ -28,8 +28,8 @@ func (a *ChainAPI) ChainHead(context.Context) (*types.TipSet, error) {
return a.Chain.GetHeaviestTipSet(), nil return a.Chain.GetHeaviestTipSet(), nil
} }
func (a *ChainAPI) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, tickets []*types.Ticket, lb int) ([]byte, error) { func (a *ChainAPI) ChainGetRandomness(ctx context.Context, pts types.TipSetKey, round int64) ([]byte, error) {
return a.Chain.GetRandomness(ctx, pts.Cids(), tickets, int64(lb)) return a.Chain.GetRandomness(ctx, pts.Cids(), round)
} }
func (a *ChainAPI) ChainGetBlock(ctx context.Context, msg cid.Cid) (*types.BlockHeader, error) { func (a *ChainAPI) ChainGetBlock(ctx context.Context, msg cid.Cid) (*types.BlockHeader, error) {

View File

@ -177,8 +177,8 @@ func (a *StateAPI) StateReadState(ctx context.Context, act *types.Actor, ts *typ
} }
// This is on StateAPI because miner.Miner requires this, and MinerAPI requires miner.Miner // This is on StateAPI because miner.Miner requires this, and MinerAPI requires miner.Miner
func (a *StateAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []*types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, ts uint64) (*types.BlockMsg, error) { func (a *StateAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, ticket *types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage, height, ts uint64) (*types.BlockMsg, error) {
fblk, err := gen.MinerCreateBlock(ctx, a.StateManager, a.Wallet, addr, parents, tickets, proof, msgs, ts) fblk, err := gen.MinerCreateBlock(ctx, a.StateManager, a.Wallet, addr, parents, ticket, proof, msgs, height, ts)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -221,7 +221,7 @@ func SealTicketGen(api api.FullNode) storage.TicketFn {
return nil, xerrors.Errorf("getting head ts for SealTicket failed: %w", err) return nil, xerrors.Errorf("getting head ts for SealTicket failed: %w", err)
} }
r, err := api.ChainGetRandomness(ctx, ts.Key(), nil, build.SealRandomnessLookback) r, err := api.ChainGetRandomness(ctx, ts.Key(), int64(ts.Height())-build.SealRandomnessLookback)
if err != nil { if err != nil {
return nil, xerrors.Errorf("getting randomness for SealTicket failed: %w", err) return nil, xerrors.Errorf("getting randomness for SealTicket failed: %w", err)
} }

View File

@ -64,7 +64,7 @@ type storageMinerApi interface {
ChainHead(context.Context) (*types.TipSet, error) ChainHead(context.Context) (*types.TipSet, error)
ChainNotify(context.Context) (<-chan []*store.HeadChange, error) ChainNotify(context.Context) (<-chan []*store.HeadChange, error)
ChainGetRandomness(context.Context, types.TipSetKey, []*types.Ticket, int) ([]byte, error) ChainGetRandomness(context.Context, types.TipSetKey, int64) ([]byte, error)
ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error) ChainGetTipSetByHeight(context.Context, uint64, *types.TipSet) (*types.TipSet, error)
ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error) ChainGetBlockMessages(context.Context, cid.Cid) (*api.BlockMessages, error)

View File

@ -154,9 +154,8 @@ func (p *post) preparePost(ctx context.Context) error {
p.sset = sset p.sset = sset
// Compute how many blocks back we have to look from the given tipset for the PoSt challenge challengeRound := int64(p.ppe) - int64(build.PoStChallangeTime+build.PoStRandomnessLookback)
challengeLookback := int((int64(p.ts.Height()) - int64(p.ppe)) + int64(build.PoStChallangeTime) + int64(build.PoStRandomnessLookback)) r, err := p.m.api.ChainGetRandomness(ctx, p.ts.Key(), challengeRound)
r, err := p.m.api.ChainGetRandomness(ctx, p.ts.Key(), nil, challengeLookback)
if err != nil { if err != nil {
return xerrors.Errorf("failed to get chain randomness for post (ts=%d; ppe=%d): %w", p.ts.Height(), p.ppe, err) return xerrors.Errorf("failed to get chain randomness for post (ts=%d; ppe=%d): %w", p.ts.Height(), p.ppe, err)
} }

View File

@ -145,7 +145,7 @@ func (m *Miner) preCommitted(ctx context.Context, sector SectorInfo) (func(*Sect
log.Infof("precommit for sector %d made it on chain, will start proof computation at height %d", sector.SectorID, randHeight) log.Infof("precommit for sector %d made it on chain, will start proof computation at height %d", sector.SectorID, randHeight)
err = m.events.ChainAt(func(ctx context.Context, ts *types.TipSet, curH uint64) error { err = m.events.ChainAt(func(ctx context.Context, ts *types.TipSet, curH uint64) error {
rand, err := m.api.ChainGetRandomness(ctx, ts.Key(), nil, int(ts.Height()-randHeight)) rand, err := m.api.ChainGetRandomness(ctx, ts.Key(), int64(randHeight))
if err != nil { if err != nil {
return xerrors.Errorf("failed to get randomness for computing seal proof: %w", err) return xerrors.Errorf("failed to get randomness for computing seal proof: %w", err)
} }

View File

@ -134,12 +134,8 @@ func loadTipsets(ctx context.Context, api api.FullNode, curr *types.TipSet, lowe
log.Printf("Walking back { height:%d }", curr.Height()) log.Printf("Walking back { height:%d }", curr.Height())
tipsets = append(tipsets, curr) tipsets = append(tipsets, curr)
ph := ParentTipsetHeight(curr) tsk := types.NewTipSetKey(curr.Parents()...)
if ph == 0 { prev, err := api.ChainGetTipSet(ctx, tsk)
break
}
prev, err := api.ChainGetTipSetByHeight(ctx, ph, curr)
if err != nil { if err != nil {
return tipsets, err return tipsets, err
} }
@ -154,11 +150,6 @@ func loadTipsets(ctx context.Context, api api.FullNode, curr *types.TipSet, lowe
return tipsets, nil return tipsets, nil
} }
func ParentTipsetHeight(tipset *types.TipSet) uint64 {
mtb := tipset.MinTicketBlock()
return tipset.Height() - uint64(len(mtb.Tickets)) - 1
}
func GetFullNodeAPI(repo string) (api.FullNode, jsonrpc.ClientCloser, error) { func GetFullNodeAPI(repo string) (api.FullNode, jsonrpc.ClientCloser, error) {
addr, headers, err := getAPI(repo) addr, headers, err := getAPI(repo)
if err != nil { if err != nil {