diff --git a/chain/actors/actors_test.go b/chain/actors/actors_test.go index 6611e8c51..5a3be6c13 100644 --- a/chain/actors/actors_test.go +++ b/chain/actors/actors_test.go @@ -51,7 +51,8 @@ func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address) { cs := store.NewChainStore(bs, nil) - vm, err := vm.NewVM(stateroot, 1, maddr, cs) + // TODO: should probabaly mock out the randomness bit, nil works for now + vm, err := vm.NewVM(stateroot, 1, nil, maddr, cs) if err != nil { t.Fatal(err) } diff --git a/chain/actors/harness2_test.go b/chain/actors/harness2_test.go index 55d766a8c..3e08a2bc2 100644 --- a/chain/actors/harness2_test.go +++ b/chain/actors/harness2_test.go @@ -158,7 +158,7 @@ func NewHarness(t *testing.T, options ...HarnessOpt) *Harness { t.Fatal(err) } h.cs = store.NewChainStore(h.bs, nil) - h.vm, err = vm.NewVM(stateroot, 1, h.HI.Miner, h.cs) + h.vm, err = vm.NewVM(stateroot, 1, nil, h.HI.Miner, h.cs) if err != nil { t.Fatal(err) } diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 9a06af5af..e4b700196 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -360,7 +360,7 @@ type mca struct { } func (mca mca) ChainGetRandomness(ctx context.Context, pts *types.TipSet, ticks []*types.Ticket, lb int) ([]byte, error) { - return mca.sm.ChainStore().GetRandomness(ctx, pts, ticks, lb) + return mca.sm.ChainStore().GetRandomness(ctx, pts, ticks, int64(lb)) } func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, ts *types.TipSet) (api.MinerPower, error) { diff --git a/chain/gen/mining.go b/chain/gen/mining.go index 0d484f4ca..3b34e5d56 100644 --- a/chain/gen/mining.go +++ b/chain/gen/mining.go @@ -27,7 +27,8 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal height := parents.Height() + uint64(len(tickets)) - vmi, err := vm.NewVM(st, height, miner, sm.ChainStore()) + r := vm.NewChainRand(sm.ChainStore(), parents, tickets) + vmi, err := vm.NewVM(st, height, r, miner, sm.ChainStore()) if err != nil { return nil, err } diff --git a/chain/gen/utils.go b/chain/gen/utils.go index 7ee1053de..1a1f9fcb4 100644 --- a/chain/gen/utils.go +++ b/chain/gen/utils.go @@ -169,7 +169,7 @@ func mustEnc(i cbg.CBORMarshaler) []byte { } func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, gmcfg *GenMinerCfg) (cid.Cid, error) { - vm, err := vm.NewVM(sroot, 0, actors.NetworkAddress, cs) + vm, err := vm.NewVM(sroot, 0, nil, actors.NetworkAddress, cs) if err != nil { return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err) } diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 607273724..3c5af3079 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -11,8 +11,8 @@ import ( "golang.org/x/xerrors" ) -func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate cid.Cid, bheight uint64) (*types.MessageReceipt, error) { - vmi, err := vm.NewVM(bstate, bheight, actors.NetworkAddress, sm.cs) +func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate cid.Cid, r vm.Rand, bheight uint64) (*types.MessageReceipt, error) { + vmi, err := vm.NewVM(bstate, bheight, r, actors.NetworkAddress, sm.cs) if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } @@ -57,5 +57,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types. return nil, err } - return sm.CallRaw(ctx, msg, state, ts.Height()) + r := vm.NewChainRand(sm.cs, ts, nil) + + return sm.CallRaw(ctx, msg, state, r, ts.Height()) } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 03f14a371..e72e53d3f 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -78,7 +78,9 @@ func (sm *StateManager) computeTipSetState(cids []cid.Cid) (cid.Cid, error) { return cid.Undef, xerrors.Errorf("recursive TipSetState failed: %w", err) } - vmi, err := vm.NewVM(pstate, ts.Height(), address.Undef, sm.cs) + r := vm.NewChainRand(sm.cs, ts, nil) + + vmi, err := vm.NewVM(pstate, ts.Height(), r, address.Undef, sm.cs) if err != nil { return cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err) } diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index a9707acc9..07e6115ec 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -22,7 +22,7 @@ func GetMinerWorker(ctx context.Context, sm *StateManager, st cid.Cid, maddr add To: maddr, From: maddr, Method: actors.MAMethods.GetWorkerAddr, - }, st, 0) + }, st, nil, 0) if err != nil { return address.Undef, xerrors.Errorf("callRaw failed: %w", err) } @@ -48,7 +48,7 @@ func GetMinerOwner(ctx context.Context, sm *StateManager, st cid.Cid, maddr addr To: maddr, From: maddr, Method: actors.MAMethods.GetOwner, - }, st, 0) + }, st, nil, 0) if err != nil { return address.Undef, xerrors.Errorf("callRaw failed: %w", err) } diff --git a/chain/store/store.go b/chain/store/store.go index 204b7de98..2298db7ef 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -701,16 +701,20 @@ func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) { return NewFullTipSet(out), nil } -func (cs *ChainStore) GetRandomness(ctx context.Context, pts *types.TipSet, tickets []*types.Ticket, lb int) ([]byte, error) { - if lb < len(tickets) { +func (cs *ChainStore) GetRandomness(ctx context.Context, pts *types.TipSet, tickets []*types.Ticket, lb int64) ([]byte, error) { + if lb < 0 { + return nil, fmt.Errorf("negative lookback parameters are not valid (got %d)", lb) + } + lt := int64(len(tickets)) + if lb < lt { log.Warn("self sampling randomness. this should be extremely rare, if you see this often it may be a bug") - t := tickets[len(tickets)-(1+lb)] + t := tickets[lt-(1+lb)] return t.VDFResult, nil } - nv := lb - len(tickets) + nv := lb - lt nextCids := pts.Cids() for { @@ -720,13 +724,14 @@ func (cs *ChainStore) GetRandomness(ctx context.Context, pts *types.TipSet, tick } mtb := nts.MinTicketBlock() - if nv < len(mtb.Tickets) { - t := mtb.Tickets[len(mtb.Tickets)-(1+nv)] - log.Infof("Returning randomness: H:%d, t:%d, mtb:%s", nts.Height(), len(mtb.Tickets)-(1+nv), mtb.Cid()) + lt := int64(len(mtb.Tickets)) + if nv < lt { + t := mtb.Tickets[lt-(1+nv)] + log.Infof("Returning randomness: H:%d, t:%d, mtb:%s", nts.Height(), lt-(1+nv), mtb.Cid()) return t.VDFResult, nil } - nv -= len(mtb.Tickets) + nv -= lt // special case for lookback behind genesis block // TODO(spec): this is not in the spec, need to sync that @@ -735,7 +740,7 @@ func (cs *ChainStore) GetRandomness(ctx context.Context, pts *types.TipSet, tick t := mtb.Tickets[0] rval := t.VDFResult - for i := 0; i < nv; i++ { + for i := int64(0); i < nv; i++ { h := sha256.Sum256(rval) rval = h[:] } diff --git a/chain/sync.go b/chain/sync.go index 3af36f57f..b9de70f4e 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -313,7 +313,7 @@ func (syncer *Syncer) ValidateTipSet(ctx context.Context, fts *store.FullTipSet) for _, b := range fts.Blocks { if err := syncer.ValidateBlock(ctx, b); err != nil { - return err + return xerrors.Errorf("validating block %s: %w", b.Cid(), err) } } return nil @@ -440,7 +440,8 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("miner created a block but was not a winner") } - vmi, err := vm.NewVM(stateroot, h.Height, h.Miner, syncer.store) + r := vm.NewChainRand(syncer.store, baseTs, h.Tickets) + vmi, err := vm.NewVM(stateroot, h.Height, r, h.Miner, syncer.store) if err != nil { return xerrors.Errorf("failed to instantiate VM: %w", err) } diff --git a/chain/vm/vm.go b/chain/vm/vm.go index 644203564..f5c8a21b1 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -67,14 +67,8 @@ func (vmc *VMContext) Message() *types.Message { } func (vmc *VMContext) GetRandomness(height uint64) ([]byte, aerrors.ActorError) { - hts := vmc.vm.cs.GetHeaviestTipSet() - relHeight := int(hts.Height() - height) - if relHeight < 0 { - return nil, aerrors.Newf(1, "negative relHeight in GetRandomness: %d", relHeight) - } - - res, err := vmc.vm.cs.GetRandomness(vmc.ctx, hts, nil, relHeight) + res, err := vmc.vm.rand.GetRandomness(vmc.ctx, int64(height)) if err != nil { return nil, aerrors.Escalate(err, "could not get randomness") } @@ -294,9 +288,10 @@ type VM struct { blockHeight uint64 blockMiner address.Address inv *invoker + rand Rand } -func NewVM(base cid.Cid, height uint64, maddr address.Address, cs *store.ChainStore) (*VM, error) { +func NewVM(base cid.Cid, height uint64, r Rand, maddr address.Address, cs *store.ChainStore) (*VM, error) { buf := bufbstore.NewBufferedBstore(cs.Blockstore()) cst := hamt.CSTFromBstore(buf) state, err := state.LoadStateTree(cst, base) @@ -313,9 +308,33 @@ func NewVM(base cid.Cid, height uint64, maddr address.Address, cs *store.ChainSt blockHeight: height, blockMiner: maddr, inv: newInvoker(), + rand: r, }, nil } +type Rand interface { + GetRandomness(ctx context.Context, h int64) ([]byte, error) +} + +type chainRand struct { + cs *store.ChainStore + pts *types.TipSet + tickets []*types.Ticket +} + +func NewChainRand(cs *store.ChainStore, pts *types.TipSet, tickets []*types.Ticket) Rand { + return &chainRand{ + cs: cs, + pts: pts, + tickets: tickets, + } +} + +func (cr *chainRand) GetRandomness(ctx context.Context, h int64) ([]byte, error) { + lb := (int64(cr.pts.Height()) + int64(len(cr.tickets))) - h + return cr.cs.GetRandomness(ctx, cr.pts, cr.tickets, lb) +} + type ApplyRet struct { types.MessageReceipt ActorErr aerrors.ActorError diff --git a/go.sum b/go.sum index f2f20f3b9..a4bfa81ef 100644 --- a/go.sum +++ b/go.sum @@ -68,7 +68,6 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/filecoin-project/go-amt-ipld v0.0.0-20190917221444-2ed85149c65d/go.mod h1:lKjJYPg2kwbav5f78i5YA8kGccnZn18IySbpneXvaQs= github.com/filecoin-project/go-amt-ipld v0.0.0-20190919045431-3650716fff16 h1:NzojcJU1VbS6zdLG13JMYis/cQy/MrN3rxmZRq56jKA= github.com/filecoin-project/go-amt-ipld v0.0.0-20190919045431-3650716fff16/go.mod h1:lKjJYPg2kwbav5f78i5YA8kGccnZn18IySbpneXvaQs= github.com/filecoin-project/go-leb128 v0.0.0-20190212224330-8d79a5489543 h1:aMJGfgqe1QDhAVwxRg5fjCRF533xHidiKsugk7Vvzug= diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index 94fb5717e..a9e0482c5 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -45,7 +45,7 @@ func (a *ChainAPI) ChainHead(context.Context) (*types.TipSet, error) { } func (a *ChainAPI) ChainGetRandomness(ctx context.Context, pts *types.TipSet, tickets []*types.Ticket, lb int) ([]byte, error) { - return a.Chain.GetRandomness(ctx, pts, tickets, lb) + return a.Chain.GetRandomness(ctx, pts, tickets, int64(lb)) } func (a *ChainAPI) ChainWaitMsg(ctx context.Context, msg cid.Cid) (*api.MsgWait, error) {