diff --git a/api/api.go b/api/api.go index b821d01db..d9e7ff571 100644 --- a/api/api.go +++ b/api/api.go @@ -191,7 +191,6 @@ type DealInfo struct { } type MsgWait struct { - InBlock cid.Cid Receipt types.MessageReceipt } diff --git a/build/proof-params/mkparamfetch.sh b/build/proof-params/mkparamfetch.sh index b503e22ef..a9f8023ea 100755 --- a/build/proof-params/mkparamfetch.sh +++ b/build/proof-params/mkparamfetch.sh @@ -2,5 +2,19 @@ set -euo pipefail -sed "s/{{PARAMSJSON}}/$(base64 build/proof-params/parameters.json -w 0)/g" build/proof-params/paramfetch.sh.template > ./build/paramfetch.sh +function b64() { + f="$1" + case `uname` in + Darwin) + base64 -i "$f" + ;; + Linux) + base64 "$f" -w 0 + ;; + esac + printf "unsupported system" 1>&2 + exit 1 +} + +sed "s/{{PARAMSJSON}}/$(b64 build/proof-params/parameters.json)/g" build/proof-params/paramfetch.sh.template > ./build/paramfetch.sh chmod +x ./build/paramfetch.sh diff --git a/chain/actors/actor_storagemarket_test.go b/chain/actors/actor_storagemarket_test.go index 0f42269af..06c86aac2 100644 --- a/chain/actors/actor_storagemarket_test.go +++ b/chain/actors/actor_storagemarket_test.go @@ -145,7 +145,7 @@ func cheatStorageMarketTotal(t *testing.T, vm *vm.VM, bs bstore.Blockstore) { func fakeBlock(t *testing.T, minerAddr address.Address, ts uint64) *types.BlockHeader { c := fakeCid(t, 1) - return &types.BlockHeader{Height: 5, Miner: minerAddr, Timestamp: ts, StateRoot: c, Messages: c, MessageReceipts: c, BLSAggregate: types.Signature{Type: types.KTBLS}} + return &types.BlockHeader{Height: 5, Miner: minerAddr, Timestamp: ts, ParentStateRoot: c, Messages: c, ParentMessageReceipts: c, BLSAggregate: types.Signature{Type: types.KTBLS}} } func fakeCid(t *testing.T, s int) cid.Cid { diff --git a/chain/deals/asks.go b/chain/deals/asks.go index 689eeb680..c4e40af9d 100644 --- a/chain/deals/asks.go +++ b/chain/deals/asks.go @@ -143,10 +143,7 @@ func (h *Handler) saveAsk(a *types.SignedStorageAsk) error { } func (c *Client) checkAskSignature(ask *types.SignedStorageAsk) error { - tss, err := c.sm.TipSetState(c.sm.ChainStore().GetHeaviestTipSet().Cids()) - if err != nil { - return xerrors.Errorf("failed to get tipsetstate to query for miner worker: %w", err) - } + tss := c.sm.ChainStore().GetHeaviestTipSet().ParentState() w, err := stmgr.GetMinerWorker(context.TODO(), c.sm, tss, ask.Ask.Miner) if err != nil { diff --git a/chain/events/events_test.go b/chain/events/events_test.go index 4628b5fae..9d267d67c 100644 --- a/chain/events/events_test.go +++ b/chain/events/events_test.go @@ -3,11 +3,12 @@ package events import ( "context" "fmt" - "github.com/filecoin-project/go-lotus/api" - "github.com/filecoin-project/go-lotus/chain/store" "testing" "time" + "github.com/filecoin-project/go-lotus/api" + "github.com/filecoin-project/go-lotus/chain/store" + "github.com/ipfs/go-cid" "github.com/multiformats/go-multihash" "github.com/stretchr/testify/require" @@ -48,9 +49,9 @@ func makeTs(t *testing.T, h uint64, msgcid cid.Cid) *types.TipSet { { Height: h, - StateRoot: dummyCid, - Messages: msgcid, - MessageReceipts: dummyCid, + ParentStateRoot: dummyCid, + Messages: msgcid, + ParentMessageReceipts: dummyCid, }, }) @@ -608,5 +609,4 @@ func TestCalledOrder(t *testing.T) { }) fcs.advance(9, 1, nil) - } diff --git a/chain/events/tscache_test.go b/chain/events/tscache_test.go index dffe97a42..beedfe8da 100644 --- a/chain/events/tscache_test.go +++ b/chain/events/tscache_test.go @@ -3,8 +3,9 @@ package events import ( "context" "fmt" - "github.com/filecoin-project/go-lotus/chain/types" "testing" + + "github.com/filecoin-project/go-lotus/chain/types" ) func TestTsCache(t *testing.T) { @@ -17,10 +18,10 @@ func TestTsCache(t *testing.T) { add := func() { ts, err := types.NewTipSet([]*types.BlockHeader{{ - Height: h, - StateRoot: dummyCid, - Messages: dummyCid, - MessageReceipts: dummyCid, + Height: h, + ParentStateRoot: dummyCid, + Messages: dummyCid, + ParentMessageReceipts: dummyCid, }}) if err != nil { t.Fatal(err) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 75e9e3d80..9bfbba373 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -201,10 +201,7 @@ func (cg *ChainGen) nextBlockProof(ctx context.Context, m address.Address, ticks lastTicket = ticks[len(ticks)-1] } - st, err := cg.sm.TipSetState(pts.Cids()) - if err != nil { - return nil, nil, err - } + st := cg.curTipset.TipSet().ParentState() worker, err := stmgr.GetMinerWorker(ctx, cg.sm, st, m) if err != nil { @@ -258,7 +255,7 @@ func (cg *ChainGen) NextTipSet() (*MinedTipSet, error) { for i, m := range cg.miners { proof, t, err := cg.nextBlockProof(context.TODO(), m, ticketSets[i]) if err != nil { - return nil, err + return nil, xerrors.Errorf("next block proof: %w", err) } ticketSets[i] = append(ticketSets[i], t) @@ -269,7 +266,7 @@ func (cg *ChainGen) NextTipSet() (*MinedTipSet, error) { } if err := cg.cs.AddBlock(fblk.Header); err != nil { - return nil, err + return nil, xerrors.Errorf("chainstore AddBlock: %w", err) } blks = append(blks, fblk) @@ -376,12 +373,7 @@ func (mca mca) StateMinerPower(ctx context.Context, maddr address.Address, ts *t } func (mca mca) StateMinerWorker(ctx context.Context, maddr address.Address, ts *types.TipSet) (address.Address, error) { - st, err := mca.sm.TipSetState(ts.Cids()) - if err != nil { - return address.Undef, err - } - - return stmgr.GetMinerWorker(ctx, mca.sm, st, maddr) + return stmgr.GetMinerWorker(ctx, mca.sm, ts.ParentState(), maddr) } func (mca mca) WalletSign(ctx context.Context, a address.Address, v []byte) (*types.Signature, error) { diff --git a/chain/gen/mining.go b/chain/gen/mining.go index a691c5db5..dcff62df8 100644 --- a/chain/gen/mining.go +++ b/chain/gen/mining.go @@ -20,7 +20,7 @@ import ( ) 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) { - st, err := sm.TipSetState(parents.Cids()) + st, recpts, err := sm.TipSetState(parents) if err != nil { return nil, errors.Wrap(err, "failed to load tipset state") } @@ -53,12 +53,14 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal } next := &types.BlockHeader{ - Miner: miner, - Parents: parents.Cids(), - Tickets: tickets, - Height: height, - Timestamp: timestamp, - ElectionProof: proof, + Miner: miner, + Parents: parents.Cids(), + Tickets: tickets, + Height: height, + Timestamp: timestamp, + ElectionProof: proof, + ParentStateRoot: st, + ParentMessageReceipts: recpts, } var blsMessages []*types.Message @@ -83,24 +85,6 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal } } - var receipts []cbg.CBORMarshaler - for _, msg := range blsMessages { - rec, err := vmi.ApplyMessage(ctx, msg) - if err != nil { - return nil, errors.Wrap(err, "apply message failure") - } - - receipts = append(receipts, &rec.MessageReceipt) - } - for _, msg := range secpkMessages { - rec, err := vmi.ApplyMessage(ctx, &msg.Message) - if err != nil { - return nil, errors.Wrap(err, "apply message failure") - } - - receipts = append(receipts, &rec.MessageReceipt) - } - bs := amt.WrapBlockstore(sm.ChainStore().Blockstore()) blsmsgroot, err := amt.FromArray(bs, toIfArr(blsMsgCids)) if err != nil { @@ -120,24 +104,12 @@ func MinerCreateBlock(ctx context.Context, sm *stmgr.StateManager, w *wallet.Wal } next.Messages = mmcid - rectroot, err := amt.FromArray(bs, receipts) - if err != nil { - return nil, xerrors.Errorf("failed to build receipts amt: %w", err) - } - next.MessageReceipts = rectroot - - stateRoot, err := vmi.Flush(context.TODO()) - if err != nil { - return nil, errors.Wrap(err, "flushing state tree failed") - } - aggSig, err := aggregateSignatures(blsSigs) if err != nil { return nil, err } next.BLSAggregate = aggSig - next.StateRoot = stateRoot pweight := sm.ChainStore().Weight(parents) next.ParentWeight = types.NewInt(pweight) diff --git a/chain/gen/utils.go b/chain/gen/utils.go index 249a1ab73..75feea3ea 100644 --- a/chain/gen/utils.go +++ b/chain/gen/utils.go @@ -335,18 +335,18 @@ func MakeGenesisBlock(bs bstore.Blockstore, balances map[address.Address]types.B } b := &types.BlockHeader{ - Miner: actors.InitActorAddress, - Tickets: []*types.Ticket{genesisticket}, - ElectionProof: []byte("the Genesis block"), - Parents: []cid.Cid{}, - Height: 0, - ParentWeight: types.NewInt(0), - StateRoot: stateroot, - Messages: mmb.Cid(), - MessageReceipts: emptyroot, - BLSAggregate: types.Signature{Type: types.KTBLS, Data: []byte("signatureeee")}, - BlockSig: types.Signature{Type: types.KTBLS, Data: []byte("block signatureeee")}, - Timestamp: ts, + Miner: actors.InitActorAddress, + Tickets: []*types.Ticket{genesisticket}, + ElectionProof: []byte("the Genesis block"), + Parents: []cid.Cid{}, + Height: 0, + ParentWeight: types.NewInt(0), + ParentStateRoot: stateroot, + Messages: mmb.Cid(), + ParentMessageReceipts: emptyroot, + BLSAggregate: types.Signature{Type: types.KTBLS, Data: []byte("signatureeee")}, + BlockSig: types.Signature{Type: types.KTBLS, Data: []byte("block signatureeee")}, + Timestamp: ts, } sb, err := b.ToStorageBlock() diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index ef5fa220b..de6f36f43 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -53,10 +53,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types. ts = sm.cs.GetHeaviestTipSet() } - state, err := sm.TipSetState(ts.Cids()) - if err != nil { - return nil, xerrors.Errorf("getting tipset state: %w", err) - } + state := ts.ParentState() r := vm.NewChainRand(sm.cs, ts.Cids(), ts.Height(), nil) @@ -69,7 +66,7 @@ func (sm *StateManager) Replay(ctx context.Context, ts *types.TipSet, mcid cid.C var outm *types.Message var outr *vm.ApplyRet - _, err := sm.computeTipSetState(ctx, ts.Blocks(), func(c cid.Cid, m *types.Message, ret *vm.ApplyRet) error { + _, _, err := sm.computeTipSetState(ctx, ts.Blocks(), func(c cid.Cid, m *types.Message, ret *vm.ApplyRet) error { if c == mcid { outm = m outr = ret diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 798637e00..6fdbeeed3 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -4,11 +4,14 @@ import ( "context" "sync" + amt "github.com/filecoin-project/go-amt-ipld" + "github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/state" "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/chain/vm" + cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/ipfs/go-cid" @@ -21,14 +24,14 @@ var log = logging.Logger("statemgr") type StateManager struct { cs *store.ChainStore - stCache map[string]cid.Cid + stCache map[string][]cid.Cid stlk sync.Mutex } func NewStateManager(cs *store.ChainStore) *StateManager { return &StateManager{ cs: cs, - stCache: make(map[string]cid.Cid), + stCache: make(map[string][]cid.Cid), } } @@ -40,42 +43,38 @@ func cidsToKey(cids []cid.Cid) string { return out } -func (sm *StateManager) TipSetState(cids []cid.Cid) (cid.Cid, error) { +func (sm *StateManager) TipSetState(ts *types.TipSet) (cid.Cid, cid.Cid, error) { ctx := context.TODO() - ck := cidsToKey(cids) + ck := cidsToKey(ts.Cids()) sm.stlk.Lock() cached, ok := sm.stCache[ck] sm.stlk.Unlock() if ok { - return cached, nil + return cached[0], cached[1], nil } - ts, err := sm.cs.LoadTipSet(cids) - if err != nil { - return cid.Undef, err + if ts.Height() == 0 { + // NB: This is here because the process that executes blocks requires that the + // block miner reference a valid miner in the state tree. Unless we create some + // magical genesis miner, this won't work properly, so we short circuit here + // This avoids the question of 'who gets paid the genesis block reward' + return ts.Blocks()[0].ParentStateRoot, ts.Blocks()[0].ParentMessageReceipts, nil } - out, err := sm.computeTipSetState(ctx, ts.Blocks(), nil) + st, rec, err := sm.computeTipSetState(ctx, ts.Blocks(), nil) if err != nil { - return cid.Undef, err + return cid.Undef, cid.Undef, err } sm.stlk.Lock() - sm.stCache[ck] = out + sm.stCache[ck] = []cid.Cid{st, rec} sm.stlk.Unlock() - return out, nil + return st, rec, nil } -func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.BlockHeader, cb func(cid.Cid, *types.Message, *vm.ApplyRet) error) (cid.Cid, error) { - if len(blks) == 1 && cb == nil { - return blks[0].StateRoot, nil - } - - pstate, err := sm.TipSetState(blks[0].Parents) - if err != nil { - return cid.Undef, xerrors.Errorf("recursive TipSetState failed: %w", err) - } +func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.BlockHeader, cb func(cid.Cid, *types.Message, *vm.ApplyRet) error) (cid.Cid, cid.Cid, error) { + pstate := blks[0].ParentStateRoot cids := make([]cid.Cid, len(blks)) for i, v := range blks { @@ -86,56 +85,110 @@ func (sm *StateManager) computeTipSetState(ctx context.Context, blks []*types.Bl vmi, err := vm.NewVM(pstate, blks[0].Height, r, address.Undef, sm.cs) if err != nil { - return cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err) + return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err) } - applied := make(map[cid.Cid]bool) + netact, err := vmi.StateTree().GetActor(actors.NetworkAddress) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to get network actor: %w", err) + } + + reward := vm.MiningReward(netact.Balance) + for _, b := range blks { + owner, err := GetMinerOwner(ctx, sm, pstate, b.Miner) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to get owner for miner %s: %w", b.Miner, err) + } + + act, err := vmi.StateTree().GetActor(owner) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to get miner owner actor") + } + + if err := vm.DeductFunds(netact, reward); err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to deduct funds from network actor: %w", err) + } + + vm.DepositFunds(act, reward) + } + + // TODO: can't use method from chainstore because it doesnt let us know who the block miners were + applied := make(map[address.Address]uint64) + balances := make(map[address.Address]types.BigInt) + + preloadAddr := func(a address.Address) error { + if _, ok := applied[a]; !ok { + act, err := vmi.StateTree().GetActor(a) + if err != nil { + return err + } + + applied[a] = act.Nonce + balances[a] = act.Balance + } + return nil + } + + var receipts []cbg.CBORMarshaler for _, b := range blks { vmi.SetBlockMiner(b.Miner) bms, sms, err := sm.cs.MessagesForBlock(b) if err != nil { - return cid.Undef, xerrors.Errorf("failed to get messages for block: %w", err) + return cid.Undef, cid.Undef, xerrors.Errorf("failed to get messages for block: %w", err) } + cmsgs := make([]store.ChainMsg, 0, len(bms)+len(sms)) for _, m := range bms { - if applied[m.Cid()] { + cmsgs = append(cmsgs, m) + } + for _, sm := range sms { + cmsgs = append(cmsgs, sm) + } + + for _, cm := range cmsgs { + m := cm.VMMessage() + if err := preloadAddr(m.From); err != nil { + return cid.Undef, cid.Undef, err + } + + if applied[m.From] != m.Nonce { continue } - applied[m.Cid()] = true + applied[m.From]++ + + if balances[m.From].LessThan(m.RequiredFunds()) { + continue + } + balances[m.From] = types.BigSub(balances[m.From], m.RequiredFunds()) r, err := vmi.ApplyMessage(ctx, m) if err != nil { - return cid.Undef, err + return cid.Undef, cid.Undef, err } + receipts = append(receipts, &r.MessageReceipt) + if cb != nil { - if err := cb(m.Cid(), m, r); err != nil { - return cid.Undef, err - } - } - } - - for _, sm := range sms { - if applied[sm.Cid()] { - continue - } - applied[sm.Cid()] = true - - r, err := vmi.ApplyMessage(ctx, &sm.Message) - if err != nil { - return cid.Undef, err - } - - if cb != nil { - if err := cb(sm.Cid(), &sm.Message, r); err != nil { - return cid.Undef, err + if err := cb(cm.Cid(), m, r); err != nil { + return cid.Undef, cid.Undef, err } } } } - return vmi.Flush(ctx) + bs := amt.WrapBlockstore(sm.cs.Blockstore()) + rectroot, err := amt.FromArray(bs, receipts) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err) + } + + st, err := vmi.Flush(ctx) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err) + } + + return st, rectroot, nil } func (sm *StateManager) GetActor(addr address.Address, ts *types.TipSet) (*types.Actor, error) { @@ -143,10 +196,7 @@ func (sm *StateManager) GetActor(addr address.Address, ts *types.TipSet) (*types ts = sm.cs.GetHeaviestTipSet() } - stcid, err := sm.TipSetState(ts.Cids()) - if err != nil { - return nil, xerrors.Errorf("tipset state: %w", err) - } + stcid := ts.ParentState() cst := hamt.CSTFromBstore(sm.cs.Blockstore()) state, err := state.LoadStateTree(cst, stcid) diff --git a/chain/store/store.go b/chain/store/store.go index 533ee224c..9c4d77204 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -8,6 +8,8 @@ import ( "sync" "github.com/filecoin-project/go-lotus/build" + "github.com/filecoin-project/go-lotus/chain/address" + "github.com/filecoin-project/go-lotus/chain/state" amt "github.com/filecoin-project/go-amt-ipld" "github.com/filecoin-project/go-lotus/chain/types" @@ -519,6 +521,72 @@ func (cs *ChainStore) readAMTCids(root cid.Cid) ([]cid.Cid, error) { return cids, nil } +type ChainMsg interface { + Cid() cid.Cid + VMMessage() *types.Message +} + +func (cs *ChainStore) MessagesForTipset(ts *types.TipSet) ([]ChainMsg, error) { + applied := make(map[address.Address]uint64) + balances := make(map[address.Address]types.BigInt) + + cst := hamt.CSTFromBstore(cs.bs) + st, err := state.LoadStateTree(cst, ts.Blocks()[0].ParentStateRoot) + if err != nil { + return nil, xerrors.Errorf("failed to load state tree") + } + + preloadAddr := func(a address.Address) error { + if _, ok := applied[a]; !ok { + act, err := st.GetActor(a) + if err != nil { + return err + } + + applied[a] = act.Nonce + balances[a] = act.Balance + } + return nil + } + + var out []ChainMsg + for _, b := range ts.Blocks() { + bms, sms, err := cs.MessagesForBlock(b) + if err != nil { + return nil, xerrors.Errorf("failed to get messages for block: %w", err) + } + + cmsgs := make([]ChainMsg, 0, len(bms)+len(sms)) + for _, m := range bms { + cmsgs = append(cmsgs, m) + } + for _, sm := range sms { + cmsgs = append(cmsgs, sm) + } + + for _, cm := range cmsgs { + m := cm.VMMessage() + if err := preloadAddr(m.From); err != nil { + return nil, err + } + + if applied[m.From] != m.Nonce { + continue + } + applied[m.From]++ + + if balances[m.From].LessThan(m.RequiredFunds()) { + continue + } + balances[m.From] = types.BigSub(balances[m.From], m.RequiredFunds()) + + out = append(out, cm) + } + } + + return out, nil +} + func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) { cst := hamt.CSTFromBstore(cs.bs) var msgmeta types.MsgMeta @@ -551,7 +619,7 @@ func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, func (cs *ChainStore) GetReceipt(b *types.BlockHeader, i int) (*types.MessageReceipt, error) { bs := amt.WrapBlockstore(cs.bs) - a, err := amt.LoadAMT(bs, b.MessageReceipts) + a, err := amt.LoadAMT(bs, b.ParentMessageReceipts) if err != nil { return nil, errors.Wrap(err, "amt load") } @@ -592,57 +660,69 @@ func (cs *ChainStore) LoadSignedMessagesFromCids(cids []cid.Cid) ([]*types.Signe return msgs, nil } -func (cs *ChainStore) WaitForMessage(ctx context.Context, mcid cid.Cid) (cid.Cid, *types.MessageReceipt, error) { +func (cs *ChainStore) WaitForMessage(ctx context.Context, mcid cid.Cid) (*types.MessageReceipt, error) { tsub := cs.SubHeadChanges(ctx) head := cs.GetHeaviestTipSet() - bc, r, err := cs.tipsetContainsMsg(head, mcid) + r, err := cs.tipsetExecutedMessage(head, mcid) if err != nil { - return cid.Undef, nil, err + return nil, err } if r != nil { - return bc, r, nil + return r, nil } for { select { case notif, ok := <-tsub: if !ok { - return cid.Undef, nil, ctx.Err() + return nil, ctx.Err() } for _, val := range notif { switch val.Type { case HCRevert: continue case HCApply: - bc, r, err := cs.tipsetContainsMsg(val.Val, mcid) + r, err := cs.tipsetExecutedMessage(val.Val, mcid) if err != nil { - return cid.Undef, nil, err + return nil, err } if r != nil { - return bc, r, nil + return r, nil } } } case <-ctx.Done(): - return cid.Undef, nil, ctx.Err() + return nil, ctx.Err() } } } -func (cs *ChainStore) tipsetContainsMsg(ts *types.TipSet, msg cid.Cid) (cid.Cid, *types.MessageReceipt, error) { - for _, b := range ts.Blocks() { - r, err := cs.blockContainsMsg(b, msg) - if err != nil { - return cid.Undef, nil, err - } - if r != nil { - return b.Cid(), r, nil +func (cs *ChainStore) tipsetExecutedMessage(ts *types.TipSet, msg cid.Cid) (*types.MessageReceipt, error) { + // The genesis block did not execute any messages + if ts.Height() == 0 { + return nil, nil + } + + pts, err := cs.LoadTipSet(ts.Parents()) + if err != nil { + return nil, err + } + + cm, err := cs.MessagesForTipset(pts) + if err != nil { + return nil, err + } + + for i, m := range cm { + if m.Cid() == msg { + return cs.GetReceipt(ts.Blocks()[0], i) } } - return cid.Undef, nil, nil + + return nil, nil } func (cs *ChainStore) blockContainsMsg(blk *types.BlockHeader, msg cid.Cid) (*types.MessageReceipt, error) { diff --git a/chain/sync.go b/chain/sync.go index 68435a011..0c224b84a 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -10,15 +10,16 @@ import ( "github.com/filecoin-project/go-lotus/build" "github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/address" + "github.com/filecoin-project/go-lotus/chain/state" "github.com/filecoin-project/go-lotus/chain/stmgr" "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" - "github.com/filecoin-project/go-lotus/chain/vm" "github.com/filecoin-project/go-lotus/lib/vdf" amt "github.com/filecoin-project/go-amt-ipld" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" + hamt "github.com/ipfs/go-hamt-ipld" bstore "github.com/ipfs/go-ipfs-blockstore" logging "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p-core/peer" @@ -380,16 +381,25 @@ func (syncer *Syncer) validateTickets(ctx context.Context, mworker address.Addre // Should match up with 'Semantical Validation' in validation.md in the spec func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) error { h := b.Header - stateroot, err := syncer.sm.TipSetState(h.Parents) - if err != nil { - return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err) - } baseTs, err := syncer.store.LoadTipSet(h.Parents) if err != nil { return xerrors.Errorf("load tipset failed: %w", err) } + stateroot, precp, err := syncer.sm.TipSetState(baseTs) + if err != nil { + return xerrors.Errorf("get tipsetstate(%d, %s) failed: %w", h.Height, h.Parents, err) + } + + if stateroot != h.ParentStateRoot { + return xerrors.Errorf("parent state root did not match computed state (%s != %s)", stateroot, h.ParentStateRoot) + } + + if precp != h.ParentMessageReceipts { + return xerrors.Errorf("parent receipts root did not match computed value (%s != %s)", precp, h.ParentMessageReceipts) + } + if h.Timestamp > uint64(time.Now().Unix()+build.AllowableClockDrift) { return xerrors.Errorf("block was from the future") } @@ -434,61 +444,48 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) err return xerrors.Errorf("miner created a block but was not a winner") } - r := vm.NewChainRand(syncer.store, baseTs.Cids(), baseTs.Height(), h.Tickets) - vmi, err := vm.NewVM(stateroot, h.Height, r, h.Miner, syncer.store) + nonces := make(map[address.Address]uint64) + balances := make(map[address.Address]types.BigInt) + + cst := hamt.CSTFromBstore(syncer.store.Blockstore()) + st, err := state.LoadStateTree(cst, stateroot) if err != nil { - return xerrors.Errorf("failed to instantiate VM: %w", err) + return xerrors.Errorf("failed to load base state tree: %w", err) } - owner, err := stmgr.GetMinerOwner(ctx, syncer.sm, stateroot, b.Header.Miner) - if err != nil { - return xerrors.Errorf("getting miner owner for block miner failed: %w", err) - } - networkBalance, err := vmi.ActorBalance(actors.NetworkAddress) - if err != nil { - return xerrors.Errorf("getting network balance") - } - - if err := vmi.TransferFunds(actors.NetworkAddress, owner, - vm.MiningReward(networkBalance)); err != nil { - return xerrors.Errorf("fund transfer failed: %w", err) - } - - var receipts []cbg.CBORMarshaler - for i, m := range b.BlsMessages { - receipt, err := vmi.ApplyMessage(ctx, m) - if err != nil { - return xerrors.Errorf("failed executing bls message %d in block %s: %w", i, b.Header.Cid(), err) + checkMsg := func(m *types.Message) error { + if _, ok := nonces[m.From]; !ok { + act, err := st.GetActor(m.From) + if err != nil { + return xerrors.Errorf("failed to get actor: %w", err) + } + nonces[m.From] = act.Nonce + balances[m.From] = act.Balance } - receipts = append(receipts, &receipt.MessageReceipt) + if nonces[m.From] != m.Nonce { + return xerrors.Errorf("wrong nonce") + } + nonces[m.From]++ + + if balances[m.From].LessThan(m.RequiredFunds()) { + return xerrors.Errorf("not enough funds for message execution") + } + + balances[m.From] = types.BigSub(balances[m.From], m.RequiredFunds()) + return nil + } + + for i, m := range b.BlsMessages { + if err := checkMsg(m); err != nil { + xerrors.Errorf("block had invalid bls message at index %d: %w", i, err) + } } for i, m := range b.SecpkMessages { - receipt, err := vmi.ApplyMessage(ctx, &m.Message) - if err != nil { - return xerrors.Errorf("failed executing secpk message %d in block %s: %w", i, b.Header.Cid(), err) + if err := checkMsg(&m.Message); err != nil { + xerrors.Errorf("block had invalid secpk message at index %d: %w", i, err) } - - receipts = append(receipts, &receipt.MessageReceipt) - } - - bs := amt.WrapBlockstore(syncer.store.Blockstore()) - recptRoot, err := amt.FromArray(bs, receipts) - if err != nil { - return xerrors.Errorf("building receipts amt failed: %w", err) - } - if recptRoot != b.Header.MessageReceipts { - return fmt.Errorf("receipts mismatched") - } - - final, err := vmi.Flush(context.TODO()) - if err != nil { - return xerrors.Errorf("failed to flush VM state: %w", err) - } - - if b.Header.StateRoot != final { - return fmt.Errorf("final state root does not match block") } return nil @@ -549,7 +546,7 @@ loop: blockSet = append(blockSet, b) } - syncer.syncState.SetHeight(blks[len(blockSet)-1].Height()) + syncer.syncState.SetHeight(blks[len(blks)-1].Height()) at = blks[len(blks)-1].Parents() } diff --git a/chain/types/blockheader.go b/chain/types/blockheader.go index 2821de0b5..55b09f870 100644 --- a/chain/types/blockheader.go +++ b/chain/types/blockheader.go @@ -34,14 +34,14 @@ type BlockHeader struct { Height uint64 - StateRoot cid.Cid + ParentStateRoot cid.Cid + + ParentMessageReceipts cid.Cid Messages cid.Cid BLSAggregate Signature - MessageReceipts cid.Cid - Timestamp uint64 BlockSig Signature diff --git a/chain/types/blockheader_test.go b/chain/types/blockheader_test.go index 8a996753b..fadac1698 100644 --- a/chain/types/blockheader_test.go +++ b/chain/types/blockheader_test.go @@ -32,14 +32,14 @@ func testBlockHeader(t testing.TB) *BlockHeader { VDFProof: []byte("vrf proof"), }, }, - Parents: []cid.Cid{c, c}, - MessageReceipts: c, - BLSAggregate: Signature{Type: KTBLS, Data: []byte("boo! im a signature")}, - ParentWeight: NewInt(123125126212), - Messages: c, - Height: 85919298723, - StateRoot: c, - BlockSig: Signature{Type: KTBLS, Data: []byte("boo! im a signature")}, + Parents: []cid.Cid{c, c}, + ParentMessageReceipts: c, + BLSAggregate: Signature{Type: KTBLS, Data: []byte("boo! im a signature")}, + ParentWeight: NewInt(123125126212), + Messages: c, + Height: 85919298723, + ParentStateRoot: c, + BlockSig: Signature{Type: KTBLS, Data: []byte("boo! im a signature")}, } } diff --git a/chain/types/cbor_gen.go b/chain/types/cbor_gen.go index c19ac85ca..5841abf03 100644 --- a/chain/types/cbor_gen.go +++ b/chain/types/cbor_gen.go @@ -5,7 +5,7 @@ import ( "io" "math" - "github.com/ipfs/go-cid" + cid "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" ) @@ -66,10 +66,16 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error { return err } - // t.t.StateRoot (cid.Cid) + // t.t.ParentStateRoot (cid.Cid) - if err := cbg.WriteCid(w, t.StateRoot); err != nil { - return xerrors.Errorf("failed to write cid field t.StateRoot: %w", err) + if err := cbg.WriteCid(w, t.ParentStateRoot); err != nil { + return xerrors.Errorf("failed to write cid field t.ParentStateRoot: %w", err) + } + + // t.t.ParentMessageReceipts (cid.Cid) + + if err := cbg.WriteCid(w, t.ParentMessageReceipts); err != nil { + return xerrors.Errorf("failed to write cid field t.ParentMessageReceipts: %w", err) } // t.t.Messages (cid.Cid) @@ -83,12 +89,6 @@ func (t *BlockHeader) MarshalCBOR(w io.Writer) error { return err } - // t.t.MessageReceipts (cid.Cid) - - if err := cbg.WriteCid(w, t.MessageReceipts); err != nil { - return xerrors.Errorf("failed to write cid field t.MessageReceipts: %w", err) - } - // t.t.Timestamp (uint64) if _, err := w.Write(cbg.CborEncodeMajorType(cbg.MajUnsignedInt, t.Timestamp)); err != nil { return err @@ -212,16 +212,28 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) error { return fmt.Errorf("wrong type for uint64 field") } t.Height = extra - // t.t.StateRoot (cid.Cid) + // t.t.ParentStateRoot (cid.Cid) { c, err := cbg.ReadCid(br) if err != nil { - return xerrors.Errorf("failed to read cid field t.StateRoot: %w", err) + return xerrors.Errorf("failed to read cid field t.ParentStateRoot: %w", err) } - t.StateRoot = c + t.ParentStateRoot = c + + } + // t.t.ParentMessageReceipts (cid.Cid) + + { + + c, err := cbg.ReadCid(br) + if err != nil { + return xerrors.Errorf("failed to read cid field t.ParentMessageReceipts: %w", err) + } + + t.ParentMessageReceipts = c } // t.t.Messages (cid.Cid) @@ -244,18 +256,6 @@ func (t *BlockHeader) UnmarshalCBOR(r io.Reader) error { return err } - } - // t.t.MessageReceipts (cid.Cid) - - { - - c, err := cbg.ReadCid(br) - if err != nil { - return xerrors.Errorf("failed to read cid field t.MessageReceipts: %w", err) - } - - t.MessageReceipts = c - } // t.t.Timestamp (uint64) diff --git a/chain/types/message.go b/chain/types/message.go index a66671e68..b00783b20 100644 --- a/chain/types/message.go +++ b/chain/types/message.go @@ -73,3 +73,7 @@ func (m *Message) RequiredFunds() BigInt { BigMul(m.GasPrice, m.GasLimit), ) } + +func (m *Message) VMMessage() *Message { + return m +} diff --git a/chain/types/signedmessage.go b/chain/types/signedmessage.go index 946e31a6d..657abe2e7 100644 --- a/chain/types/signedmessage.go +++ b/chain/types/signedmessage.go @@ -57,3 +57,7 @@ func (sm *SignedMessage) Serialize() ([]byte, error) { } return buf.Bytes(), nil } + +func (sm *SignedMessage) VMMessage() *Message { + return &sm.Message +} diff --git a/chain/types/tipset.go b/chain/types/tipset.go index 3337a823e..688edacd4 100644 --- a/chain/types/tipset.go +++ b/chain/types/tipset.go @@ -150,6 +150,10 @@ func (ts *TipSet) MinTicketBlock() *BlockHeader { return min } +func (ts *TipSet) ParentState() cid.Cid { + return ts.blks[0].ParentStateRoot +} + func (ts *TipSet) Contains(oc cid.Cid) bool { for _, c := range ts.cids { if c == oc { diff --git a/cli/createminer.go b/cli/createminer.go index 14e151a23..47f9b38aa 100644 --- a/cli/createminer.go +++ b/cli/createminer.go @@ -112,7 +112,6 @@ var createMinerCmd = &cli.Command{ return err } - fmt.Printf("miner created in block %s\n", mwait.InBlock) fmt.Printf("new miner address: %s\n", maddr) return nil diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index fccad5fcd..9ff7a8989 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -51,13 +51,12 @@ func (a *ChainAPI) ChainGetRandomness(ctx context.Context, pts *types.TipSet, ti func (a *ChainAPI) ChainWaitMsg(ctx context.Context, msg cid.Cid) (*api.MsgWait, error) { // TODO: consider using event system for this, expose confidence - blkcid, recpt, err := a.Chain.WaitForMessage(ctx, msg) + recpt, err := a.Chain.WaitForMessage(ctx, msg) if err != nil { return nil, err } return &api.MsgWait{ - InBlock: blkcid, Receipt: *recpt, }, nil } diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index ee9613b88..bd852a2f8 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -2,6 +2,7 @@ package full import ( "context" + "go.uber.org/fx" "golang.org/x/xerrors" diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 1e1558cf8..13dbf5cb7 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -134,7 +134,7 @@ func (a *StateAPI) stateForTs(ts *types.TipSet) (*state.StateTree, error) { ts = a.Chain.GetHeaviestTipSet() } - st, err := a.StateManager.TipSetState(ts.Cids()) + st, _, err := a.StateManager.TipSetState(ts) if err != nil { return nil, err }