diff --git a/chain/store/messages.go b/chain/store/messages.go new file mode 100644 index 000000000..50cf0e6a2 --- /dev/null +++ b/chain/store/messages.go @@ -0,0 +1,303 @@ +package store + +import ( + "context" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + block "github.com/ipfs/go-block-format" + cbor "github.com/ipfs/go-ipld-cbor" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/go-address" + blockadt "github.com/filecoin-project/specs-actors/actors/util/adt" + + bstore "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/state" + "github.com/filecoin-project/lotus/chain/types" +) + +type storable interface { + ToStorageBlock() (block.Block, error) +} + +func PutMessage(bs bstore.Blockstore, m storable) (cid.Cid, error) { + b, err := m.ToStorageBlock() + if err != nil { + return cid.Undef, err + } + + if err := bs.Put(b); err != nil { + return cid.Undef, err + } + + return b.Cid(), nil +} + +func (cs *ChainStore) PutMessage(m storable) (cid.Cid, error) { + return PutMessage(cs.chainBlockstore, m) +} + +func (cs *ChainStore) GetCMessage(c cid.Cid) (types.ChainMsg, error) { + m, err := cs.GetMessage(c) + if err == nil { + return m, nil + } + if err != bstore.ErrNotFound { + log.Warnf("GetCMessage: unexpected error getting unsigned message: %s", err) + } + + return cs.GetSignedMessage(c) +} + +func (cs *ChainStore) GetMessage(c cid.Cid) (*types.Message, error) { + var msg *types.Message + err := cs.chainLocalBlockstore.View(c, func(b []byte) (err error) { + msg, err = types.DecodeMessage(b) + return err + }) + return msg, err +} + +func (cs *ChainStore) GetSignedMessage(c cid.Cid) (*types.SignedMessage, error) { + var msg *types.SignedMessage + err := cs.chainLocalBlockstore.View(c, func(b []byte) (err error) { + msg, err = types.DecodeSignedMessage(b) + return err + }) + return msg, err +} + +func (cs *ChainStore) readAMTCids(root cid.Cid) ([]cid.Cid, error) { + ctx := context.TODO() + // block headers use adt0, for now. + a, err := blockadt.AsArray(cs.ActorStore(ctx), root) + if err != nil { + return nil, xerrors.Errorf("amt load: %w", err) + } + + var ( + cids []cid.Cid + cborCid cbg.CborCid + ) + if err := a.ForEach(&cborCid, func(i int64) error { + c := cid.Cid(cborCid) + cids = append(cids, c) + return nil + }); err != nil { + return nil, xerrors.Errorf("failed to traverse amt: %w", err) + } + + if uint64(len(cids)) != a.Length() { + return nil, xerrors.Errorf("found %d cids, expected %d", len(cids), a.Length()) + } + + return cids, nil +} + +type BlockMessages struct { + Miner address.Address + BlsMessages []types.ChainMsg + SecpkMessages []types.ChainMsg + WinCount int64 +} + +func (cs *ChainStore) BlockMsgsForTipset(ts *types.TipSet) ([]BlockMessages, error) { + applied := make(map[address.Address]uint64) + + cst := cbor.NewCborStore(cs.stateBlockstore) + st, err := state.LoadStateTree(cst, ts.Blocks()[0].ParentStateRoot) + if err != nil { + return nil, xerrors.Errorf("failed to load state tree") + } + + selectMsg := func(m *types.Message) (bool, error) { + var sender address.Address + if ts.Height() >= build.UpgradeHyperdriveHeight { + sender, err = st.LookupID(m.From) + if err != nil { + return false, err + } + } else { + sender = m.From + } + + // The first match for a sender is guaranteed to have correct nonce -- the block isn't valid otherwise + if _, ok := applied[sender]; !ok { + applied[sender] = m.Nonce + } + + if applied[sender] != m.Nonce { + return false, nil + } + + applied[sender]++ + + return true, nil + } + + var out []BlockMessages + 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) + } + + bm := BlockMessages{ + Miner: b.Miner, + BlsMessages: make([]types.ChainMsg, 0, len(bms)), + SecpkMessages: make([]types.ChainMsg, 0, len(sms)), + WinCount: b.ElectionProof.WinCount, + } + + for _, bmsg := range bms { + b, err := selectMsg(bmsg.VMMessage()) + if err != nil { + return nil, xerrors.Errorf("failed to decide whether to select message for block: %w", err) + } + + if b { + bm.BlsMessages = append(bm.BlsMessages, bmsg) + } + } + + for _, smsg := range sms { + b, err := selectMsg(smsg.VMMessage()) + if err != nil { + return nil, xerrors.Errorf("failed to decide whether to select message for block: %w", err) + } + + if b { + bm.SecpkMessages = append(bm.SecpkMessages, smsg) + } + } + + out = append(out, bm) + } + + return out, nil +} + +func (cs *ChainStore) MessagesForTipset(ts *types.TipSet) ([]types.ChainMsg, error) { + bmsgs, err := cs.BlockMsgsForTipset(ts) + if err != nil { + return nil, err + } + + var out []types.ChainMsg + for _, bm := range bmsgs { + for _, blsm := range bm.BlsMessages { + out = append(out, blsm) + } + + for _, secm := range bm.SecpkMessages { + out = append(out, secm) + } + } + + return out, nil +} + +type mmCids struct { + bls []cid.Cid + secpk []cid.Cid +} + +func (cs *ChainStore) ReadMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error) { + o, ok := cs.mmCache.Get(mmc) + if ok { + mmcids := o.(*mmCids) + return mmcids.bls, mmcids.secpk, nil + } + + cst := cbor.NewCborStore(cs.chainLocalBlockstore) + var msgmeta types.MsgMeta + if err := cst.Get(context.TODO(), mmc, &msgmeta); err != nil { + return nil, nil, xerrors.Errorf("failed to load msgmeta (%s): %w", mmc, err) + } + + blscids, err := cs.readAMTCids(msgmeta.BlsMessages) + if err != nil { + return nil, nil, xerrors.Errorf("loading bls message cids for block: %w", err) + } + + secpkcids, err := cs.readAMTCids(msgmeta.SecpkMessages) + if err != nil { + return nil, nil, xerrors.Errorf("loading secpk message cids for block: %w", err) + } + + cs.mmCache.Add(mmc, &mmCids{ + bls: blscids, + secpk: secpkcids, + }) + + return blscids, secpkcids, nil +} + +func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) { + blscids, secpkcids, err := cs.ReadMsgMetaCids(b.Messages) + if err != nil { + return nil, nil, err + } + + blsmsgs, err := cs.LoadMessagesFromCids(blscids) + if err != nil { + return nil, nil, xerrors.Errorf("loading bls messages for block: %w", err) + } + + secpkmsgs, err := cs.LoadSignedMessagesFromCids(secpkcids) + if err != nil { + return nil, nil, xerrors.Errorf("loading secpk messages for block: %w", err) + } + + return blsmsgs, secpkmsgs, nil +} + +func (cs *ChainStore) GetParentReceipt(b *types.BlockHeader, i int) (*types.MessageReceipt, error) { + ctx := context.TODO() + // block headers use adt0, for now. + a, err := blockadt.AsArray(cs.ActorStore(ctx), b.ParentMessageReceipts) + if err != nil { + return nil, xerrors.Errorf("amt load: %w", err) + } + + var r types.MessageReceipt + if found, err := a.Get(uint64(i), &r); err != nil { + return nil, err + } else if !found { + return nil, xerrors.Errorf("failed to find receipt %d", i) + } + + return &r, nil +} + +func (cs *ChainStore) LoadMessagesFromCids(cids []cid.Cid) ([]*types.Message, error) { + msgs := make([]*types.Message, 0, len(cids)) + for i, c := range cids { + m, err := cs.GetMessage(c) + if err != nil { + return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err) + } + + msgs = append(msgs, m) + } + + return msgs, nil +} + +func (cs *ChainStore) LoadSignedMessagesFromCids(cids []cid.Cid) ([]*types.SignedMessage, error) { + msgs := make([]*types.SignedMessage, 0, len(cids)) + for i, c := range cids { + m, err := cs.GetSignedMessage(c) + if err != nil { + return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err) + } + + msgs = append(msgs, m) + } + + return msgs, nil +} diff --git a/chain/store/rand.go b/chain/store/rand.go new file mode 100644 index 000000000..1fa9e678f --- /dev/null +++ b/chain/store/rand.go @@ -0,0 +1,182 @@ +package store + +import ( + "context" + "encoding/binary" + "os" + + "github.com/ipfs/go-cid" + "github.com/minio/blake2b-simd" + "go.opencensus.io/trace" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" +) + +func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + h := blake2b.New256() + if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil { + return nil, xerrors.Errorf("deriving randomness: %w", err) + } + VRFDigest := blake2b.Sum256(rbase) + _, err := h.Write(VRFDigest[:]) + if err != nil { + return nil, xerrors.Errorf("hashing VRFDigest: %w", err) + } + if err := binary.Write(h, binary.BigEndian, round); err != nil { + return nil, xerrors.Errorf("deriving randomness: %w", err) + } + _, err = h.Write(entropy) + if err != nil { + return nil, xerrors.Errorf("hashing entropy: %w", err) + } + + return h.Sum(nil), nil +} + +func (cs *ChainStore) GetBeaconRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, true) +} + +func (cs *ChainStore) GetBeaconRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, false) +} + +func (cs *ChainStore) GetBeaconRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) { + _, span := trace.StartSpan(ctx, "store.GetBeaconRandomness") + defer span.End() + span.AddAttributes(trace.Int64Attribute("round", int64(round))) + + ts, err := cs.LoadTipSet(types.NewTipSetKey(blks...)) + if err != nil { + return nil, err + } + + if round > ts.Height() { + return nil, xerrors.Errorf("cannot draw randomness from the future") + } + + searchHeight := round + if searchHeight < 0 { + searchHeight = 0 + } + + randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback) + if err != nil { + return nil, err + } + + be, err := cs.GetLatestBeaconEntry(randTs) + if err != nil { + return nil, err + } + + // if at (or just past -- for null epochs) appropriate epoch + // or at genesis (works for negative epochs) + return DrawRandomness(be.Data, pers, round, entropy) +} + +func (cs *ChainStore) GetChainRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cs.GetChainRandomness(ctx, blks, pers, round, entropy, true) +} + +func (cs *ChainStore) GetChainRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cs.GetChainRandomness(ctx, blks, pers, round, entropy, false) +} + +func (cs *ChainStore) GetChainRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) { + _, span := trace.StartSpan(ctx, "store.GetChainRandomness") + defer span.End() + span.AddAttributes(trace.Int64Attribute("round", int64(round))) + + ts, err := cs.LoadTipSet(types.NewTipSetKey(blks...)) + if err != nil { + return nil, err + } + + if round > ts.Height() { + return nil, xerrors.Errorf("cannot draw randomness from the future") + } + + searchHeight := round + if searchHeight < 0 { + searchHeight = 0 + } + + randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback) + if err != nil { + return nil, err + } + + mtb := randTs.MinTicketBlock() + + // if at (or just past -- for null epochs) appropriate epoch + // or at genesis (works for negative epochs) + return DrawRandomness(mtb.Ticket.VRFProof, pers, round, entropy) +} + +func (cs *ChainStore) GetLatestBeaconEntry(ts *types.TipSet) (*types.BeaconEntry, error) { + cur := ts + for i := 0; i < 20; i++ { + cbe := cur.Blocks()[0].BeaconEntries + if len(cbe) > 0 { + return &cbe[len(cbe)-1], nil + } + + if cur.Height() == 0 { + return nil, xerrors.Errorf("made it back to genesis block without finding beacon entry") + } + + next, err := cs.LoadTipSet(cur.Parents()) + if err != nil { + return nil, xerrors.Errorf("failed to load parents when searching back for latest beacon entry: %w", err) + } + cur = next + } + + if os.Getenv("LOTUS_IGNORE_DRAND") == "_yes_" { + return &types.BeaconEntry{ + Data: []byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, + }, nil + } + + return nil, xerrors.Errorf("found NO beacon entries in the 20 latest tipsets") +} + +type chainRand struct { + cs *ChainStore + blks []cid.Cid +} + +func NewChainRand(cs *ChainStore, blks []cid.Cid) vm.Rand { + return &chainRand{ + cs: cs, + blks: blks, + } +} + +func (cr *chainRand) GetChainRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cr.cs.GetChainRandomnessLookingBack(ctx, cr.blks, pers, round, entropy) +} + +func (cr *chainRand) GetChainRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cr.cs.GetChainRandomnessLookingForward(ctx, cr.blks, pers, round, entropy) +} + +func (cr *chainRand) GetBeaconRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cr.cs.GetBeaconRandomnessLookingBack(ctx, cr.blks, pers, round, entropy) +} + +func (cr *chainRand) GetBeaconRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { + return cr.cs.GetBeaconRandomnessLookingForward(ctx, cr.blks, pers, round, entropy) +} + +func (cs *ChainStore) GetTipSetFromKey(tsk types.TipSetKey) (*types.TipSet, error) { + if tsk.IsEmpty() { + return cs.GetHeaviestTipSet(), nil + } + return cs.LoadTipSet(tsk) +} diff --git a/chain/store/snapshot.go b/chain/store/snapshot.go new file mode 100644 index 000000000..1d4ce3758 --- /dev/null +++ b/chain/store/snapshot.go @@ -0,0 +1,205 @@ +package store + +import ( + "bytes" + "context" + "io" + + "github.com/ipfs/go-cid" + "github.com/ipld/go-car" + carutil "github.com/ipld/go-car/util" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + bstore "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/types" +) + +func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRoots abi.ChainEpoch, skipOldMsgs bool, w io.Writer) error { + h := &car.CarHeader{ + Roots: ts.Cids(), + Version: 1, + } + + if err := car.WriteHeader(h, w); err != nil { + return xerrors.Errorf("failed to write car header: %s", err) + } + + unionBs := bstore.Union(cs.stateBlockstore, cs.chainBlockstore) + return cs.WalkSnapshot(ctx, ts, inclRecentRoots, skipOldMsgs, true, func(c cid.Cid) error { + blk, err := unionBs.Get(c) + if err != nil { + return xerrors.Errorf("writing object to car, bs.Get: %w", err) + } + + if err := carutil.LdWrite(w, c.Bytes(), blk.RawData()); err != nil { + return xerrors.Errorf("failed to write block to car output: %w", err) + } + + return nil + }) +} + +func (cs *ChainStore) Import(r io.Reader) (*types.TipSet, error) { + // TODO: writing only to the state blockstore is incorrect. + // At this time, both the state and chain blockstores are backed by the + // universal store. When we physically segregate the stores, we will need + // to route state objects to the state blockstore, and chain objects to + // the chain blockstore. + header, err := car.LoadCar(cs.StateBlockstore(), r) + if err != nil { + return nil, xerrors.Errorf("loadcar failed: %w", err) + } + + root, err := cs.LoadTipSet(types.NewTipSetKey(header.Roots...)) + if err != nil { + return nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err) + } + + return root, nil +} + +func (cs *ChainStore) WalkSnapshot(ctx context.Context, ts *types.TipSet, inclRecentRoots abi.ChainEpoch, skipOldMsgs, skipMsgReceipts bool, cb func(cid.Cid) error) error { + if ts == nil { + ts = cs.GetHeaviestTipSet() + } + + seen := cid.NewSet() + walked := cid.NewSet() + + blocksToWalk := ts.Cids() + currentMinHeight := ts.Height() + + walkChain := func(blk cid.Cid) error { + if !seen.Visit(blk) { + return nil + } + + if err := cb(blk); err != nil { + return err + } + + data, err := cs.chainBlockstore.Get(blk) + if err != nil { + return xerrors.Errorf("getting block: %w", err) + } + + var b types.BlockHeader + if err := b.UnmarshalCBOR(bytes.NewBuffer(data.RawData())); err != nil { + return xerrors.Errorf("unmarshaling block header (cid=%s): %w", blk, err) + } + + if currentMinHeight > b.Height { + currentMinHeight = b.Height + if currentMinHeight%builtin.EpochsInDay == 0 { + log.Infow("export", "height", currentMinHeight) + } + } + + var cids []cid.Cid + if !skipOldMsgs || b.Height > ts.Height()-inclRecentRoots { + if walked.Visit(b.Messages) { + mcids, err := recurseLinks(cs.chainBlockstore, walked, b.Messages, []cid.Cid{b.Messages}) + if err != nil { + return xerrors.Errorf("recursing messages failed: %w", err) + } + cids = mcids + } + } + + if b.Height > 0 { + for _, p := range b.Parents { + blocksToWalk = append(blocksToWalk, p) + } + } else { + // include the genesis block + cids = append(cids, b.Parents...) + } + + out := cids + + if b.Height == 0 || b.Height > ts.Height()-inclRecentRoots { + if walked.Visit(b.ParentStateRoot) { + cids, err := recurseLinks(cs.stateBlockstore, walked, b.ParentStateRoot, []cid.Cid{b.ParentStateRoot}) + if err != nil { + return xerrors.Errorf("recursing genesis state failed: %w", err) + } + + out = append(out, cids...) + } + + if !skipMsgReceipts && walked.Visit(b.ParentMessageReceipts) { + out = append(out, b.ParentMessageReceipts) + } + } + + for _, c := range out { + if seen.Visit(c) { + if c.Prefix().Codec != cid.DagCBOR { + continue + } + + if err := cb(c); err != nil { + return err + } + + } + } + + return nil + } + + log.Infow("export started") + exportStart := build.Clock.Now() + + for len(blocksToWalk) > 0 { + next := blocksToWalk[0] + blocksToWalk = blocksToWalk[1:] + if err := walkChain(next); err != nil { + return xerrors.Errorf("walk chain failed: %w", err) + } + } + + log.Infow("export finished", "duration", build.Clock.Now().Sub(exportStart).Seconds()) + + return nil +} + +func recurseLinks(bs bstore.Blockstore, walked *cid.Set, root cid.Cid, in []cid.Cid) ([]cid.Cid, error) { + if root.Prefix().Codec != cid.DagCBOR { + return in, nil + } + + data, err := bs.Get(root) + if err != nil { + return nil, xerrors.Errorf("recurse links get (%s) failed: %w", root, err) + } + + var rerr error + err = cbg.ScanForLinks(bytes.NewReader(data.RawData()), func(c cid.Cid) { + if rerr != nil { + // No error return on ScanForLinks :( + return + } + + // traversed this already... + if !walked.Visit(c) { + return + } + + in = append(in, c) + var err error + in, err = recurseLinks(bs, walked, c, in) + if err != nil { + rerr = err + } + }) + if err != nil { + return nil, xerrors.Errorf("scanning for links failed: %w", err) + } + + return in, rerr +} diff --git a/chain/store/store.go b/chain/store/store.go index 523726863..b81bcf61e 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -1,35 +1,24 @@ package store import ( - "bytes" "context" - "encoding/binary" "encoding/json" "errors" - "io" "os" "strconv" "strings" "sync" "time" - "github.com/filecoin-project/lotus/chain/state" - "golang.org/x/sync/errgroup" - "github.com/filecoin-project/go-state-types/crypto" - "github.com/minio/blake2b-simd" - "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - blockadt "github.com/filecoin-project/specs-actors/actors/util/adt" - "github.com/filecoin-project/lotus/api" bstore "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/adt" - "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/journal" "github.com/filecoin-project/lotus/metrics" @@ -48,9 +37,6 @@ import ( "github.com/ipfs/go-datastore/query" cbor "github.com/ipfs/go-ipld-cbor" logging "github.com/ipfs/go-log/v2" - "github.com/ipld/go-car" - carutil "github.com/ipld/go-car/util" - cbg "github.com/whyrusleeping/cbor-gen" "github.com/whyrusleeping/pubsub" "golang.org/x/xerrors" ) @@ -988,27 +974,6 @@ func (cs *ChainStore) PersistBlockHeaders(b ...*types.BlockHeader) error { return err } -type storable interface { - ToStorageBlock() (block.Block, error) -} - -func PutMessage(bs bstore.Blockstore, m storable) (cid.Cid, error) { - b, err := m.ToStorageBlock() - if err != nil { - return cid.Undef, err - } - - if err := bs.Put(b); err != nil { - return cid.Undef, err - } - - return b.Cid(), nil -} - -func (cs *ChainStore) PutMessage(m storable) (cid.Cid, error) { - return PutMessage(cs.chainBlockstore, m) -} - func (cs *ChainStore) expandTipset(b *types.BlockHeader) (*types.TipSet, error) { // Hold lock for the whole function for now, if it becomes a problem we can // fix pretty easily @@ -1080,203 +1045,6 @@ func (cs *ChainStore) GetGenesis() (*types.BlockHeader, error) { return cs.GetBlock(c) } -func (cs *ChainStore) GetCMessage(c cid.Cid) (types.ChainMsg, error) { - m, err := cs.GetMessage(c) - if err == nil { - return m, nil - } - if err != bstore.ErrNotFound { - log.Warnf("GetCMessage: unexpected error getting unsigned message: %s", err) - } - - return cs.GetSignedMessage(c) -} - -func (cs *ChainStore) GetMessage(c cid.Cid) (*types.Message, error) { - var msg *types.Message - err := cs.chainLocalBlockstore.View(c, func(b []byte) (err error) { - msg, err = types.DecodeMessage(b) - return err - }) - return msg, err -} - -func (cs *ChainStore) GetSignedMessage(c cid.Cid) (*types.SignedMessage, error) { - var msg *types.SignedMessage - err := cs.chainLocalBlockstore.View(c, func(b []byte) (err error) { - msg, err = types.DecodeSignedMessage(b) - return err - }) - return msg, err -} - -func (cs *ChainStore) readAMTCids(root cid.Cid) ([]cid.Cid, error) { - ctx := context.TODO() - // block headers use adt0, for now. - a, err := blockadt.AsArray(cs.ActorStore(ctx), root) - if err != nil { - return nil, xerrors.Errorf("amt load: %w", err) - } - - var ( - cids []cid.Cid - cborCid cbg.CborCid - ) - if err := a.ForEach(&cborCid, func(i int64) error { - c := cid.Cid(cborCid) - cids = append(cids, c) - return nil - }); err != nil { - return nil, xerrors.Errorf("failed to traverse amt: %w", err) - } - - if uint64(len(cids)) != a.Length() { - return nil, xerrors.Errorf("found %d cids, expected %d", len(cids), a.Length()) - } - - return cids, nil -} - -type BlockMessages struct { - Miner address.Address - BlsMessages []types.ChainMsg - SecpkMessages []types.ChainMsg - WinCount int64 -} - -func (cs *ChainStore) BlockMsgsForTipset(ts *types.TipSet) ([]BlockMessages, error) { - applied := make(map[address.Address]uint64) - - cst := cbor.NewCborStore(cs.stateBlockstore) - st, err := state.LoadStateTree(cst, ts.Blocks()[0].ParentStateRoot) - if err != nil { - return nil, xerrors.Errorf("failed to load state tree") - } - - selectMsg := func(m *types.Message) (bool, error) { - var sender address.Address - if ts.Height() >= build.UpgradeHyperdriveHeight { - sender, err = st.LookupID(m.From) - if err != nil { - return false, err - } - } else { - sender = m.From - } - - // The first match for a sender is guaranteed to have correct nonce -- the block isn't valid otherwise - if _, ok := applied[sender]; !ok { - applied[sender] = m.Nonce - } - - if applied[sender] != m.Nonce { - return false, nil - } - - applied[sender]++ - - return true, nil - } - - var out []BlockMessages - 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) - } - - bm := BlockMessages{ - Miner: b.Miner, - BlsMessages: make([]types.ChainMsg, 0, len(bms)), - SecpkMessages: make([]types.ChainMsg, 0, len(sms)), - WinCount: b.ElectionProof.WinCount, - } - - for _, bmsg := range bms { - b, err := selectMsg(bmsg.VMMessage()) - if err != nil { - return nil, xerrors.Errorf("failed to decide whether to select message for block: %w", err) - } - - if b { - bm.BlsMessages = append(bm.BlsMessages, bmsg) - } - } - - for _, smsg := range sms { - b, err := selectMsg(smsg.VMMessage()) - if err != nil { - return nil, xerrors.Errorf("failed to decide whether to select message for block: %w", err) - } - - if b { - bm.SecpkMessages = append(bm.SecpkMessages, smsg) - } - } - - out = append(out, bm) - } - - return out, nil -} - -func (cs *ChainStore) MessagesForTipset(ts *types.TipSet) ([]types.ChainMsg, error) { - bmsgs, err := cs.BlockMsgsForTipset(ts) - if err != nil { - return nil, err - } - - var out []types.ChainMsg - for _, bm := range bmsgs { - for _, blsm := range bm.BlsMessages { - out = append(out, blsm) - } - - for _, secm := range bm.SecpkMessages { - out = append(out, secm) - } - } - - return out, nil -} - -type mmCids struct { - bls []cid.Cid - secpk []cid.Cid -} - -func (cs *ChainStore) ReadMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error) { - o, ok := cs.mmCache.Get(mmc) - if ok { - mmcids := o.(*mmCids) - return mmcids.bls, mmcids.secpk, nil - } - - cst := cbor.NewCborStore(cs.chainLocalBlockstore) - var msgmeta types.MsgMeta - if err := cst.Get(context.TODO(), mmc, &msgmeta); err != nil { - return nil, nil, xerrors.Errorf("failed to load msgmeta (%s): %w", mmc, err) - } - - blscids, err := cs.readAMTCids(msgmeta.BlsMessages) - if err != nil { - return nil, nil, xerrors.Errorf("loading bls message cids for block: %w", err) - } - - secpkcids, err := cs.readAMTCids(msgmeta.SecpkMessages) - if err != nil { - return nil, nil, xerrors.Errorf("loading secpk message cids for block: %w", err) - } - - cs.mmCache.Add(mmc, &mmCids{ - bls: blscids, - secpk: secpkcids, - }) - - return blscids, secpkcids, nil -} - // GetPath returns the sequence of atomic head change operations that // need to be applied in order to switch the head of the chain from the `from` // tipset to the `to` tipset. @@ -1304,71 +1072,6 @@ func (cs *ChainStore) GetPath(ctx context.Context, from types.TipSetKey, to type return path, nil } -func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) { - blscids, secpkcids, err := cs.ReadMsgMetaCids(b.Messages) - if err != nil { - return nil, nil, err - } - - blsmsgs, err := cs.LoadMessagesFromCids(blscids) - if err != nil { - return nil, nil, xerrors.Errorf("loading bls messages for block: %w", err) - } - - secpkmsgs, err := cs.LoadSignedMessagesFromCids(secpkcids) - if err != nil { - return nil, nil, xerrors.Errorf("loading secpk messages for block: %w", err) - } - - return blsmsgs, secpkmsgs, nil -} - -func (cs *ChainStore) GetParentReceipt(b *types.BlockHeader, i int) (*types.MessageReceipt, error) { - ctx := context.TODO() - // block headers use adt0, for now. - a, err := blockadt.AsArray(cs.ActorStore(ctx), b.ParentMessageReceipts) - if err != nil { - return nil, xerrors.Errorf("amt load: %w", err) - } - - var r types.MessageReceipt - if found, err := a.Get(uint64(i), &r); err != nil { - return nil, err - } else if !found { - return nil, xerrors.Errorf("failed to find receipt %d", i) - } - - return &r, nil -} - -func (cs *ChainStore) LoadMessagesFromCids(cids []cid.Cid) ([]*types.Message, error) { - msgs := make([]*types.Message, 0, len(cids)) - for i, c := range cids { - m, err := cs.GetMessage(c) - if err != nil { - return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err) - } - - msgs = append(msgs, m) - } - - return msgs, nil -} - -func (cs *ChainStore) LoadSignedMessagesFromCids(cids []cid.Cid) ([]*types.SignedMessage, error) { - msgs := make([]*types.SignedMessage, 0, len(cids)) - for i, c := range cids { - m, err := cs.GetSignedMessage(c) - if err != nil { - return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err) - } - - msgs = append(msgs, m) - } - - return msgs, nil -} - // ChainBlockstore returns the chain blockstore. Currently the chain and state // // stores are both backed by the same physical store, albeit with different // // caching policies, but in the future they will segregate. @@ -1417,108 +1120,6 @@ func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) { return NewFullTipSet(out), nil } -func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - h := blake2b.New256() - if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil { - return nil, xerrors.Errorf("deriving randomness: %w", err) - } - VRFDigest := blake2b.Sum256(rbase) - _, err := h.Write(VRFDigest[:]) - if err != nil { - return nil, xerrors.Errorf("hashing VRFDigest: %w", err) - } - if err := binary.Write(h, binary.BigEndian, round); err != nil { - return nil, xerrors.Errorf("deriving randomness: %w", err) - } - _, err = h.Write(entropy) - if err != nil { - return nil, xerrors.Errorf("hashing entropy: %w", err) - } - - return h.Sum(nil), nil -} - -func (cs *ChainStore) GetBeaconRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, true) -} - -func (cs *ChainStore) GetBeaconRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, false) -} - -func (cs *ChainStore) GetBeaconRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) { - _, span := trace.StartSpan(ctx, "store.GetBeaconRandomness") - defer span.End() - span.AddAttributes(trace.Int64Attribute("round", int64(round))) - - ts, err := cs.LoadTipSet(types.NewTipSetKey(blks...)) - if err != nil { - return nil, err - } - - if round > ts.Height() { - return nil, xerrors.Errorf("cannot draw randomness from the future") - } - - searchHeight := round - if searchHeight < 0 { - searchHeight = 0 - } - - randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback) - if err != nil { - return nil, err - } - - be, err := cs.GetLatestBeaconEntry(randTs) - if err != nil { - return nil, err - } - - // if at (or just past -- for null epochs) appropriate epoch - // or at genesis (works for negative epochs) - return DrawRandomness(be.Data, pers, round, entropy) -} - -func (cs *ChainStore) GetChainRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cs.GetChainRandomness(ctx, blks, pers, round, entropy, true) -} - -func (cs *ChainStore) GetChainRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cs.GetChainRandomness(ctx, blks, pers, round, entropy, false) -} - -func (cs *ChainStore) GetChainRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) { - _, span := trace.StartSpan(ctx, "store.GetChainRandomness") - defer span.End() - span.AddAttributes(trace.Int64Attribute("round", int64(round))) - - ts, err := cs.LoadTipSet(types.NewTipSetKey(blks...)) - if err != nil { - return nil, err - } - - if round > ts.Height() { - return nil, xerrors.Errorf("cannot draw randomness from the future") - } - - searchHeight := round - if searchHeight < 0 { - searchHeight = 0 - } - - randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback) - if err != nil { - return nil, err - } - - mtb := randTs.MinTicketBlock() - - // if at (or just past -- for null epochs) appropriate epoch - // or at genesis (works for negative epochs) - return DrawRandomness(mtb.Ticket.VRFProof, pers, round, entropy) -} - // GetTipsetByHeight returns the tipset on the chain behind 'ts' at the given // height. In the case that the given height is a null round, the 'prev' flag // selects the tipset before the null round if true, and the tipset following @@ -1555,252 +1156,3 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h abi.ChainEpoch, t return cs.LoadTipSet(lbts.Parents()) } - -func recurseLinks(bs bstore.Blockstore, walked *cid.Set, root cid.Cid, in []cid.Cid) ([]cid.Cid, error) { - if root.Prefix().Codec != cid.DagCBOR { - return in, nil - } - - data, err := bs.Get(root) - if err != nil { - return nil, xerrors.Errorf("recurse links get (%s) failed: %w", root, err) - } - - var rerr error - err = cbg.ScanForLinks(bytes.NewReader(data.RawData()), func(c cid.Cid) { - if rerr != nil { - // No error return on ScanForLinks :( - return - } - - // traversed this already... - if !walked.Visit(c) { - return - } - - in = append(in, c) - var err error - in, err = recurseLinks(bs, walked, c, in) - if err != nil { - rerr = err - } - }) - if err != nil { - return nil, xerrors.Errorf("scanning for links failed: %w", err) - } - - return in, rerr -} - -func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRoots abi.ChainEpoch, skipOldMsgs bool, w io.Writer) error { - h := &car.CarHeader{ - Roots: ts.Cids(), - Version: 1, - } - - if err := car.WriteHeader(h, w); err != nil { - return xerrors.Errorf("failed to write car header: %s", err) - } - - unionBs := bstore.Union(cs.stateBlockstore, cs.chainBlockstore) - return cs.WalkSnapshot(ctx, ts, inclRecentRoots, skipOldMsgs, true, func(c cid.Cid) error { - blk, err := unionBs.Get(c) - if err != nil { - return xerrors.Errorf("writing object to car, bs.Get: %w", err) - } - - if err := carutil.LdWrite(w, c.Bytes(), blk.RawData()); err != nil { - return xerrors.Errorf("failed to write block to car output: %w", err) - } - - return nil - }) -} - -func (cs *ChainStore) WalkSnapshot(ctx context.Context, ts *types.TipSet, inclRecentRoots abi.ChainEpoch, skipOldMsgs, skipMsgReceipts bool, cb func(cid.Cid) error) error { - if ts == nil { - ts = cs.GetHeaviestTipSet() - } - - seen := cid.NewSet() - walked := cid.NewSet() - - blocksToWalk := ts.Cids() - currentMinHeight := ts.Height() - - walkChain := func(blk cid.Cid) error { - if !seen.Visit(blk) { - return nil - } - - if err := cb(blk); err != nil { - return err - } - - data, err := cs.chainBlockstore.Get(blk) - if err != nil { - return xerrors.Errorf("getting block: %w", err) - } - - var b types.BlockHeader - if err := b.UnmarshalCBOR(bytes.NewBuffer(data.RawData())); err != nil { - return xerrors.Errorf("unmarshaling block header (cid=%s): %w", blk, err) - } - - if currentMinHeight > b.Height { - currentMinHeight = b.Height - if currentMinHeight%builtin.EpochsInDay == 0 { - log.Infow("export", "height", currentMinHeight) - } - } - - var cids []cid.Cid - if !skipOldMsgs || b.Height > ts.Height()-inclRecentRoots { - if walked.Visit(b.Messages) { - mcids, err := recurseLinks(cs.chainBlockstore, walked, b.Messages, []cid.Cid{b.Messages}) - if err != nil { - return xerrors.Errorf("recursing messages failed: %w", err) - } - cids = mcids - } - } - - if b.Height > 0 { - for _, p := range b.Parents { - blocksToWalk = append(blocksToWalk, p) - } - } else { - // include the genesis block - cids = append(cids, b.Parents...) - } - - out := cids - - if b.Height == 0 || b.Height > ts.Height()-inclRecentRoots { - if walked.Visit(b.ParentStateRoot) { - cids, err := recurseLinks(cs.stateBlockstore, walked, b.ParentStateRoot, []cid.Cid{b.ParentStateRoot}) - if err != nil { - return xerrors.Errorf("recursing genesis state failed: %w", err) - } - - out = append(out, cids...) - } - - if !skipMsgReceipts && walked.Visit(b.ParentMessageReceipts) { - out = append(out, b.ParentMessageReceipts) - } - } - - for _, c := range out { - if seen.Visit(c) { - if c.Prefix().Codec != cid.DagCBOR { - continue - } - - if err := cb(c); err != nil { - return err - } - - } - } - - return nil - } - - log.Infow("export started") - exportStart := build.Clock.Now() - - for len(blocksToWalk) > 0 { - next := blocksToWalk[0] - blocksToWalk = blocksToWalk[1:] - if err := walkChain(next); err != nil { - return xerrors.Errorf("walk chain failed: %w", err) - } - } - - log.Infow("export finished", "duration", build.Clock.Now().Sub(exportStart).Seconds()) - - return nil -} - -func (cs *ChainStore) Import(r io.Reader) (*types.TipSet, error) { - // TODO: writing only to the state blockstore is incorrect. - // At this time, both the state and chain blockstores are backed by the - // universal store. When we physically segregate the stores, we will need - // to route state objects to the state blockstore, and chain objects to - // the chain blockstore. - header, err := car.LoadCar(cs.StateBlockstore(), r) - if err != nil { - return nil, xerrors.Errorf("loadcar failed: %w", err) - } - - root, err := cs.LoadTipSet(types.NewTipSetKey(header.Roots...)) - if err != nil { - return nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err) - } - - return root, nil -} - -func (cs *ChainStore) GetLatestBeaconEntry(ts *types.TipSet) (*types.BeaconEntry, error) { - cur := ts - for i := 0; i < 20; i++ { - cbe := cur.Blocks()[0].BeaconEntries - if len(cbe) > 0 { - return &cbe[len(cbe)-1], nil - } - - if cur.Height() == 0 { - return nil, xerrors.Errorf("made it back to genesis block without finding beacon entry") - } - - next, err := cs.LoadTipSet(cur.Parents()) - if err != nil { - return nil, xerrors.Errorf("failed to load parents when searching back for latest beacon entry: %w", err) - } - cur = next - } - - if os.Getenv("LOTUS_IGNORE_DRAND") == "_yes_" { - return &types.BeaconEntry{ - Data: []byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, - }, nil - } - - return nil, xerrors.Errorf("found NO beacon entries in the 20 latest tipsets") -} - -type chainRand struct { - cs *ChainStore - blks []cid.Cid -} - -func NewChainRand(cs *ChainStore, blks []cid.Cid) vm.Rand { - return &chainRand{ - cs: cs, - blks: blks, - } -} - -func (cr *chainRand) GetChainRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cr.cs.GetChainRandomnessLookingBack(ctx, cr.blks, pers, round, entropy) -} - -func (cr *chainRand) GetChainRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cr.cs.GetChainRandomnessLookingForward(ctx, cr.blks, pers, round, entropy) -} - -func (cr *chainRand) GetBeaconRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cr.cs.GetBeaconRandomnessLookingBack(ctx, cr.blks, pers, round, entropy) -} - -func (cr *chainRand) GetBeaconRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) { - return cr.cs.GetBeaconRandomnessLookingForward(ctx, cr.blks, pers, round, entropy) -} - -func (cs *ChainStore) GetTipSetFromKey(tsk types.TipSetKey) (*types.TipSet, error) { - if tsk.IsEmpty() { - return cs.GetHeaviestTipSet(), nil - } - return cs.LoadTipSet(tsk) -}