lotus/chain/chain.go

521 lines
10 KiB
Go
Raw Normal View History

2019-07-05 14:29:17 +00:00
package chain
import (
"context"
"encoding/json"
2019-07-05 14:29:17 +00:00
"fmt"
"sync"
2019-07-08 12:51:45 +00:00
"github.com/filecoin-project/go-lotus/chain/address"
"github.com/filecoin-project/go-lotus/chain/state"
"github.com/filecoin-project/go-lotus/chain/types"
2019-07-08 12:51:45 +00:00
2019-07-05 14:29:17 +00:00
"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/pkg/errors"
pubsub "github.com/whyrusleeping/pubsub"
2019-07-05 14:36:08 +00:00
sharray "github.com/whyrusleeping/sharray"
2019-07-05 14:29:17 +00:00
)
const ForkLengthThreshold = 20
var log = logging.Logger("f2")
var chainHeadKey = dstore.NewKey("head")
2019-07-05 14:29:17 +00:00
type ChainStore struct {
bs bstore.Blockstore
ds dstore.Datastore
2019-07-05 14:29:17 +00:00
heaviestLk sync.Mutex
heaviest *TipSet
bestTips *pubsub.PubSub
headChangeNotifs []func(rev, app []*TipSet) error
2019-07-05 14:29:17 +00:00
}
func NewChainStore(bs bstore.Blockstore, ds dstore.Batching) *ChainStore {
2019-07-05 14:29:17 +00:00
return &ChainStore{
bs: bs,
ds: ds,
bestTips: pubsub.New(64),
}
}
func (cs *ChainStore) Load() error {
head, err := cs.ds.Get(chainHeadKey)
2019-07-24 21:10:27 +00:00
if err == dstore.ErrNotFound {
log.Warn("no previous chain state found")
return nil
}
if err != nil {
return errors.Wrap(err, "failed to load chain state from datastore")
}
var tscids []cid.Cid
if err := json.Unmarshal(head, &tscids); err != nil {
return errors.Wrap(err, "failed to unmarshal stored chain head")
}
ts, err := cs.LoadTipSet(tscids)
if err != nil {
return err
}
cs.heaviest = ts
return nil
}
func (cs *ChainStore) writeHead(ts *TipSet) error {
data, err := json.Marshal(ts.Cids())
if err != nil {
return errors.Wrap(err, "failed to marshal tipset")
}
if err := cs.ds.Put(chainHeadKey, data); err != nil {
return errors.Wrap(err, "failed to write chain head to datastore")
}
return nil
}
func (cs *ChainStore) SubNewTips() chan *TipSet {
subch := cs.bestTips.Sub("best")
out := make(chan *TipSet)
go func() {
defer close(out)
for val := range subch {
out <- val.(*TipSet)
}
}()
return out
}
const (
HCRevert = "revert"
HCApply = "apply"
)
type HeadChange struct {
Type string
Val *TipSet
}
func (cs *ChainStore) SubHeadChanges() chan *HeadChange {
subch := cs.bestTips.Sub("headchange")
out := make(chan *HeadChange, 16)
go func() {
defer close(out)
for val := range subch {
out <- val.(*HeadChange)
}
}()
return out
}
func (cs *ChainStore) SubscribeHeadChanges(f func(rev, app []*TipSet) error) {
cs.headChangeNotifs = append(cs.headChangeNotifs, f)
2019-07-05 14:29:17 +00:00
}
func (cs *ChainStore) SetGenesis(b *types.BlockHeader) error {
gents, err := NewTipSet([]*types.BlockHeader{b})
2019-07-05 14:29:17 +00:00
if err != nil {
return err
}
fts := &FullTipSet{
Blocks: []*types.FullBlock{
2019-07-05 14:29:17 +00:00
{Header: b},
},
}
cs.heaviest = gents
if err := cs.PutTipSet(fts); err != nil {
return err
}
return cs.ds.Put(dstore.NewKey("0"), b.Cid().Bytes())
2019-07-05 14:29:17 +00:00
}
func (cs *ChainStore) PutTipSet(ts *FullTipSet) error {
for _, b := range ts.Blocks {
if err := cs.persistBlock(b); err != nil {
return err
}
}
cs.maybeTakeHeavierTipSet(ts.TipSet())
return nil
}
func (cs *ChainStore) maybeTakeHeavierTipSet(ts *TipSet) error {
cs.heaviestLk.Lock()
defer cs.heaviestLk.Unlock()
if cs.heaviest == nil || cs.Weight(ts) > cs.Weight(cs.heaviest) {
revert, apply, err := cs.ReorgOps(cs.heaviest, ts)
if err != nil {
return err
}
for _, hcf := range cs.headChangeNotifs {
hcf(revert, apply)
}
2019-07-11 05:17:49 +00:00
log.Infof("New heaviest tipset! %s", ts.Cids())
2019-07-05 14:29:17 +00:00
cs.heaviest = ts
if err := cs.writeHead(ts); err != nil {
2019-07-24 21:10:27 +00:00
log.Errorf("failed to write chain head: %s", err)
return nil
}
2019-07-05 14:29:17 +00:00
}
return nil
}
func (cs *ChainStore) Contains(ts *TipSet) (bool, error) {
for _, c := range ts.cids {
has, err := cs.bs.Has(c)
if err != nil {
return false, err
}
if !has {
return false, nil
}
}
return true, nil
}
func (cs *ChainStore) GetBlock(c cid.Cid) (*types.BlockHeader, error) {
2019-07-05 14:29:17 +00:00
sb, err := cs.bs.Get(c)
if err != nil {
return nil, err
}
return types.DecodeBlock(sb.RawData())
2019-07-05 14:29:17 +00:00
}
func (cs *ChainStore) LoadTipSet(cids []cid.Cid) (*TipSet, error) {
var blks []*types.BlockHeader
2019-07-05 14:29:17 +00:00
for _, c := range cids {
b, err := cs.GetBlock(c)
if err != nil {
return nil, err
}
blks = append(blks, b)
}
return NewTipSet(blks)
}
// returns true if 'a' is an ancestor of 'b'
func (cs *ChainStore) IsAncestorOf(a, b *TipSet) (bool, error) {
if b.Height() <= a.Height() {
return false, nil
}
cur := b
for !a.Equals(cur) && cur.Height() > a.Height() {
next, err := cs.LoadTipSet(b.Parents())
if err != nil {
return false, err
}
cur = next
}
return cur.Equals(a), nil
}
func (cs *ChainStore) NearestCommonAncestor(a, b *TipSet) (*TipSet, error) {
l, _, err := cs.ReorgOps(a, b)
if err != nil {
return nil, err
}
return cs.LoadTipSet(l[len(l)-1].Parents())
}
func (cs *ChainStore) ReorgOps(a, b *TipSet) ([]*TipSet, []*TipSet, error) {
left := a
right := b
var leftChain, rightChain []*TipSet
for !left.Equals(right) {
if left.Height() > right.Height() {
leftChain = append(leftChain, left)
par, err := cs.LoadTipSet(left.Parents())
if err != nil {
return nil, nil, err
}
left = par
} else {
rightChain = append(rightChain, right)
par, err := cs.LoadTipSet(right.Parents())
if err != nil {
return nil, nil, err
}
right = par
}
}
return leftChain, rightChain, nil
}
func (cs *ChainStore) Weight(ts *TipSet) uint64 {
return ts.Blocks()[0].ParentWeight.Uint64() + uint64(len(ts.Cids()))
}
func (cs *ChainStore) GetHeaviestTipSet() *TipSet {
cs.heaviestLk.Lock()
defer cs.heaviestLk.Unlock()
return cs.heaviest
}
func (cs *ChainStore) persistBlockHeader(b *types.BlockHeader) error {
2019-07-05 14:29:17 +00:00
sb, err := b.ToStorageBlock()
if err != nil {
return err
}
return cs.bs.Put(sb)
}
func (cs *ChainStore) persistBlock(b *types.FullBlock) error {
2019-07-05 14:29:17 +00:00
if err := cs.persistBlockHeader(b.Header); err != nil {
return err
}
for _, m := range b.Messages {
if err := cs.PutMessage(m); err != nil {
return err
}
}
return nil
}
func (cs *ChainStore) PutMessage(m *types.SignedMessage) error {
2019-07-05 14:29:17 +00:00
sb, err := m.ToStorageBlock()
if err != nil {
return err
}
return cs.bs.Put(sb)
}
func (cs *ChainStore) AddBlock(b *types.BlockHeader) error {
2019-07-05 14:29:17 +00:00
if err := cs.persistBlockHeader(b); err != nil {
return err
}
ts, _ := NewTipSet([]*types.BlockHeader{b})
2019-07-05 14:29:17 +00:00
cs.maybeTakeHeavierTipSet(ts)
return nil
}
func (cs *ChainStore) GetGenesis() (*types.BlockHeader, error) {
data, err := cs.ds.Get(dstore.NewKey("0"))
2019-07-05 14:29:17 +00:00
if err != nil {
return nil, err
}
c, err := cid.Cast(data)
if err != nil {
return nil, err
}
genb, err := cs.bs.Get(c)
if err != nil {
return nil, err
}
return types.DecodeBlock(genb.RawData())
2019-07-05 14:29:17 +00:00
}
func (cs *ChainStore) TipSetState(cids []cid.Cid) (cid.Cid, error) {
ts, err := cs.LoadTipSet(cids)
if err != nil {
log.Error("failed loading tipset: ", cids)
return cid.Undef, err
}
if len(ts.Blocks()) == 1 {
return ts.Blocks()[0].StateRoot, nil
}
panic("cant handle multiblock tipsets yet")
}
func (cs *ChainStore) GetMessage(c cid.Cid) (*types.SignedMessage, error) {
2019-07-05 14:29:17 +00:00
sb, err := cs.bs.Get(c)
if err != nil {
return nil, err
}
return types.DecodeSignedMessage(sb.RawData())
2019-07-05 14:29:17 +00:00
}
func (cs *ChainStore) MessageCidsForBlock(b *types.BlockHeader) ([]cid.Cid, error) {
2019-07-05 14:29:17 +00:00
cst := hamt.CSTFromBstore(cs.bs)
shar, err := sharray.Load(context.TODO(), b.Messages, 4, cst)
if err != nil {
return nil, errors.Wrap(err, "sharray load")
}
var cids []cid.Cid
err = shar.ForEach(context.TODO(), func(i interface{}) error {
c, ok := i.(cid.Cid)
if !ok {
return fmt.Errorf("value in message sharray was not a cid")
}
cids = append(cids, c)
return nil
})
if err != nil {
return nil, err
}
return cids, nil
}
func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.SignedMessage, error) {
cids, err := cs.MessageCidsForBlock(b)
if err != nil {
return nil, err
}
2019-07-05 14:29:17 +00:00
return cs.LoadMessagesFromCids(cids)
}
func (cs *ChainStore) GetReceipt(b *types.BlockHeader, i int) (*types.MessageReceipt, error) {
cst := hamt.CSTFromBstore(cs.bs)
shar, err := sharray.Load(context.TODO(), b.MessageReceipts, 4, cst)
if err != nil {
return nil, errors.Wrap(err, "sharray load")
}
ival, err := shar.Get(context.TODO(), i)
if err != nil {
return nil, err
}
// @warpfork, @EricMyhre help me. save me.
out, err := json.Marshal(ival)
if err != nil {
return nil, err
}
var r types.MessageReceipt
if err := json.Unmarshal(out, &r); err != nil {
return nil, err
}
return &r, nil
}
func (cs *ChainStore) LoadMessagesFromCids(cids []cid.Cid) ([]*types.SignedMessage, error) {
msgs := make([]*types.SignedMessage, 0, len(cids))
2019-07-05 14:29:17 +00:00
for _, c := range cids {
m, err := cs.GetMessage(c)
if err != nil {
return nil, err
}
msgs = append(msgs, m)
}
return msgs, nil
}
2019-07-18 20:26:04 +00:00
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)
2019-07-18 20:26:04 +00:00
if err != nil {
return types.BigInt{}, err
}
act, err := state.GetActor(addr)
if err != nil {
return types.BigInt{}, err
}
return act.Balance, nil
}
func (cs *ChainStore) WaitForMessage(ctx context.Context, mcid cid.Cid) (cid.Cid, *types.MessageReceipt, error) {
tsub := cs.SubHeadChanges()
head := cs.GetHeaviestTipSet()
bc, r, err := cs.tipsetContainsMsg(head, mcid)
if err != nil {
return cid.Undef, nil, err
}
if r != nil {
return bc, r, nil
}
for {
select {
case val := <-tsub:
switch val.Type {
case HCRevert:
continue
case HCApply:
bc, r, err := cs.tipsetContainsMsg(val.Val, mcid)
if err != nil {
return cid.Undef, nil, err
}
if r != nil {
return bc, r, nil
}
}
case <-ctx.Done():
return cid.Undef, nil, ctx.Err()
}
}
}
func (cs *ChainStore) tipsetContainsMsg(ts *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
}
}
return cid.Undef, nil, nil
}
func (cs *ChainStore) blockContainsMsg(blk *types.BlockHeader, msg cid.Cid) (*types.MessageReceipt, error) {
msgs, err := cs.MessageCidsForBlock(blk)
if err != nil {
return nil, err
}
for i, mc := range msgs {
if mc == msg {
return cs.GetReceipt(blk, i)
}
}
return nil, nil
}