From 78da356752ea6fa89409447edb23e6cd979af9da Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 25 Jul 2019 21:54:22 -0700 Subject: [PATCH] pull more things apart --- api/api.go | 8 +- api/struct.go | 16 +-- chain/actors/actors_test.go | 9 +- chain/actors/harness_test.go | 11 +- chain/blocksync.go | 25 ++-- chain/messagepool.go | 31 +---- chain/mining.go | 17 ++- chain/store/fts.go | 51 ++++++++ chain/{chain.go => store/store.go} | 108 +++++++++------- chain/sync.go | 199 +++++++---------------------- chain/types.go | 84 ------------ chain/types/tipset.go | 91 +++++++++++++ chain/{ => vm}/invoker.go | 6 +- chain/{ => vm}/invoker_test.go | 2 +- chain/vm/mkactor.go | 69 ++++++++++ chain/{ => vm}/vm.go | 29 ++++- go.sum | 3 + miner/miner.go | 10 +- node/builder.go | 3 +- node/hello/hello.go | 5 +- node/impl/full.go | 12 +- node/modules/core.go | 8 +- 22 files changed, 415 insertions(+), 382 deletions(-) create mode 100644 chain/store/fts.go rename chain/{chain.go => store/store.go} (80%) create mode 100644 chain/types/tipset.go rename chain/{ => vm}/invoker.go (93%) rename chain/{ => vm}/invoker_test.go (99%) create mode 100644 chain/vm/mkactor.go rename chain/{ => vm}/vm.go (93%) diff --git a/api/api.go b/api/api.go index d2209b86d..2c9018bf0 100644 --- a/api/api.go +++ b/api/api.go @@ -64,16 +64,16 @@ type FullNode interface { Common // chain - ChainHead(context.Context) (*chain.TipSet, error) // TODO: check serialization + ChainHead(context.Context) (*types.TipSet, error) // TODO: check serialization ChainSubmitBlock(ctx context.Context, blk *chain.BlockMsg) error // TODO: check serialization - ChainGetRandomness(context.Context, *chain.TipSet) ([]byte, error) + ChainGetRandomness(context.Context, *types.TipSet) ([]byte, error) ChainWaitMsg(context.Context, cid.Cid) (*MsgWait, error) ChainGetBlock(context.Context, cid.Cid) (*types.BlockHeader, error) ChainGetBlockMessages(context.Context, cid.Cid) ([]*types.SignedMessage, error) // messages - MpoolPending(context.Context, *chain.TipSet) ([]*types.SignedMessage, error) + MpoolPending(context.Context, *types.TipSet) ([]*types.SignedMessage, error) MpoolPush(context.Context, *types.SignedMessage) error // FullNodeStruct @@ -81,7 +81,7 @@ type FullNode interface { // miner MinerStart(context.Context, address.Address) error - MinerCreateBlock(context.Context, address.Address, *chain.TipSet, []types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) + MinerCreateBlock(context.Context, address.Address, *types.TipSet, []types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) // // UX ? diff --git a/api/struct.go b/api/struct.go index 0847cd164..3243bc6c3 100644 --- a/api/struct.go +++ b/api/struct.go @@ -38,17 +38,17 @@ type FullNodeStruct struct { Internal struct { ChainSubmitBlock func(ctx context.Context, blk *chain.BlockMsg) error `perm:"write"` - ChainHead func(context.Context) (*chain.TipSet, error) `perm:"read"` - ChainGetRandomness func(context.Context, *chain.TipSet) ([]byte, error) `perm:"read"` + ChainHead func(context.Context) (*types.TipSet, error) `perm:"read"` + ChainGetRandomness func(context.Context, *types.TipSet) ([]byte, error) `perm:"read"` ChainWaitMsg func(context.Context, cid.Cid) (*MsgWait, error) `perm:"read"` ChainGetBlock func(context.Context, cid.Cid) (*types.BlockHeader, error) `perm:"read"` ChainGetBlockMessages func(context.Context, cid.Cid) ([]*types.SignedMessage, error) `perm:"read"` - MpoolPending func(context.Context, *chain.TipSet) ([]*types.SignedMessage, error) `perm:"read"` + MpoolPending func(context.Context, *types.TipSet) ([]*types.SignedMessage, error) `perm:"read"` MpoolPush func(context.Context, *types.SignedMessage) error `perm:"write"` MinerStart func(context.Context, address.Address) error `perm:"admin"` - MinerCreateBlock func(context.Context, address.Address, *chain.TipSet, []types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) `perm:"write"` + MinerCreateBlock func(context.Context, address.Address, *types.TipSet, []types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) `perm:"write"` WalletNew func(context.Context, string) (address.Address, error) `perm:"write"` WalletList func(context.Context) ([]address.Address, error) `perm:"write"` @@ -115,7 +115,7 @@ func (c *FullNodeStruct) ClientImport(ctx context.Context, path string) (cid.Cid return c.Internal.ClientImport(ctx, path) } -func (c *FullNodeStruct) MpoolPending(ctx context.Context, ts *chain.TipSet) ([]*types.SignedMessage, error) { +func (c *FullNodeStruct) MpoolPending(ctx context.Context, ts *types.TipSet) ([]*types.SignedMessage, error) { return c.Internal.MpoolPending(ctx, ts) } @@ -127,7 +127,7 @@ func (c *FullNodeStruct) MinerStart(ctx context.Context, addr address.Address) e return c.Internal.MinerStart(ctx, addr) } -func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *chain.TipSet, tickets []types.Ticket, eproof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) { +func (c *FullNodeStruct) MinerCreateBlock(ctx context.Context, addr address.Address, base *types.TipSet, tickets []types.Ticket, eproof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) { return c.Internal.MinerCreateBlock(ctx, addr, base, tickets, eproof, msgs) } @@ -135,11 +135,11 @@ func (c *FullNodeStruct) ChainSubmitBlock(ctx context.Context, blk *chain.BlockM return c.Internal.ChainSubmitBlock(ctx, blk) } -func (c *FullNodeStruct) ChainHead(ctx context.Context) (*chain.TipSet, error) { +func (c *FullNodeStruct) ChainHead(ctx context.Context) (*types.TipSet, error) { return c.Internal.ChainHead(ctx) } -func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts *chain.TipSet) ([]byte, error) { +func (c *FullNodeStruct) ChainGetRandomness(ctx context.Context, pts *types.TipSet) ([]byte, error) { return c.Internal.ChainGetRandomness(ctx, pts) } diff --git a/chain/actors/actors_test.go b/chain/actors/actors_test.go index 8437e15b2..6aa54f6e5 100644 --- a/chain/actors/actors_test.go +++ b/chain/actors/actors_test.go @@ -4,11 +4,12 @@ import ( "encoding/binary" "testing" - "github.com/filecoin-project/go-lotus/chain" . "github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/gen" + "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" + "github.com/filecoin-project/go-lotus/chain/vm" dstore "github.com/ipfs/go-datastore" bstore "github.com/ipfs/go-ipfs-blockstore" cbor "github.com/ipfs/go-ipld-cbor" @@ -26,7 +27,7 @@ func blsaddr(n uint64) address.Address { return addr } -func setupVMTestEnv(t *testing.T) (*chain.VM, []address.Address) { +func setupVMTestEnv(t *testing.T) (*vm.VM, []address.Address) { bs := bstore.NewBlockstore(dstore.NewMapDatastore()) from := blsaddr(0) @@ -46,9 +47,9 @@ func setupVMTestEnv(t *testing.T) (*chain.VM, []address.Address) { t.Fatal(err) } - cs := chain.NewChainStore(bs, nil) + cs := store.NewChainStore(bs, nil) - vm, err := chain.NewVM(stateroot, 1, maddr, cs) + vm, err := vm.NewVM(stateroot, 1, maddr, cs) if err != nil { t.Fatal(err) } diff --git a/chain/actors/harness_test.go b/chain/actors/harness_test.go index fd1af2f1e..2edc0dd28 100644 --- a/chain/actors/harness_test.go +++ b/chain/actors/harness_test.go @@ -6,8 +6,9 @@ import ( . "github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/gen" + "github.com/filecoin-project/go-lotus/chain/store" + "github.com/filecoin-project/go-lotus/chain/vm" - "github.com/filecoin-project/go-lotus/chain" "github.com/filecoin-project/go-lotus/chain/address" "github.com/filecoin-project/go-lotus/chain/state" "github.com/filecoin-project/go-lotus/chain/types" @@ -27,9 +28,9 @@ type Harness struct { t *testing.T actors []address.Address - vm *chain.VM + vm *vm.VM bs bstore.Blockstore - cs *chain.ChainStore + cs *store.ChainStore } type Step struct { @@ -61,9 +62,9 @@ func NewHarness(t *testing.T) *Harness { t.Fatal(err) } - h.cs = chain.NewChainStore(h.bs, nil) + h.cs = store.NewChainStore(h.bs, nil) - h.vm, err = chain.NewVM(stateroot, 1, maddr, h.cs) + h.vm, err = vm.NewVM(stateroot, 1, maddr, h.cs) if err != nil { t.Fatal(err) } diff --git a/chain/blocksync.go b/chain/blocksync.go index 1c69264bb..ebbe963d6 100644 --- a/chain/blocksync.go +++ b/chain/blocksync.go @@ -11,6 +11,7 @@ import ( "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/protocol" + "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/lib/cborrpc" @@ -31,7 +32,7 @@ func init() { } type BlockSyncService struct { - cs *ChainStore + cs *store.ChainStore } type BlockSyncRequest struct { @@ -72,7 +73,7 @@ type BSTipSet struct { MsgIncludes [][]int } -func NewBlockSyncService(cs *ChainStore) *BlockSyncService { +func NewBlockSyncService(cs *store.ChainStore) *BlockSyncService { return &BlockSyncService{ cs: cs, } @@ -154,7 +155,7 @@ func (bss *BlockSyncService) collectChainSegment(start []cid.Cid, length uint64, } } -func (bss *BlockSyncService) gatherMessages(ts *TipSet) ([]*types.SignedMessage, [][]int, error) { +func (bss *BlockSyncService) gatherMessages(ts *types.TipSet) ([]*types.SignedMessage, [][]int, error) { msgmap := make(map[cid.Cid]int) var allmsgs []*types.SignedMessage var msgincl [][]int @@ -209,7 +210,7 @@ func (bs *BlockSync) getPeers() []peer.ID { return out } -func (bs *BlockSync) GetBlocks(ctx context.Context, tipset []cid.Cid, count int) ([]*TipSet, error) { +func (bs *BlockSync) GetBlocks(ctx context.Context, tipset []cid.Cid, count int) ([]*types.TipSet, error) { peers := bs.getPeers() perm := rand.Perm(len(peers)) // TODO: round robin through these peers on error @@ -241,7 +242,7 @@ func (bs *BlockSync) GetBlocks(ctx context.Context, tipset []cid.Cid, count int) } } -func (bs *BlockSync) GetFullTipSet(ctx context.Context, p peer.ID, h []cid.Cid) (*FullTipSet, error) { +func (bs *BlockSync) GetFullTipSet(ctx context.Context, p peer.ID, h []cid.Cid) (*store.FullTipSet, error) { // TODO: round robin through these peers on error req := &BlockSyncRequest{ @@ -276,7 +277,7 @@ func (bs *BlockSync) GetFullTipSet(ctx context.Context, p peer.ID, h []cid.Cid) } } -func (bs *BlockSync) GetChainMessages(ctx context.Context, h *TipSet, count uint64) ([]*BSTipSet, error) { +func (bs *BlockSync) GetChainMessages(ctx context.Context, h *types.TipSet, count uint64) ([]*BSTipSet, error) { peers := bs.getPeers() perm := rand.Perm(len(peers)) // TODO: round robin through these peers on error @@ -308,8 +309,8 @@ func (bs *BlockSync) GetChainMessages(ctx context.Context, h *TipSet, count uint } } -func bstsToFullTipSet(bts *BSTipSet) (*FullTipSet, error) { - fts := &FullTipSet{} +func bstsToFullTipSet(bts *BSTipSet) (*store.FullTipSet, error) { + fts := &store.FullTipSet{} for i, b := range bts.Blocks { fb := &types.FullBlock{ Header: b, @@ -341,16 +342,16 @@ func (bs *BlockSync) sendRequestToPeer(ctx context.Context, p peer.ID, req *Bloc return &res, nil } -func (bs *BlockSync) processBlocksResponse(req *BlockSyncRequest, res *BlockSyncResponse) ([]*TipSet, error) { - cur, err := NewTipSet(res.Chain[0].Blocks) +func (bs *BlockSync) processBlocksResponse(req *BlockSyncRequest, res *BlockSyncResponse) ([]*types.TipSet, error) { + cur, err := types.NewTipSet(res.Chain[0].Blocks) if err != nil { return nil, err } - out := []*TipSet{cur} + out := []*types.TipSet{cur} for bi := 1; bi < len(res.Chain); bi++ { next := res.Chain[bi].Blocks - nts, err := NewTipSet(next) + nts, err := types.NewTipSet(next) if err != nil { return nil, err } diff --git a/chain/messagepool.go b/chain/messagepool.go index e734287be..b3e90cc8c 100644 --- a/chain/messagepool.go +++ b/chain/messagepool.go @@ -4,9 +4,8 @@ import ( "sync" "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" - hamt "github.com/ipfs/go-hamt-ipld" ) type MessagePool struct { @@ -14,7 +13,7 @@ type MessagePool struct { pending map[address.Address]*msgSet - cs *ChainStore + cs *store.ChainStore } type msgSet struct { @@ -35,7 +34,7 @@ func (ms *msgSet) add(m *types.SignedMessage) { ms.msgs[m.Message.Nonce] = m } -func NewMessagePool(cs *ChainStore) *MessagePool { +func NewMessagePool(cs *store.ChainStore) *MessagePool { mp := &MessagePool{ pending: make(map[address.Address]*msgSet), cs: cs, @@ -58,12 +57,7 @@ func (mp *MessagePool) Add(m *types.SignedMessage) error { return err } - msb, err := m.ToStorageBlock() - if err != nil { - return err - } - - if err := mp.cs.bs.Put(msb); err != nil { + if _, err := mp.cs.PutMessage(m); err != nil { return err } @@ -86,20 +80,7 @@ func (mp *MessagePool) GetNonce(addr address.Address) (uint64, error) { return mset.startNonce + uint64(len(mset.msgs)), nil } - head := mp.cs.GetHeaviestTipSet() - - stc, err := mp.cs.TipSetState(head.Cids()) - if err != nil { - return 0, err - } - - cst := hamt.CSTFromBstore(mp.cs.bs) - st, err := state.LoadStateTree(cst, stc) - if err != nil { - return 0, err - } - - act, err := st.GetActor(addr) + act, err := mp.cs.GetActor(addr) if err != nil { return 0, err } @@ -142,7 +123,7 @@ func (mp *MessagePool) Pending() []*types.SignedMessage { return out } -func (mp *MessagePool) HeadChange(revert []*TipSet, apply []*TipSet) error { +func (mp *MessagePool) HeadChange(revert []*types.TipSet, apply []*types.TipSet) error { for _, ts := range revert { for _, b := range ts.Blocks() { msgs, err := mp.cs.MessagesForBlock(b) diff --git a/chain/mining.go b/chain/mining.go index 6084beed3..ec2396675 100644 --- a/chain/mining.go +++ b/chain/mining.go @@ -12,14 +12,16 @@ import ( "github.com/filecoin-project/go-lotus/chain/actors" "github.com/filecoin-project/go-lotus/chain/address" + "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" + "github.com/filecoin-project/go-lotus/chain/vm" ) -func miningRewardForBlock(base *TipSet) types.BigInt { +func miningRewardForBlock(base *types.TipSet) types.BigInt { return types.NewInt(10000) } -func MinerCreateBlock(cs *ChainStore, miner address.Address, parents *TipSet, tickets []types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*types.FullBlock, error) { +func MinerCreateBlock(cs *store.ChainStore, miner address.Address, parents *types.TipSet, tickets []types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*types.FullBlock, error) { st, err := cs.TipSetState(parents.Cids()) if err != nil { return nil, errors.Wrap(err, "failed to load tipset state") @@ -27,7 +29,7 @@ func MinerCreateBlock(cs *ChainStore, miner address.Address, parents *TipSet, ti height := parents.Height() + uint64(len(tickets)) - vm, err := NewVM(st, height, miner, cs) + vm, err := vm.NewVM(st, height, miner, cs) if err != nil { return nil, err } @@ -52,15 +54,12 @@ func MinerCreateBlock(cs *ChainStore, miner address.Address, parents *TipSet, ti if msg.Signature.TypeCode() == 2 { blsSigs = append(blsSigs, msg.Signature) - blk, err := msg.Message.ToStorageBlock() + c, err := cs.PutMessage(&msg.Message) if err != nil { return nil, err } - if err := cs.bs.Put(blk); err != nil { - return nil, err - } - msgCids = append(msgCids, blk.Cid()) + msgCids = append(msgCids, c) } else { msgCids = append(msgCids, msg.Cid()) } @@ -72,7 +71,7 @@ func MinerCreateBlock(cs *ChainStore, miner address.Address, parents *TipSet, ti receipts = append(receipts, rec) } - cst := hamt.CSTFromBstore(cs.bs) + cst := hamt.CSTFromBstore(cs.Blockstore()) msgroot, err := sharray.Build(context.TODO(), 4, toIfArr(msgCids), cst) if err != nil { return nil, err diff --git a/chain/store/fts.go b/chain/store/fts.go new file mode 100644 index 000000000..19801174c --- /dev/null +++ b/chain/store/fts.go @@ -0,0 +1,51 @@ +package store + +import ( + "github.com/filecoin-project/go-lotus/chain/types" + "github.com/ipfs/go-cid" +) + +// FullTipSet is an expanded version of the TipSet that contains all the blocks and messages +type FullTipSet struct { + Blocks []*types.FullBlock + tipset *types.TipSet + cids []cid.Cid +} + +func NewFullTipSet(blks []*types.FullBlock) *FullTipSet { + return &FullTipSet{ + Blocks: blks, + } +} + +func (fts *FullTipSet) Cids() []cid.Cid { + if fts.cids != nil { + return fts.cids + } + + var cids []cid.Cid + for _, b := range fts.Blocks { + cids = append(cids, b.Cid()) + } + fts.cids = cids + + return cids +} + +func (fts *FullTipSet) TipSet() *types.TipSet { + if fts.tipset != nil { + return fts.tipset + } + + var headers []*types.BlockHeader + for _, b := range fts.Blocks { + headers = append(headers, b.Header) + } + + ts, err := types.NewTipSet(headers) + if err != nil { + panic(err) + } + + return ts +} diff --git a/chain/chain.go b/chain/store/store.go similarity index 80% rename from chain/chain.go rename to chain/store/store.go index 1710a7dce..2ada7e3d1 100644 --- a/chain/chain.go +++ b/chain/store/store.go @@ -1,4 +1,4 @@ -package chain +package store import ( "context" @@ -10,9 +10,11 @@ import ( "github.com/filecoin-project/go-lotus/chain/state" "github.com/filecoin-project/go-lotus/chain/types" + block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" hamt "github.com/ipfs/go-hamt-ipld" + blockstore "github.com/ipfs/go-ipfs-blockstore" bstore "github.com/ipfs/go-ipfs-blockstore" logging "github.com/ipfs/go-log" "github.com/pkg/errors" @@ -20,9 +22,7 @@ import ( sharray "github.com/whyrusleeping/sharray" ) -const ForkLengthThreshold = 20 - -var log = logging.Logger("f2") +var log = logging.Logger("chainstore") var chainHeadKey = dstore.NewKey("head") @@ -31,11 +31,11 @@ type ChainStore struct { ds dstore.Datastore heaviestLk sync.Mutex - heaviest *TipSet + heaviest *types.TipSet bestTips *pubsub.PubSub - headChangeNotifs []func(rev, app []*TipSet) error + headChangeNotifs []func(rev, app []*types.TipSet) error } func NewChainStore(bs bstore.Blockstore, ds dstore.Batching) *ChainStore { @@ -71,7 +71,7 @@ func (cs *ChainStore) Load() error { return nil } -func (cs *ChainStore) writeHead(ts *TipSet) error { +func (cs *ChainStore) writeHead(ts *types.TipSet) error { data, err := json.Marshal(ts.Cids()) if err != nil { return errors.Wrap(err, "failed to marshal tipset") @@ -84,13 +84,13 @@ func (cs *ChainStore) writeHead(ts *TipSet) error { return nil } -func (cs *ChainStore) SubNewTips() chan *TipSet { +func (cs *ChainStore) SubNewTips() chan *types.TipSet { subch := cs.bestTips.Sub("best") - out := make(chan *TipSet) + out := make(chan *types.TipSet) go func() { defer close(out) for val := range subch { - out <- val.(*TipSet) + out <- val.(*types.TipSet) } }() return out @@ -103,7 +103,7 @@ const ( type HeadChange struct { Type string - Val *TipSet + Val *types.TipSet } func (cs *ChainStore) SubHeadChanges() chan *HeadChange { @@ -118,12 +118,12 @@ func (cs *ChainStore) SubHeadChanges() chan *HeadChange { return out } -func (cs *ChainStore) SubscribeHeadChanges(f func(rev, app []*TipSet) error) { +func (cs *ChainStore) SubscribeHeadChanges(f func(rev, app []*types.TipSet) error) { cs.headChangeNotifs = append(cs.headChangeNotifs, f) } func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error { - gents, err := NewTipSet([]*types.BlockHeader{b}) + gents, err := types.NewTipSet([]*types.BlockHeader{b}) if err != nil { return err } @@ -149,11 +149,11 @@ func (cs *ChainStore) PutTipSet(ts *FullTipSet) error { } } - cs.maybeTakeHeavierTipSet(ts.TipSet()) + cs.MaybeTakeHeavierTipSet(ts.TipSet()) return nil } -func (cs *ChainStore) maybeTakeHeavierTipSet(ts *TipSet) error { +func (cs *ChainStore) MaybeTakeHeavierTipSet(ts *types.TipSet) error { cs.heaviestLk.Lock() defer cs.heaviestLk.Unlock() if cs.heaviest == nil || cs.Weight(ts) > cs.Weight(cs.heaviest) { @@ -175,8 +175,8 @@ func (cs *ChainStore) maybeTakeHeavierTipSet(ts *TipSet) error { return nil } -func (cs *ChainStore) Contains(ts *TipSet) (bool, error) { - for _, c := range ts.cids { +func (cs *ChainStore) Contains(ts *types.TipSet) (bool, error) { + for _, c := range ts.Cids() { has, err := cs.bs.Has(c) if err != nil { return false, err @@ -198,7 +198,7 @@ func (cs *ChainStore) GetBlock(c cid.Cid) (*types.BlockHeader, error) { return types.DecodeBlock(sb.RawData()) } -func (cs *ChainStore) LoadTipSet(cids []cid.Cid) (*TipSet, error) { +func (cs *ChainStore) LoadTipSet(cids []cid.Cid) (*types.TipSet, error) { var blks []*types.BlockHeader for _, c := range cids { b, err := cs.GetBlock(c) @@ -209,11 +209,11 @@ func (cs *ChainStore) LoadTipSet(cids []cid.Cid) (*TipSet, error) { blks = append(blks, b) } - return NewTipSet(blks) + return types.NewTipSet(blks) } // returns true if 'a' is an ancestor of 'b' -func (cs *ChainStore) IsAncestorOf(a, b *TipSet) (bool, error) { +func (cs *ChainStore) IsAncestorOf(a, b *types.TipSet) (bool, error) { if b.Height() <= a.Height() { return false, nil } @@ -231,7 +231,7 @@ func (cs *ChainStore) IsAncestorOf(a, b *TipSet) (bool, error) { return cur.Equals(a), nil } -func (cs *ChainStore) NearestCommonAncestor(a, b *TipSet) (*TipSet, error) { +func (cs *ChainStore) NearestCommonAncestor(a, b *types.TipSet) (*types.TipSet, error) { l, _, err := cs.ReorgOps(a, b) if err != nil { return nil, err @@ -240,11 +240,11 @@ func (cs *ChainStore) NearestCommonAncestor(a, b *TipSet) (*TipSet, error) { return cs.LoadTipSet(l[len(l)-1].Parents()) } -func (cs *ChainStore) ReorgOps(a, b *TipSet) ([]*TipSet, []*TipSet, error) { +func (cs *ChainStore) ReorgOps(a, b *types.TipSet) ([]*types.TipSet, []*types.TipSet, error) { left := a right := b - var leftChain, rightChain []*TipSet + var leftChain, rightChain []*types.TipSet for !left.Equals(right) { if left.Height() > right.Height() { leftChain = append(leftChain, left) @@ -268,17 +268,17 @@ func (cs *ChainStore) ReorgOps(a, b *TipSet) ([]*TipSet, []*TipSet, error) { return leftChain, rightChain, nil } -func (cs *ChainStore) Weight(ts *TipSet) uint64 { +func (cs *ChainStore) Weight(ts *types.TipSet) uint64 { return ts.Blocks()[0].ParentWeight.Uint64() + uint64(len(ts.Cids())) } -func (cs *ChainStore) GetHeaviestTipSet() *TipSet { +func (cs *ChainStore) GetHeaviestTipSet() *types.TipSet { cs.heaviestLk.Lock() defer cs.heaviestLk.Unlock() return cs.heaviest } -func (cs *ChainStore) persistBlockHeader(b *types.BlockHeader) error { +func (cs *ChainStore) PersistBlockHeader(b *types.BlockHeader) error { sb, err := b.ToStorageBlock() if err != nil { return err @@ -288,34 +288,38 @@ func (cs *ChainStore) persistBlockHeader(b *types.BlockHeader) error { } func (cs *ChainStore) persistBlock(b *types.FullBlock) error { - if err := cs.persistBlockHeader(b.Header); err != nil { + if err := cs.PersistBlockHeader(b.Header); err != nil { return err } for _, m := range b.Messages { - if err := cs.PutMessage(m); err != nil { + if _, err := cs.PutMessage(m); err != nil { return err } } return nil } -func (cs *ChainStore) PutMessage(m *types.SignedMessage) error { +type storable interface { + ToStorageBlock() (block.Block, error) +} + +func (cs *ChainStore) PutMessage(m storable) (cid.Cid, error) { sb, err := m.ToStorageBlock() if err != nil { - return err + return cid.Undef, err } - return cs.bs.Put(sb) + return sb.Cid(), cs.bs.Put(sb) } func (cs *ChainStore) AddBlock(b *types.BlockHeader) error { - if err := cs.persistBlockHeader(b); err != nil { + if err := cs.PersistBlockHeader(b); err != nil { return err } - ts, _ := NewTipSet([]*types.BlockHeader{b}) - cs.maybeTakeHeavierTipSet(ts) + ts, _ := types.NewTipSet([]*types.BlockHeader{b}) + cs.MaybeTakeHeavierTipSet(ts) return nil } @@ -436,19 +440,7 @@ func (cs *ChainStore) LoadMessagesFromCids(cids []cid.Cid) ([]*types.SignedMessa } func (cs *ChainStore) GetBalance(addr address.Address) (types.BigInt, error) { - ts := cs.GetHeaviestTipSet() - stcid, err := cs.TipSetState(ts.Cids()) - if err != nil { - return types.BigInt{}, err - } - - cst := hamt.CSTFromBstore(cs.bs) - state, err := state.LoadStateTree(cst, stcid) - if err != nil { - return types.BigInt{}, err - } - - act, err := state.GetActor(addr) + act, err := cs.GetActor(addr) if err != nil { return types.BigInt{}, err } @@ -456,6 +448,22 @@ func (cs *ChainStore) GetBalance(addr address.Address) (types.BigInt, error) { return act.Balance, nil } +func (cs *ChainStore) GetActor(addr address.Address) (*types.Actor, error) { + ts := cs.GetHeaviestTipSet() + stcid, err := cs.TipSetState(ts.Cids()) + if err != nil { + return nil, err + } + + cst := hamt.CSTFromBstore(cs.bs) + state, err := state.LoadStateTree(cst, stcid) + if err != nil { + return nil, err + } + + return state.GetActor(addr) +} + func (cs *ChainStore) WaitForMessage(ctx context.Context, mcid cid.Cid) (cid.Cid, *types.MessageReceipt, error) { tsub := cs.SubHeadChanges() @@ -491,7 +499,7 @@ func (cs *ChainStore) WaitForMessage(ctx context.Context, mcid cid.Cid) (cid.Cid } } -func (cs *ChainStore) tipsetContainsMsg(ts *TipSet, msg cid.Cid) (cid.Cid, *types.MessageReceipt, error) { +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 { @@ -518,3 +526,7 @@ func (cs *ChainStore) blockContainsMsg(blk *types.BlockHeader, msg cid.Cid) (*ty return nil, nil } + +func (cs *ChainStore) Blockstore() blockstore.Blockstore { + return cs.bs +} diff --git a/chain/sync.go b/chain/sync.go index 199eccd60..bb1cc5023 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -6,29 +6,33 @@ import ( "sync" "github.com/filecoin-project/go-lotus/chain/actors" - "github.com/filecoin-project/go-lotus/chain/address" - "github.com/filecoin-project/go-lotus/chain/gen" - "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" "github.com/ipfs/go-cid" dstore "github.com/ipfs/go-datastore" "github.com/ipfs/go-hamt-ipld" bstore "github.com/ipfs/go-ipfs-blockstore" + logging "github.com/ipfs/go-log" peer "github.com/libp2p/go-libp2p-core/peer" "github.com/pkg/errors" "github.com/whyrusleeping/sharray" ) +const ForkLengthThreshold = 20 + +var log = logging.Logger("chain") + type Syncer struct { // The heaviest known tipset in the network. - head *TipSet + head *types.TipSet // The interface for accessing and putting tipsets into local storage - store *ChainStore + store *store.ChainStore // The known Genesis tipset - Genesis *TipSet + Genesis *types.TipSet // the current mode the syncer is in syncMode SyncMode @@ -45,17 +49,17 @@ type Syncer struct { // peer heads // Note: clear cache on disconnects - peerHeads map[peer.ID]*TipSet + peerHeads map[peer.ID]*types.TipSet peerHeadsLk sync.Mutex } -func NewSyncer(cs *ChainStore, bsync *BlockSync, self peer.ID) (*Syncer, error) { +func NewSyncer(cs *store.ChainStore, bsync *BlockSync, self peer.ID) (*Syncer, error) { gen, err := cs.GetGenesis() if err != nil { return nil, err } - gent, err := NewTipSet([]*types.BlockHeader{gen}) + gent, err := types.NewTipSet([]*types.BlockHeader{gen}) if err != nil { return nil, err } @@ -64,7 +68,7 @@ func NewSyncer(cs *ChainStore, bsync *BlockSync, self peer.ID) (*Syncer, error) syncMode: Bootstrap, Genesis: gent, Bsync: bsync, - peerHeads: make(map[peer.ID]*TipSet), + peerHeads: make(map[peer.ID]*types.TipSet), head: cs.GetHeaviestTipSet(), store: cs, self: self, @@ -84,13 +88,13 @@ type BadTipSetCache struct { } type BlockSet struct { - tset map[uint64]*TipSet - head *TipSet + tset map[uint64]*types.TipSet + head *types.TipSet } -func (bs *BlockSet) Insert(ts *TipSet) { +func (bs *BlockSet) Insert(ts *types.TipSet) { if bs.tset == nil { - bs.tset = make(map[uint64]*TipSet) + bs.tset = make(map[uint64]*types.TipSet) } if bs.head == nil || ts.Height() > bs.head.Height() { @@ -99,14 +103,14 @@ func (bs *BlockSet) Insert(ts *TipSet) { bs.tset[ts.Height()] = ts } -func (bs *BlockSet) GetByHeight(h uint64) *TipSet { +func (bs *BlockSet) GetByHeight(h uint64) *types.TipSet { return bs.tset[h] } -func (bs *BlockSet) PersistTo(cs *ChainStore) error { +func (bs *BlockSet) PersistTo(cs *store.ChainStore) error { for _, ts := range bs.tset { for _, b := range ts.Blocks() { - if err := cs.persistBlockHeader(b); err != nil { + if err := cs.PersistBlockHeader(b); err != nil { return err } } @@ -114,7 +118,7 @@ func (bs *BlockSet) PersistTo(cs *ChainStore) error { return nil } -func (bs *BlockSet) Head() *TipSet { +func (bs *BlockSet) Head() *types.TipSet { return bs.head } @@ -123,7 +127,7 @@ const BootstrapPeerThreshold = 1 // InformNewHead informs the syncer about a new potential tipset // This should be called when connecting to new peers, and additionally // when receiving new blocks from the network -func (syncer *Syncer) InformNewHead(from peer.ID, fts *FullTipSet) { +func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) { if fts == nil { panic("bad") } @@ -178,7 +182,7 @@ func (syncer *Syncer) InformNewBlock(from peer.ID, blk *types.FullBlock) { // TODO: search for other blocks that could form a tipset with this block // and then send that tipset to InformNewHead - fts := &FullTipSet{Blocks: []*types.FullBlock{blk}} + fts := &store.FullTipSet{Blocks: []*types.FullBlock{blk}} syncer.InformNewHead(from, fts) } @@ -200,7 +204,7 @@ func (syncer *Syncer) SyncBootstrap() { return } - blockSet := []*TipSet{selectedHead} + blockSet := []*types.TipSet{selectedHead} cur := selectedHead.Cids() // If, for some reason, we have a suffix of the chain locally, handle that here @@ -255,7 +259,7 @@ func (syncer *Syncer) SyncBootstrap() { for _, ts := range blockSet { for _, b := range ts.Blocks() { - if err := syncer.store.persistBlockHeader(b); err != nil { + if err := syncer.store.PersistBlockHeader(b); err != nil { log.Errorf("failed to persist synced blocks to the chainstore: %s", err) return } @@ -309,7 +313,7 @@ func (syncer *Syncer) SyncBootstrap() { } } - if err := copyBlockstore(bs, syncer.store.bs); err != nil { + if err := copyBlockstore(bs, syncer.store.Blockstore()); err != nil { log.Errorf("failed to persist temp blocks: %s", err) return } @@ -317,13 +321,13 @@ func (syncer *Syncer) SyncBootstrap() { head := blockSet[len(blockSet)-1] log.Errorf("Finished syncing! new head: %s", head.Cids()) - syncer.store.maybeTakeHeavierTipSet(selectedHead) + syncer.store.MaybeTakeHeavierTipSet(selectedHead) syncer.head = head syncer.syncMode = CaughtUp } -func reverse(tips []*TipSet) []*TipSet { - out := make([]*TipSet, len(tips)) +func reverse(tips []*types.TipSet) []*types.TipSet { + out := make([]*types.TipSet, len(tips)) for i := 0; i < len(tips); i++ { out[i] = tips[len(tips)-(i+1)] } @@ -350,14 +354,14 @@ func copyBlockstore(from, to bstore.Blockstore) error { return nil } -func zipTipSetAndMessages(cst *hamt.CborIpldStore, ts *TipSet, messages []*types.SignedMessage, msgincl [][]int) (*FullTipSet, error) { +func zipTipSetAndMessages(cst *hamt.CborIpldStore, ts *types.TipSet, messages []*types.SignedMessage, msgincl [][]int) (*store.FullTipSet, error) { if len(ts.Blocks()) != len(msgincl) { return nil, fmt.Errorf("msgincl length didnt match tipset size") } fmt.Println("zipping messages: ", msgincl) fmt.Println("into block: ", ts.Blocks()[0].Height) - fts := &FullTipSet{} + fts := &store.FullTipSet{} for bi, b := range ts.Blocks() { var msgs []*types.SignedMessage var msgCids []interface{} @@ -388,8 +392,8 @@ func zipTipSetAndMessages(cst *hamt.CborIpldStore, ts *TipSet, messages []*types return fts, nil } -func (syncer *Syncer) selectHead(heads map[peer.ID]*TipSet) (*TipSet, error) { - var headsArr []*TipSet +func (syncer *Syncer) selectHead(heads map[peer.ID]*types.TipSet) (*types.TipSet, error) { + var headsArr []*types.TipSet for _, ts := range heads { headsArr = append(headsArr, ts) } @@ -432,7 +436,7 @@ func (syncer *Syncer) selectHead(heads map[peer.ID]*TipSet) (*TipSet, error) { return sel, nil } -func (syncer *Syncer) FetchTipSet(ctx context.Context, p peer.ID, cids []cid.Cid) (*FullTipSet, error) { +func (syncer *Syncer) FetchTipSet(ctx context.Context, p peer.ID, cids []cid.Cid) (*store.FullTipSet, error) { if fts, err := syncer.tryLoadFullTipSet(cids); err == nil { return fts, nil } @@ -440,13 +444,13 @@ func (syncer *Syncer) FetchTipSet(ctx context.Context, p peer.ID, cids []cid.Cid return syncer.Bsync.GetFullTipSet(ctx, p, cids) } -func (syncer *Syncer) tryLoadFullTipSet(cids []cid.Cid) (*FullTipSet, error) { +func (syncer *Syncer) tryLoadFullTipSet(cids []cid.Cid) (*store.FullTipSet, error) { ts, err := syncer.store.LoadTipSet(cids) if err != nil { return nil, err } - fts := &FullTipSet{} + fts := &store.FullTipSet{} for _, b := range ts.Blocks() { messages, err := syncer.store.MessagesForBlock(b) if err != nil { @@ -463,54 +467,9 @@ func (syncer *Syncer) tryLoadFullTipSet(cids []cid.Cid) (*FullTipSet, error) { return fts, nil } -// FullTipSet is an expanded version of the TipSet that contains all the blocks and messages -type FullTipSet struct { - Blocks []*types.FullBlock - tipset *TipSet - cids []cid.Cid -} - -func NewFullTipSet(blks []*types.FullBlock) *FullTipSet { - return &FullTipSet{ - Blocks: blks, - } -} - -func (fts *FullTipSet) Cids() []cid.Cid { - if fts.cids != nil { - return fts.cids - } - - var cids []cid.Cid - for _, b := range fts.Blocks { - cids = append(cids, b.Cid()) - } - fts.cids = cids - - return cids -} - -func (fts *FullTipSet) TipSet() *TipSet { - if fts.tipset != nil { - return fts.tipset - } - - var headers []*types.BlockHeader - for _, b := range fts.Blocks { - headers = append(headers, b.Header) - } - - ts, err := NewTipSet(headers) - if err != nil { - panic(err) - } - - return ts -} - // SyncCaughtUp is used to stay in sync once caught up to // the rest of the network. -func (syncer *Syncer) SyncCaughtUp(maybeHead *FullTipSet) error { +func (syncer *Syncer) SyncCaughtUp(maybeHead *store.FullTipSet) error { ts := maybeHead.TipSet() if syncer.Genesis.Equals(ts) { return nil @@ -541,7 +500,7 @@ func (syncer *Syncer) SyncCaughtUp(maybeHead *FullTipSet) error { return nil } -func (syncer *Syncer) ValidateTipSet(fts *FullTipSet) error { +func (syncer *Syncer) ValidateTipSet(fts *store.FullTipSet) error { ts := fts.TipSet() if ts.Equals(syncer.Genesis) { return nil @@ -567,7 +526,7 @@ func (syncer *Syncer) ValidateBlock(b *types.FullBlock) error { return err } - vm, err := NewVM(stateroot, b.Header.Height, b.Header.Miner, syncer.store) + vm, err := vm.NewVM(stateroot, b.Header.Height, b.Header.Miner, syncer.store) if err != nil { return err } @@ -586,7 +545,7 @@ func (syncer *Syncer) ValidateBlock(b *types.FullBlock) error { receipts = append(receipts, receipt) } - cst := hamt.CSTFromBstore(syncer.store.bs) + cst := hamt.CSTFromBstore(syncer.store.Blockstore()) recptRoot, err := sharray.Build(context.TODO(), 4, receipts, cst) if err != nil { return err @@ -608,84 +567,14 @@ func (syncer *Syncer) ValidateBlock(b *types.FullBlock) error { } -func DeductFunds(act *types.Actor, amt types.BigInt) error { - if types.BigCmp(act.Balance, amt) < 0 { - return fmt.Errorf("not enough funds") - } - - act.Balance = types.BigSub(act.Balance, amt) - return nil -} - -func DepositFunds(act *types.Actor, amt types.BigInt) { - act.Balance = types.BigAdd(act.Balance, amt) -} - -func TryCreateAccountActor(st *state.StateTree, addr address.Address) (*types.Actor, error) { - act, err := makeActor(st, addr) - if err != nil { - return nil, err - } - - _, err = st.RegisterNewAddress(addr, act) - if err != nil { - return nil, err - } - - return act, nil -} - -func makeActor(st *state.StateTree, addr address.Address) (*types.Actor, error) { - switch addr.Protocol() { - case address.BLS: - return NewBLSAccountActor(st, addr) - case address.SECP256K1: - return NewSecp256k1AccountActor(st, addr) - case address.ID: - return nil, fmt.Errorf("no actor with given ID") - case address.Actor: - return nil, fmt.Errorf("no such actor") - default: - return nil, fmt.Errorf("address has unsupported protocol: %d", addr.Protocol()) - } -} - -func NewBLSAccountActor(st *state.StateTree, addr address.Address) (*types.Actor, error) { - var acstate actors.AccountActorState - acstate.Address = addr - - c, err := st.Store.Put(context.TODO(), acstate) - if err != nil { - return nil, err - } - - nact := &types.Actor{ - Code: actors.AccountActorCodeCid, - Balance: types.NewInt(0), - Head: c, - } - - return nact, nil -} - -func NewSecp256k1AccountActor(st *state.StateTree, addr address.Address) (*types.Actor, error) { - nact := &types.Actor{ - Code: actors.AccountActorCodeCid, - Balance: types.NewInt(0), - Head: gen.EmptyObjectCid, - } - - return nact, nil -} - -func (syncer *Syncer) Punctual(ts *TipSet) bool { +func (syncer *Syncer) Punctual(ts *types.TipSet) bool { return true } -func (syncer *Syncer) collectChainCaughtUp(fts *FullTipSet) ([]*FullTipSet, error) { +func (syncer *Syncer) collectChainCaughtUp(fts *store.FullTipSet) ([]*store.FullTipSet, error) { // fetch tipset and messages via bitswap - chain := []*FullTipSet{fts} + chain := []*store.FullTipSet{fts} cur := fts.TipSet() for { diff --git a/chain/types.go b/chain/types.go index 56928f0f3..6a021a35a 100644 --- a/chain/types.go +++ b/chain/types.go @@ -1,7 +1,6 @@ package chain import ( - "encoding/json" "fmt" block "github.com/ipfs/go-block-format" @@ -18,89 +17,6 @@ func init() { cbor.RegisterCborType(BlockMsg{}) } -type TipSet struct { - cids []cid.Cid - blks []*types.BlockHeader - height uint64 -} - -// why didnt i just export the fields? Because the struct has methods with the -// same names already -type expTipSet struct { - Cids []cid.Cid - Blocks []*types.BlockHeader - Height uint64 -} - -func (ts *TipSet) MarshalJSON() ([]byte, error) { - return json.Marshal(expTipSet{ - Cids: ts.cids, - Blocks: ts.blks, - Height: ts.height, - }) -} - -func (ts *TipSet) UnmarshalJSON(b []byte) error { - var ets expTipSet - if err := json.Unmarshal(b, &ets); err != nil { - return err - } - - ts.cids = ets.Cids - ts.blks = ets.Blocks - ts.height = ets.Height - return nil -} - -func NewTipSet(blks []*types.BlockHeader) (*TipSet, error) { - var ts TipSet - ts.cids = []cid.Cid{blks[0].Cid()} - ts.blks = blks - for _, b := range blks[1:] { - if b.Height != blks[0].Height { - return nil, fmt.Errorf("cannot create tipset with mismatching heights") - } - ts.cids = append(ts.cids, b.Cid()) - } - ts.height = blks[0].Height - - return &ts, nil -} - -func (ts *TipSet) Cids() []cid.Cid { - return ts.cids -} - -func (ts *TipSet) Height() uint64 { - return ts.height -} - -func (ts *TipSet) Weight() uint64 { - panic("if tipsets are going to have weight on them, we need to wire that through") -} - -func (ts *TipSet) Parents() []cid.Cid { - return ts.blks[0].Parents -} - -func (ts *TipSet) Blocks() []*types.BlockHeader { - return ts.blks -} - -func (ts *TipSet) Equals(ots *TipSet) bool { - if len(ts.blks) != len(ots.blks) { - return false - } - - for i, b := range ts.blks { - if b.Cid() != ots.blks[i].Cid() { - return false - } - } - - return true -} - func IpldDecode(block block.Block) (ipld.Node, error) { var i interface{} if err := cbor.DecodeInto(block.RawData(), &i); err != nil { diff --git a/chain/types/tipset.go b/chain/types/tipset.go new file mode 100644 index 000000000..e4b8945c6 --- /dev/null +++ b/chain/types/tipset.go @@ -0,0 +1,91 @@ +package types + +import ( + "encoding/json" + "fmt" + + "github.com/ipfs/go-cid" +) + +type TipSet struct { + cids []cid.Cid + blks []*BlockHeader + height uint64 +} + +// why didnt i just export the fields? Because the struct has methods with the +// same names already +type expTipSet struct { + Cids []cid.Cid + Blocks []*BlockHeader + Height uint64 +} + +func (ts *TipSet) MarshalJSON() ([]byte, error) { + return json.Marshal(expTipSet{ + Cids: ts.cids, + Blocks: ts.blks, + Height: ts.height, + }) +} + +func (ts *TipSet) UnmarshalJSON(b []byte) error { + var ets expTipSet + if err := json.Unmarshal(b, &ets); err != nil { + return err + } + + ts.cids = ets.Cids + ts.blks = ets.Blocks + ts.height = ets.Height + return nil +} + +func NewTipSet(blks []*BlockHeader) (*TipSet, error) { + var ts TipSet + ts.cids = []cid.Cid{blks[0].Cid()} + ts.blks = blks + for _, b := range blks[1:] { + if b.Height != blks[0].Height { + return nil, fmt.Errorf("cannot create tipset with mismatching heights") + } + ts.cids = append(ts.cids, b.Cid()) + } + ts.height = blks[0].Height + + return &ts, nil +} + +func (ts *TipSet) Cids() []cid.Cid { + return ts.cids +} + +func (ts *TipSet) Height() uint64 { + return ts.height +} + +func (ts *TipSet) Weight() uint64 { + panic("if tipsets are going to have weight on them, we need to wire that through") +} + +func (ts *TipSet) Parents() []cid.Cid { + return ts.blks[0].Parents +} + +func (ts *TipSet) Blocks() []*BlockHeader { + return ts.blks +} + +func (ts *TipSet) Equals(ots *TipSet) bool { + if len(ts.blks) != len(ots.blks) { + return false + } + + for i, b := range ts.blks { + if b.Cid() != ots.blks[i].Cid() { + return false + } + } + + return true +} diff --git a/chain/invoker.go b/chain/vm/invoker.go similarity index 93% rename from chain/invoker.go rename to chain/vm/invoker.go index 0d8884d92..c9ec606da 100644 --- a/chain/invoker.go +++ b/chain/vm/invoker.go @@ -1,4 +1,4 @@ -package chain +package vm import ( "fmt" @@ -15,7 +15,7 @@ type invoker struct { builtInCode map[cid.Cid]nativeCode } -type invokeFunc func(act *types.Actor, vmctx *VMContext, params []byte) ([]byte, aerrors.ActorError) +type invokeFunc func(act *types.Actor, vmctx types.VMContext, params []byte) ([]byte, aerrors.ActorError) type nativeCode []invokeFunc func newInvoker() *invoker { @@ -31,7 +31,7 @@ func newInvoker() *invoker { return inv } -func (inv *invoker) Invoke(act *types.Actor, vmctx *VMContext, method uint64, params []byte) ([]byte, aerrors.ActorError) { +func (inv *invoker) Invoke(act *types.Actor, vmctx types.VMContext, method uint64, params []byte) ([]byte, aerrors.ActorError) { code, ok := inv.builtInCode[act.Code] if !ok { diff --git a/chain/invoker_test.go b/chain/vm/invoker_test.go similarity index 99% rename from chain/invoker_test.go rename to chain/vm/invoker_test.go index 7afca2ab8..d1a8207fc 100644 --- a/chain/invoker_test.go +++ b/chain/vm/invoker_test.go @@ -1,4 +1,4 @@ -package chain +package vm import ( "testing" diff --git a/chain/vm/mkactor.go b/chain/vm/mkactor.go new file mode 100644 index 000000000..52e8cd7f2 --- /dev/null +++ b/chain/vm/mkactor.go @@ -0,0 +1,69 @@ +package vm + +import ( + "context" + "fmt" + + "github.com/filecoin-project/go-lotus/chain/actors" + "github.com/filecoin-project/go-lotus/chain/address" + "github.com/filecoin-project/go-lotus/chain/gen" + "github.com/filecoin-project/go-lotus/chain/state" + "github.com/filecoin-project/go-lotus/chain/types" +) + +func TryCreateAccountActor(st *state.StateTree, addr address.Address) (*types.Actor, error) { + act, err := makeActor(st, addr) + if err != nil { + return nil, err + } + + _, err = st.RegisterNewAddress(addr, act) + if err != nil { + return nil, err + } + + return act, nil +} + +func makeActor(st *state.StateTree, addr address.Address) (*types.Actor, error) { + switch addr.Protocol() { + case address.BLS: + return NewBLSAccountActor(st, addr) + case address.SECP256K1: + return NewSecp256k1AccountActor(st, addr) + case address.ID: + return nil, fmt.Errorf("no actor with given ID") + case address.Actor: + return nil, fmt.Errorf("no such actor") + default: + return nil, fmt.Errorf("address has unsupported protocol: %d", addr.Protocol()) + } +} + +func NewBLSAccountActor(st *state.StateTree, addr address.Address) (*types.Actor, error) { + var acstate actors.AccountActorState + acstate.Address = addr + + c, err := st.Store.Put(context.TODO(), acstate) + if err != nil { + return nil, err + } + + nact := &types.Actor{ + Code: actors.AccountActorCodeCid, + Balance: types.NewInt(0), + Head: c, + } + + return nact, nil +} + +func NewSecp256k1AccountActor(st *state.StateTree, addr address.Address) (*types.Actor, error) { + nact := &types.Actor{ + Code: actors.AccountActorCodeCid, + Balance: types.NewInt(0), + Head: gen.EmptyObjectCid, + } + + return nact, nil +} diff --git a/chain/vm.go b/chain/vm/vm.go similarity index 93% rename from chain/vm.go rename to chain/vm/vm.go index ce244323d..d1d7c3f38 100644 --- a/chain/vm.go +++ b/chain/vm/vm.go @@ -1,4 +1,4 @@ -package chain +package vm import ( "context" @@ -8,6 +8,7 @@ import ( "github.com/filecoin-project/go-lotus/chain/actors/aerrors" "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/lib/bufbstore" @@ -135,7 +136,6 @@ func (vmctx *VMContext) VerifySignature(sig types.Signature, act address.Address } func (vm *VM) makeVMContext(sroot cid.Cid, origin address.Address, msg *types.Message) *VMContext { - cst := hamt.CSTFromBstore(vm.cs.bs) return &VMContext{ vm: vm, @@ -144,9 +144,9 @@ func (vm *VM) makeVMContext(sroot cid.Cid, origin address.Address, msg *types.Me msg: msg, origin: origin, height: vm.blockHeight, - cst: cst, + cst: vm.cst, storage: &storage{ - cst: cst, + cst: vm.cst, head: sroot, }, } @@ -155,15 +155,16 @@ func (vm *VM) makeVMContext(sroot cid.Cid, origin address.Address, msg *types.Me type VM struct { cstate *state.StateTree base cid.Cid - cs *ChainStore + cs *store.ChainStore + cst *hamt.CborIpldStore buf *bufbstore.BufferedBS blockHeight uint64 blockMiner address.Address inv *invoker } -func NewVM(base cid.Cid, height uint64, maddr address.Address, cs *ChainStore) (*VM, error) { - buf := bufbstore.NewBufferedBstore(cs.bs) +func NewVM(base cid.Cid, height uint64, maddr address.Address, cs *store.ChainStore) (*VM, error) { + buf := bufbstore.NewBufferedBstore(cs.Blockstore()) cst := hamt.CSTFromBstore(buf) state, err := state.LoadStateTree(cst, base) if err != nil { @@ -174,6 +175,7 @@ func NewVM(base cid.Cid, height uint64, maddr address.Address, cs *ChainStore) ( cstate: state, base: base, cs: cs, + cst: cst, buf: buf, blockHeight: height, blockMiner: maddr, @@ -340,3 +342,16 @@ func (vm *VM) Invoke(act *types.Actor, vmctx *VMContext, method uint64, params [ } return ret, nil } + +func DeductFunds(act *types.Actor, amt types.BigInt) error { + if types.BigCmp(act.Balance, amt) < 0 { + return fmt.Errorf("not enough funds") + } + + act.Balance = types.BigSub(act.Balance, amt) + return nil +} + +func DepositFunds(act *types.Actor, amt types.BigInt) { + act.Balance = types.BigAdd(act.Balance, amt) +} diff --git a/go.sum b/go.sum index 23c5146ee..838fc3446 100644 --- a/go.sum +++ b/go.sum @@ -458,6 +458,7 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= @@ -572,6 +573,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -629,6 +631,7 @@ google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRn google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= diff --git a/miner/miner.go b/miner/miner.go index 5e0e53018..35fb2da21 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -19,22 +19,22 @@ type api interface { // returns a set of messages that havent been included in the chain as of // the given tipset - MpoolPending(ctx context.Context, base *chain.TipSet) ([]*types.SignedMessage, error) + MpoolPending(ctx context.Context, base *types.TipSet) ([]*types.SignedMessage, error) // Returns the best tipset for the miner to mine on top of. // TODO: Not sure this feels right (including the messages api). Miners // will likely want to have more control over exactly which blocks get // mined on, and which messages are included. - ChainHead(context.Context) (*chain.TipSet, error) + ChainHead(context.Context) (*types.TipSet, error) // returns the lookback randomness from the chain used for the election - ChainGetRandomness(context.Context, *chain.TipSet) ([]byte, error) + ChainGetRandomness(context.Context, *types.TipSet) ([]byte, error) // create a block // it seems realllllly annoying to do all the actions necessary to build a // block through the API. so, we just add the block creation to the API // now, all the 'miner' does is check if they win, and call create block - MinerCreateBlock(context.Context, address.Address, *chain.TipSet, []types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) + MinerCreateBlock(context.Context, address.Address, *types.TipSet, []types.Ticket, types.ElectionProof, []*types.SignedMessage) (*chain.BlockMsg, error) } func NewMiner(api api, addr address.Address) *Miner { @@ -79,7 +79,7 @@ func (m *Miner) Mine(ctx context.Context) { } type MiningBase struct { - ts *chain.TipSet + ts *types.TipSet tickets []types.Ticket } diff --git a/node/builder.go b/node/builder.go index ce11ff085..17cd26a3f 100644 --- a/node/builder.go +++ b/node/builder.go @@ -24,6 +24,7 @@ import ( "github.com/filecoin-project/go-lotus/api" "github.com/filecoin-project/go-lotus/chain" + "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/node/config" "github.com/filecoin-project/go-lotus/node/hello" @@ -183,7 +184,7 @@ func Online() Option { Override(HandleIncomingMessagesKey, modules.HandleIncomingMessages), - Override(new(*chain.ChainStore), modules.ChainStore), + Override(new(*store.ChainStore), modules.ChainStore), Override(new(blockstore.GCLocker), blockstore.NewGCLocker), Override(new(blockstore.GCBlockstore), blockstore.NewGCBlockstore), diff --git a/node/hello/hello.go b/node/hello/hello.go index a3deb387a..4d7e59495 100644 --- a/node/hello/hello.go +++ b/node/hello/hello.go @@ -12,6 +12,7 @@ import ( "github.com/libp2p/go-libp2p-core/peer" "github.com/filecoin-project/go-lotus/chain" + "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/lib/cborrpc" ) @@ -32,11 +33,11 @@ type Message struct { type Service struct { newStream chain.NewStreamFunc - cs *chain.ChainStore + cs *store.ChainStore syncer *chain.Syncer } -func NewHelloService(h host.Host, cs *chain.ChainStore, syncer *chain.Syncer) *Service { +func NewHelloService(h host.Host, cs *store.ChainStore, syncer *chain.Syncer) *Service { return &Service{ newStream: h.NewStream, diff --git a/node/impl/full.go b/node/impl/full.go index a0921300c..99c75eb5f 100644 --- a/node/impl/full.go +++ b/node/impl/full.go @@ -2,11 +2,13 @@ package impl import ( "context" + "golang.org/x/xerrors" "github.com/filecoin-project/go-lotus/api" "github.com/filecoin-project/go-lotus/chain" "github.com/filecoin-project/go-lotus/chain/address" + "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/miner" "github.com/filecoin-project/go-lotus/node/client" @@ -23,7 +25,7 @@ type FullNodeAPI struct { CommonAPI - Chain *chain.ChainStore + Chain *store.ChainStore PubSub *pubsub.PubSub Mpool *chain.MessagePool Wallet *chain.Wallet @@ -43,11 +45,11 @@ func (a *FullNodeAPI) ChainSubmitBlock(ctx context.Context, blk *chain.BlockMsg) return a.PubSub.Publish("/fil/blocks", b) } -func (a *FullNodeAPI) ChainHead(context.Context) (*chain.TipSet, error) { +func (a *FullNodeAPI) ChainHead(context.Context) (*types.TipSet, error) { return a.Chain.GetHeaviestTipSet(), nil } -func (a *FullNodeAPI) ChainGetRandomness(ctx context.Context, pts *chain.TipSet) ([]byte, error) { +func (a *FullNodeAPI) ChainGetRandomness(ctx context.Context, pts *types.TipSet) ([]byte, error) { // TODO: this needs to look back in the chain for the right random beacon value return []byte("foo bar random"), nil } @@ -69,7 +71,7 @@ func (a *FullNodeAPI) ChainGetBlockMessages(ctx context.Context, msg cid.Cid) ([ return a.Chain.MessagesForBlock(b) } -func (a *FullNodeAPI) MpoolPending(ctx context.Context, ts *chain.TipSet) ([]*types.SignedMessage, error) { +func (a *FullNodeAPI) MpoolPending(ctx context.Context, ts *types.TipSet) ([]*types.SignedMessage, error) { // TODO: need to make sure we don't return messages that were already included in the referenced chain // also need to accept ts == nil just fine, assume nil == chain.Head() return a.Mpool.Pending(), nil @@ -97,7 +99,7 @@ func (a *FullNodeAPI) MinerStart(ctx context.Context, addr address.Address) erro return nil } -func (a *FullNodeAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *chain.TipSet, tickets []types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) { +func (a *FullNodeAPI) MinerCreateBlock(ctx context.Context, addr address.Address, parents *types.TipSet, tickets []types.Ticket, proof types.ElectionProof, msgs []*types.SignedMessage) (*chain.BlockMsg, error) { fblk, err := chain.MinerCreateBlock(a.Chain, addr, parents, tickets, proof, msgs) if err != nil { return nil, err diff --git a/node/modules/core.go b/node/modules/core.go index 7dae7ee41..cf7600ffd 100644 --- a/node/modules/core.go +++ b/node/modules/core.go @@ -30,7 +30,7 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-lotus/api" - "github.com/filecoin-project/go-lotus/chain" + "github.com/filecoin-project/go-lotus/chain/store" "github.com/filecoin-project/go-lotus/chain/types" "github.com/filecoin-project/go-lotus/node/modules/helpers" "github.com/filecoin-project/go-lotus/node/repo" @@ -58,7 +58,7 @@ func Bitswap(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, rt routin return exch } -func SetGenesis(cs *chain.ChainStore, g Genesis) error { +func SetGenesis(cs *store.ChainStore, g Genesis) error { _, err := cs.GetGenesis() if err == nil { return nil // already set, noop @@ -179,8 +179,8 @@ func ClientDAG(lc fx.Lifecycle, fstore *filestore.Filestore) ipld.DAGService { return dag } -func ChainStore(lc fx.Lifecycle, bs blockstore.Blockstore, ds datastore.Batching) *chain.ChainStore { - chain := chain.NewChainStore(bs, ds) +func ChainStore(lc fx.Lifecycle, bs blockstore.Blockstore, ds datastore.Batching) *store.ChainStore { + chain := store.NewChainStore(bs, ds) lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error {