chainstore: refactor store.go into more subfiles
This commit is contained in:
parent
e4ebbf106a
commit
6ba42e3e6b
303
chain/store/messages.go
Normal file
303
chain/store/messages.go
Normal file
@ -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
|
||||||
|
}
|
182
chain/store/rand.go
Normal file
182
chain/store/rand.go
Normal file
@ -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)
|
||||||
|
}
|
205
chain/store/snapshot.go
Normal file
205
chain/store/snapshot.go
Normal file
@ -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
|
||||||
|
}
|
@ -1,35 +1,24 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/state"
|
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
"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-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
bstore "github.com/filecoin-project/lotus/blockstore"
|
bstore "github.com/filecoin-project/lotus/blockstore"
|
||||||
"github.com/filecoin-project/lotus/build"
|
"github.com/filecoin-project/lotus/build"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/adt"
|
"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/chain/vm"
|
||||||
"github.com/filecoin-project/lotus/journal"
|
"github.com/filecoin-project/lotus/journal"
|
||||||
"github.com/filecoin-project/lotus/metrics"
|
"github.com/filecoin-project/lotus/metrics"
|
||||||
@ -48,9 +37,6 @@ import (
|
|||||||
"github.com/ipfs/go-datastore/query"
|
"github.com/ipfs/go-datastore/query"
|
||||||
cbor "github.com/ipfs/go-ipld-cbor"
|
cbor "github.com/ipfs/go-ipld-cbor"
|
||||||
logging "github.com/ipfs/go-log/v2"
|
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"
|
"github.com/whyrusleeping/pubsub"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
)
|
)
|
||||||
@ -988,27 +974,6 @@ func (cs *ChainStore) PersistBlockHeaders(b ...*types.BlockHeader) error {
|
|||||||
return err
|
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) {
|
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
|
// Hold lock for the whole function for now, if it becomes a problem we can
|
||||||
// fix pretty easily
|
// fix pretty easily
|
||||||
@ -1080,203 +1045,6 @@ func (cs *ChainStore) GetGenesis() (*types.BlockHeader, error) {
|
|||||||
return cs.GetBlock(c)
|
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
|
// 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`
|
// need to be applied in order to switch the head of the chain from the `from`
|
||||||
// tipset to the `to` tipset.
|
// tipset to the `to` tipset.
|
||||||
@ -1304,71 +1072,6 @@ func (cs *ChainStore) GetPath(ctx context.Context, from types.TipSetKey, to type
|
|||||||
return path, nil
|
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
|
// ChainBlockstore returns the chain blockstore. Currently the chain and state
|
||||||
// // stores are both backed by the same physical store, albeit with different
|
// // stores are both backed by the same physical store, albeit with different
|
||||||
// // caching policies, but in the future they will segregate.
|
// // 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
|
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
|
// 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
|
// 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
|
// 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())
|
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)
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user