From 6ba42e3e6bbcb119ac3691ddb9cdda79c5a724fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Jul 2021 14:41:36 +0200 Subject: [PATCH 1/9] chainstore: refactor store.go into more subfiles --- chain/store/messages.go | 303 +++++++++++++++++++ chain/store/rand.go | 182 +++++++++++ chain/store/snapshot.go | 205 +++++++++++++ chain/store/store.go | 648 ---------------------------------------- 4 files changed, 690 insertions(+), 648 deletions(-) create mode 100644 chain/store/messages.go create mode 100644 chain/store/rand.go create mode 100644 chain/store/snapshot.go 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) -} From 50b217817e4d7e439b0f5ffd874d30ad222e254a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Jul 2021 14:48:30 +0200 Subject: [PATCH 2/9] stmgr: Split upgrade code from upgrade runtime --- chain/stmgr/forks.go | 1088 +------------------------------------- chain/stmgr/upgrades.go | 1094 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1102 insertions(+), 1080 deletions(-) create mode 100644 chain/stmgr/upgrades.go diff --git a/chain/stmgr/forks.go b/chain/stmgr/forks.go index fb8e407ed..212272a95 100644 --- a/chain/stmgr/forks.go +++ b/chain/stmgr/forks.go @@ -4,42 +4,27 @@ import ( "bytes" "context" "encoding/binary" - "runtime" "sort" "sync" "time" - "github.com/filecoin-project/specs-actors/v5/actors/migration/nv13" - - "github.com/filecoin-project/go-state-types/rt" + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/network" - "github.com/filecoin-project/lotus/blockstore" - "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/go-state-types/rt" + + "github.com/filecoin-project/specs-actors/v3/actors/migration/nv10" + "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin" init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" - "github.com/filecoin-project/lotus/chain/actors/builtin/multisig" "github.com/filecoin-project/lotus/chain/state" - "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" - builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" - miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" - multisig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" - power0 "github.com/filecoin-project/specs-actors/actors/builtin/power" - "github.com/filecoin-project/specs-actors/actors/migration/nv3" - adt0 "github.com/filecoin-project/specs-actors/actors/util/adt" - "github.com/filecoin-project/specs-actors/v2/actors/migration/nv4" - "github.com/filecoin-project/specs-actors/v2/actors/migration/nv7" - "github.com/filecoin-project/specs-actors/v3/actors/migration/nv10" - "github.com/filecoin-project/specs-actors/v4/actors/migration/nv12" - "github.com/ipfs/go-cid" - cbor "github.com/ipfs/go-ipld-cbor" - "golang.org/x/xerrors" ) // MigrationCache can be used to cache information used by a migration. This is primarily useful to @@ -125,121 +110,6 @@ func (ml migrationLogger) Log(level rt.LogLevel, msg string, args ...interface{} } } -func DefaultUpgradeSchedule() UpgradeSchedule { - var us UpgradeSchedule - - updates := []Upgrade{{ - Height: build.UpgradeBreezeHeight, - Network: network.Version1, - Migration: UpgradeFaucetBurnRecovery, - }, { - Height: build.UpgradeSmokeHeight, - Network: network.Version2, - Migration: nil, - }, { - Height: build.UpgradeIgnitionHeight, - Network: network.Version3, - Migration: UpgradeIgnition, - }, { - Height: build.UpgradeRefuelHeight, - Network: network.Version3, - Migration: UpgradeRefuel, - }, { - Height: build.UpgradeAssemblyHeight, - Network: network.Version4, - Expensive: true, - Migration: UpgradeActorsV2, - }, { - Height: build.UpgradeTapeHeight, - Network: network.Version5, - Migration: nil, - }, { - Height: build.UpgradeLiftoffHeight, - Network: network.Version5, - Migration: UpgradeLiftoff, - }, { - Height: build.UpgradeKumquatHeight, - Network: network.Version6, - Migration: nil, - }, { - Height: build.UpgradePricelistOopsHeight, - Network: network.Version6AndAHalf, - Migration: nil, - }, { - Height: build.UpgradeCalicoHeight, - Network: network.Version7, - Migration: UpgradeCalico, - }, { - Height: build.UpgradePersianHeight, - Network: network.Version8, - Migration: nil, - }, { - Height: build.UpgradeOrangeHeight, - Network: network.Version9, - Migration: nil, - }, { - Height: build.UpgradeTrustHeight, - Network: network.Version10, - Migration: UpgradeActorsV3, - PreMigrations: []PreMigration{{ - PreMigration: PreUpgradeActorsV3, - StartWithin: 120, - DontStartWithin: 60, - StopWithin: 35, - }, { - PreMigration: PreUpgradeActorsV3, - StartWithin: 30, - DontStartWithin: 15, - StopWithin: 5, - }}, - Expensive: true, - }, { - Height: build.UpgradeNorwegianHeight, - Network: network.Version11, - Migration: nil, - }, { - Height: build.UpgradeTurboHeight, - Network: network.Version12, - Migration: UpgradeActorsV4, - PreMigrations: []PreMigration{{ - PreMigration: PreUpgradeActorsV4, - StartWithin: 120, - DontStartWithin: 60, - StopWithin: 35, - }, { - PreMigration: PreUpgradeActorsV4, - StartWithin: 30, - DontStartWithin: 15, - StopWithin: 5, - }}, - Expensive: true, - }, { - Height: build.UpgradeHyperdriveHeight, - Network: network.Version13, - Migration: UpgradeActorsV5, - PreMigrations: []PreMigration{{ - PreMigration: PreUpgradeActorsV5, - StartWithin: 120, - DontStartWithin: 60, - StopWithin: 35, - }, { - PreMigration: PreUpgradeActorsV5, - StartWithin: 30, - DontStartWithin: 15, - StopWithin: 5, - }}, - Expensive: true}} - - for _, u := range updates { - if u.Height < 0 { - // upgrade disabled - continue - } - us = append(us, u) - } - return us -} - func (us UpgradeSchedule) Validate() error { // Make sure each upgrade is valid. for _, u := range us { @@ -488,469 +358,6 @@ func doTransfer(tree types.StateTree, from, to address.Address, amt abi.TokenAmo return nil } -func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, _ MigrationCache, em ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { - // Some initial parameters - FundsForMiners := types.FromFil(1_000_000) - LookbackEpoch := abi.ChainEpoch(32000) - AccountCap := types.FromFil(0) - BaseMinerBalance := types.FromFil(20) - DesiredReimbursementBalance := types.FromFil(5_000_000) - - isSystemAccount := func(addr address.Address) (bool, error) { - id, err := address.IDFromAddress(addr) - if err != nil { - return false, xerrors.Errorf("id address: %w", err) - } - - if id < 1000 { - return true, nil - } - return false, nil - } - - minerFundsAlloc := func(pow, tpow abi.StoragePower) abi.TokenAmount { - return types.BigDiv(types.BigMul(pow, FundsForMiners), tpow) - } - - // Grab lookback state for account checks - lbts, err := sm.ChainStore().GetTipsetByHeight(ctx, LookbackEpoch, ts, false) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to get tipset at lookback height: %w", err) - } - - lbtree, err := sm.ParentState(lbts) - if err != nil { - return cid.Undef, xerrors.Errorf("loading state tree failed: %w", err) - } - - tree, err := sm.StateTree(root) - if err != nil { - return cid.Undef, xerrors.Errorf("getting state tree: %w", err) - } - - type transfer struct { - From address.Address - To address.Address - Amt abi.TokenAmount - } - - var transfers []transfer - subcalls := make([]types.ExecutionTrace, 0) - transferCb := func(trace types.ExecutionTrace) { - subcalls = append(subcalls, trace) - } - - // Take all excess funds away, put them into the reserve account - err = tree.ForEach(func(addr address.Address, act *types.Actor) error { - switch act.Code { - case builtin0.AccountActorCodeID, builtin0.MultisigActorCodeID, builtin0.PaymentChannelActorCodeID: - sysAcc, err := isSystemAccount(addr) - if err != nil { - return xerrors.Errorf("checking system account: %w", err) - } - - if !sysAcc { - transfers = append(transfers, transfer{ - From: addr, - To: builtin.ReserveAddress, - Amt: act.Balance, - }) - } - case builtin0.StorageMinerActorCodeID: - var st miner0.State - if err := sm.ChainStore().ActorStore(ctx).Get(ctx, act.Head, &st); err != nil { - return xerrors.Errorf("failed to load miner state: %w", err) - } - - var available abi.TokenAmount - { - defer func() { - if err := recover(); err != nil { - log.Warnf("Get available balance failed (%s, %s, %s): %s", addr, act.Head, act.Balance, err) - } - available = abi.NewTokenAmount(0) - }() - // this panics if the miner doesnt have enough funds to cover their locked pledge - available = st.GetAvailableBalance(act.Balance) - } - - if !available.IsZero() { - transfers = append(transfers, transfer{ - From: addr, - To: builtin.ReserveAddress, - Amt: available, - }) - } - } - return nil - }) - if err != nil { - return cid.Undef, xerrors.Errorf("foreach over state tree failed: %w", err) - } - - // Execute transfers from previous step - for _, t := range transfers { - if err := doTransfer(tree, t.From, t.To, t.Amt, transferCb); err != nil { - return cid.Undef, xerrors.Errorf("transfer %s %s->%s failed: %w", t.Amt, t.From, t.To, err) - } - } - - // pull up power table to give miners back some funds proportional to their power - var ps power0.State - powAct, err := tree.GetActor(builtin0.StoragePowerActorAddr) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to load power actor: %w", err) - } - - cst := cbor.NewCborStore(sm.ChainStore().StateBlockstore()) - if err := cst.Get(ctx, powAct.Head, &ps); err != nil { - return cid.Undef, xerrors.Errorf("failed to get power actor state: %w", err) - } - - totalPower := ps.TotalBytesCommitted - - var transfersBack []transfer - // Now, we return some funds to places where they are needed - err = tree.ForEach(func(addr address.Address, act *types.Actor) error { - lbact, err := lbtree.GetActor(addr) - if err != nil { - if !xerrors.Is(err, types.ErrActorNotFound) { - return xerrors.Errorf("failed to get actor in lookback state") - } - } - - prevBalance := abi.NewTokenAmount(0) - if lbact != nil { - prevBalance = lbact.Balance - } - - switch act.Code { - case builtin0.AccountActorCodeID, builtin0.MultisigActorCodeID, builtin0.PaymentChannelActorCodeID: - nbalance := big.Min(prevBalance, AccountCap) - if nbalance.Sign() != 0 { - transfersBack = append(transfersBack, transfer{ - From: builtin.ReserveAddress, - To: addr, - Amt: nbalance, - }) - } - case builtin0.StorageMinerActorCodeID: - var st miner0.State - if err := sm.ChainStore().ActorStore(ctx).Get(ctx, act.Head, &st); err != nil { - return xerrors.Errorf("failed to load miner state: %w", err) - } - - var minfo miner0.MinerInfo - if err := cst.Get(ctx, st.Info, &minfo); err != nil { - return xerrors.Errorf("failed to get miner info: %w", err) - } - - sectorsArr, err := adt0.AsArray(sm.ChainStore().ActorStore(ctx), st.Sectors) - if err != nil { - return xerrors.Errorf("failed to load sectors array: %w", err) - } - - slen := sectorsArr.Length() - - power := types.BigMul(types.NewInt(slen), types.NewInt(uint64(minfo.SectorSize))) - - mfunds := minerFundsAlloc(power, totalPower) - transfersBack = append(transfersBack, transfer{ - From: builtin.ReserveAddress, - To: minfo.Worker, - Amt: mfunds, - }) - - // Now make sure to give each miner who had power at the lookback some FIL - lbact, err := lbtree.GetActor(addr) - if err == nil { - var lbst miner0.State - if err := sm.ChainStore().ActorStore(ctx).Get(ctx, lbact.Head, &lbst); err != nil { - return xerrors.Errorf("failed to load miner state: %w", err) - } - - lbsectors, err := adt0.AsArray(sm.ChainStore().ActorStore(ctx), lbst.Sectors) - if err != nil { - return xerrors.Errorf("failed to load lb sectors array: %w", err) - } - - if lbsectors.Length() > 0 { - transfersBack = append(transfersBack, transfer{ - From: builtin.ReserveAddress, - To: minfo.Worker, - Amt: BaseMinerBalance, - }) - } - - } else { - log.Warnf("failed to get miner in lookback state: %s", err) - } - } - return nil - }) - if err != nil { - return cid.Undef, xerrors.Errorf("foreach over state tree failed: %w", err) - } - - for _, t := range transfersBack { - if err := doTransfer(tree, t.From, t.To, t.Amt, transferCb); err != nil { - return cid.Undef, xerrors.Errorf("transfer %s %s->%s failed: %w", t.Amt, t.From, t.To, err) - } - } - - // transfer all burnt funds back to the reserve account - burntAct, err := tree.GetActor(builtin0.BurntFundsActorAddr) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to load burnt funds actor: %w", err) - } - if err := doTransfer(tree, builtin0.BurntFundsActorAddr, builtin.ReserveAddress, burntAct.Balance, transferCb); err != nil { - return cid.Undef, xerrors.Errorf("failed to unburn funds: %w", err) - } - - // Top up the reimbursement service - reimbAddr, err := address.NewFromString("t0111") - if err != nil { - return cid.Undef, xerrors.Errorf("failed to parse reimbursement service address") - } - - reimb, err := tree.GetActor(reimbAddr) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to load reimbursement account actor: %w", err) - } - - difference := types.BigSub(DesiredReimbursementBalance, reimb.Balance) - if err := doTransfer(tree, builtin.ReserveAddress, reimbAddr, difference, transferCb); err != nil { - return cid.Undef, xerrors.Errorf("failed to top up reimbursement account: %w", err) - } - - // Now, a final sanity check to make sure the balances all check out - total := abi.NewTokenAmount(0) - err = tree.ForEach(func(addr address.Address, act *types.Actor) error { - total = types.BigAdd(total, act.Balance) - return nil - }) - if err != nil { - return cid.Undef, xerrors.Errorf("checking final state balance failed: %w", err) - } - - exp := types.FromFil(build.FilBase) - if !exp.Equals(total) { - return cid.Undef, xerrors.Errorf("resultant state tree account balance was not correct: %s", total) - } - - if em != nil { - // record the transfer in execution traces - - fakeMsg := makeFakeMsg(builtin.SystemActorAddr, builtin.SystemActorAddr, big.Zero(), uint64(epoch)) - - if err := em.MessageApplied(ctx, ts, fakeMsg.Cid(), fakeMsg, &vm.ApplyRet{ - MessageReceipt: *makeFakeRct(), - ActorErr: nil, - ExecutionTrace: types.ExecutionTrace{ - Msg: fakeMsg, - MsgRct: makeFakeRct(), - Error: "", - Duration: 0, - GasCharges: nil, - Subcalls: subcalls, - }, - Duration: 0, - GasCosts: nil, - }, false); err != nil { - return cid.Undef, xerrors.Errorf("recording transfers: %w", err) - } - } - - return tree.Flush(ctx) -} - -func UpgradeIgnition(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { - store := sm.cs.ActorStore(ctx) - - if build.UpgradeLiftoffHeight <= epoch { - return cid.Undef, xerrors.Errorf("liftoff height must be beyond ignition height") - } - - nst, err := nv3.MigrateStateTree(ctx, store, root, epoch) - if err != nil { - return cid.Undef, xerrors.Errorf("migrating actors state: %w", err) - } - - tree, err := sm.StateTree(nst) - if err != nil { - return cid.Undef, xerrors.Errorf("getting state tree: %w", err) - } - - err = setNetworkName(ctx, store, tree, "ignition") - if err != nil { - return cid.Undef, xerrors.Errorf("setting network name: %w", err) - } - - split1, err := address.NewFromString("t0115") - if err != nil { - return cid.Undef, xerrors.Errorf("first split address: %w", err) - } - - split2, err := address.NewFromString("t0116") - if err != nil { - return cid.Undef, xerrors.Errorf("second split address: %w", err) - } - - err = resetGenesisMsigs0(ctx, sm, store, tree, build.UpgradeLiftoffHeight) - if err != nil { - return cid.Undef, xerrors.Errorf("resetting genesis msig start epochs: %w", err) - } - - err = splitGenesisMultisig0(ctx, cb, split1, store, tree, 50, epoch, ts) - if err != nil { - return cid.Undef, xerrors.Errorf("splitting first msig: %w", err) - } - - err = splitGenesisMultisig0(ctx, cb, split2, store, tree, 50, epoch, ts) - if err != nil { - return cid.Undef, xerrors.Errorf("splitting second msig: %w", err) - } - - err = nv3.CheckStateTree(ctx, store, nst, epoch, builtin0.TotalFilecoin) - if err != nil { - return cid.Undef, xerrors.Errorf("sanity check after ignition upgrade failed: %w", err) - } - - return tree.Flush(ctx) -} - -func UpgradeRefuel(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { - - store := sm.cs.ActorStore(ctx) - tree, err := sm.StateTree(root) - if err != nil { - return cid.Undef, xerrors.Errorf("getting state tree: %w", err) - } - - err = resetMultisigVesting0(ctx, store, tree, builtin.SaftAddress, 0, 0, big.Zero()) - if err != nil { - return cid.Undef, xerrors.Errorf("tweaking msig vesting: %w", err) - } - - err = resetMultisigVesting0(ctx, store, tree, builtin.ReserveAddress, 0, 0, big.Zero()) - if err != nil { - return cid.Undef, xerrors.Errorf("tweaking msig vesting: %w", err) - } - - err = resetMultisigVesting0(ctx, store, tree, builtin.RootVerifierAddress, 0, 0, big.Zero()) - if err != nil { - return cid.Undef, xerrors.Errorf("tweaking msig vesting: %w", err) - } - - return tree.Flush(ctx) -} - -func UpgradeActorsV2(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { - buf := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) - store := store.ActorStore(ctx, buf) - - info, err := store.Put(ctx, new(types.StateInfo0)) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to create new state info for actors v2: %w", err) - } - - newHamtRoot, err := nv4.MigrateStateTree(ctx, store, root, epoch, nv4.DefaultConfig()) - if err != nil { - return cid.Undef, xerrors.Errorf("upgrading to actors v2: %w", err) - } - - newRoot, err := store.Put(ctx, &types.StateRoot{ - Version: types.StateTreeVersion1, - Actors: newHamtRoot, - Info: info, - }) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) - } - - // perform some basic sanity checks to make sure everything still works. - if newSm, err := state.LoadStateTree(store, newRoot); err != nil { - return cid.Undef, xerrors.Errorf("state tree sanity load failed: %w", err) - } else if newRoot2, err := newSm.Flush(ctx); err != nil { - return cid.Undef, xerrors.Errorf("state tree sanity flush failed: %w", err) - } else if newRoot2 != newRoot { - return cid.Undef, xerrors.Errorf("state-root mismatch: %s != %s", newRoot, newRoot2) - } else if _, err := newSm.GetActor(builtin0.InitActorAddr); err != nil { - return cid.Undef, xerrors.Errorf("failed to load init actor after upgrade: %w", err) - } - - { - from := buf - to := buf.Read() - - if err := vm.Copy(ctx, from, to, newRoot); err != nil { - return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) - } - } - - return newRoot, nil -} - -func UpgradeLiftoff(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { - tree, err := sm.StateTree(root) - if err != nil { - return cid.Undef, xerrors.Errorf("getting state tree: %w", err) - } - - err = setNetworkName(ctx, sm.cs.ActorStore(ctx), tree, "mainnet") - if err != nil { - return cid.Undef, xerrors.Errorf("setting network name: %w", err) - } - - return tree.Flush(ctx) -} - -func UpgradeCalico(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { - if build.BuildType != build.BuildMainnet { - return root, nil - } - - store := sm.cs.ActorStore(ctx) - var stateRoot types.StateRoot - if err := store.Get(ctx, root, &stateRoot); err != nil { - return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) - } - - if stateRoot.Version != types.StateTreeVersion1 { - return cid.Undef, xerrors.Errorf( - "expected state root version 1 for calico upgrade, got %d", - stateRoot.Version, - ) - } - - newHamtRoot, err := nv7.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, nv7.DefaultConfig()) - if err != nil { - return cid.Undef, xerrors.Errorf("running nv7 migration: %w", err) - } - - newRoot, err := store.Put(ctx, &types.StateRoot{ - Version: stateRoot.Version, - Actors: newHamtRoot, - Info: stateRoot.Info, - }) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) - } - - // perform some basic sanity checks to make sure everything still works. - if newSm, err := state.LoadStateTree(store, newRoot); err != nil { - return cid.Undef, xerrors.Errorf("state tree sanity load failed: %w", err) - } else if newRoot2, err := newSm.Flush(ctx); err != nil { - return cid.Undef, xerrors.Errorf("state tree sanity flush failed: %w", err) - } else if newRoot2 != newRoot { - return cid.Undef, xerrors.Errorf("state-root mismatch: %s != %s", newRoot, newRoot2) - } else if _, err := newSm.GetActor(builtin0.InitActorAddr); err != nil { - return cid.Undef, xerrors.Errorf("failed to load init actor after upgrade: %w", err) - } - - return newRoot, nil -} - func terminateActor(ctx context.Context, tree *state.StateTree, addr address.Address, em ExecMonitor, epoch abi.ChainEpoch, ts *types.TipSet) error { a, err := tree.GetActor(addr) if xerrors.Is(err, types.ErrActorNotFound) { @@ -1011,282 +418,8 @@ func terminateActor(ctx context.Context, tree *state.StateTree, addr address.Add return tree.SetActor(init_.Address, ia) } -func UpgradeActorsV3(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { - // Use all the CPUs except 3. - workerCount := runtime.NumCPU() - 3 - if workerCount <= 0 { - workerCount = 1 - } - - config := nv10.Config{ - MaxWorkers: uint(workerCount), - JobQueueSize: 1000, - ResultQueueSize: 100, - ProgressLogPeriod: 10 * time.Second, - } - newRoot, err := upgradeActorsV3Common(ctx, sm, cache, root, epoch, ts, config) - if err != nil { - return cid.Undef, xerrors.Errorf("migrating actors v3 state: %w", err) - } - - tree, err := sm.StateTree(newRoot) - if err != nil { - return cid.Undef, xerrors.Errorf("getting state tree: %w", err) - } - - if build.BuildType == build.BuildMainnet { - err := terminateActor(ctx, tree, build.ZeroAddress, cb, epoch, ts) - if err != nil && !xerrors.Is(err, types.ErrActorNotFound) { - return cid.Undef, xerrors.Errorf("deleting zero bls actor: %w", err) - } - - newRoot, err = tree.Flush(ctx) - if err != nil { - return cid.Undef, xerrors.Errorf("flushing state tree: %w", err) - } - } - - return newRoot, nil -} - -func PreUpgradeActorsV3(ctx context.Context, sm *StateManager, cache MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { - // Use half the CPUs for pre-migration, but leave at least 3. - workerCount := runtime.NumCPU() - if workerCount <= 4 { - workerCount = 1 - } else { - workerCount /= 2 - } - config := nv10.Config{MaxWorkers: uint(workerCount)} - _, err := upgradeActorsV3Common(ctx, sm, cache, root, epoch, ts, config) - return err -} - -func upgradeActorsV3Common( - ctx context.Context, sm *StateManager, cache MigrationCache, - root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, - config nv10.Config, -) (cid.Cid, error) { - buf := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) - store := store.ActorStore(ctx, buf) - - // Load the state root. - var stateRoot types.StateRoot - if err := store.Get(ctx, root, &stateRoot); err != nil { - return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) - } - - if stateRoot.Version != types.StateTreeVersion1 { - return cid.Undef, xerrors.Errorf( - "expected state root version 1 for actors v3 upgrade, got %d", - stateRoot.Version, - ) - } - - // Perform the migration - newHamtRoot, err := nv10.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, config, migrationLogger{}, cache) - if err != nil { - return cid.Undef, xerrors.Errorf("upgrading to actors v3: %w", err) - } - - // Persist the result. - newRoot, err := store.Put(ctx, &types.StateRoot{ - Version: types.StateTreeVersion2, - Actors: newHamtRoot, - Info: stateRoot.Info, - }) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) - } - - // Persist the new tree. - - { - from := buf - to := buf.Read() - - if err := vm.Copy(ctx, from, to, newRoot); err != nil { - return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) - } - } - - return newRoot, nil -} - -func UpgradeActorsV4(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { - // Use all the CPUs except 3. - workerCount := runtime.NumCPU() - 3 - if workerCount <= 0 { - workerCount = 1 - } - - config := nv12.Config{ - MaxWorkers: uint(workerCount), - JobQueueSize: 1000, - ResultQueueSize: 100, - ProgressLogPeriod: 10 * time.Second, - } - - newRoot, err := upgradeActorsV4Common(ctx, sm, cache, root, epoch, ts, config) - if err != nil { - return cid.Undef, xerrors.Errorf("migrating actors v4 state: %w", err) - } - - return newRoot, nil -} - -func PreUpgradeActorsV4(ctx context.Context, sm *StateManager, cache MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { - // Use half the CPUs for pre-migration, but leave at least 3. - workerCount := runtime.NumCPU() - if workerCount <= 4 { - workerCount = 1 - } else { - workerCount /= 2 - } - config := nv12.Config{MaxWorkers: uint(workerCount)} - _, err := upgradeActorsV4Common(ctx, sm, cache, root, epoch, ts, config) - return err -} - -func upgradeActorsV4Common( - ctx context.Context, sm *StateManager, cache MigrationCache, - root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, - config nv12.Config, -) (cid.Cid, error) { - buf := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) - store := store.ActorStore(ctx, buf) - - // Load the state root. - var stateRoot types.StateRoot - if err := store.Get(ctx, root, &stateRoot); err != nil { - return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) - } - - if stateRoot.Version != types.StateTreeVersion2 { - return cid.Undef, xerrors.Errorf( - "expected state root version 2 for actors v4 upgrade, got %d", - stateRoot.Version, - ) - } - - // Perform the migration - newHamtRoot, err := nv12.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, config, migrationLogger{}, cache) - if err != nil { - return cid.Undef, xerrors.Errorf("upgrading to actors v4: %w", err) - } - - // Persist the result. - newRoot, err := store.Put(ctx, &types.StateRoot{ - Version: types.StateTreeVersion3, - Actors: newHamtRoot, - Info: stateRoot.Info, - }) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) - } - - // Persist the new tree. - - { - from := buf - to := buf.Read() - - if err := vm.Copy(ctx, from, to, newRoot); err != nil { - return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) - } - } - - return newRoot, nil -} - -func UpgradeActorsV5(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { - // Use all the CPUs except 3. - workerCount := runtime.NumCPU() - 3 - if workerCount <= 0 { - workerCount = 1 - } - - config := nv13.Config{ - MaxWorkers: uint(workerCount), - JobQueueSize: 1000, - ResultQueueSize: 100, - ProgressLogPeriod: 10 * time.Second, - } - - newRoot, err := upgradeActorsV5Common(ctx, sm, cache, root, epoch, ts, config) - if err != nil { - return cid.Undef, xerrors.Errorf("migrating actors v5 state: %w", err) - } - - return newRoot, nil -} - -func PreUpgradeActorsV5(ctx context.Context, sm *StateManager, cache MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { - // Use half the CPUs for pre-migration, but leave at least 3. - workerCount := runtime.NumCPU() - if workerCount <= 4 { - workerCount = 1 - } else { - workerCount /= 2 - } - config := nv13.Config{MaxWorkers: uint(workerCount)} - _, err := upgradeActorsV5Common(ctx, sm, cache, root, epoch, ts, config) - return err -} - -func upgradeActorsV5Common( - ctx context.Context, sm *StateManager, cache MigrationCache, - root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, - config nv13.Config, -) (cid.Cid, error) { - buf := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) - store := store.ActorStore(ctx, buf) - - // Load the state root. - var stateRoot types.StateRoot - if err := store.Get(ctx, root, &stateRoot); err != nil { - return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) - } - - if stateRoot.Version != types.StateTreeVersion3 { - return cid.Undef, xerrors.Errorf( - "expected state root version 3 for actors v5 upgrade, got %d", - stateRoot.Version, - ) - } - - // Perform the migration - newHamtRoot, err := nv13.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, config, migrationLogger{}, cache) - if err != nil { - return cid.Undef, xerrors.Errorf("upgrading to actors v5: %w", err) - } - - // Persist the result. - newRoot, err := store.Put(ctx, &types.StateRoot{ - Version: types.StateTreeVersion4, - Actors: newHamtRoot, - Info: stateRoot.Info, - }) - if err != nil { - return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) - } - - // Persist the new tree. - - { - from := buf - to := buf.Read() - - if err := vm.Copy(ctx, from, to, newRoot); err != nil { - return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) - } - } - - return newRoot, nil -} - func setNetworkName(ctx context.Context, store adt.Store, tree *state.StateTree, name string) error { - ia, err := tree.GetActor(builtin0.InitActorAddr) + ia, err := tree.GetActor(init_.Address) if err != nil { return xerrors.Errorf("getting init actor: %w", err) } @@ -1305,136 +438,13 @@ func setNetworkName(ctx context.Context, store adt.Store, tree *state.StateTree, return xerrors.Errorf("writing new init state: %w", err) } - if err := tree.SetActor(builtin0.InitActorAddr, ia); err != nil { + if err := tree.SetActor(init_.Address, ia); err != nil { return xerrors.Errorf("setting init actor: %w", err) } return nil } -func splitGenesisMultisig0(ctx context.Context, em ExecMonitor, addr address.Address, store adt0.Store, tree *state.StateTree, portions uint64, epoch abi.ChainEpoch, ts *types.TipSet) error { - if portions < 1 { - return xerrors.Errorf("cannot split into 0 portions") - } - - mact, err := tree.GetActor(addr) - if err != nil { - return xerrors.Errorf("getting msig actor: %w", err) - } - - mst, err := multisig.Load(store, mact) - if err != nil { - return xerrors.Errorf("getting msig state: %w", err) - } - - signers, err := mst.Signers() - if err != nil { - return xerrors.Errorf("getting msig signers: %w", err) - } - - thresh, err := mst.Threshold() - if err != nil { - return xerrors.Errorf("getting msig threshold: %w", err) - } - - ibal, err := mst.InitialBalance() - if err != nil { - return xerrors.Errorf("getting msig initial balance: %w", err) - } - - se, err := mst.StartEpoch() - if err != nil { - return xerrors.Errorf("getting msig start epoch: %w", err) - } - - ud, err := mst.UnlockDuration() - if err != nil { - return xerrors.Errorf("getting msig unlock duration: %w", err) - } - - pending, err := adt0.MakeEmptyMap(store).Root() - if err != nil { - return xerrors.Errorf("failed to create empty map: %w", err) - } - - newIbal := big.Div(ibal, types.NewInt(portions)) - newState := &multisig0.State{ - Signers: signers, - NumApprovalsThreshold: thresh, - NextTxnID: 0, - InitialBalance: newIbal, - StartEpoch: se, - UnlockDuration: ud, - PendingTxns: pending, - } - - scid, err := store.Put(ctx, newState) - if err != nil { - return xerrors.Errorf("storing new state: %w", err) - } - - newActor := types.Actor{ - Code: builtin0.MultisigActorCodeID, - Head: scid, - Nonce: 0, - Balance: big.Zero(), - } - - i := uint64(0) - subcalls := make([]types.ExecutionTrace, 0, portions) - transferCb := func(trace types.ExecutionTrace) { - subcalls = append(subcalls, trace) - } - - for i < portions { - keyAddr, err := makeKeyAddr(addr, i) - if err != nil { - return xerrors.Errorf("creating key address: %w", err) - } - - idAddr, err := tree.RegisterNewAddress(keyAddr) - if err != nil { - return xerrors.Errorf("registering new address: %w", err) - } - - err = tree.SetActor(idAddr, &newActor) - if err != nil { - return xerrors.Errorf("setting new msig actor state: %w", err) - } - - if err := doTransfer(tree, addr, idAddr, newIbal, transferCb); err != nil { - return xerrors.Errorf("transferring split msig balance: %w", err) - } - - i++ - } - - if em != nil { - // record the transfer in execution traces - - fakeMsg := makeFakeMsg(builtin.SystemActorAddr, addr, big.Zero(), uint64(epoch)) - - if err := em.MessageApplied(ctx, ts, fakeMsg.Cid(), fakeMsg, &vm.ApplyRet{ - MessageReceipt: *makeFakeRct(), - ActorErr: nil, - ExecutionTrace: types.ExecutionTrace{ - Msg: fakeMsg, - MsgRct: makeFakeRct(), - Error: "", - Duration: 0, - GasCharges: nil, - Subcalls: subcalls, - }, - Duration: 0, - GasCosts: nil, - }, false); err != nil { - return xerrors.Errorf("recording transfers: %w", err) - } - } - - return nil -} - func makeKeyAddr(splitAddr address.Address, count uint64) (address.Address, error) { var b bytes.Buffer if err := splitAddr.MarshalCBOR(&b); err != nil { @@ -1457,88 +467,6 @@ func makeKeyAddr(splitAddr address.Address, count uint64) (address.Address, erro return addr, nil } -// TODO: After the Liftoff epoch, refactor this to use resetMultisigVesting -func resetGenesisMsigs0(ctx context.Context, sm *StateManager, store adt0.Store, tree *state.StateTree, startEpoch abi.ChainEpoch) error { - gb, err := sm.cs.GetGenesis() - if err != nil { - return xerrors.Errorf("getting genesis block: %w", err) - } - - gts, err := types.NewTipSet([]*types.BlockHeader{gb}) - if err != nil { - return xerrors.Errorf("getting genesis tipset: %w", err) - } - - cst := cbor.NewCborStore(sm.cs.StateBlockstore()) - genesisTree, err := state.LoadStateTree(cst, gts.ParentState()) - if err != nil { - return xerrors.Errorf("loading state tree: %w", err) - } - - err = genesisTree.ForEach(func(addr address.Address, genesisActor *types.Actor) error { - if genesisActor.Code == builtin0.MultisigActorCodeID { - currActor, err := tree.GetActor(addr) - if err != nil { - return xerrors.Errorf("loading actor: %w", err) - } - - var currState multisig0.State - if err := store.Get(ctx, currActor.Head, &currState); err != nil { - return xerrors.Errorf("reading multisig state: %w", err) - } - - currState.StartEpoch = startEpoch - - currActor.Head, err = store.Put(ctx, &currState) - if err != nil { - return xerrors.Errorf("writing new multisig state: %w", err) - } - - if err := tree.SetActor(addr, currActor); err != nil { - return xerrors.Errorf("setting multisig actor: %w", err) - } - } - return nil - }) - - if err != nil { - return xerrors.Errorf("iterating over genesis actors: %w", err) - } - - return nil -} - -func resetMultisigVesting0(ctx context.Context, store adt0.Store, tree *state.StateTree, addr address.Address, startEpoch abi.ChainEpoch, duration abi.ChainEpoch, balance abi.TokenAmount) error { - act, err := tree.GetActor(addr) - if err != nil { - return xerrors.Errorf("getting actor: %w", err) - } - - if !builtin.IsMultisigActor(act.Code) { - return xerrors.Errorf("actor wasn't msig: %w", err) - } - - var msigState multisig0.State - if err := store.Get(ctx, act.Head, &msigState); err != nil { - return xerrors.Errorf("reading multisig state: %w", err) - } - - msigState.StartEpoch = startEpoch - msigState.UnlockDuration = duration - msigState.InitialBalance = balance - - act.Head, err = store.Put(ctx, &msigState) - if err != nil { - return xerrors.Errorf("writing new multisig state: %w", err) - } - - if err := tree.SetActor(addr, act); err != nil { - return xerrors.Errorf("setting multisig actor: %w", err) - } - - return nil -} - func makeFakeMsg(from address.Address, to address.Address, amt abi.TokenAmount, nonce uint64) *types.Message { return &types.Message{ From: from, diff --git a/chain/stmgr/upgrades.go b/chain/stmgr/upgrades.go new file mode 100644 index 000000000..968a0e273 --- /dev/null +++ b/chain/stmgr/upgrades.go @@ -0,0 +1,1094 @@ +package stmgr + +import ( + "context" + "runtime" + "time" + + "github.com/ipfs/go-cid" + cbor "github.com/ipfs/go-ipld-cbor" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/network" + + builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" + miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner" + multisig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + power0 "github.com/filecoin-project/specs-actors/actors/builtin/power" + "github.com/filecoin-project/specs-actors/actors/migration/nv3" + adt0 "github.com/filecoin-project/specs-actors/actors/util/adt" + "github.com/filecoin-project/specs-actors/v2/actors/migration/nv4" + "github.com/filecoin-project/specs-actors/v2/actors/migration/nv7" + "github.com/filecoin-project/specs-actors/v3/actors/migration/nv10" + "github.com/filecoin-project/specs-actors/v4/actors/migration/nv12" + "github.com/filecoin-project/specs-actors/v5/actors/migration/nv13" + + "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/actors/builtin/multisig" + "github.com/filecoin-project/lotus/chain/state" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" +) + +func DefaultUpgradeSchedule() UpgradeSchedule { + var us UpgradeSchedule + + updates := []Upgrade{{ + Height: build.UpgradeBreezeHeight, + Network: network.Version1, + Migration: UpgradeFaucetBurnRecovery, + }, { + Height: build.UpgradeSmokeHeight, + Network: network.Version2, + Migration: nil, + }, { + Height: build.UpgradeIgnitionHeight, + Network: network.Version3, + Migration: UpgradeIgnition, + }, { + Height: build.UpgradeRefuelHeight, + Network: network.Version3, + Migration: UpgradeRefuel, + }, { + Height: build.UpgradeAssemblyHeight, + Network: network.Version4, + Expensive: true, + Migration: UpgradeActorsV2, + }, { + Height: build.UpgradeTapeHeight, + Network: network.Version5, + Migration: nil, + }, { + Height: build.UpgradeLiftoffHeight, + Network: network.Version5, + Migration: UpgradeLiftoff, + }, { + Height: build.UpgradeKumquatHeight, + Network: network.Version6, + Migration: nil, + }, { + Height: build.UpgradePricelistOopsHeight, + Network: network.Version6AndAHalf, + Migration: nil, + }, { + Height: build.UpgradeCalicoHeight, + Network: network.Version7, + Migration: UpgradeCalico, + }, { + Height: build.UpgradePersianHeight, + Network: network.Version8, + Migration: nil, + }, { + Height: build.UpgradeOrangeHeight, + Network: network.Version9, + Migration: nil, + }, { + Height: build.UpgradeTrustHeight, + Network: network.Version10, + Migration: UpgradeActorsV3, + PreMigrations: []PreMigration{{ + PreMigration: PreUpgradeActorsV3, + StartWithin: 120, + DontStartWithin: 60, + StopWithin: 35, + }, { + PreMigration: PreUpgradeActorsV3, + StartWithin: 30, + DontStartWithin: 15, + StopWithin: 5, + }}, + Expensive: true, + }, { + Height: build.UpgradeNorwegianHeight, + Network: network.Version11, + Migration: nil, + }, { + Height: build.UpgradeTurboHeight, + Network: network.Version12, + Migration: UpgradeActorsV4, + PreMigrations: []PreMigration{{ + PreMigration: PreUpgradeActorsV4, + StartWithin: 120, + DontStartWithin: 60, + StopWithin: 35, + }, { + PreMigration: PreUpgradeActorsV4, + StartWithin: 30, + DontStartWithin: 15, + StopWithin: 5, + }}, + Expensive: true, + }, { + Height: build.UpgradeHyperdriveHeight, + Network: network.Version13, + Migration: UpgradeActorsV5, + PreMigrations: []PreMigration{{ + PreMigration: PreUpgradeActorsV5, + StartWithin: 120, + DontStartWithin: 60, + StopWithin: 35, + }, { + PreMigration: PreUpgradeActorsV5, + StartWithin: 30, + DontStartWithin: 15, + StopWithin: 5, + }}, + Expensive: true}} + + for _, u := range updates { + if u.Height < 0 { + // upgrade disabled + continue + } + us = append(us, u) + } + return us +} + +func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, _ MigrationCache, em ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + // Some initial parameters + FundsForMiners := types.FromFil(1_000_000) + LookbackEpoch := abi.ChainEpoch(32000) + AccountCap := types.FromFil(0) + BaseMinerBalance := types.FromFil(20) + DesiredReimbursementBalance := types.FromFil(5_000_000) + + isSystemAccount := func(addr address.Address) (bool, error) { + id, err := address.IDFromAddress(addr) + if err != nil { + return false, xerrors.Errorf("id address: %w", err) + } + + if id < 1000 { + return true, nil + } + return false, nil + } + + minerFundsAlloc := func(pow, tpow abi.StoragePower) abi.TokenAmount { + return types.BigDiv(types.BigMul(pow, FundsForMiners), tpow) + } + + // Grab lookback state for account checks + lbts, err := sm.ChainStore().GetTipsetByHeight(ctx, LookbackEpoch, ts, false) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to get tipset at lookback height: %w", err) + } + + lbtree, err := sm.ParentState(lbts) + if err != nil { + return cid.Undef, xerrors.Errorf("loading state tree failed: %w", err) + } + + tree, err := sm.StateTree(root) + if err != nil { + return cid.Undef, xerrors.Errorf("getting state tree: %w", err) + } + + type transfer struct { + From address.Address + To address.Address + Amt abi.TokenAmount + } + + var transfers []transfer + subcalls := make([]types.ExecutionTrace, 0) + transferCb := func(trace types.ExecutionTrace) { + subcalls = append(subcalls, trace) + } + + // Take all excess funds away, put them into the reserve account + err = tree.ForEach(func(addr address.Address, act *types.Actor) error { + switch act.Code { + case builtin0.AccountActorCodeID, builtin0.MultisigActorCodeID, builtin0.PaymentChannelActorCodeID: + sysAcc, err := isSystemAccount(addr) + if err != nil { + return xerrors.Errorf("checking system account: %w", err) + } + + if !sysAcc { + transfers = append(transfers, transfer{ + From: addr, + To: builtin.ReserveAddress, + Amt: act.Balance, + }) + } + case builtin0.StorageMinerActorCodeID: + var st miner0.State + if err := sm.ChainStore().ActorStore(ctx).Get(ctx, act.Head, &st); err != nil { + return xerrors.Errorf("failed to load miner state: %w", err) + } + + var available abi.TokenAmount + { + defer func() { + if err := recover(); err != nil { + log.Warnf("Get available balance failed (%s, %s, %s): %s", addr, act.Head, act.Balance, err) + } + available = abi.NewTokenAmount(0) + }() + // this panics if the miner doesnt have enough funds to cover their locked pledge + available = st.GetAvailableBalance(act.Balance) + } + + if !available.IsZero() { + transfers = append(transfers, transfer{ + From: addr, + To: builtin.ReserveAddress, + Amt: available, + }) + } + } + return nil + }) + if err != nil { + return cid.Undef, xerrors.Errorf("foreach over state tree failed: %w", err) + } + + // Execute transfers from previous step + for _, t := range transfers { + if err := doTransfer(tree, t.From, t.To, t.Amt, transferCb); err != nil { + return cid.Undef, xerrors.Errorf("transfer %s %s->%s failed: %w", t.Amt, t.From, t.To, err) + } + } + + // pull up power table to give miners back some funds proportional to their power + var ps power0.State + powAct, err := tree.GetActor(builtin0.StoragePowerActorAddr) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to load power actor: %w", err) + } + + cst := cbor.NewCborStore(sm.ChainStore().StateBlockstore()) + if err := cst.Get(ctx, powAct.Head, &ps); err != nil { + return cid.Undef, xerrors.Errorf("failed to get power actor state: %w", err) + } + + totalPower := ps.TotalBytesCommitted + + var transfersBack []transfer + // Now, we return some funds to places where they are needed + err = tree.ForEach(func(addr address.Address, act *types.Actor) error { + lbact, err := lbtree.GetActor(addr) + if err != nil { + if !xerrors.Is(err, types.ErrActorNotFound) { + return xerrors.Errorf("failed to get actor in lookback state") + } + } + + prevBalance := abi.NewTokenAmount(0) + if lbact != nil { + prevBalance = lbact.Balance + } + + switch act.Code { + case builtin0.AccountActorCodeID, builtin0.MultisigActorCodeID, builtin0.PaymentChannelActorCodeID: + nbalance := big.Min(prevBalance, AccountCap) + if nbalance.Sign() != 0 { + transfersBack = append(transfersBack, transfer{ + From: builtin.ReserveAddress, + To: addr, + Amt: nbalance, + }) + } + case builtin0.StorageMinerActorCodeID: + var st miner0.State + if err := sm.ChainStore().ActorStore(ctx).Get(ctx, act.Head, &st); err != nil { + return xerrors.Errorf("failed to load miner state: %w", err) + } + + var minfo miner0.MinerInfo + if err := cst.Get(ctx, st.Info, &minfo); err != nil { + return xerrors.Errorf("failed to get miner info: %w", err) + } + + sectorsArr, err := adt0.AsArray(sm.ChainStore().ActorStore(ctx), st.Sectors) + if err != nil { + return xerrors.Errorf("failed to load sectors array: %w", err) + } + + slen := sectorsArr.Length() + + power := types.BigMul(types.NewInt(slen), types.NewInt(uint64(minfo.SectorSize))) + + mfunds := minerFundsAlloc(power, totalPower) + transfersBack = append(transfersBack, transfer{ + From: builtin.ReserveAddress, + To: minfo.Worker, + Amt: mfunds, + }) + + // Now make sure to give each miner who had power at the lookback some FIL + lbact, err := lbtree.GetActor(addr) + if err == nil { + var lbst miner0.State + if err := sm.ChainStore().ActorStore(ctx).Get(ctx, lbact.Head, &lbst); err != nil { + return xerrors.Errorf("failed to load miner state: %w", err) + } + + lbsectors, err := adt0.AsArray(sm.ChainStore().ActorStore(ctx), lbst.Sectors) + if err != nil { + return xerrors.Errorf("failed to load lb sectors array: %w", err) + } + + if lbsectors.Length() > 0 { + transfersBack = append(transfersBack, transfer{ + From: builtin.ReserveAddress, + To: minfo.Worker, + Amt: BaseMinerBalance, + }) + } + + } else { + log.Warnf("failed to get miner in lookback state: %s", err) + } + } + return nil + }) + if err != nil { + return cid.Undef, xerrors.Errorf("foreach over state tree failed: %w", err) + } + + for _, t := range transfersBack { + if err := doTransfer(tree, t.From, t.To, t.Amt, transferCb); err != nil { + return cid.Undef, xerrors.Errorf("transfer %s %s->%s failed: %w", t.Amt, t.From, t.To, err) + } + } + + // transfer all burnt funds back to the reserve account + burntAct, err := tree.GetActor(builtin0.BurntFundsActorAddr) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to load burnt funds actor: %w", err) + } + if err := doTransfer(tree, builtin0.BurntFundsActorAddr, builtin.ReserveAddress, burntAct.Balance, transferCb); err != nil { + return cid.Undef, xerrors.Errorf("failed to unburn funds: %w", err) + } + + // Top up the reimbursement service + reimbAddr, err := address.NewFromString("t0111") + if err != nil { + return cid.Undef, xerrors.Errorf("failed to parse reimbursement service address") + } + + reimb, err := tree.GetActor(reimbAddr) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to load reimbursement account actor: %w", err) + } + + difference := types.BigSub(DesiredReimbursementBalance, reimb.Balance) + if err := doTransfer(tree, builtin.ReserveAddress, reimbAddr, difference, transferCb); err != nil { + return cid.Undef, xerrors.Errorf("failed to top up reimbursement account: %w", err) + } + + // Now, a final sanity check to make sure the balances all check out + total := abi.NewTokenAmount(0) + err = tree.ForEach(func(addr address.Address, act *types.Actor) error { + total = types.BigAdd(total, act.Balance) + return nil + }) + if err != nil { + return cid.Undef, xerrors.Errorf("checking final state balance failed: %w", err) + } + + exp := types.FromFil(build.FilBase) + if !exp.Equals(total) { + return cid.Undef, xerrors.Errorf("resultant state tree account balance was not correct: %s", total) + } + + if em != nil { + // record the transfer in execution traces + + fakeMsg := makeFakeMsg(builtin.SystemActorAddr, builtin.SystemActorAddr, big.Zero(), uint64(epoch)) + + if err := em.MessageApplied(ctx, ts, fakeMsg.Cid(), fakeMsg, &vm.ApplyRet{ + MessageReceipt: *makeFakeRct(), + ActorErr: nil, + ExecutionTrace: types.ExecutionTrace{ + Msg: fakeMsg, + MsgRct: makeFakeRct(), + Error: "", + Duration: 0, + GasCharges: nil, + Subcalls: subcalls, + }, + Duration: 0, + GasCosts: nil, + }, false); err != nil { + return cid.Undef, xerrors.Errorf("recording transfers: %w", err) + } + } + + return tree.Flush(ctx) +} + +func UpgradeIgnition(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + store := sm.cs.ActorStore(ctx) + + if build.UpgradeLiftoffHeight <= epoch { + return cid.Undef, xerrors.Errorf("liftoff height must be beyond ignition height") + } + + nst, err := nv3.MigrateStateTree(ctx, store, root, epoch) + if err != nil { + return cid.Undef, xerrors.Errorf("migrating actors state: %w", err) + } + + tree, err := sm.StateTree(nst) + if err != nil { + return cid.Undef, xerrors.Errorf("getting state tree: %w", err) + } + + err = setNetworkName(ctx, store, tree, "ignition") + if err != nil { + return cid.Undef, xerrors.Errorf("setting network name: %w", err) + } + + split1, err := address.NewFromString("t0115") + if err != nil { + return cid.Undef, xerrors.Errorf("first split address: %w", err) + } + + split2, err := address.NewFromString("t0116") + if err != nil { + return cid.Undef, xerrors.Errorf("second split address: %w", err) + } + + err = resetGenesisMsigs0(ctx, sm, store, tree, build.UpgradeLiftoffHeight) + if err != nil { + return cid.Undef, xerrors.Errorf("resetting genesis msig start epochs: %w", err) + } + + err = splitGenesisMultisig0(ctx, cb, split1, store, tree, 50, epoch, ts) + if err != nil { + return cid.Undef, xerrors.Errorf("splitting first msig: %w", err) + } + + err = splitGenesisMultisig0(ctx, cb, split2, store, tree, 50, epoch, ts) + if err != nil { + return cid.Undef, xerrors.Errorf("splitting second msig: %w", err) + } + + err = nv3.CheckStateTree(ctx, store, nst, epoch, builtin0.TotalFilecoin) + if err != nil { + return cid.Undef, xerrors.Errorf("sanity check after ignition upgrade failed: %w", err) + } + + return tree.Flush(ctx) +} + +func splitGenesisMultisig0(ctx context.Context, em ExecMonitor, addr address.Address, store adt0.Store, tree *state.StateTree, portions uint64, epoch abi.ChainEpoch, ts *types.TipSet) error { + if portions < 1 { + return xerrors.Errorf("cannot split into 0 portions") + } + + mact, err := tree.GetActor(addr) + if err != nil { + return xerrors.Errorf("getting msig actor: %w", err) + } + + mst, err := multisig.Load(store, mact) + if err != nil { + return xerrors.Errorf("getting msig state: %w", err) + } + + signers, err := mst.Signers() + if err != nil { + return xerrors.Errorf("getting msig signers: %w", err) + } + + thresh, err := mst.Threshold() + if err != nil { + return xerrors.Errorf("getting msig threshold: %w", err) + } + + ibal, err := mst.InitialBalance() + if err != nil { + return xerrors.Errorf("getting msig initial balance: %w", err) + } + + se, err := mst.StartEpoch() + if err != nil { + return xerrors.Errorf("getting msig start epoch: %w", err) + } + + ud, err := mst.UnlockDuration() + if err != nil { + return xerrors.Errorf("getting msig unlock duration: %w", err) + } + + pending, err := adt0.MakeEmptyMap(store).Root() + if err != nil { + return xerrors.Errorf("failed to create empty map: %w", err) + } + + newIbal := big.Div(ibal, types.NewInt(portions)) + newState := &multisig0.State{ + Signers: signers, + NumApprovalsThreshold: thresh, + NextTxnID: 0, + InitialBalance: newIbal, + StartEpoch: se, + UnlockDuration: ud, + PendingTxns: pending, + } + + scid, err := store.Put(ctx, newState) + if err != nil { + return xerrors.Errorf("storing new state: %w", err) + } + + newActor := types.Actor{ + Code: builtin0.MultisigActorCodeID, + Head: scid, + Nonce: 0, + Balance: big.Zero(), + } + + i := uint64(0) + subcalls := make([]types.ExecutionTrace, 0, portions) + transferCb := func(trace types.ExecutionTrace) { + subcalls = append(subcalls, trace) + } + + for i < portions { + keyAddr, err := makeKeyAddr(addr, i) + if err != nil { + return xerrors.Errorf("creating key address: %w", err) + } + + idAddr, err := tree.RegisterNewAddress(keyAddr) + if err != nil { + return xerrors.Errorf("registering new address: %w", err) + } + + err = tree.SetActor(idAddr, &newActor) + if err != nil { + return xerrors.Errorf("setting new msig actor state: %w", err) + } + + if err := doTransfer(tree, addr, idAddr, newIbal, transferCb); err != nil { + return xerrors.Errorf("transferring split msig balance: %w", err) + } + + i++ + } + + if em != nil { + // record the transfer in execution traces + + fakeMsg := makeFakeMsg(builtin.SystemActorAddr, addr, big.Zero(), uint64(epoch)) + + if err := em.MessageApplied(ctx, ts, fakeMsg.Cid(), fakeMsg, &vm.ApplyRet{ + MessageReceipt: *makeFakeRct(), + ActorErr: nil, + ExecutionTrace: types.ExecutionTrace{ + Msg: fakeMsg, + MsgRct: makeFakeRct(), + Error: "", + Duration: 0, + GasCharges: nil, + Subcalls: subcalls, + }, + Duration: 0, + GasCosts: nil, + }, false); err != nil { + return xerrors.Errorf("recording transfers: %w", err) + } + } + + return nil +} + +// TODO: After the Liftoff epoch, refactor this to use resetMultisigVesting +func resetGenesisMsigs0(ctx context.Context, sm *StateManager, store adt0.Store, tree *state.StateTree, startEpoch abi.ChainEpoch) error { + gb, err := sm.cs.GetGenesis() + if err != nil { + return xerrors.Errorf("getting genesis block: %w", err) + } + + gts, err := types.NewTipSet([]*types.BlockHeader{gb}) + if err != nil { + return xerrors.Errorf("getting genesis tipset: %w", err) + } + + cst := cbor.NewCborStore(sm.cs.StateBlockstore()) + genesisTree, err := state.LoadStateTree(cst, gts.ParentState()) + if err != nil { + return xerrors.Errorf("loading state tree: %w", err) + } + + err = genesisTree.ForEach(func(addr address.Address, genesisActor *types.Actor) error { + if genesisActor.Code == builtin0.MultisigActorCodeID { + currActor, err := tree.GetActor(addr) + if err != nil { + return xerrors.Errorf("loading actor: %w", err) + } + + var currState multisig0.State + if err := store.Get(ctx, currActor.Head, &currState); err != nil { + return xerrors.Errorf("reading multisig state: %w", err) + } + + currState.StartEpoch = startEpoch + + currActor.Head, err = store.Put(ctx, &currState) + if err != nil { + return xerrors.Errorf("writing new multisig state: %w", err) + } + + if err := tree.SetActor(addr, currActor); err != nil { + return xerrors.Errorf("setting multisig actor: %w", err) + } + } + return nil + }) + + if err != nil { + return xerrors.Errorf("iterating over genesis actors: %w", err) + } + + return nil +} + +func resetMultisigVesting0(ctx context.Context, store adt0.Store, tree *state.StateTree, addr address.Address, startEpoch abi.ChainEpoch, duration abi.ChainEpoch, balance abi.TokenAmount) error { + act, err := tree.GetActor(addr) + if err != nil { + return xerrors.Errorf("getting actor: %w", err) + } + + if !builtin.IsMultisigActor(act.Code) { + return xerrors.Errorf("actor wasn't msig: %w", err) + } + + var msigState multisig0.State + if err := store.Get(ctx, act.Head, &msigState); err != nil { + return xerrors.Errorf("reading multisig state: %w", err) + } + + msigState.StartEpoch = startEpoch + msigState.UnlockDuration = duration + msigState.InitialBalance = balance + + act.Head, err = store.Put(ctx, &msigState) + if err != nil { + return xerrors.Errorf("writing new multisig state: %w", err) + } + + if err := tree.SetActor(addr, act); err != nil { + return xerrors.Errorf("setting multisig actor: %w", err) + } + + return nil +} + +func UpgradeRefuel(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + + store := sm.cs.ActorStore(ctx) + tree, err := sm.StateTree(root) + if err != nil { + return cid.Undef, xerrors.Errorf("getting state tree: %w", err) + } + + err = resetMultisigVesting0(ctx, store, tree, builtin.SaftAddress, 0, 0, big.Zero()) + if err != nil { + return cid.Undef, xerrors.Errorf("tweaking msig vesting: %w", err) + } + + err = resetMultisigVesting0(ctx, store, tree, builtin.ReserveAddress, 0, 0, big.Zero()) + if err != nil { + return cid.Undef, xerrors.Errorf("tweaking msig vesting: %w", err) + } + + err = resetMultisigVesting0(ctx, store, tree, builtin.RootVerifierAddress, 0, 0, big.Zero()) + if err != nil { + return cid.Undef, xerrors.Errorf("tweaking msig vesting: %w", err) + } + + return tree.Flush(ctx) +} + +func UpgradeActorsV2(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + buf := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) + store := store.ActorStore(ctx, buf) + + info, err := store.Put(ctx, new(types.StateInfo0)) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to create new state info for actors v2: %w", err) + } + + newHamtRoot, err := nv4.MigrateStateTree(ctx, store, root, epoch, nv4.DefaultConfig()) + if err != nil { + return cid.Undef, xerrors.Errorf("upgrading to actors v2: %w", err) + } + + newRoot, err := store.Put(ctx, &types.StateRoot{ + Version: types.StateTreeVersion1, + Actors: newHamtRoot, + Info: info, + }) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) + } + + // perform some basic sanity checks to make sure everything still works. + if newSm, err := state.LoadStateTree(store, newRoot); err != nil { + return cid.Undef, xerrors.Errorf("state tree sanity load failed: %w", err) + } else if newRoot2, err := newSm.Flush(ctx); err != nil { + return cid.Undef, xerrors.Errorf("state tree sanity flush failed: %w", err) + } else if newRoot2 != newRoot { + return cid.Undef, xerrors.Errorf("state-root mismatch: %s != %s", newRoot, newRoot2) + } else if _, err := newSm.GetActor(builtin0.InitActorAddr); err != nil { + return cid.Undef, xerrors.Errorf("failed to load init actor after upgrade: %w", err) + } + + { + from := buf + to := buf.Read() + + if err := vm.Copy(ctx, from, to, newRoot); err != nil { + return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) + } + } + + return newRoot, nil +} + +func UpgradeLiftoff(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + tree, err := sm.StateTree(root) + if err != nil { + return cid.Undef, xerrors.Errorf("getting state tree: %w", err) + } + + err = setNetworkName(ctx, sm.cs.ActorStore(ctx), tree, "mainnet") + if err != nil { + return cid.Undef, xerrors.Errorf("setting network name: %w", err) + } + + return tree.Flush(ctx) +} + +func UpgradeCalico(ctx context.Context, sm *StateManager, _ MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + if build.BuildType != build.BuildMainnet { + return root, nil + } + + store := sm.cs.ActorStore(ctx) + var stateRoot types.StateRoot + if err := store.Get(ctx, root, &stateRoot); err != nil { + return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) + } + + if stateRoot.Version != types.StateTreeVersion1 { + return cid.Undef, xerrors.Errorf( + "expected state root version 1 for calico upgrade, got %d", + stateRoot.Version, + ) + } + + newHamtRoot, err := nv7.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, nv7.DefaultConfig()) + if err != nil { + return cid.Undef, xerrors.Errorf("running nv7 migration: %w", err) + } + + newRoot, err := store.Put(ctx, &types.StateRoot{ + Version: stateRoot.Version, + Actors: newHamtRoot, + Info: stateRoot.Info, + }) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) + } + + // perform some basic sanity checks to make sure everything still works. + if newSm, err := state.LoadStateTree(store, newRoot); err != nil { + return cid.Undef, xerrors.Errorf("state tree sanity load failed: %w", err) + } else if newRoot2, err := newSm.Flush(ctx); err != nil { + return cid.Undef, xerrors.Errorf("state tree sanity flush failed: %w", err) + } else if newRoot2 != newRoot { + return cid.Undef, xerrors.Errorf("state-root mismatch: %s != %s", newRoot, newRoot2) + } else if _, err := newSm.GetActor(builtin0.InitActorAddr); err != nil { + return cid.Undef, xerrors.Errorf("failed to load init actor after upgrade: %w", err) + } + + return newRoot, nil +} + +func UpgradeActorsV3(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + // Use all the CPUs except 3. + workerCount := runtime.NumCPU() - 3 + if workerCount <= 0 { + workerCount = 1 + } + + config := nv10.Config{ + MaxWorkers: uint(workerCount), + JobQueueSize: 1000, + ResultQueueSize: 100, + ProgressLogPeriod: 10 * time.Second, + } + newRoot, err := upgradeActorsV3Common(ctx, sm, cache, root, epoch, ts, config) + if err != nil { + return cid.Undef, xerrors.Errorf("migrating actors v3 state: %w", err) + } + + tree, err := sm.StateTree(newRoot) + if err != nil { + return cid.Undef, xerrors.Errorf("getting state tree: %w", err) + } + + if build.BuildType == build.BuildMainnet { + err := terminateActor(ctx, tree, build.ZeroAddress, cb, epoch, ts) + if err != nil && !xerrors.Is(err, types.ErrActorNotFound) { + return cid.Undef, xerrors.Errorf("deleting zero bls actor: %w", err) + } + + newRoot, err = tree.Flush(ctx) + if err != nil { + return cid.Undef, xerrors.Errorf("flushing state tree: %w", err) + } + } + + return newRoot, nil +} + +func PreUpgradeActorsV3(ctx context.Context, sm *StateManager, cache MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { + // Use half the CPUs for pre-migration, but leave at least 3. + workerCount := runtime.NumCPU() + if workerCount <= 4 { + workerCount = 1 + } else { + workerCount /= 2 + } + config := nv10.Config{MaxWorkers: uint(workerCount)} + _, err := upgradeActorsV3Common(ctx, sm, cache, root, epoch, ts, config) + return err +} + +func upgradeActorsV3Common( + ctx context.Context, sm *StateManager, cache MigrationCache, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, + config nv10.Config, +) (cid.Cid, error) { + buf := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) + store := store.ActorStore(ctx, buf) + + // Load the state root. + var stateRoot types.StateRoot + if err := store.Get(ctx, root, &stateRoot); err != nil { + return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) + } + + if stateRoot.Version != types.StateTreeVersion1 { + return cid.Undef, xerrors.Errorf( + "expected state root version 1 for actors v3 upgrade, got %d", + stateRoot.Version, + ) + } + + // Perform the migration + newHamtRoot, err := nv10.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, config, migrationLogger{}, cache) + if err != nil { + return cid.Undef, xerrors.Errorf("upgrading to actors v3: %w", err) + } + + // Persist the result. + newRoot, err := store.Put(ctx, &types.StateRoot{ + Version: types.StateTreeVersion2, + Actors: newHamtRoot, + Info: stateRoot.Info, + }) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) + } + + // Persist the new tree. + + { + from := buf + to := buf.Read() + + if err := vm.Copy(ctx, from, to, newRoot); err != nil { + return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) + } + } + + return newRoot, nil +} + +func UpgradeActorsV4(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + // Use all the CPUs except 3. + workerCount := runtime.NumCPU() - 3 + if workerCount <= 0 { + workerCount = 1 + } + + config := nv12.Config{ + MaxWorkers: uint(workerCount), + JobQueueSize: 1000, + ResultQueueSize: 100, + ProgressLogPeriod: 10 * time.Second, + } + + newRoot, err := upgradeActorsV4Common(ctx, sm, cache, root, epoch, ts, config) + if err != nil { + return cid.Undef, xerrors.Errorf("migrating actors v4 state: %w", err) + } + + return newRoot, nil +} + +func PreUpgradeActorsV4(ctx context.Context, sm *StateManager, cache MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { + // Use half the CPUs for pre-migration, but leave at least 3. + workerCount := runtime.NumCPU() + if workerCount <= 4 { + workerCount = 1 + } else { + workerCount /= 2 + } + config := nv12.Config{MaxWorkers: uint(workerCount)} + _, err := upgradeActorsV4Common(ctx, sm, cache, root, epoch, ts, config) + return err +} + +func upgradeActorsV4Common( + ctx context.Context, sm *StateManager, cache MigrationCache, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, + config nv12.Config, +) (cid.Cid, error) { + buf := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) + store := store.ActorStore(ctx, buf) + + // Load the state root. + var stateRoot types.StateRoot + if err := store.Get(ctx, root, &stateRoot); err != nil { + return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) + } + + if stateRoot.Version != types.StateTreeVersion2 { + return cid.Undef, xerrors.Errorf( + "expected state root version 2 for actors v4 upgrade, got %d", + stateRoot.Version, + ) + } + + // Perform the migration + newHamtRoot, err := nv12.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, config, migrationLogger{}, cache) + if err != nil { + return cid.Undef, xerrors.Errorf("upgrading to actors v4: %w", err) + } + + // Persist the result. + newRoot, err := store.Put(ctx, &types.StateRoot{ + Version: types.StateTreeVersion3, + Actors: newHamtRoot, + Info: stateRoot.Info, + }) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) + } + + // Persist the new tree. + + { + from := buf + to := buf.Read() + + if err := vm.Copy(ctx, from, to, newRoot); err != nil { + return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) + } + } + + return newRoot, nil +} + +func UpgradeActorsV5(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) (cid.Cid, error) { + // Use all the CPUs except 3. + workerCount := runtime.NumCPU() - 3 + if workerCount <= 0 { + workerCount = 1 + } + + config := nv13.Config{ + MaxWorkers: uint(workerCount), + JobQueueSize: 1000, + ResultQueueSize: 100, + ProgressLogPeriod: 10 * time.Second, + } + + newRoot, err := upgradeActorsV5Common(ctx, sm, cache, root, epoch, ts, config) + if err != nil { + return cid.Undef, xerrors.Errorf("migrating actors v5 state: %w", err) + } + + return newRoot, nil +} + +func PreUpgradeActorsV5(ctx context.Context, sm *StateManager, cache MigrationCache, root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet) error { + // Use half the CPUs for pre-migration, but leave at least 3. + workerCount := runtime.NumCPU() + if workerCount <= 4 { + workerCount = 1 + } else { + workerCount /= 2 + } + config := nv13.Config{MaxWorkers: uint(workerCount)} + _, err := upgradeActorsV5Common(ctx, sm, cache, root, epoch, ts, config) + return err +} + +func upgradeActorsV5Common( + ctx context.Context, sm *StateManager, cache MigrationCache, + root cid.Cid, epoch abi.ChainEpoch, ts *types.TipSet, + config nv13.Config, +) (cid.Cid, error) { + buf := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) + store := store.ActorStore(ctx, buf) + + // Load the state root. + var stateRoot types.StateRoot + if err := store.Get(ctx, root, &stateRoot); err != nil { + return cid.Undef, xerrors.Errorf("failed to decode state root: %w", err) + } + + if stateRoot.Version != types.StateTreeVersion3 { + return cid.Undef, xerrors.Errorf( + "expected state root version 3 for actors v5 upgrade, got %d", + stateRoot.Version, + ) + } + + // Perform the migration + newHamtRoot, err := nv13.MigrateStateTree(ctx, store, stateRoot.Actors, epoch, config, migrationLogger{}, cache) + if err != nil { + return cid.Undef, xerrors.Errorf("upgrading to actors v5: %w", err) + } + + // Persist the result. + newRoot, err := store.Put(ctx, &types.StateRoot{ + Version: types.StateTreeVersion4, + Actors: newHamtRoot, + Info: stateRoot.Info, + }) + if err != nil { + return cid.Undef, xerrors.Errorf("failed to persist new state root: %w", err) + } + + // Persist the new tree. + + { + from := buf + to := buf.Read() + + if err := vm.Copy(ctx, from, to, newRoot); err != nil { + return cid.Undef, xerrors.Errorf("copying migrated tree: %w", err) + } + } + + return newRoot, nil +} From ca1cd741c39ffbe3dae7d096b42f7d2d5f4522f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Jul 2021 15:15:50 +0200 Subject: [PATCH 3/9] stmgr: Split stmgr.go into smaller subfiles --- chain/stmgr/execute.go | 326 +++++++++++ chain/stmgr/read.go | 8 + chain/stmgr/searchwait.go | 279 +++++++++ chain/stmgr/stmgr.go | 1144 +------------------------------------ chain/stmgr/supply.go | 473 +++++++++++++++ chain/stmgr/utils.go | 91 +++ 6 files changed, 1178 insertions(+), 1143 deletions(-) create mode 100644 chain/stmgr/execute.go create mode 100644 chain/stmgr/searchwait.go create mode 100644 chain/stmgr/supply.go diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go new file mode 100644 index 000000000..9194f711e --- /dev/null +++ b/chain/stmgr/execute.go @@ -0,0 +1,326 @@ +package stmgr + +import ( + "context" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "go.opencensus.io/stats" + "go.opencensus.io/trace" + "golang.org/x/xerrors" + "sync/atomic" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + blockadt "github.com/filecoin-project/specs-actors/actors/util/adt" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/builtin/cron" + "github.com/filecoin-project/lotus/chain/actors/builtin/reward" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" + "github.com/filecoin-project/lotus/metrics" +) + +func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEpoch, pstate cid.Cid, bms []store.BlockMessages, epoch abi.ChainEpoch, r vm.Rand, em ExecMonitor, baseFee abi.TokenAmount, ts *types.TipSet) (cid.Cid, cid.Cid, error) { + done := metrics.Timer(ctx, metrics.VMApplyBlocksTotal) + defer done() + + partDone := metrics.Timer(ctx, metrics.VMApplyEarly) + defer func() { + partDone() + }() + + makeVmWithBaseState := func(base cid.Cid) (*vm.VM, error) { + vmopt := &vm.VMOpts{ + StateBase: base, + Epoch: epoch, + Rand: r, + Bstore: sm.cs.StateBlockstore(), + Syscalls: sm.cs.VMSys(), + CircSupplyCalc: sm.GetVMCirculatingSupply, + NtwkVersion: sm.GetNtwkVersion, + BaseFee: baseFee, + LookbackState: LookbackStateGetterForTipset(sm, ts), + } + + return sm.newVM(ctx, vmopt) + } + + vmi, err := makeVmWithBaseState(pstate) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) + } + + runCron := func(epoch abi.ChainEpoch) error { + cronMsg := &types.Message{ + To: cron.Address, + From: builtin.SystemActorAddr, + Nonce: uint64(epoch), + Value: types.NewInt(0), + GasFeeCap: types.NewInt(0), + GasPremium: types.NewInt(0), + GasLimit: build.BlockGasLimit * 10000, // Make super sure this is never too little + Method: cron.Methods.EpochTick, + Params: nil, + } + ret, err := vmi.ApplyImplicitMessage(ctx, cronMsg) + if err != nil { + return err + } + if em != nil { + if err := em.MessageApplied(ctx, ts, cronMsg.Cid(), cronMsg, ret, true); err != nil { + return xerrors.Errorf("callback failed on cron message: %w", err) + } + } + if ret.ExitCode != 0 { + return xerrors.Errorf("CheckProofSubmissions exit was non-zero: %d", ret.ExitCode) + } + + return nil + } + + for i := parentEpoch; i < epoch; i++ { + if i > parentEpoch { + // run cron for null rounds if any + if err := runCron(i); err != nil { + return cid.Undef, cid.Undef, err + } + + pstate, err = vmi.Flush(ctx) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("flushing vm: %w", err) + } + } + + // handle state forks + // XXX: The state tree + newState, err := sm.handleStateForks(ctx, pstate, i, em, ts) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("error handling state forks: %w", err) + } + + if pstate != newState { + vmi, err = makeVmWithBaseState(newState) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) + } + } + + vmi.SetBlockHeight(i + 1) + pstate = newState + } + + partDone() + partDone = metrics.Timer(ctx, metrics.VMApplyMessages) + + var receipts []cbg.CBORMarshaler + processedMsgs := make(map[cid.Cid]struct{}) + for _, b := range bms { + penalty := types.NewInt(0) + gasReward := big.Zero() + + for _, cm := range append(b.BlsMessages, b.SecpkMessages...) { + m := cm.VMMessage() + if _, found := processedMsgs[m.Cid()]; found { + continue + } + r, err := vmi.ApplyMessage(ctx, cm) + if err != nil { + return cid.Undef, cid.Undef, err + } + + receipts = append(receipts, &r.MessageReceipt) + gasReward = big.Add(gasReward, r.GasCosts.MinerTip) + penalty = big.Add(penalty, r.GasCosts.MinerPenalty) + + if em != nil { + if err := em.MessageApplied(ctx, ts, cm.Cid(), m, r, false); err != nil { + return cid.Undef, cid.Undef, err + } + } + processedMsgs[m.Cid()] = struct{}{} + } + + params, err := actors.SerializeParams(&reward.AwardBlockRewardParams{ + Miner: b.Miner, + Penalty: penalty, + GasReward: gasReward, + WinCount: b.WinCount, + }) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to serialize award params: %w", err) + } + + rwMsg := &types.Message{ + From: builtin.SystemActorAddr, + To: reward.Address, + Nonce: uint64(epoch), + Value: types.NewInt(0), + GasFeeCap: types.NewInt(0), + GasPremium: types.NewInt(0), + GasLimit: 1 << 30, + Method: reward.Methods.AwardBlockReward, + Params: params, + } + ret, actErr := vmi.ApplyImplicitMessage(ctx, rwMsg) + if actErr != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to apply reward message for miner %s: %w", b.Miner, actErr) + } + if em != nil { + if err := em.MessageApplied(ctx, ts, rwMsg.Cid(), rwMsg, ret, true); err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("callback failed on reward message: %w", err) + } + } + + if ret.ExitCode != 0 { + return cid.Undef, cid.Undef, xerrors.Errorf("reward application message failed (exit %d): %s", ret.ExitCode, ret.ActorErr) + } + } + + partDone() + partDone = metrics.Timer(ctx, metrics.VMApplyCron) + + if err := runCron(epoch); err != nil { + return cid.Cid{}, cid.Cid{}, err + } + + partDone() + partDone = metrics.Timer(ctx, metrics.VMApplyFlush) + + rectarr := blockadt.MakeEmptyArray(sm.cs.ActorStore(ctx)) + for i, receipt := range receipts { + if err := rectarr.Set(uint64(i), receipt); err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err) + } + } + rectroot, err := rectarr.Root() + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err) + } + + st, err := vmi.Flush(ctx) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err) + } + + stats.Record(ctx, metrics.VMSends.M(int64(atomic.LoadUint64(&vm.StatSends))), + metrics.VMApplied.M(int64(atomic.LoadUint64(&vm.StatApplied)))) + + return st, rectroot, nil +} + +func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st cid.Cid, rec cid.Cid, err error) { + ctx, span := trace.StartSpan(ctx, "tipSetState") + defer span.End() + if span.IsRecordingEvents() { + span.AddAttributes(trace.StringAttribute("tipset", fmt.Sprint(ts.Cids()))) + } + + ck := cidsToKey(ts.Cids()) + sm.stlk.Lock() + cw, cwok := sm.compWait[ck] + if cwok { + sm.stlk.Unlock() + span.AddAttributes(trace.BoolAttribute("waited", true)) + select { + case <-cw: + sm.stlk.Lock() + case <-ctx.Done(): + return cid.Undef, cid.Undef, ctx.Err() + } + } + cached, ok := sm.stCache[ck] + if ok { + sm.stlk.Unlock() + span.AddAttributes(trace.BoolAttribute("cache", true)) + return cached[0], cached[1], nil + } + ch := make(chan struct{}) + sm.compWait[ck] = ch + + defer func() { + sm.stlk.Lock() + delete(sm.compWait, ck) + if st != cid.Undef { + sm.stCache[ck] = []cid.Cid{st, rec} + } + sm.stlk.Unlock() + close(ch) + }() + + sm.stlk.Unlock() + + if ts.Height() == 0 { + // NB: This is here because the process that executes blocks requires that the + // block miner reference a valid miner in the state tree. Unless we create some + // magical genesis miner, this won't work properly, so we short circuit here + // This avoids the question of 'who gets paid the genesis block reward' + return ts.Blocks()[0].ParentStateRoot, ts.Blocks()[0].ParentMessageReceipts, nil + } + + st, rec, err = sm.computeTipSetState(ctx, ts, sm.tsExecMonitor) + if err != nil { + return cid.Undef, cid.Undef, err + } + + return st, rec, nil +} + +func (sm *StateManager) ExecutionTraceWithMonitor(ctx context.Context, ts *types.TipSet, em ExecMonitor) (cid.Cid, error) { + st, _, err := sm.computeTipSetState(ctx, ts, em) + return st, err +} + +func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) { + var invocTrace []*api.InvocResult + st, err := sm.ExecutionTraceWithMonitor(ctx, ts, &InvocationTracer{trace: &invocTrace}) + if err != nil { + return cid.Undef, nil, err + } + return st, invocTrace, nil +} + +func (sm *StateManager) computeTipSetState(ctx context.Context, ts *types.TipSet, em ExecMonitor) (cid.Cid, cid.Cid, error) { + ctx, span := trace.StartSpan(ctx, "computeTipSetState") + defer span.End() + + blks := ts.Blocks() + + for i := 0; i < len(blks); i++ { + for j := i + 1; j < len(blks); j++ { + if blks[i].Miner == blks[j].Miner { + return cid.Undef, cid.Undef, + xerrors.Errorf("duplicate miner in a tipset (%s %s)", + blks[i].Miner, blks[j].Miner) + } + } + } + + var parentEpoch abi.ChainEpoch + pstate := blks[0].ParentStateRoot + if blks[0].Height > 0 { + parent, err := sm.cs.GetBlock(blks[0].Parents[0]) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("getting parent block: %w", err) + } + + parentEpoch = parent.Height + } + + r := store.NewChainRand(sm.cs, ts.Cids()) + + blkmsgs, err := sm.cs.BlockMsgsForTipset(ts) + if err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("getting block messages for tipset: %w", err) + } + + baseFee := blks[0].ParentBaseFee + + return sm.ApplyBlocks(ctx, parentEpoch, pstate, blkmsgs, blks[0].Height, r, em, baseFee, ts) +} diff --git a/chain/stmgr/read.go b/chain/stmgr/read.go index 3c7fb5d91..bc259f227 100644 --- a/chain/stmgr/read.go +++ b/chain/stmgr/read.go @@ -31,6 +31,14 @@ func (sm *StateManager) ParentState(ts *types.TipSet) (*state.StateTree, error) return state, nil } +func (sm *StateManager) parentState(ts *types.TipSet) cid.Cid { + if ts == nil { + ts = sm.cs.GetHeaviestTipSet() + } + + return ts.ParentState() +} + func (sm *StateManager) StateTree(st cid.Cid) (*state.StateTree, error) { cst := cbor.NewCborStore(sm.cs.StateBlockstore()) state, err := state.LoadStateTree(cst, st) diff --git a/chain/stmgr/searchwait.go b/chain/stmgr/searchwait.go new file mode 100644 index 000000000..518180824 --- /dev/null +++ b/chain/stmgr/searchwait.go @@ -0,0 +1,279 @@ +package stmgr + +import ( + "context" + "errors" + "fmt" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" +) + +// WaitForMessage blocks until a message appears on chain. It looks backwards in the chain to see if this has already +// happened, with an optional limit to how many epochs it will search. It guarantees that the message has been on +// chain for at least confidence epochs without being reverted before returning. +func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confidence uint64, lookbackLimit abi.ChainEpoch, allowReplaced bool) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + msg, err := sm.cs.GetCMessage(mcid) + if err != nil { + return nil, nil, cid.Undef, fmt.Errorf("failed to load message: %w", err) + } + + tsub := sm.cs.SubHeadChanges(ctx) + + head, ok := <-tsub + if !ok { + return nil, nil, cid.Undef, fmt.Errorf("SubHeadChanges stream was invalid") + } + + if len(head) != 1 { + return nil, nil, cid.Undef, fmt.Errorf("SubHeadChanges first entry should have been one item") + } + + if head[0].Type != store.HCCurrent { + return nil, nil, cid.Undef, fmt.Errorf("expected current head on SHC stream (got %s)", head[0].Type) + } + + r, foundMsg, err := sm.tipsetExecutedMessage(head[0].Val, mcid, msg.VMMessage(), allowReplaced) + if err != nil { + return nil, nil, cid.Undef, err + } + + if r != nil { + return head[0].Val, r, foundMsg, nil + } + + var backTs *types.TipSet + var backRcp *types.MessageReceipt + var backFm cid.Cid + backSearchWait := make(chan struct{}) + go func() { + fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head[0].Val, msg, lookbackLimit, allowReplaced) + if err != nil { + log.Warnf("failed to look back through chain for message: %v", err) + return + } + + backTs = fts + backRcp = r + backFm = foundMsg + close(backSearchWait) + }() + + var candidateTs *types.TipSet + var candidateRcp *types.MessageReceipt + var candidateFm cid.Cid + heightOfHead := head[0].Val.Height() + reverts := map[types.TipSetKey]bool{} + + for { + select { + case notif, ok := <-tsub: + if !ok { + return nil, nil, cid.Undef, ctx.Err() + } + for _, val := range notif { + switch val.Type { + case store.HCRevert: + if val.Val.Equals(candidateTs) { + candidateTs = nil + candidateRcp = nil + candidateFm = cid.Undef + } + if backSearchWait != nil { + reverts[val.Val.Key()] = true + } + case store.HCApply: + if candidateTs != nil && val.Val.Height() >= candidateTs.Height()+abi.ChainEpoch(confidence) { + return candidateTs, candidateRcp, candidateFm, nil + } + r, foundMsg, err := sm.tipsetExecutedMessage(val.Val, mcid, msg.VMMessage(), allowReplaced) + if err != nil { + return nil, nil, cid.Undef, err + } + if r != nil { + if confidence == 0 { + return val.Val, r, foundMsg, err + } + candidateTs = val.Val + candidateRcp = r + candidateFm = foundMsg + } + heightOfHead = val.Val.Height() + } + } + case <-backSearchWait: + // check if we found the message in the chain and that is hasn't been reverted since we started searching + if backTs != nil && !reverts[backTs.Key()] { + // if head is at or past confidence interval, return immediately + if heightOfHead >= backTs.Height()+abi.ChainEpoch(confidence) { + return backTs, backRcp, backFm, nil + } + + // wait for confidence interval + candidateTs = backTs + candidateRcp = backRcp + candidateFm = backFm + } + reverts = nil + backSearchWait = nil + case <-ctx.Done(): + return nil, nil, cid.Undef, ctx.Err() + } + } +} + +func (sm *StateManager) SearchForMessage(ctx context.Context, head *types.TipSet, mcid cid.Cid, lookbackLimit abi.ChainEpoch, allowReplaced bool) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { + msg, err := sm.cs.GetCMessage(mcid) + if err != nil { + return nil, nil, cid.Undef, fmt.Errorf("failed to load message: %w", err) + } + + r, foundMsg, err := sm.tipsetExecutedMessage(head, mcid, msg.VMMessage(), allowReplaced) + if err != nil { + return nil, nil, cid.Undef, err + } + + if r != nil { + return head, r, foundMsg, nil + } + + fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head, msg, lookbackLimit, allowReplaced) + + if err != nil { + log.Warnf("failed to look back through chain for message %s", mcid) + return nil, nil, cid.Undef, err + } + + if fts == nil { + return nil, nil, cid.Undef, nil + } + + return fts, r, foundMsg, nil +} + +// searchBackForMsg searches up to limit tipsets backwards from the given +// tipset for a message receipt. +// If limit is +// - 0 then no tipsets are searched +// - 5 then five tipset are searched +// - LookbackNoLimit then there is no limit +func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m types.ChainMsg, limit abi.ChainEpoch, allowReplaced bool) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { + limitHeight := from.Height() - limit + noLimit := limit == LookbackNoLimit + + cur := from + curActor, err := sm.LoadActor(ctx, m.VMMessage().From, cur) + if err != nil { + return nil, nil, cid.Undef, xerrors.Errorf("failed to load initital tipset") + } + + mFromId, err := sm.LookupID(ctx, m.VMMessage().From, from) + if err != nil { + return nil, nil, cid.Undef, xerrors.Errorf("looking up From id address: %w", err) + } + + mNonce := m.VMMessage().Nonce + + for { + // If we've reached the genesis block, or we've reached the limit of + // how far back to look + if cur.Height() == 0 || !noLimit && cur.Height() <= limitHeight { + // it ain't here! + return nil, nil, cid.Undef, nil + } + + select { + case <-ctx.Done(): + return nil, nil, cid.Undef, nil + default: + } + + // we either have no messages from the sender, or the latest message we found has a lower nonce than the one being searched for, + // either way, no reason to lookback, it ain't there + if curActor == nil || curActor.Nonce == 0 || curActor.Nonce < mNonce { + return nil, nil, cid.Undef, nil + } + + pts, err := sm.cs.LoadTipSet(cur.Parents()) + if err != nil { + return nil, nil, cid.Undef, xerrors.Errorf("failed to load tipset during msg wait searchback: %w", err) + } + + act, err := sm.LoadActor(ctx, mFromId, pts) + actorNoExist := errors.Is(err, types.ErrActorNotFound) + if err != nil && !actorNoExist { + return nil, nil, cid.Cid{}, xerrors.Errorf("failed to load the actor: %w", err) + } + + // check that between cur and parent tipset the nonce fell into range of our message + if actorNoExist || (curActor.Nonce > mNonce && act.Nonce <= mNonce) { + r, foundMsg, err := sm.tipsetExecutedMessage(cur, m.Cid(), m.VMMessage(), allowReplaced) + if err != nil { + return nil, nil, cid.Undef, xerrors.Errorf("checking for message execution during lookback: %w", err) + } + + if r != nil { + return cur, r, foundMsg, nil + } + } + + cur = pts + curActor = act + } +} + +func (sm *StateManager) tipsetExecutedMessage(ts *types.TipSet, msg cid.Cid, vmm *types.Message, allowReplaced bool) (*types.MessageReceipt, cid.Cid, error) { + // The genesis block did not execute any messages + if ts.Height() == 0 { + return nil, cid.Undef, nil + } + + pts, err := sm.cs.LoadTipSet(ts.Parents()) + if err != nil { + return nil, cid.Undef, err + } + + cm, err := sm.cs.MessagesForTipset(pts) + if err != nil { + return nil, cid.Undef, err + } + + for ii := range cm { + // iterate in reverse because we going backwards through the chain + i := len(cm) - ii - 1 + m := cm[i] + + if m.VMMessage().From == vmm.From { // cheaper to just check origin first + if m.VMMessage().Nonce == vmm.Nonce { + if allowReplaced && m.VMMessage().EqualCall(vmm) { + if m.Cid() != msg { + log.Warnw("found message with equal nonce and call params but different CID", + "wanted", msg, "found", m.Cid(), "nonce", vmm.Nonce, "from", vmm.From) + } + + pr, err := sm.cs.GetParentReceipt(ts.Blocks()[0], i) + if err != nil { + return nil, cid.Undef, err + } + return pr, m.Cid(), nil + } + + // this should be that message + return nil, cid.Undef, xerrors.Errorf("found message with equal nonce as the one we are looking for (F:%s n %d, TS: %s n%d)", + msg, vmm.Nonce, m.Cid(), m.VMMessage().Nonce) + } + if m.VMMessage().Nonce < vmm.Nonce { + return nil, cid.Undef, nil // don't bother looking further + } + } + } + + return nil, cid.Undef, nil +} diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 4f1351d2c..8f03b6bf9 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -2,52 +2,30 @@ package stmgr import ( "context" - "errors" "fmt" "sync" - "sync/atomic" - - "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/ipfs/go-cid" cbor "github.com/ipfs/go-ipld-cbor" logging "github.com/ipfs/go-log/v2" - cbg "github.com/whyrusleeping/cbor-gen" - "go.opencensus.io/stats" - "go.opencensus.io/trace" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/network" // Used for genesis. msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" "github.com/filecoin-project/specs-actors/v3/actors/migration/nv10" - // we use the same adt for all receipts - blockadt "github.com/filecoin-project/specs-actors/actors/util/adt" - "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/actors" - "github.com/filecoin-project/lotus/chain/actors/adt" - "github.com/filecoin-project/lotus/chain/actors/builtin" - "github.com/filecoin-project/lotus/chain/actors/builtin/cron" - _init "github.com/filecoin-project/lotus/chain/actors/builtin/init" - "github.com/filecoin-project/lotus/chain/actors/builtin/market" - "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - "github.com/filecoin-project/lotus/chain/actors/builtin/multisig" "github.com/filecoin-project/lotus/chain/actors/builtin/paych" - "github.com/filecoin-project/lotus/chain/actors/builtin/power" - "github.com/filecoin-project/lotus/chain/actors/builtin/reward" - "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" + "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" - "github.com/filecoin-project/lotus/metrics" ) const LookbackNoLimit = api.LookbackNoLimit @@ -217,312 +195,6 @@ func (sm *StateManager) Stop(ctx context.Context) error { return nil } -func (sm *StateManager) TipSetState(ctx context.Context, ts *types.TipSet) (st cid.Cid, rec cid.Cid, err error) { - ctx, span := trace.StartSpan(ctx, "tipSetState") - defer span.End() - if span.IsRecordingEvents() { - span.AddAttributes(trace.StringAttribute("tipset", fmt.Sprint(ts.Cids()))) - } - - ck := cidsToKey(ts.Cids()) - sm.stlk.Lock() - cw, cwok := sm.compWait[ck] - if cwok { - sm.stlk.Unlock() - span.AddAttributes(trace.BoolAttribute("waited", true)) - select { - case <-cw: - sm.stlk.Lock() - case <-ctx.Done(): - return cid.Undef, cid.Undef, ctx.Err() - } - } - cached, ok := sm.stCache[ck] - if ok { - sm.stlk.Unlock() - span.AddAttributes(trace.BoolAttribute("cache", true)) - return cached[0], cached[1], nil - } - ch := make(chan struct{}) - sm.compWait[ck] = ch - - defer func() { - sm.stlk.Lock() - delete(sm.compWait, ck) - if st != cid.Undef { - sm.stCache[ck] = []cid.Cid{st, rec} - } - sm.stlk.Unlock() - close(ch) - }() - - sm.stlk.Unlock() - - if ts.Height() == 0 { - // NB: This is here because the process that executes blocks requires that the - // block miner reference a valid miner in the state tree. Unless we create some - // magical genesis miner, this won't work properly, so we short circuit here - // This avoids the question of 'who gets paid the genesis block reward' - return ts.Blocks()[0].ParentStateRoot, ts.Blocks()[0].ParentMessageReceipts, nil - } - - st, rec, err = sm.computeTipSetState(ctx, ts, sm.tsExecMonitor) - if err != nil { - return cid.Undef, cid.Undef, err - } - - return st, rec, nil -} - -func (sm *StateManager) ExecutionTraceWithMonitor(ctx context.Context, ts *types.TipSet, em ExecMonitor) (cid.Cid, error) { - st, _, err := sm.computeTipSetState(ctx, ts, em) - return st, err -} - -func (sm *StateManager) ExecutionTrace(ctx context.Context, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) { - var invocTrace []*api.InvocResult - st, err := sm.ExecutionTraceWithMonitor(ctx, ts, &InvocationTracer{trace: &invocTrace}) - if err != nil { - return cid.Undef, nil, err - } - return st, invocTrace, nil -} - -func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEpoch, pstate cid.Cid, bms []store.BlockMessages, epoch abi.ChainEpoch, r vm.Rand, em ExecMonitor, baseFee abi.TokenAmount, ts *types.TipSet) (cid.Cid, cid.Cid, error) { - done := metrics.Timer(ctx, metrics.VMApplyBlocksTotal) - defer done() - - partDone := metrics.Timer(ctx, metrics.VMApplyEarly) - defer func() { - partDone() - }() - - makeVmWithBaseState := func(base cid.Cid) (*vm.VM, error) { - vmopt := &vm.VMOpts{ - StateBase: base, - Epoch: epoch, - Rand: r, - Bstore: sm.cs.StateBlockstore(), - Syscalls: sm.cs.VMSys(), - CircSupplyCalc: sm.GetVMCirculatingSupply, - NtwkVersion: sm.GetNtwkVersion, - BaseFee: baseFee, - LookbackState: LookbackStateGetterForTipset(sm, ts), - } - - return sm.newVM(ctx, vmopt) - } - - vmi, err := makeVmWithBaseState(pstate) - if err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) - } - - runCron := func(epoch abi.ChainEpoch) error { - cronMsg := &types.Message{ - To: cron.Address, - From: builtin.SystemActorAddr, - Nonce: uint64(epoch), - Value: types.NewInt(0), - GasFeeCap: types.NewInt(0), - GasPremium: types.NewInt(0), - GasLimit: build.BlockGasLimit * 10000, // Make super sure this is never too little - Method: cron.Methods.EpochTick, - Params: nil, - } - ret, err := vmi.ApplyImplicitMessage(ctx, cronMsg) - if err != nil { - return err - } - if em != nil { - if err := em.MessageApplied(ctx, ts, cronMsg.Cid(), cronMsg, ret, true); err != nil { - return xerrors.Errorf("callback failed on cron message: %w", err) - } - } - if ret.ExitCode != 0 { - return xerrors.Errorf("CheckProofSubmissions exit was non-zero: %d", ret.ExitCode) - } - - return nil - } - - for i := parentEpoch; i < epoch; i++ { - if i > parentEpoch { - // run cron for null rounds if any - if err := runCron(i); err != nil { - return cid.Undef, cid.Undef, err - } - - pstate, err = vmi.Flush(ctx) - if err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("flushing vm: %w", err) - } - } - - // handle state forks - // XXX: The state tree - newState, err := sm.handleStateForks(ctx, pstate, i, em, ts) - if err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("error handling state forks: %w", err) - } - - if pstate != newState { - vmi, err = makeVmWithBaseState(newState) - if err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) - } - } - - vmi.SetBlockHeight(i + 1) - pstate = newState - } - - partDone() - partDone = metrics.Timer(ctx, metrics.VMApplyMessages) - - var receipts []cbg.CBORMarshaler - processedMsgs := make(map[cid.Cid]struct{}) - for _, b := range bms { - penalty := types.NewInt(0) - gasReward := big.Zero() - - for _, cm := range append(b.BlsMessages, b.SecpkMessages...) { - m := cm.VMMessage() - if _, found := processedMsgs[m.Cid()]; found { - continue - } - r, err := vmi.ApplyMessage(ctx, cm) - if err != nil { - return cid.Undef, cid.Undef, err - } - - receipts = append(receipts, &r.MessageReceipt) - gasReward = big.Add(gasReward, r.GasCosts.MinerTip) - penalty = big.Add(penalty, r.GasCosts.MinerPenalty) - - if em != nil { - if err := em.MessageApplied(ctx, ts, cm.Cid(), m, r, false); err != nil { - return cid.Undef, cid.Undef, err - } - } - processedMsgs[m.Cid()] = struct{}{} - } - - params, err := actors.SerializeParams(&reward.AwardBlockRewardParams{ - Miner: b.Miner, - Penalty: penalty, - GasReward: gasReward, - WinCount: b.WinCount, - }) - if err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("failed to serialize award params: %w", err) - } - - rwMsg := &types.Message{ - From: builtin.SystemActorAddr, - To: reward.Address, - Nonce: uint64(epoch), - Value: types.NewInt(0), - GasFeeCap: types.NewInt(0), - GasPremium: types.NewInt(0), - GasLimit: 1 << 30, - Method: reward.Methods.AwardBlockReward, - Params: params, - } - ret, actErr := vmi.ApplyImplicitMessage(ctx, rwMsg) - if actErr != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("failed to apply reward message for miner %s: %w", b.Miner, actErr) - } - if em != nil { - if err := em.MessageApplied(ctx, ts, rwMsg.Cid(), rwMsg, ret, true); err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("callback failed on reward message: %w", err) - } - } - - if ret.ExitCode != 0 { - return cid.Undef, cid.Undef, xerrors.Errorf("reward application message failed (exit %d): %s", ret.ExitCode, ret.ActorErr) - } - } - - partDone() - partDone = metrics.Timer(ctx, metrics.VMApplyCron) - - if err := runCron(epoch); err != nil { - return cid.Cid{}, cid.Cid{}, err - } - - partDone() - partDone = metrics.Timer(ctx, metrics.VMApplyFlush) - - rectarr := blockadt.MakeEmptyArray(sm.cs.ActorStore(ctx)) - for i, receipt := range receipts { - if err := rectarr.Set(uint64(i), receipt); err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err) - } - } - rectroot, err := rectarr.Root() - if err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err) - } - - st, err := vmi.Flush(ctx) - if err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err) - } - - stats.Record(ctx, metrics.VMSends.M(int64(atomic.LoadUint64(&vm.StatSends))), - metrics.VMApplied.M(int64(atomic.LoadUint64(&vm.StatApplied)))) - - return st, rectroot, nil -} - -func (sm *StateManager) computeTipSetState(ctx context.Context, ts *types.TipSet, em ExecMonitor) (cid.Cid, cid.Cid, error) { - ctx, span := trace.StartSpan(ctx, "computeTipSetState") - defer span.End() - - blks := ts.Blocks() - - for i := 0; i < len(blks); i++ { - for j := i + 1; j < len(blks); j++ { - if blks[i].Miner == blks[j].Miner { - return cid.Undef, cid.Undef, - xerrors.Errorf("duplicate miner in a tipset (%s %s)", - blks[i].Miner, blks[j].Miner) - } - } - } - - var parentEpoch abi.ChainEpoch - pstate := blks[0].ParentStateRoot - if blks[0].Height > 0 { - parent, err := sm.cs.GetBlock(blks[0].Parents[0]) - if err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("getting parent block: %w", err) - } - - parentEpoch = parent.Height - } - - r := store.NewChainRand(sm.cs, ts.Cids()) - - blkmsgs, err := sm.cs.BlockMsgsForTipset(ts) - if err != nil { - return cid.Undef, cid.Undef, xerrors.Errorf("getting block messages for tipset: %w", err) - } - - baseFee := blks[0].ParentBaseFee - - return sm.ApplyBlocks(ctx, parentEpoch, pstate, blkmsgs, blks[0].Height, r, em, baseFee, ts) -} - -func (sm *StateManager) parentState(ts *types.TipSet) cid.Cid { - if ts == nil { - ts = sm.cs.GetHeaviestTipSet() - } - - return ts.ParentState() -} - func (sm *StateManager) ChainStore() *store.ChainStore { return sm.cs } @@ -637,338 +309,6 @@ func (sm *StateManager) LookupID(ctx context.Context, addr address.Address, ts * return state.LookupID(addr) } -// WaitForMessage blocks until a message appears on chain. It looks backwards in the chain to see if this has already -// happened, with an optional limit to how many epochs it will search. It guarantees that the message has been on -// chain for at least confidence epochs without being reverted before returning. -func (sm *StateManager) WaitForMessage(ctx context.Context, mcid cid.Cid, confidence uint64, lookbackLimit abi.ChainEpoch, allowReplaced bool) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - msg, err := sm.cs.GetCMessage(mcid) - if err != nil { - return nil, nil, cid.Undef, fmt.Errorf("failed to load message: %w", err) - } - - tsub := sm.cs.SubHeadChanges(ctx) - - head, ok := <-tsub - if !ok { - return nil, nil, cid.Undef, fmt.Errorf("SubHeadChanges stream was invalid") - } - - if len(head) != 1 { - return nil, nil, cid.Undef, fmt.Errorf("SubHeadChanges first entry should have been one item") - } - - if head[0].Type != store.HCCurrent { - return nil, nil, cid.Undef, fmt.Errorf("expected current head on SHC stream (got %s)", head[0].Type) - } - - r, foundMsg, err := sm.tipsetExecutedMessage(head[0].Val, mcid, msg.VMMessage(), allowReplaced) - if err != nil { - return nil, nil, cid.Undef, err - } - - if r != nil { - return head[0].Val, r, foundMsg, nil - } - - var backTs *types.TipSet - var backRcp *types.MessageReceipt - var backFm cid.Cid - backSearchWait := make(chan struct{}) - go func() { - fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head[0].Val, msg, lookbackLimit, allowReplaced) - if err != nil { - log.Warnf("failed to look back through chain for message: %v", err) - return - } - - backTs = fts - backRcp = r - backFm = foundMsg - close(backSearchWait) - }() - - var candidateTs *types.TipSet - var candidateRcp *types.MessageReceipt - var candidateFm cid.Cid - heightOfHead := head[0].Val.Height() - reverts := map[types.TipSetKey]bool{} - - for { - select { - case notif, ok := <-tsub: - if !ok { - return nil, nil, cid.Undef, ctx.Err() - } - for _, val := range notif { - switch val.Type { - case store.HCRevert: - if val.Val.Equals(candidateTs) { - candidateTs = nil - candidateRcp = nil - candidateFm = cid.Undef - } - if backSearchWait != nil { - reverts[val.Val.Key()] = true - } - case store.HCApply: - if candidateTs != nil && val.Val.Height() >= candidateTs.Height()+abi.ChainEpoch(confidence) { - return candidateTs, candidateRcp, candidateFm, nil - } - r, foundMsg, err := sm.tipsetExecutedMessage(val.Val, mcid, msg.VMMessage(), allowReplaced) - if err != nil { - return nil, nil, cid.Undef, err - } - if r != nil { - if confidence == 0 { - return val.Val, r, foundMsg, err - } - candidateTs = val.Val - candidateRcp = r - candidateFm = foundMsg - } - heightOfHead = val.Val.Height() - } - } - case <-backSearchWait: - // check if we found the message in the chain and that is hasn't been reverted since we started searching - if backTs != nil && !reverts[backTs.Key()] { - // if head is at or past confidence interval, return immediately - if heightOfHead >= backTs.Height()+abi.ChainEpoch(confidence) { - return backTs, backRcp, backFm, nil - } - - // wait for confidence interval - candidateTs = backTs - candidateRcp = backRcp - candidateFm = backFm - } - reverts = nil - backSearchWait = nil - case <-ctx.Done(): - return nil, nil, cid.Undef, ctx.Err() - } - } -} - -func (sm *StateManager) SearchForMessage(ctx context.Context, head *types.TipSet, mcid cid.Cid, lookbackLimit abi.ChainEpoch, allowReplaced bool) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { - msg, err := sm.cs.GetCMessage(mcid) - if err != nil { - return nil, nil, cid.Undef, fmt.Errorf("failed to load message: %w", err) - } - - r, foundMsg, err := sm.tipsetExecutedMessage(head, mcid, msg.VMMessage(), allowReplaced) - if err != nil { - return nil, nil, cid.Undef, err - } - - if r != nil { - return head, r, foundMsg, nil - } - - fts, r, foundMsg, err := sm.searchBackForMsg(ctx, head, msg, lookbackLimit, allowReplaced) - - if err != nil { - log.Warnf("failed to look back through chain for message %s", mcid) - return nil, nil, cid.Undef, err - } - - if fts == nil { - return nil, nil, cid.Undef, nil - } - - return fts, r, foundMsg, nil -} - -// searchBackForMsg searches up to limit tipsets backwards from the given -// tipset for a message receipt. -// If limit is -// - 0 then no tipsets are searched -// - 5 then five tipset are searched -// - LookbackNoLimit then there is no limit -func (sm *StateManager) searchBackForMsg(ctx context.Context, from *types.TipSet, m types.ChainMsg, limit abi.ChainEpoch, allowReplaced bool) (*types.TipSet, *types.MessageReceipt, cid.Cid, error) { - limitHeight := from.Height() - limit - noLimit := limit == LookbackNoLimit - - cur := from - curActor, err := sm.LoadActor(ctx, m.VMMessage().From, cur) - if err != nil { - return nil, nil, cid.Undef, xerrors.Errorf("failed to load initital tipset") - } - - mFromId, err := sm.LookupID(ctx, m.VMMessage().From, from) - if err != nil { - return nil, nil, cid.Undef, xerrors.Errorf("looking up From id address: %w", err) - } - - mNonce := m.VMMessage().Nonce - - for { - // If we've reached the genesis block, or we've reached the limit of - // how far back to look - if cur.Height() == 0 || !noLimit && cur.Height() <= limitHeight { - // it ain't here! - return nil, nil, cid.Undef, nil - } - - select { - case <-ctx.Done(): - return nil, nil, cid.Undef, nil - default: - } - - // we either have no messages from the sender, or the latest message we found has a lower nonce than the one being searched for, - // either way, no reason to lookback, it ain't there - if curActor == nil || curActor.Nonce == 0 || curActor.Nonce < mNonce { - return nil, nil, cid.Undef, nil - } - - pts, err := sm.cs.LoadTipSet(cur.Parents()) - if err != nil { - return nil, nil, cid.Undef, xerrors.Errorf("failed to load tipset during msg wait searchback: %w", err) - } - - act, err := sm.LoadActor(ctx, mFromId, pts) - actorNoExist := errors.Is(err, types.ErrActorNotFound) - if err != nil && !actorNoExist { - return nil, nil, cid.Cid{}, xerrors.Errorf("failed to load the actor: %w", err) - } - - // check that between cur and parent tipset the nonce fell into range of our message - if actorNoExist || (curActor.Nonce > mNonce && act.Nonce <= mNonce) { - r, foundMsg, err := sm.tipsetExecutedMessage(cur, m.Cid(), m.VMMessage(), allowReplaced) - if err != nil { - return nil, nil, cid.Undef, xerrors.Errorf("checking for message execution during lookback: %w", err) - } - - if r != nil { - return cur, r, foundMsg, nil - } - } - - cur = pts - curActor = act - } -} - -func (sm *StateManager) tipsetExecutedMessage(ts *types.TipSet, msg cid.Cid, vmm *types.Message, allowReplaced bool) (*types.MessageReceipt, cid.Cid, error) { - // The genesis block did not execute any messages - if ts.Height() == 0 { - return nil, cid.Undef, nil - } - - pts, err := sm.cs.LoadTipSet(ts.Parents()) - if err != nil { - return nil, cid.Undef, err - } - - cm, err := sm.cs.MessagesForTipset(pts) - if err != nil { - return nil, cid.Undef, err - } - - for ii := range cm { - // iterate in reverse because we going backwards through the chain - i := len(cm) - ii - 1 - m := cm[i] - - if m.VMMessage().From == vmm.From { // cheaper to just check origin first - if m.VMMessage().Nonce == vmm.Nonce { - if allowReplaced && m.VMMessage().EqualCall(vmm) { - if m.Cid() != msg { - log.Warnw("found message with equal nonce and call params but different CID", - "wanted", msg, "found", m.Cid(), "nonce", vmm.Nonce, "from", vmm.From) - } - - pr, err := sm.cs.GetParentReceipt(ts.Blocks()[0], i) - if err != nil { - return nil, cid.Undef, err - } - return pr, m.Cid(), nil - } - - // this should be that message - return nil, cid.Undef, xerrors.Errorf("found message with equal nonce as the one we are looking for (F:%s n %d, TS: %s n%d)", - msg, vmm.Nonce, m.Cid(), m.VMMessage().Nonce) - } - if m.VMMessage().Nonce < vmm.Nonce { - return nil, cid.Undef, nil // don't bother looking further - } - } - } - - return nil, cid.Undef, nil -} - -func (sm *StateManager) ListAllActors(ctx context.Context, ts *types.TipSet) ([]address.Address, error) { - if ts == nil { - ts = sm.cs.GetHeaviestTipSet() - } - st := ts.ParentState() - - stateTree, err := sm.StateTree(st) - if err != nil { - return nil, err - } - - var out []address.Address - err = stateTree.ForEach(func(addr address.Address, act *types.Actor) error { - out = append(out, addr) - return nil - }) - if err != nil { - return nil, err - } - - return out, nil -} - -func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (api.MarketBalance, error) { - st, err := sm.ParentState(ts) - if err != nil { - return api.MarketBalance{}, err - } - - act, err := st.GetActor(market.Address) - if err != nil { - return api.MarketBalance{}, err - } - - mstate, err := market.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return api.MarketBalance{}, err - } - - addr, err = sm.LookupID(ctx, addr, ts) - if err != nil { - return api.MarketBalance{}, err - } - - var out api.MarketBalance - - et, err := mstate.EscrowTable() - if err != nil { - return api.MarketBalance{}, err - } - out.Escrow, err = et.Get(addr) - if err != nil { - return api.MarketBalance{}, xerrors.Errorf("getting escrow balance: %w", err) - } - - lt, err := mstate.LockedTable() - if err != nil { - return api.MarketBalance{}, err - } - out.Locked, err = lt.Get(addr) - if err != nil { - return api.MarketBalance{}, xerrors.Errorf("getting locked balance: %w", err) - } - - return out, nil -} - func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) error { tschain := []*types.TipSet{ts} for ts.Height() != 0 { @@ -1002,450 +342,6 @@ func (sm *StateManager) SetVMConstructor(nvm func(context.Context, *vm.VMOpts) ( sm.newVM = nvm } -// sets up information about the vesting schedule -func (sm *StateManager) setupGenesisVestingSchedule(ctx context.Context) error { - - gb, err := sm.cs.GetGenesis() - if err != nil { - return xerrors.Errorf("getting genesis block: %w", err) - } - - gts, err := types.NewTipSet([]*types.BlockHeader{gb}) - if err != nil { - return xerrors.Errorf("getting genesis tipset: %w", err) - } - - st, _, err := sm.TipSetState(ctx, gts) - if err != nil { - return xerrors.Errorf("getting genesis tipset state: %w", err) - } - - cst := cbor.NewCborStore(sm.cs.StateBlockstore()) - sTree, err := state.LoadStateTree(cst, st) - if err != nil { - return xerrors.Errorf("loading state tree: %w", err) - } - - gmf, err := getFilMarketLocked(ctx, sTree) - if err != nil { - return xerrors.Errorf("setting up genesis market funds: %w", err) - } - - gp, err := getFilPowerLocked(ctx, sTree) - if err != nil { - return xerrors.Errorf("setting up genesis pledge: %w", err) - } - - sm.genesisMarketFunds = gmf - sm.genesisPledge = gp - - totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount) - - // 6 months - sixMonths := abi.ChainEpoch(183 * builtin.EpochsInDay) - totalsByEpoch[sixMonths] = big.NewInt(49_929_341) - totalsByEpoch[sixMonths] = big.Add(totalsByEpoch[sixMonths], big.NewInt(32_787_700)) - - // 1 year - oneYear := abi.ChainEpoch(365 * builtin.EpochsInDay) - totalsByEpoch[oneYear] = big.NewInt(22_421_712) - - // 2 years - twoYears := abi.ChainEpoch(2 * 365 * builtin.EpochsInDay) - totalsByEpoch[twoYears] = big.NewInt(7_223_364) - - // 3 years - threeYears := abi.ChainEpoch(3 * 365 * builtin.EpochsInDay) - totalsByEpoch[threeYears] = big.NewInt(87_637_883) - - // 6 years - sixYears := abi.ChainEpoch(6 * 365 * builtin.EpochsInDay) - totalsByEpoch[sixYears] = big.NewInt(100_000_000) - totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(300_000_000)) - - sm.preIgnitionVesting = make([]msig0.State, 0, len(totalsByEpoch)) - for k, v := range totalsByEpoch { - ns := msig0.State{ - InitialBalance: v, - UnlockDuration: k, - PendingTxns: cid.Undef, - } - sm.preIgnitionVesting = append(sm.preIgnitionVesting, ns) - } - - return nil -} - -// sets up information about the vesting schedule post the ignition upgrade -func (sm *StateManager) setupPostIgnitionVesting(ctx context.Context) error { - - totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount) - - // 6 months - sixMonths := abi.ChainEpoch(183 * builtin.EpochsInDay) - totalsByEpoch[sixMonths] = big.NewInt(49_929_341) - totalsByEpoch[sixMonths] = big.Add(totalsByEpoch[sixMonths], big.NewInt(32_787_700)) - - // 1 year - oneYear := abi.ChainEpoch(365 * builtin.EpochsInDay) - totalsByEpoch[oneYear] = big.NewInt(22_421_712) - - // 2 years - twoYears := abi.ChainEpoch(2 * 365 * builtin.EpochsInDay) - totalsByEpoch[twoYears] = big.NewInt(7_223_364) - - // 3 years - threeYears := abi.ChainEpoch(3 * 365 * builtin.EpochsInDay) - totalsByEpoch[threeYears] = big.NewInt(87_637_883) - - // 6 years - sixYears := abi.ChainEpoch(6 * 365 * builtin.EpochsInDay) - totalsByEpoch[sixYears] = big.NewInt(100_000_000) - totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(300_000_000)) - - sm.postIgnitionVesting = make([]msig0.State, 0, len(totalsByEpoch)) - for k, v := range totalsByEpoch { - ns := msig0.State{ - // In the pre-ignition logic, we incorrectly set this value in Fil, not attoFil, an off-by-10^18 error - InitialBalance: big.Mul(v, big.NewInt(int64(build.FilecoinPrecision))), - UnlockDuration: k, - PendingTxns: cid.Undef, - // In the pre-ignition logic, the start epoch was 0. This changes in the fork logic of the Ignition upgrade itself. - StartEpoch: build.UpgradeLiftoffHeight, - } - sm.postIgnitionVesting = append(sm.postIgnitionVesting, ns) - } - - return nil -} - -// sets up information about the vesting schedule post the calico upgrade -func (sm *StateManager) setupPostCalicoVesting(ctx context.Context) error { - - totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount) - - // 0 days - zeroDays := abi.ChainEpoch(0) - totalsByEpoch[zeroDays] = big.NewInt(10_632_000) - - // 6 months - sixMonths := abi.ChainEpoch(183 * builtin.EpochsInDay) - totalsByEpoch[sixMonths] = big.NewInt(19_015_887) - totalsByEpoch[sixMonths] = big.Add(totalsByEpoch[sixMonths], big.NewInt(32_787_700)) - - // 1 year - oneYear := abi.ChainEpoch(365 * builtin.EpochsInDay) - totalsByEpoch[oneYear] = big.NewInt(22_421_712) - totalsByEpoch[oneYear] = big.Add(totalsByEpoch[oneYear], big.NewInt(9_400_000)) - - // 2 years - twoYears := abi.ChainEpoch(2 * 365 * builtin.EpochsInDay) - totalsByEpoch[twoYears] = big.NewInt(7_223_364) - - // 3 years - threeYears := abi.ChainEpoch(3 * 365 * builtin.EpochsInDay) - totalsByEpoch[threeYears] = big.NewInt(87_637_883) - totalsByEpoch[threeYears] = big.Add(totalsByEpoch[threeYears], big.NewInt(898_958)) - - // 6 years - sixYears := abi.ChainEpoch(6 * 365 * builtin.EpochsInDay) - totalsByEpoch[sixYears] = big.NewInt(100_000_000) - totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(300_000_000)) - totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(9_805_053)) - - sm.postCalicoVesting = make([]msig0.State, 0, len(totalsByEpoch)) - for k, v := range totalsByEpoch { - ns := msig0.State{ - InitialBalance: big.Mul(v, big.NewInt(int64(build.FilecoinPrecision))), - UnlockDuration: k, - PendingTxns: cid.Undef, - StartEpoch: build.UpgradeLiftoffHeight, - } - sm.postCalicoVesting = append(sm.postCalicoVesting, ns) - } - - return nil -} - -// GetVestedFunds returns all funds that have "left" actors that are in the genesis state: -// - For Multisigs, it counts the actual amounts that have vested at the given epoch -// - For Accounts, it counts max(currentBalance - genesisBalance, 0). -func (sm *StateManager) GetFilVested(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) { - vf := big.Zero() - if height <= build.UpgradeIgnitionHeight { - for _, v := range sm.preIgnitionVesting { - au := big.Sub(v.InitialBalance, v.AmountLocked(height)) - vf = big.Add(vf, au) - } - } else if height <= build.UpgradeCalicoHeight { - for _, v := range sm.postIgnitionVesting { - // In the pre-ignition logic, we simply called AmountLocked(height), assuming startEpoch was 0. - // The start epoch changed in the Ignition upgrade. - au := big.Sub(v.InitialBalance, v.AmountLocked(height-v.StartEpoch)) - vf = big.Add(vf, au) - } - } else { - for _, v := range sm.postCalicoVesting { - // In the pre-ignition logic, we simply called AmountLocked(height), assuming startEpoch was 0. - // The start epoch changed in the Ignition upgrade. - au := big.Sub(v.InitialBalance, v.AmountLocked(height-v.StartEpoch)) - vf = big.Add(vf, au) - } - } - - // After UpgradeAssemblyHeight these funds are accounted for in GetFilReserveDisbursed - if height <= build.UpgradeAssemblyHeight { - // continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch - vf = big.Add(vf, sm.genesisPledge) - // continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch - vf = big.Add(vf, sm.genesisMarketFunds) - } - - return vf, nil -} - -func GetFilReserveDisbursed(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { - ract, err := st.GetActor(builtin.ReserveAddress) - if err != nil { - return big.Zero(), xerrors.Errorf("failed to get reserve actor: %w", err) - } - - // If money enters the reserve actor, this could lead to a negative term - return big.Sub(big.NewFromGo(build.InitialFilReserved), ract.Balance), nil -} - -func GetFilMined(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { - ractor, err := st.GetActor(reward.Address) - if err != nil { - return big.Zero(), xerrors.Errorf("failed to load reward actor state: %w", err) - } - - rst, err := reward.Load(adt.WrapStore(ctx, st.Store), ractor) - if err != nil { - return big.Zero(), err - } - - return rst.TotalStoragePowerReward() -} - -func getFilMarketLocked(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { - act, err := st.GetActor(market.Address) - if err != nil { - return big.Zero(), xerrors.Errorf("failed to load market actor: %w", err) - } - - mst, err := market.Load(adt.WrapStore(ctx, st.Store), act) - if err != nil { - return big.Zero(), xerrors.Errorf("failed to load market state: %w", err) - } - - return mst.TotalLocked() -} - -func getFilPowerLocked(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { - pactor, err := st.GetActor(power.Address) - if err != nil { - return big.Zero(), xerrors.Errorf("failed to load power actor: %w", err) - } - - pst, err := power.Load(adt.WrapStore(ctx, st.Store), pactor) - if err != nil { - return big.Zero(), xerrors.Errorf("failed to load power state: %w", err) - } - - return pst.TotalLocked() -} - -func (sm *StateManager) GetFilLocked(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { - - filMarketLocked, err := getFilMarketLocked(ctx, st) - if err != nil { - return big.Zero(), xerrors.Errorf("failed to get filMarketLocked: %w", err) - } - - filPowerLocked, err := getFilPowerLocked(ctx, st) - if err != nil { - return big.Zero(), xerrors.Errorf("failed to get filPowerLocked: %w", err) - } - - return types.BigAdd(filMarketLocked, filPowerLocked), nil -} - -func GetFilBurnt(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { - burnt, err := st.GetActor(builtin.BurntFundsActorAddr) - if err != nil { - return big.Zero(), xerrors.Errorf("failed to load burnt actor: %w", err) - } - - return burnt.Balance, nil -} - -func (sm *StateManager) GetVMCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) { - cs, err := sm.GetVMCirculatingSupplyDetailed(ctx, height, st) - if err != nil { - return types.EmptyInt, err - } - - return cs.FilCirculating, err -} - -func (sm *StateManager) GetVMCirculatingSupplyDetailed(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (api.CirculatingSupply, error) { - sm.genesisMsigLk.Lock() - defer sm.genesisMsigLk.Unlock() - if sm.preIgnitionVesting == nil || sm.genesisPledge.IsZero() || sm.genesisMarketFunds.IsZero() { - err := sm.setupGenesisVestingSchedule(ctx) - if err != nil { - return api.CirculatingSupply{}, xerrors.Errorf("failed to setup pre-ignition vesting schedule: %w", err) - } - } - if sm.postIgnitionVesting == nil { - err := sm.setupPostIgnitionVesting(ctx) - if err != nil { - return api.CirculatingSupply{}, xerrors.Errorf("failed to setup post-ignition vesting schedule: %w", err) - } - } - if sm.postCalicoVesting == nil { - err := sm.setupPostCalicoVesting(ctx) - if err != nil { - return api.CirculatingSupply{}, xerrors.Errorf("failed to setup post-calico vesting schedule: %w", err) - } - } - - filVested, err := sm.GetFilVested(ctx, height, st) - if err != nil { - return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filVested: %w", err) - } - - filReserveDisbursed := big.Zero() - if height > build.UpgradeAssemblyHeight { - filReserveDisbursed, err = GetFilReserveDisbursed(ctx, st) - if err != nil { - return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filReserveDisbursed: %w", err) - } - } - - filMined, err := GetFilMined(ctx, st) - if err != nil { - return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filMined: %w", err) - } - - filBurnt, err := GetFilBurnt(ctx, st) - if err != nil { - return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filBurnt: %w", err) - } - - filLocked, err := sm.GetFilLocked(ctx, st) - if err != nil { - return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filLocked: %w", err) - } - - ret := types.BigAdd(filVested, filMined) - ret = types.BigAdd(ret, filReserveDisbursed) - ret = types.BigSub(ret, filBurnt) - ret = types.BigSub(ret, filLocked) - - if ret.LessThan(big.Zero()) { - ret = big.Zero() - } - - return api.CirculatingSupply{ - FilVested: filVested, - FilMined: filMined, - FilBurnt: filBurnt, - FilLocked: filLocked, - FilCirculating: ret, - FilReserveDisbursed: filReserveDisbursed, - }, nil -} - -func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) { - circ := big.Zero() - unCirc := big.Zero() - err := st.ForEach(func(a address.Address, actor *types.Actor) error { - switch { - case actor.Balance.IsZero(): - // Do nothing for zero-balance actors - break - case a == _init.Address || - a == reward.Address || - a == verifreg.Address || - // The power actor itself should never receive funds - a == power.Address || - a == builtin.SystemActorAddr || - a == builtin.CronActorAddr || - a == builtin.BurntFundsActorAddr || - a == builtin.SaftAddress || - a == builtin.ReserveAddress: - - unCirc = big.Add(unCirc, actor.Balance) - - case a == market.Address: - mst, err := market.Load(sm.cs.ActorStore(ctx), actor) - if err != nil { - return err - } - - lb, err := mst.TotalLocked() - if err != nil { - return err - } - - circ = big.Add(circ, big.Sub(actor.Balance, lb)) - unCirc = big.Add(unCirc, lb) - - case builtin.IsAccountActor(actor.Code) || builtin.IsPaymentChannelActor(actor.Code): - circ = big.Add(circ, actor.Balance) - - case builtin.IsStorageMinerActor(actor.Code): - mst, err := miner.Load(sm.cs.ActorStore(ctx), actor) - if err != nil { - return err - } - - ab, err := mst.AvailableBalance(actor.Balance) - - if err == nil { - circ = big.Add(circ, ab) - unCirc = big.Add(unCirc, big.Sub(actor.Balance, ab)) - } else { - // Assume any error is because the miner state is "broken" (lower actor balance than locked funds) - // In this case, the actor's entire balance is considered "uncirculating" - unCirc = big.Add(unCirc, actor.Balance) - } - - case builtin.IsMultisigActor(actor.Code): - mst, err := multisig.Load(sm.cs.ActorStore(ctx), actor) - if err != nil { - return err - } - - lb, err := mst.LockedBalance(height) - if err != nil { - return err - } - - ab := big.Sub(actor.Balance, lb) - circ = big.Add(circ, big.Max(ab, big.Zero())) - unCirc = big.Add(unCirc, big.Min(actor.Balance, lb)) - default: - return xerrors.Errorf("unexpected actor: %s", a) - } - - return nil - }) - - if err != nil { - return types.EmptyInt, err - } - - total := big.Add(circ, unCirc) - if !total.Equals(types.TotalFilecoinInt) { - return types.EmptyInt, xerrors.Errorf("total filecoin didn't add to expected amount: %s != %s", total, types.TotalFilecoinInt) - } - - return circ, nil -} - func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoch) network.Version { // The epochs here are the _last_ epoch for every version, or -1 if the // version is disabled. @@ -1456,41 +352,3 @@ func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoc } return sm.latestVersion } - -func (sm *StateManager) GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error) { - st, err := sm.ParentState(ts) - if err != nil { - return nil, nil, err - } - - act, err := st.GetActor(addr) - if err != nil { - return nil, nil, err - } - - actState, err := paych.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, nil, err - } - return act, actState, nil -} - -func (sm *StateManager) GetMarketState(ctx context.Context, ts *types.TipSet) (market.State, error) { - st, err := sm.ParentState(ts) - if err != nil { - return nil, err - } - - act, err := st.GetActor(market.Address) - if err != nil { - return nil, err - } - - actState, err := market.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, err - } - return actState, nil -} - -var _ StateManagerAPI = (*StateManager)(nil) diff --git a/chain/stmgr/supply.go b/chain/stmgr/supply.go new file mode 100644 index 000000000..c9475a51e --- /dev/null +++ b/chain/stmgr/supply.go @@ -0,0 +1,473 @@ +package stmgr + +import ( + "context" + + "github.com/ipfs/go-cid" + cbor "github.com/ipfs/go-ipld-cbor" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + + msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" + _init "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/actors/builtin/market" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/builtin/multisig" + "github.com/filecoin-project/lotus/chain/actors/builtin/power" + "github.com/filecoin-project/lotus/chain/actors/builtin/reward" + "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" + "github.com/filecoin-project/lotus/chain/state" + "github.com/filecoin-project/lotus/chain/types" +) + +// sets up information about the vesting schedule +func (sm *StateManager) setupGenesisVestingSchedule(ctx context.Context) error { + + gb, err := sm.cs.GetGenesis() + if err != nil { + return xerrors.Errorf("getting genesis block: %w", err) + } + + gts, err := types.NewTipSet([]*types.BlockHeader{gb}) + if err != nil { + return xerrors.Errorf("getting genesis tipset: %w", err) + } + + st, _, err := sm.TipSetState(ctx, gts) + if err != nil { + return xerrors.Errorf("getting genesis tipset state: %w", err) + } + + cst := cbor.NewCborStore(sm.cs.StateBlockstore()) + sTree, err := state.LoadStateTree(cst, st) + if err != nil { + return xerrors.Errorf("loading state tree: %w", err) + } + + gmf, err := getFilMarketLocked(ctx, sTree) + if err != nil { + return xerrors.Errorf("setting up genesis market funds: %w", err) + } + + gp, err := getFilPowerLocked(ctx, sTree) + if err != nil { + return xerrors.Errorf("setting up genesis pledge: %w", err) + } + + sm.genesisMarketFunds = gmf + sm.genesisPledge = gp + + totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount) + + // 6 months + sixMonths := abi.ChainEpoch(183 * builtin.EpochsInDay) + totalsByEpoch[sixMonths] = big.NewInt(49_929_341) + totalsByEpoch[sixMonths] = big.Add(totalsByEpoch[sixMonths], big.NewInt(32_787_700)) + + // 1 year + oneYear := abi.ChainEpoch(365 * builtin.EpochsInDay) + totalsByEpoch[oneYear] = big.NewInt(22_421_712) + + // 2 years + twoYears := abi.ChainEpoch(2 * 365 * builtin.EpochsInDay) + totalsByEpoch[twoYears] = big.NewInt(7_223_364) + + // 3 years + threeYears := abi.ChainEpoch(3 * 365 * builtin.EpochsInDay) + totalsByEpoch[threeYears] = big.NewInt(87_637_883) + + // 6 years + sixYears := abi.ChainEpoch(6 * 365 * builtin.EpochsInDay) + totalsByEpoch[sixYears] = big.NewInt(100_000_000) + totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(300_000_000)) + + sm.preIgnitionVesting = make([]msig0.State, 0, len(totalsByEpoch)) + for k, v := range totalsByEpoch { + ns := msig0.State{ + InitialBalance: v, + UnlockDuration: k, + PendingTxns: cid.Undef, + } + sm.preIgnitionVesting = append(sm.preIgnitionVesting, ns) + } + + return nil +} + +// sets up information about the vesting schedule post the ignition upgrade +func (sm *StateManager) setupPostIgnitionVesting(ctx context.Context) error { + + totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount) + + // 6 months + sixMonths := abi.ChainEpoch(183 * builtin.EpochsInDay) + totalsByEpoch[sixMonths] = big.NewInt(49_929_341) + totalsByEpoch[sixMonths] = big.Add(totalsByEpoch[sixMonths], big.NewInt(32_787_700)) + + // 1 year + oneYear := abi.ChainEpoch(365 * builtin.EpochsInDay) + totalsByEpoch[oneYear] = big.NewInt(22_421_712) + + // 2 years + twoYears := abi.ChainEpoch(2 * 365 * builtin.EpochsInDay) + totalsByEpoch[twoYears] = big.NewInt(7_223_364) + + // 3 years + threeYears := abi.ChainEpoch(3 * 365 * builtin.EpochsInDay) + totalsByEpoch[threeYears] = big.NewInt(87_637_883) + + // 6 years + sixYears := abi.ChainEpoch(6 * 365 * builtin.EpochsInDay) + totalsByEpoch[sixYears] = big.NewInt(100_000_000) + totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(300_000_000)) + + sm.postIgnitionVesting = make([]msig0.State, 0, len(totalsByEpoch)) + for k, v := range totalsByEpoch { + ns := msig0.State{ + // In the pre-ignition logic, we incorrectly set this value in Fil, not attoFil, an off-by-10^18 error + InitialBalance: big.Mul(v, big.NewInt(int64(build.FilecoinPrecision))), + UnlockDuration: k, + PendingTxns: cid.Undef, + // In the pre-ignition logic, the start epoch was 0. This changes in the fork logic of the Ignition upgrade itself. + StartEpoch: build.UpgradeLiftoffHeight, + } + sm.postIgnitionVesting = append(sm.postIgnitionVesting, ns) + } + + return nil +} + +// sets up information about the vesting schedule post the calico upgrade +func (sm *StateManager) setupPostCalicoVesting(ctx context.Context) error { + + totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount) + + // 0 days + zeroDays := abi.ChainEpoch(0) + totalsByEpoch[zeroDays] = big.NewInt(10_632_000) + + // 6 months + sixMonths := abi.ChainEpoch(183 * builtin.EpochsInDay) + totalsByEpoch[sixMonths] = big.NewInt(19_015_887) + totalsByEpoch[sixMonths] = big.Add(totalsByEpoch[sixMonths], big.NewInt(32_787_700)) + + // 1 year + oneYear := abi.ChainEpoch(365 * builtin.EpochsInDay) + totalsByEpoch[oneYear] = big.NewInt(22_421_712) + totalsByEpoch[oneYear] = big.Add(totalsByEpoch[oneYear], big.NewInt(9_400_000)) + + // 2 years + twoYears := abi.ChainEpoch(2 * 365 * builtin.EpochsInDay) + totalsByEpoch[twoYears] = big.NewInt(7_223_364) + + // 3 years + threeYears := abi.ChainEpoch(3 * 365 * builtin.EpochsInDay) + totalsByEpoch[threeYears] = big.NewInt(87_637_883) + totalsByEpoch[threeYears] = big.Add(totalsByEpoch[threeYears], big.NewInt(898_958)) + + // 6 years + sixYears := abi.ChainEpoch(6 * 365 * builtin.EpochsInDay) + totalsByEpoch[sixYears] = big.NewInt(100_000_000) + totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(300_000_000)) + totalsByEpoch[sixYears] = big.Add(totalsByEpoch[sixYears], big.NewInt(9_805_053)) + + sm.postCalicoVesting = make([]msig0.State, 0, len(totalsByEpoch)) + for k, v := range totalsByEpoch { + ns := msig0.State{ + InitialBalance: big.Mul(v, big.NewInt(int64(build.FilecoinPrecision))), + UnlockDuration: k, + PendingTxns: cid.Undef, + StartEpoch: build.UpgradeLiftoffHeight, + } + sm.postCalicoVesting = append(sm.postCalicoVesting, ns) + } + + return nil +} + +// GetVestedFunds returns all funds that have "left" actors that are in the genesis state: +// - For Multisigs, it counts the actual amounts that have vested at the given epoch +// - For Accounts, it counts max(currentBalance - genesisBalance, 0). +func (sm *StateManager) GetFilVested(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) { + vf := big.Zero() + if height <= build.UpgradeIgnitionHeight { + for _, v := range sm.preIgnitionVesting { + au := big.Sub(v.InitialBalance, v.AmountLocked(height)) + vf = big.Add(vf, au) + } + } else if height <= build.UpgradeCalicoHeight { + for _, v := range sm.postIgnitionVesting { + // In the pre-ignition logic, we simply called AmountLocked(height), assuming startEpoch was 0. + // The start epoch changed in the Ignition upgrade. + au := big.Sub(v.InitialBalance, v.AmountLocked(height-v.StartEpoch)) + vf = big.Add(vf, au) + } + } else { + for _, v := range sm.postCalicoVesting { + // In the pre-ignition logic, we simply called AmountLocked(height), assuming startEpoch was 0. + // The start epoch changed in the Ignition upgrade. + au := big.Sub(v.InitialBalance, v.AmountLocked(height-v.StartEpoch)) + vf = big.Add(vf, au) + } + } + + // After UpgradeAssemblyHeight these funds are accounted for in GetFilReserveDisbursed + if height <= build.UpgradeAssemblyHeight { + // continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch + vf = big.Add(vf, sm.genesisPledge) + // continue to use preIgnitionGenInfos, nothing changed at the Ignition epoch + vf = big.Add(vf, sm.genesisMarketFunds) + } + + return vf, nil +} + +func GetFilReserveDisbursed(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { + ract, err := st.GetActor(builtin.ReserveAddress) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to get reserve actor: %w", err) + } + + // If money enters the reserve actor, this could lead to a negative term + return big.Sub(big.NewFromGo(build.InitialFilReserved), ract.Balance), nil +} + +func GetFilMined(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { + ractor, err := st.GetActor(reward.Address) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to load reward actor state: %w", err) + } + + rst, err := reward.Load(adt.WrapStore(ctx, st.Store), ractor) + if err != nil { + return big.Zero(), err + } + + return rst.TotalStoragePowerReward() +} + +func getFilMarketLocked(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { + act, err := st.GetActor(market.Address) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to load market actor: %w", err) + } + + mst, err := market.Load(adt.WrapStore(ctx, st.Store), act) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to load market state: %w", err) + } + + return mst.TotalLocked() +} + +func getFilPowerLocked(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { + pactor, err := st.GetActor(power.Address) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to load power actor: %w", err) + } + + pst, err := power.Load(adt.WrapStore(ctx, st.Store), pactor) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to load power state: %w", err) + } + + return pst.TotalLocked() +} + +func (sm *StateManager) GetFilLocked(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { + + filMarketLocked, err := getFilMarketLocked(ctx, st) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to get filMarketLocked: %w", err) + } + + filPowerLocked, err := getFilPowerLocked(ctx, st) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to get filPowerLocked: %w", err) + } + + return types.BigAdd(filMarketLocked, filPowerLocked), nil +} + +func GetFilBurnt(ctx context.Context, st *state.StateTree) (abi.TokenAmount, error) { + burnt, err := st.GetActor(builtin.BurntFundsActorAddr) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to load burnt actor: %w", err) + } + + return burnt.Balance, nil +} + +func (sm *StateManager) GetVMCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) { + cs, err := sm.GetVMCirculatingSupplyDetailed(ctx, height, st) + if err != nil { + return types.EmptyInt, err + } + + return cs.FilCirculating, err +} + +func (sm *StateManager) GetVMCirculatingSupplyDetailed(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (api.CirculatingSupply, error) { + sm.genesisMsigLk.Lock() + defer sm.genesisMsigLk.Unlock() + if sm.preIgnitionVesting == nil || sm.genesisPledge.IsZero() || sm.genesisMarketFunds.IsZero() { + err := sm.setupGenesisVestingSchedule(ctx) + if err != nil { + return api.CirculatingSupply{}, xerrors.Errorf("failed to setup pre-ignition vesting schedule: %w", err) + } + } + if sm.postIgnitionVesting == nil { + err := sm.setupPostIgnitionVesting(ctx) + if err != nil { + return api.CirculatingSupply{}, xerrors.Errorf("failed to setup post-ignition vesting schedule: %w", err) + } + } + if sm.postCalicoVesting == nil { + err := sm.setupPostCalicoVesting(ctx) + if err != nil { + return api.CirculatingSupply{}, xerrors.Errorf("failed to setup post-calico vesting schedule: %w", err) + } + } + + filVested, err := sm.GetFilVested(ctx, height, st) + if err != nil { + return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filVested: %w", err) + } + + filReserveDisbursed := big.Zero() + if height > build.UpgradeAssemblyHeight { + filReserveDisbursed, err = GetFilReserveDisbursed(ctx, st) + if err != nil { + return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filReserveDisbursed: %w", err) + } + } + + filMined, err := GetFilMined(ctx, st) + if err != nil { + return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filMined: %w", err) + } + + filBurnt, err := GetFilBurnt(ctx, st) + if err != nil { + return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filBurnt: %w", err) + } + + filLocked, err := sm.GetFilLocked(ctx, st) + if err != nil { + return api.CirculatingSupply{}, xerrors.Errorf("failed to calculate filLocked: %w", err) + } + + ret := types.BigAdd(filVested, filMined) + ret = types.BigAdd(ret, filReserveDisbursed) + ret = types.BigSub(ret, filBurnt) + ret = types.BigSub(ret, filLocked) + + if ret.LessThan(big.Zero()) { + ret = big.Zero() + } + + return api.CirculatingSupply{ + FilVested: filVested, + FilMined: filMined, + FilBurnt: filBurnt, + FilLocked: filLocked, + FilCirculating: ret, + FilReserveDisbursed: filReserveDisbursed, + }, nil +} + +func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) { + circ := big.Zero() + unCirc := big.Zero() + err := st.ForEach(func(a address.Address, actor *types.Actor) error { + switch { + case actor.Balance.IsZero(): + // Do nothing for zero-balance actors + break + case a == _init.Address || + a == reward.Address || + a == verifreg.Address || + // The power actor itself should never receive funds + a == power.Address || + a == builtin.SystemActorAddr || + a == builtin.CronActorAddr || + a == builtin.BurntFundsActorAddr || + a == builtin.SaftAddress || + a == builtin.ReserveAddress: + + unCirc = big.Add(unCirc, actor.Balance) + + case a == market.Address: + mst, err := market.Load(sm.cs.ActorStore(ctx), actor) + if err != nil { + return err + } + + lb, err := mst.TotalLocked() + if err != nil { + return err + } + + circ = big.Add(circ, big.Sub(actor.Balance, lb)) + unCirc = big.Add(unCirc, lb) + + case builtin.IsAccountActor(actor.Code) || builtin.IsPaymentChannelActor(actor.Code): + circ = big.Add(circ, actor.Balance) + + case builtin.IsStorageMinerActor(actor.Code): + mst, err := miner.Load(sm.cs.ActorStore(ctx), actor) + if err != nil { + return err + } + + ab, err := mst.AvailableBalance(actor.Balance) + + if err == nil { + circ = big.Add(circ, ab) + unCirc = big.Add(unCirc, big.Sub(actor.Balance, ab)) + } else { + // Assume any error is because the miner state is "broken" (lower actor balance than locked funds) + // In this case, the actor's entire balance is considered "uncirculating" + unCirc = big.Add(unCirc, actor.Balance) + } + + case builtin.IsMultisigActor(actor.Code): + mst, err := multisig.Load(sm.cs.ActorStore(ctx), actor) + if err != nil { + return err + } + + lb, err := mst.LockedBalance(height) + if err != nil { + return err + } + + ab := big.Sub(actor.Balance, lb) + circ = big.Add(circ, big.Max(ab, big.Zero())) + unCirc = big.Add(unCirc, big.Min(actor.Balance, lb)) + default: + return xerrors.Errorf("unexpected actor: %s", a) + } + + return nil + }) + + if err != nil { + return types.EmptyInt, err + } + + total := big.Add(circ, unCirc) + if !total.Equals(types.TotalFilecoinInt) { + return types.EmptyInt, xerrors.Errorf("total filecoin didn't add to expected amount: %s != %s", total, types.TotalFilecoinInt) + } + + return circ, nil +} diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index d2a2c6e60..8bcdbd755 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -35,6 +35,7 @@ import ( init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" "github.com/filecoin-project/lotus/chain/actors/builtin/market" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/builtin/paych" "github.com/filecoin-project/lotus/chain/actors/builtin/power" "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/beacon" @@ -729,3 +730,93 @@ func MakeMsgGasCost(msg *types.Message, ret *vm.ApplyRet) api.MsgGasCost { TotalCost: big.Sub(msg.RequiredFunds(), ret.GasCosts.Refund), } } + +func (sm *StateManager) ListAllActors(ctx context.Context, ts *types.TipSet) ([]address.Address, error) { + stateTree, err := sm.StateTree(sm.parentState(ts)) + if err != nil { + return nil, err + } + + var out []address.Address + err = stateTree.ForEach(func(addr address.Address, act *types.Actor) error { + out = append(out, addr) + return nil + }) + if err != nil { + return nil, err + } + + return out, nil +} + +func (sm *StateManager) GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error) { + st, err := sm.ParentState(ts) + if err != nil { + return nil, nil, err + } + + act, err := st.GetActor(addr) + if err != nil { + return nil, nil, err + } + + actState, err := paych.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, nil, err + } + return act, actState, nil +} + +func (sm *StateManager) GetMarketState(ctx context.Context, ts *types.TipSet) (market.State, error) { + st, err := sm.ParentState(ts) + if err != nil { + return nil, err + } + + act, err := st.GetActor(market.Address) + if err != nil { + return nil, err + } + + actState, err := market.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, err + } + return actState, nil +} + +func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (api.MarketBalance, error) { + mstate, err := sm.GetMarketState(ctx, ts) + if err != nil { + return api.MarketBalance{}, err + } + + addr, err = sm.LookupID(ctx, addr, ts) + if err != nil { + return api.MarketBalance{}, err + } + + var out api.MarketBalance + + et, err := mstate.EscrowTable() + if err != nil { + return api.MarketBalance{}, err + } + out.Escrow, err = et.Get(addr) + if err != nil { + return api.MarketBalance{}, xerrors.Errorf("getting escrow balance: %w", err) + } + + lt, err := mstate.LockedTable() + if err != nil { + return api.MarketBalance{}, err + } + out.Locked, err = lt.Get(addr) + if err != nil { + return api.MarketBalance{}, xerrors.Errorf("getting locked balance: %w", err) + } + + return out, nil +} + +var _ StateManagerAPI = (*StateManager)(nil) From 9bd312881dd3135c8506a8258963f66ec67d6df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Jul 2021 15:30:23 +0200 Subject: [PATCH 4/9] VMSys doesn't belong in chainstore --- chain/gen/gen.go | 4 ++-- chain/gen/genesis/genesis.go | 10 +++++----- chain/gen/genesis/miners.go | 4 ++-- chain/stmgr/call.go | 4 ++-- chain/stmgr/execute.go | 2 +- chain/stmgr/stmgr.go | 12 +++++++----- chain/stmgr/utils.go | 6 +++--- chain/store/index_test.go | 2 +- chain/store/store.go | 10 +--------- chain/store/store_test.go | 6 +++--- cmd/lotus-bench/import.go | 4 ++-- cmd/lotus-shed/balances.go | 8 ++++---- cmd/lotus-shed/export.go | 2 +- cmd/lotus-shed/genesis-verify.go | 6 ++---- cmd/lotus-shed/miner-types.go | 4 +--- cmd/lotus-shed/pruning.go | 4 +--- cmd/lotus-sim/simulation/node.go | 6 +++--- cmd/lotus/daemon.go | 4 ++-- conformance/driver.go | 6 +++--- node/modules/chain.go | 8 ++++---- node/modules/stmgr.go | 5 +++-- 21 files changed, 53 insertions(+), 64 deletions(-) diff --git a/chain/gen/gen.go b/chain/gen/gen.go index 424ee6edc..6b30f99ee 100644 --- a/chain/gen/gen.go +++ b/chain/gen/gen.go @@ -233,7 +233,7 @@ func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeS return nil, xerrors.Errorf("make genesis block failed: %w", err) } - cs := store.NewChainStore(bs, bs, ds, sys, j) + cs := store.NewChainStore(bs, bs, ds, j) genfb := &types.FullBlock{Header: genb.Genesis} gents := store.NewFullTipSet([]*types.FullBlock{genfb}) @@ -247,7 +247,7 @@ func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeS mgen[genesis2.MinerAddress(uint64(i))] = &wppProvider{} } - sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, us) + sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, sys, us) if err != nil { return nil, xerrors.Errorf("initing stmgr: %w", err) } diff --git a/chain/gen/genesis/genesis.go b/chain/gen/genesis/genesis.go index 6dec3fea6..0f863ff1d 100644 --- a/chain/gen/genesis/genesis.go +++ b/chain/gen/genesis/genesis.go @@ -471,7 +471,7 @@ func createMultisigAccount(ctx context.Context, cst cbor.IpldStore, state *state return nil } -func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot cid.Cid, template genesis.Template, keyIDs map[address.Address]address.Address, nv network.Version) (cid.Cid, error) { +func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, sys vm.SyscallBuilder, stateroot cid.Cid, template genesis.Template, keyIDs map[address.Address]address.Address, nv network.Version) (cid.Cid, error) { verifNeeds := make(map[address.Address]abi.PaddedPieceSize) var sum abi.PaddedPieceSize @@ -480,7 +480,7 @@ func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot ci Epoch: 0, Rand: &fakeRand{}, Bstore: cs.StateBlockstore(), - Syscalls: mkFakedSigSyscalls(cs.VMSys()), + Syscalls: mkFakedSigSyscalls(sys), CircSupplyCalc: nil, NtwkVersion: func(_ context.Context, _ abi.ChainEpoch) network.Version { return nv @@ -559,15 +559,15 @@ func MakeGenesisBlock(ctx context.Context, j journal.Journal, bs bstore.Blocksto } // temp chainstore - cs := store.NewChainStore(bs, bs, datastore.NewMapDatastore(), sys, j) + cs := store.NewChainStore(bs, bs, datastore.NewMapDatastore(), j) // Verify PreSealed Data - stateroot, err = VerifyPreSealedData(ctx, cs, stateroot, template, keyIDs, template.NetworkVersion) + stateroot, err = VerifyPreSealedData(ctx, cs, sys, stateroot, template, keyIDs, template.NetworkVersion) if err != nil { return nil, xerrors.Errorf("failed to verify presealed data: %w", err) } - stateroot, err = SetupStorageMiners(ctx, cs, stateroot, template.Miners, template.NetworkVersion) + stateroot, err = SetupStorageMiners(ctx, cs, sys, stateroot, template.Miners, template.NetworkVersion) if err != nil { return nil, xerrors.Errorf("setup miners failed: %w", err) } diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index e6f17d677..38c769696 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -78,7 +78,7 @@ func mkFakedSigSyscalls(base vm.SyscallBuilder) vm.SyscallBuilder { } // Note: Much of this is brittle, if the methodNum / param / return changes, it will break things -func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, miners []genesis.Miner, nv network.Version) (cid.Cid, error) { +func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sys vm.SyscallBuilder, sroot cid.Cid, miners []genesis.Miner, nv network.Version) (cid.Cid, error) { cst := cbor.NewCborStore(cs.StateBlockstore()) av := actors.VersionForNetwork(nv) @@ -92,7 +92,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid Epoch: 0, Rand: &fakeRand{}, Bstore: cs.StateBlockstore(), - Syscalls: mkFakedSigSyscalls(cs.VMSys()), + Syscalls: mkFakedSigSyscalls(sys), CircSupplyCalc: csc, NtwkVersion: func(_ context.Context, _ abi.ChainEpoch) network.Version { return nv diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index dc6da0f9c..caa815132 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -64,7 +64,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types. Epoch: pheight + 1, Rand: store.NewChainRand(sm.cs, ts.Cids()), Bstore: sm.cs.StateBlockstore(), - Syscalls: sm.cs.VMSys(), + Syscalls: sm.syscalls, CircSupplyCalc: sm.GetVMCirculatingSupply, NtwkVersion: sm.GetNtwkVersion, BaseFee: types.NewInt(0), @@ -179,7 +179,7 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri Epoch: ts.Height() + 1, Rand: r, Bstore: sm.cs.StateBlockstore(), - Syscalls: sm.cs.VMSys(), + Syscalls: sm.syscalls, CircSupplyCalc: sm.GetVMCirculatingSupply, NtwkVersion: sm.GetNtwkVersion, BaseFee: ts.Blocks()[0].ParentBaseFee, diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index 9194f711e..de7ca2894 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -42,7 +42,7 @@ func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEp Epoch: epoch, Rand: r, Bstore: sm.cs.StateBlockstore(), - Syscalls: sm.cs.VMSys(), + Syscalls: sm.syscalls, CircSupplyCalc: sm.GetVMCirculatingSupply, NtwkVersion: sm.GetNtwkVersion, BaseFee: baseFee, diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 8f03b6bf9..09b8284dd 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -75,6 +75,7 @@ type StateManager struct { stlk sync.Mutex genesisMsigLk sync.Mutex newVM func(context.Context, *vm.VMOpts) (*vm.VM, error) + syscalls vm.SyscallBuilder preIgnitionVesting []msig0.State postIgnitionVesting []msig0.State postCalicoVesting []msig0.State @@ -91,15 +92,15 @@ type treeCache struct { tree *state.StateTree } -func NewStateManager(cs *store.ChainStore) *StateManager { - sm, err := NewStateManagerWithUpgradeSchedule(cs, DefaultUpgradeSchedule()) +func NewStateManager(cs *store.ChainStore, sys vm.SyscallBuilder) *StateManager { + sm, err := NewStateManagerWithUpgradeSchedule(cs, sys, DefaultUpgradeSchedule()) if err != nil { panic(fmt.Sprintf("default upgrade schedule is invalid: %s", err)) } return sm } -func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, us UpgradeSchedule) (*StateManager, error) { +func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, sys vm.SyscallBuilder, us UpgradeSchedule) (*StateManager, error) { // If we have upgrades, make sure they're in-order and make sense. if err := us.Validate(); err != nil { return nil, err @@ -141,6 +142,7 @@ func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, us UpgradeSchedule stateMigrations: stateMigrations, expensiveUpgrades: expensiveUpgrades, newVM: vm.NewVM, + syscalls: sys, cs: cs, stCache: make(map[string][]cid.Cid), tCache: treeCache{ @@ -151,8 +153,8 @@ func NewStateManagerWithUpgradeSchedule(cs *store.ChainStore, us UpgradeSchedule }, nil } -func NewStateManagerWithUpgradeScheduleAndMonitor(cs *store.ChainStore, us UpgradeSchedule, em ExecMonitor) (*StateManager, error) { - sm, err := NewStateManagerWithUpgradeSchedule(cs, us) +func NewStateManagerWithUpgradeScheduleAndMonitor(cs *store.ChainStore, sys vm.SyscallBuilder, us UpgradeSchedule, em ExecMonitor) (*StateManager, error) { + sm, err := NewStateManagerWithUpgradeSchedule(cs, sys, us) if err != nil { return nil, err } diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index 8bcdbd755..f54904aaa 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -357,7 +357,7 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, Epoch: height, Rand: r, Bstore: sm.cs.StateBlockstore(), - Syscalls: sm.cs.VMSys(), + Syscalls: sm.syscalls, CircSupplyCalc: sm.GetVMCirculatingSupply, NtwkVersion: sm.GetNtwkVersion, BaseFee: ts.Blocks()[0].ParentBaseFee, @@ -700,8 +700,8 @@ func MinerEligibleToMine(ctx context.Context, sm *StateManager, addr address.Add return true, nil } -func CheckTotalFIL(ctx context.Context, sm *StateManager, ts *types.TipSet) (abi.TokenAmount, error) { - str, err := state.LoadStateTree(sm.ChainStore().ActorStore(ctx), ts.ParentState()) +func CheckTotalFIL(ctx context.Context, cs *store.ChainStore, ts *types.TipSet) (abi.TokenAmount, error) { + str, err := state.LoadStateTree(cs.ActorStore(ctx), ts.ParentState()) if err != nil { return abi.TokenAmount{}, err } diff --git a/chain/store/index_test.go b/chain/store/index_test.go index 447071901..b74bc835b 100644 --- a/chain/store/index_test.go +++ b/chain/store/index_test.go @@ -31,7 +31,7 @@ func TestIndexSeeks(t *testing.T) { ctx := context.TODO() nbs := blockstore.NewMemorySync() - cs := store.NewChainStore(nbs, nbs, syncds.MutexWrap(datastore.NewMapDatastore()), nil, nil) + cs := store.NewChainStore(nbs, nbs, syncds.MutexWrap(datastore.NewMapDatastore()), nil) defer cs.Close() //nolint:errcheck _, err = cs.Import(bytes.NewReader(gencar)) diff --git a/chain/store/store.go b/chain/store/store.go index b81bcf61e..9715569ac 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -19,7 +19,6 @@ import ( 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/vm" "github.com/filecoin-project/lotus/journal" "github.com/filecoin-project/lotus/metrics" @@ -123,8 +122,6 @@ type ChainStore struct { mmCache *lru.ARCCache tsCache *lru.ARCCache - vmcalls vm.SyscallBuilder - evtTypes [1]journal.EventType journal journal.Journal @@ -132,7 +129,7 @@ type ChainStore struct { wg sync.WaitGroup } -func NewChainStore(chainBs bstore.Blockstore, stateBs bstore.Blockstore, ds dstore.Batching, vmcalls vm.SyscallBuilder, j journal.Journal) *ChainStore { +func NewChainStore(chainBs bstore.Blockstore, stateBs bstore.Blockstore, ds dstore.Batching, j journal.Journal) *ChainStore { c, _ := lru.NewARC(DefaultMsgMetaCacheSize) tsc, _ := lru.NewARC(DefaultTipSetCacheSize) if j == nil { @@ -152,7 +149,6 @@ func NewChainStore(chainBs bstore.Blockstore, stateBs bstore.Blockstore, ds dsto tipsets: make(map[abi.ChainEpoch][]cid.Cid), mmCache: c, tsCache: tsc, - vmcalls: vmcalls, cancelFn: cancel, journal: j, } @@ -1094,10 +1090,6 @@ func (cs *ChainStore) ActorStore(ctx context.Context) adt.Store { return ActorStore(ctx, cs.stateBlockstore) } -func (cs *ChainStore) VMSys() vm.SyscallBuilder { - return cs.vmcalls -} - func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) { var out []*types.FullBlock diff --git a/chain/store/store_test.go b/chain/store/store_test.go index 62a0430e3..898d590d0 100644 --- a/chain/store/store_test.go +++ b/chain/store/store_test.go @@ -70,7 +70,7 @@ func BenchmarkGetRandomness(b *testing.B) { b.Fatal(err) } - cs := store.NewChainStore(bs, bs, mds, nil, nil) + cs := store.NewChainStore(bs, bs, mds, nil) defer cs.Close() //nolint:errcheck b.ResetTimer() @@ -105,7 +105,7 @@ func TestChainExportImport(t *testing.T) { } nbs := blockstore.NewMemory() - cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), nil, nil) + cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), nil) defer cs.Close() //nolint:errcheck root, err := cs.Import(buf) @@ -140,7 +140,7 @@ func TestChainExportImportFull(t *testing.T) { } nbs := blockstore.NewMemory() - cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), nil, nil) + cs := store.NewChainStore(nbs, nbs, datastore.NewMapDatastore(), nil) defer cs.Close() //nolint:errcheck root, err := cs.Import(buf) diff --git a/cmd/lotus-bench/import.go b/cmd/lotus-bench/import.go index 4b464bebe..d8ef57138 100644 --- a/cmd/lotus-bench/import.go +++ b/cmd/lotus-bench/import.go @@ -253,10 +253,10 @@ var importBenchCmd = &cli.Command{ } metadataDs := datastore.NewMapDatastore() - cs := store.NewChainStore(bs, bs, metadataDs, vm.Syscalls(verifier), nil) + cs := store.NewChainStore(bs, bs, metadataDs, nil) defer cs.Close() //nolint:errcheck - stm := stmgr.NewStateManager(cs) + stm := stmgr.NewStateManager(cs, vm.Syscalls(verifier)) var carFile *os.File // open the CAR file if one is provided. diff --git a/cmd/lotus-shed/balances.go b/cmd/lotus-shed/balances.go index 87530c666..3a158483f 100644 --- a/cmd/lotus-shed/balances.go +++ b/cmd/lotus-shed/balances.go @@ -510,13 +510,13 @@ var chainBalanceStateCmd = &cli.Command{ return err } - cs := store.NewChainStore(bs, bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier), nil) + cs := store.NewChainStore(bs, bs, mds, nil) defer cs.Close() //nolint:errcheck cst := cbor.NewCborStore(bs) store := adt.WrapStore(ctx, cst) - sm := stmgr.NewStateManager(cs) + sm := stmgr.NewStateManager(cs, vm.Syscalls(ffiwrapper.ProofVerifier)) tree, err := state.LoadStateTree(cst, sroot) if err != nil { @@ -731,13 +731,13 @@ var chainPledgeCmd = &cli.Command{ return err } - cs := store.NewChainStore(bs, bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier), nil) + cs := store.NewChainStore(bs, bs, mds, nil) defer cs.Close() //nolint:errcheck cst := cbor.NewCborStore(bs) store := adt.WrapStore(ctx, cst) - sm := stmgr.NewStateManager(cs) + sm := stmgr.NewStateManager(cs, vm.Syscalls(ffiwrapper.ProofVerifier)) state, err := state.LoadStateTree(cst, sroot) if err != nil { diff --git a/cmd/lotus-shed/export.go b/cmd/lotus-shed/export.go index e711ba2bb..dc5cc3bd2 100644 --- a/cmd/lotus-shed/export.go +++ b/cmd/lotus-shed/export.go @@ -90,7 +90,7 @@ var exportChainCmd = &cli.Command{ return err } - cs := store.NewChainStore(bs, bs, mds, nil, nil) + cs := store.NewChainStore(bs, bs, mds, nil) defer cs.Close() //nolint:errcheck if err := cs.Load(); err != nil { diff --git a/cmd/lotus-shed/genesis-verify.go b/cmd/lotus-shed/genesis-verify.go index 32e4e14ad..c00ce2c7f 100644 --- a/cmd/lotus-shed/genesis-verify.go +++ b/cmd/lotus-shed/genesis-verify.go @@ -52,7 +52,7 @@ var genesisVerifyCmd = &cli.Command{ } bs := blockstore.FromDatastore(datastore.NewMapDatastore()) - cs := store.NewChainStore(bs, bs, datastore.NewMapDatastore(), nil, nil) + cs := store.NewChainStore(bs, bs, datastore.NewMapDatastore(), nil) defer cs.Close() //nolint:errcheck cf := cctx.Args().Get(0) @@ -66,9 +66,7 @@ var genesisVerifyCmd = &cli.Command{ return err } - sm := stmgr.NewStateManager(cs) - - total, err := stmgr.CheckTotalFIL(context.TODO(), sm, ts) + total, err := stmgr.CheckTotalFIL(context.TODO(), cs, ts) if err != nil { return err } diff --git a/cmd/lotus-shed/miner-types.go b/cmd/lotus-shed/miner-types.go index 19a30c4b9..491a77aa0 100644 --- a/cmd/lotus-shed/miner-types.go +++ b/cmd/lotus-shed/miner-types.go @@ -15,8 +15,6 @@ import ( "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/chain/vm" - "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/node/repo" builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin" "github.com/filecoin-project/specs-actors/v4/actors/util/adt" @@ -76,7 +74,7 @@ var minerTypesCmd = &cli.Command{ return err } - cs := store.NewChainStore(bs, bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier), nil) + cs := store.NewChainStore(bs, bs, mds, nil) defer cs.Close() //nolint:errcheck cst := cbor.NewCborStore(bs) diff --git a/cmd/lotus-shed/pruning.go b/cmd/lotus-shed/pruning.go index 1afe76c4d..1b57a1cd4 100644 --- a/cmd/lotus-shed/pruning.go +++ b/cmd/lotus-shed/pruning.go @@ -13,8 +13,6 @@ import ( badgerbs "github.com/filecoin-project/lotus/blockstore/badger" "github.com/filecoin-project/lotus/chain/store" - "github.com/filecoin-project/lotus/chain/vm" - "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/node/repo" ) @@ -169,7 +167,7 @@ var stateTreePruneCmd = &cli.Command{ return nil } - cs := store.NewChainStore(bs, bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier), nil) + cs := store.NewChainStore(bs, bs, mds, nil) defer cs.Close() //nolint:errcheck if err := cs.Load(); err != nil { diff --git a/cmd/lotus-sim/simulation/node.go b/cmd/lotus-sim/simulation/node.go index 5b8bf2bf9..c2a497bcb 100644 --- a/cmd/lotus-sim/simulation/node.go +++ b/cmd/lotus-sim/simulation/node.go @@ -61,7 +61,7 @@ func NewNode(ctx context.Context, r repo.Repo) (nd *Node, _err error) { } return &Node{ repo: lr, - Chainstore: store.NewChainStore(bs, bs, ds, vm.Syscalls(mock.Verifier), nil), + Chainstore: store.NewChainStore(bs, bs, ds, nil), MetadataDS: ds, Blockstore: bs, }, err @@ -105,7 +105,7 @@ func (nd *Node) LoadSim(ctx context.Context, name string) (*Simulation, error) { if err != nil { return nil, xerrors.Errorf("failed to create upgrade schedule for simulation %s: %w", name, err) } - sim.StateManager, err = stmgr.NewStateManagerWithUpgradeSchedule(nd.Chainstore, us) + sim.StateManager, err = stmgr.NewStateManagerWithUpgradeSchedule(nd.Chainstore, vm.Syscalls(mock.Verifier), us) if err != nil { return nil, xerrors.Errorf("failed to create state manager for simulation %s: %w", name, err) } @@ -127,7 +127,7 @@ func (nd *Node) CreateSim(ctx context.Context, name string, head *types.TipSet) sim := &Simulation{ name: name, Node: nd, - StateManager: stmgr.NewStateManager(nd.Chainstore), + StateManager: stmgr.NewStateManager(nd.Chainstore, vm.Syscalls(mock.Verifier)), stages: stages, } if has, err := nd.MetadataDS.Has(sim.key("head")); err != nil { diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 0d5961aae..486ac8ed7 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -481,7 +481,7 @@ func ImportChain(ctx context.Context, r repo.Repo, fname string, snapshot bool) return xerrors.Errorf("failed to open journal: %w", err) } - cst := store.NewChainStore(bs, bs, mds, vm.Syscalls(ffiwrapper.ProofVerifier), j) + cst := store.NewChainStore(bs, bs, mds, j) defer cst.Close() //nolint:errcheck log.Infof("importing chain from %s...", fname) @@ -517,7 +517,7 @@ func ImportChain(ctx context.Context, r repo.Repo, fname string, snapshot bool) return err } - stm := stmgr.NewStateManager(cst) + stm := stmgr.NewStateManager(cst, vm.Syscalls(ffiwrapper.ProofVerifier)) if !snapshot { log.Infof("validating imported chain...") diff --git a/conformance/driver.go b/conformance/driver.go index c7fc0d6c4..0b3d42644 100644 --- a/conformance/driver.go +++ b/conformance/driver.go @@ -101,8 +101,8 @@ func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, params tipset = params.Tipset syscalls = vm.Syscalls(ffiwrapper.ProofVerifier) - cs = store.NewChainStore(bs, bs, ds, syscalls, nil) - sm = stmgr.NewStateManager(cs) + cs = store.NewChainStore(bs, bs, ds, nil) + sm = stmgr.NewStateManager(cs, syscalls) ) if params.Rand == nil { @@ -196,7 +196,7 @@ func (d *Driver) ExecuteMessage(bs blockstore.Blockstore, params ExecuteMessageP // dummy state manager; only to reference the GetNetworkVersion method, // which does not depend on state. - sm := stmgr.NewStateManager(nil) + sm := stmgr.NewStateManager(nil, nil) vmOpts := &vm.VMOpts{ StateBase: params.Preroot, diff --git a/node/modules/chain.go b/node/modules/chain.go index c4017b8c0..a0e7f2f51 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -72,8 +72,8 @@ func MessagePool(lc fx.Lifecycle, mpp messagepool.Provider, ds dtypes.MetadataDS return mp, nil } -func ChainStore(lc fx.Lifecycle, cbs dtypes.ChainBlockstore, sbs dtypes.StateBlockstore, ds dtypes.MetadataDS, basebs dtypes.BaseBlockstore, syscalls vm.SyscallBuilder, j journal.Journal) *store.ChainStore { - chain := store.NewChainStore(cbs, sbs, ds, syscalls, j) +func ChainStore(lc fx.Lifecycle, cbs dtypes.ChainBlockstore, sbs dtypes.StateBlockstore, ds dtypes.MetadataDS, basebs dtypes.BaseBlockstore, j journal.Journal) *store.ChainStore { + chain := store.NewChainStore(cbs, sbs, ds, j) if err := chain.Load(); err != nil { log.Warnf("loading chain state from disk: %s", err) @@ -100,14 +100,14 @@ func ChainStore(lc fx.Lifecycle, cbs dtypes.ChainBlockstore, sbs dtypes.StateBlo return chain } -func NetworkName(mctx helpers.MetricsCtx, lc fx.Lifecycle, cs *store.ChainStore, us stmgr.UpgradeSchedule, _ dtypes.AfterGenesisSet) (dtypes.NetworkName, error) { +func NetworkName(mctx helpers.MetricsCtx, lc fx.Lifecycle, cs *store.ChainStore, syscalls vm.SyscallBuilder, us stmgr.UpgradeSchedule, _ dtypes.AfterGenesisSet) (dtypes.NetworkName, error) { if !build.Devnet { return "testnetnet", nil } ctx := helpers.LifecycleCtx(mctx, lc) - sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, us) + sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, syscalls, us) if err != nil { return "", err } diff --git a/node/modules/stmgr.go b/node/modules/stmgr.go index 9d3917b85..af53457f9 100644 --- a/node/modules/stmgr.go +++ b/node/modules/stmgr.go @@ -1,14 +1,15 @@ package modules import ( + "github.com/filecoin-project/lotus/chain/vm" "go.uber.org/fx" "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" ) -func StateManager(lc fx.Lifecycle, cs *store.ChainStore, us stmgr.UpgradeSchedule) (*stmgr.StateManager, error) { - sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, us) +func StateManager(lc fx.Lifecycle, cs *store.ChainStore, sys vm.SyscallBuilder, us stmgr.UpgradeSchedule) (*stmgr.StateManager, error) { + sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, sys, us) if err != nil { return nil, err } From e7d73cbe56eb328994c60a5206d3d2c7ea38c853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Jul 2021 15:34:39 +0200 Subject: [PATCH 5/9] vm: Remove unused ActorBalance --- chain/store/store.go | 2 +- chain/vm/invoker.go | 2 +- chain/vm/vm.go | 9 --------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/chain/store/store.go b/chain/store/store.go index 9715569ac..df5936c37 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -119,7 +119,7 @@ type ChainStore struct { reorgCh chan<- reorg reorgNotifeeCh chan ReorgNotifee - mmCache *lru.ARCCache + mmCache *lru.ARCCache // msg meta cache (mh.Messages -> secp, bls []cid) tsCache *lru.ARCCache evtTypes [1]journal.EventType diff --git a/chain/vm/invoker.go b/chain/vm/invoker.go index e4b154031..6bca8e9ac 100644 --- a/chain/vm/invoker.go +++ b/chain/vm/invoker.go @@ -228,7 +228,7 @@ func DumpActorState(act *types.Actor, b []byte) (interface{}, error) { return nil, nil } - i := NewActorRegistry() // TODO: register builtins in init block + i := NewActorRegistry() actInfo, ok := i.actors[act.Code] if !ok { diff --git a/chain/vm/vm.go b/chain/vm/vm.go index 2746d5f17..84f57ec9b 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -641,15 +641,6 @@ func (vm *VM) ShouldBurn(ctx context.Context, st *state.StateTree, msg *types.Me return true, nil } -func (vm *VM) ActorBalance(addr address.Address) (types.BigInt, aerrors.ActorError) { - act, err := vm.cstate.GetActor(addr) - if err != nil { - return types.EmptyInt, aerrors.Absorb(err, 1, "failed to find actor") - } - - return act.Balance, nil -} - type vmFlushKey struct{} func (vm *VM) Flush(ctx context.Context) (cid.Cid, error) { From ce82f2827b18db0eb706e0b4e46a3885f9285db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Jul 2021 15:43:03 +0200 Subject: [PATCH 6/9] stmgr: Put actor state accessors in actors.go --- chain/stmgr/actors.go | 551 ++++++++++++++++++++++++++++++++ chain/stmgr/utils.go | 715 ++++++------------------------------------ 2 files changed, 640 insertions(+), 626 deletions(-) create mode 100644 chain/stmgr/actors.go diff --git a/chain/stmgr/actors.go b/chain/stmgr/actors.go new file mode 100644 index 000000000..0c1e219c8 --- /dev/null +++ b/chain/stmgr/actors.go @@ -0,0 +1,551 @@ +package stmgr + +import ( + "bytes" + "context" + "os" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/go-state-types/network" + cid "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/actors/builtin/market" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/builtin/paych" + "github.com/filecoin-project/lotus/chain/actors/builtin/power" + "github.com/filecoin-project/lotus/chain/beacon" + "github.com/filecoin-project/lotus/chain/store" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" + "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" +) + +func GetMinerWorkerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (address.Address, error) { + state, err := sm.StateTree(st) + if err != nil { + return address.Undef, xerrors.Errorf("(get sset) failed to load state tree: %w", err) + } + act, err := state.GetActor(maddr) + if err != nil { + return address.Undef, xerrors.Errorf("(get sset) failed to load miner actor: %w", err) + } + mas, err := miner.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return address.Undef, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err) + } + + info, err := mas.Info() + if err != nil { + return address.Undef, xerrors.Errorf("failed to load actor info: %w", err) + } + + return vm.ResolveToKeyAddr(state, sm.cs.ActorStore(ctx), info.Worker) +} + +func GetPower(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (power.Claim, power.Claim, bool, error) { + return GetPowerRaw(ctx, sm, ts.ParentState(), maddr) +} + +func GetPowerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (power.Claim, power.Claim, bool, error) { + act, err := sm.LoadActorRaw(ctx, power.Address, st) + if err != nil { + return power.Claim{}, power.Claim{}, false, xerrors.Errorf("(get sset) failed to load power actor state: %w", err) + } + + pas, err := power.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return power.Claim{}, power.Claim{}, false, err + } + + tpow, err := pas.TotalPower() + if err != nil { + return power.Claim{}, power.Claim{}, false, err + } + + var mpow power.Claim + var minpow bool + if maddr != address.Undef { + var found bool + mpow, found, err = pas.MinerPower(maddr) + if err != nil || !found { + return power.Claim{}, tpow, false, err + } + + minpow, err = pas.MinerNominalPowerMeetsConsensusMinimum(maddr) + if err != nil { + return power.Claim{}, power.Claim{}, false, err + } + } + + return mpow, tpow, minpow, nil +} + +func PreCommitInfo(ctx context.Context, sm *StateManager, maddr address.Address, sid abi.SectorNumber, ts *types.TipSet) (*miner.SectorPreCommitOnChainInfo, error) { + act, err := sm.LoadActor(ctx, maddr, ts) + if err != nil { + return nil, xerrors.Errorf("(get sset) failed to load miner actor: %w", err) + } + + mas, err := miner.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err) + } + + return mas.GetPrecommittedSector(sid) +} + +func MinerSectorInfo(ctx context.Context, sm *StateManager, maddr address.Address, sid abi.SectorNumber, ts *types.TipSet) (*miner.SectorOnChainInfo, error) { + act, err := sm.LoadActor(ctx, maddr, ts) + if err != nil { + return nil, xerrors.Errorf("(get sset) failed to load miner actor: %w", err) + } + + mas, err := miner.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err) + } + + return mas.GetSector(sid) +} + +func GetSectorsForWinningPoSt(ctx context.Context, nv network.Version, pv ffiwrapper.Verifier, sm *StateManager, st cid.Cid, maddr address.Address, rand abi.PoStRandomness) ([]builtin.SectorInfo, error) { + act, err := sm.LoadActorRaw(ctx, maddr, st) + if err != nil { + return nil, xerrors.Errorf("failed to load miner actor: %w", err) + } + + mas, err := miner.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, xerrors.Errorf("failed to load miner actor state: %w", err) + } + + var provingSectors bitfield.BitField + if nv < network.Version7 { + allSectors, err := miner.AllPartSectors(mas, miner.Partition.AllSectors) + if err != nil { + return nil, xerrors.Errorf("get all sectors: %w", err) + } + + faultySectors, err := miner.AllPartSectors(mas, miner.Partition.FaultySectors) + if err != nil { + return nil, xerrors.Errorf("get faulty sectors: %w", err) + } + + provingSectors, err = bitfield.SubtractBitField(allSectors, faultySectors) + if err != nil { + return nil, xerrors.Errorf("calc proving sectors: %w", err) + } + } else { + provingSectors, err = miner.AllPartSectors(mas, miner.Partition.ActiveSectors) + if err != nil { + return nil, xerrors.Errorf("get active sectors sectors: %w", err) + } + } + + numProvSect, err := provingSectors.Count() + if err != nil { + return nil, xerrors.Errorf("failed to count bits: %w", err) + } + + // TODO(review): is this right? feels fishy to me + if numProvSect == 0 { + return nil, nil + } + + info, err := mas.Info() + if err != nil { + return nil, xerrors.Errorf("getting miner info: %w", err) + } + + mid, err := address.IDFromAddress(maddr) + if err != nil { + return nil, xerrors.Errorf("getting miner ID: %w", err) + } + + proofType, err := miner.WinningPoStProofTypeFromWindowPoStProofType(nv, info.WindowPoStProofType) + if err != nil { + return nil, xerrors.Errorf("determining winning post proof type: %w", err) + } + + ids, err := pv.GenerateWinningPoStSectorChallenge(ctx, proofType, abi.ActorID(mid), rand, numProvSect) + if err != nil { + return nil, xerrors.Errorf("generating winning post challenges: %w", err) + } + + iter, err := provingSectors.BitIterator() + if err != nil { + return nil, xerrors.Errorf("iterating over proving sectors: %w", err) + } + + // Select winning sectors by _index_ in the all-sectors bitfield. + selectedSectors := bitfield.New() + prev := uint64(0) + for _, n := range ids { + sno, err := iter.Nth(n - prev) + if err != nil { + return nil, xerrors.Errorf("iterating over proving sectors: %w", err) + } + selectedSectors.Set(sno) + prev = n + } + + sectors, err := mas.LoadSectors(&selectedSectors) + if err != nil { + return nil, xerrors.Errorf("loading proving sectors: %w", err) + } + + out := make([]builtin.SectorInfo, len(sectors)) + for i, sinfo := range sectors { + out[i] = builtin.SectorInfo{ + SealProof: sinfo.SealProof, + SectorNumber: sinfo.SectorNumber, + SealedCID: sinfo.SealedCID, + } + } + + return out, nil +} + +func GetMinerSlashed(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (bool, error) { + act, err := sm.LoadActor(ctx, power.Address, ts) + if err != nil { + return false, xerrors.Errorf("failed to load power actor: %w", err) + } + + spas, err := power.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return false, xerrors.Errorf("failed to load power actor state: %w", err) + } + + _, ok, err := spas.MinerPower(maddr) + if err != nil { + return false, xerrors.Errorf("getting miner power: %w", err) + } + + if !ok { + return true, nil + } + + return false, nil +} + +func GetStorageDeal(ctx context.Context, sm *StateManager, dealID abi.DealID, ts *types.TipSet) (*api.MarketDeal, error) { + act, err := sm.LoadActor(ctx, market.Address, ts) + if err != nil { + return nil, xerrors.Errorf("failed to load market actor: %w", err) + } + + state, err := market.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, xerrors.Errorf("failed to load market actor state: %w", err) + } + + proposals, err := state.Proposals() + if err != nil { + return nil, err + } + + proposal, found, err := proposals.Get(dealID) + + if err != nil { + return nil, err + } else if !found { + return nil, xerrors.Errorf( + "deal %d not found "+ + "- deal may not have completed sealing before deal proposal "+ + "start epoch, or deal may have been slashed", + dealID) + } + + states, err := state.States() + if err != nil { + return nil, err + } + + st, found, err := states.Get(dealID) + if err != nil { + return nil, err + } + + if !found { + st = market.EmptyDealState() + } + + return &api.MarketDeal{ + Proposal: *proposal, + State: *st, + }, nil +} + +func ListMinerActors(ctx context.Context, sm *StateManager, ts *types.TipSet) ([]address.Address, error) { + act, err := sm.LoadActor(ctx, power.Address, ts) + if err != nil { + return nil, xerrors.Errorf("failed to load power actor: %w", err) + } + + powState, err := power.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, xerrors.Errorf("failed to load power actor state: %w", err) + } + + return powState.ListAllMiners() +} + +func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule, tsk types.TipSetKey, round abi.ChainEpoch, maddr address.Address, pv ffiwrapper.Verifier) (*api.MiningBaseInfo, error) { + ts, err := sm.ChainStore().LoadTipSet(tsk) + if err != nil { + return nil, xerrors.Errorf("failed to load tipset for mining base: %w", err) + } + + prev, err := sm.ChainStore().GetLatestBeaconEntry(ts) + if err != nil { + if os.Getenv("LOTUS_IGNORE_DRAND") != "_yes_" { + return nil, xerrors.Errorf("failed to get latest beacon entry: %w", err) + } + + prev = &types.BeaconEntry{} + } + + entries, err := beacon.BeaconEntriesForBlock(ctx, bcs, round, ts.Height(), *prev) + if err != nil { + return nil, err + } + + rbase := *prev + if len(entries) > 0 { + rbase = entries[len(entries)-1] + } + + lbts, lbst, err := GetLookbackTipSetForRound(ctx, sm, ts, round) + if err != nil { + return nil, xerrors.Errorf("getting lookback miner actor state: %w", err) + } + + act, err := sm.LoadActorRaw(ctx, maddr, lbst) + if xerrors.Is(err, types.ErrActorNotFound) { + _, err := sm.LoadActor(ctx, maddr, ts) + if err != nil { + return nil, xerrors.Errorf("loading miner in current state: %w", err) + } + + return nil, nil + } + if err != nil { + return nil, xerrors.Errorf("failed to load miner actor: %w", err) + } + + mas, err := miner.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, xerrors.Errorf("failed to load miner actor state: %w", err) + } + + buf := new(bytes.Buffer) + if err := maddr.MarshalCBOR(buf); err != nil { + return nil, xerrors.Errorf("failed to marshal miner address: %w", err) + } + + prand, err := store.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, round, buf.Bytes()) + if err != nil { + return nil, xerrors.Errorf("failed to get randomness for winning post: %w", err) + } + + nv := sm.GetNtwkVersion(ctx, ts.Height()) + + sectors, err := GetSectorsForWinningPoSt(ctx, nv, pv, sm, lbst, maddr, prand) + if err != nil { + return nil, xerrors.Errorf("getting winning post proving set: %w", err) + } + + if len(sectors) == 0 { + return nil, nil + } + + mpow, tpow, _, err := GetPowerRaw(ctx, sm, lbst, maddr) + if err != nil { + return nil, xerrors.Errorf("failed to get power: %w", err) + } + + info, err := mas.Info() + if err != nil { + return nil, err + } + + worker, err := sm.ResolveToKeyAddress(ctx, info.Worker, ts) + if err != nil { + return nil, xerrors.Errorf("resolving worker address: %w", err) + } + + // TODO: Not ideal performance...This method reloads miner and power state (already looked up here and in GetPowerRaw) + eligible, err := MinerEligibleToMine(ctx, sm, maddr, ts, lbts) + if err != nil { + return nil, xerrors.Errorf("determining miner eligibility: %w", err) + } + + return &api.MiningBaseInfo{ + MinerPower: mpow.QualityAdjPower, + NetworkPower: tpow.QualityAdjPower, + Sectors: sectors, + WorkerKey: worker, + SectorSize: info.SectorSize, + PrevBeaconEntry: *prev, + BeaconEntries: entries, + EligibleForMining: eligible, + }, nil +} + +func minerHasMinPower(ctx context.Context, sm *StateManager, addr address.Address, ts *types.TipSet) (bool, error) { + pact, err := sm.LoadActor(ctx, power.Address, ts) + if err != nil { + return false, xerrors.Errorf("loading power actor state: %w", err) + } + + ps, err := power.Load(sm.cs.ActorStore(ctx), pact) + if err != nil { + return false, err + } + + return ps.MinerNominalPowerMeetsConsensusMinimum(addr) +} + +func MinerEligibleToMine(ctx context.Context, sm *StateManager, addr address.Address, baseTs *types.TipSet, lookbackTs *types.TipSet) (bool, error) { + hmp, err := minerHasMinPower(ctx, sm, addr, lookbackTs) + + // TODO: We're blurring the lines between a "runtime network version" and a "Lotus upgrade epoch", is that unavoidable? + if sm.GetNtwkVersion(ctx, baseTs.Height()) <= network.Version3 { + return hmp, err + } + + if err != nil { + return false, err + } + + if !hmp { + return false, nil + } + + // Post actors v2, also check MinerEligibleForElection with base ts + + pact, err := sm.LoadActor(ctx, power.Address, baseTs) + if err != nil { + return false, xerrors.Errorf("loading power actor state: %w", err) + } + + pstate, err := power.Load(sm.cs.ActorStore(ctx), pact) + if err != nil { + return false, err + } + + mact, err := sm.LoadActor(ctx, addr, baseTs) + if err != nil { + return false, xerrors.Errorf("loading miner actor state: %w", err) + } + + mstate, err := miner.Load(sm.cs.ActorStore(ctx), mact) + if err != nil { + return false, err + } + + // Non-empty power claim. + if claim, found, err := pstate.MinerPower(addr); err != nil { + return false, err + } else if !found { + return false, err + } else if claim.QualityAdjPower.LessThanEqual(big.Zero()) { + return false, err + } + + // No fee debt. + if debt, err := mstate.FeeDebt(); err != nil { + return false, err + } else if !debt.IsZero() { + return false, err + } + + // No active consensus faults. + if mInfo, err := mstate.Info(); err != nil { + return false, err + } else if baseTs.Height() <= mInfo.ConsensusFaultElapsed { + return false, nil + } + + return true, nil +} + +func (sm *StateManager) GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error) { + st, err := sm.ParentState(ts) + if err != nil { + return nil, nil, err + } + + act, err := st.GetActor(addr) + if err != nil { + return nil, nil, err + } + + actState, err := paych.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, nil, err + } + return act, actState, nil +} + +func (sm *StateManager) GetMarketState(ctx context.Context, ts *types.TipSet) (market.State, error) { + st, err := sm.ParentState(ts) + if err != nil { + return nil, err + } + + act, err := st.GetActor(market.Address) + if err != nil { + return nil, err + } + + actState, err := market.Load(sm.cs.ActorStore(ctx), act) + if err != nil { + return nil, err + } + return actState, nil +} + +func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (api.MarketBalance, error) { + mstate, err := sm.GetMarketState(ctx, ts) + if err != nil { + return api.MarketBalance{}, err + } + + addr, err = sm.LookupID(ctx, addr, ts) + if err != nil { + return api.MarketBalance{}, err + } + + var out api.MarketBalance + + et, err := mstate.EscrowTable() + if err != nil { + return api.MarketBalance{}, err + } + out.Escrow, err = et.Get(addr) + if err != nil { + return api.MarketBalance{}, xerrors.Errorf("getting escrow balance: %w", err) + } + + lt, err := mstate.LockedTable() + if err != nil { + return api.MarketBalance{}, err + } + out.Locked, err = lt.Get(addr) + if err != nil { + return api.MarketBalance{}, xerrors.Errorf("getting locked balance: %w", err) + } + + return out, nil +} + +var _ StateManagerAPI = (*StateManager)(nil) diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index f54904aaa..a4d78f997 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -1,52 +1,124 @@ package stmgr import ( - "bytes" "context" "fmt" - "os" "reflect" "runtime" "strings" - exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported" - - "github.com/filecoin-project/go-state-types/big" - - "github.com/filecoin-project/go-state-types/network" - - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-state-types/abi" - "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/rt" exported0 "github.com/filecoin-project/specs-actors/actors/builtin/exported" exported2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/exported" exported3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/exported" exported4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported" + exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/actors/builtin" init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" - "github.com/filecoin-project/lotus/chain/actors/builtin/market" - "github.com/filecoin-project/lotus/chain/actors/builtin/miner" - "github.com/filecoin-project/lotus/chain/actors/builtin/paych" - "github.com/filecoin-project/lotus/chain/actors/builtin/power" "github.com/filecoin-project/lotus/chain/actors/policy" - "github.com/filecoin-project/lotus/chain/beacon" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/vm" - "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/node/modules/dtypes" ) +type MethodMeta struct { + Name string + + Params reflect.Type + Ret reflect.Type +} + +var MethodsMap = map[cid.Cid]map[abi.MethodNum]MethodMeta{} + +func init() { + // TODO: combine with the runtime actor registry. + var actors []rt.VMActor + actors = append(actors, exported0.BuiltinActors()...) + actors = append(actors, exported2.BuiltinActors()...) + actors = append(actors, exported3.BuiltinActors()...) + actors = append(actors, exported4.BuiltinActors()...) + actors = append(actors, exported5.BuiltinActors()...) + + for _, actor := range actors { + exports := actor.Exports() + methods := make(map[abi.MethodNum]MethodMeta, len(exports)) + + // Explicitly add send, it's special. + methods[builtin.MethodSend] = MethodMeta{ + Name: "Send", + Params: reflect.TypeOf(new(abi.EmptyValue)), + Ret: reflect.TypeOf(new(abi.EmptyValue)), + } + + // Iterate over exported methods. Some of these _may_ be nil and + // must be skipped. + for number, export := range exports { + if export == nil { + continue + } + + ev := reflect.ValueOf(export) + et := ev.Type() + + // Extract the method names using reflection. These + // method names always match the field names in the + // `builtin.Method*` structs (tested in the specs-actors + // tests). + fnName := runtime.FuncForPC(ev.Pointer()).Name() + fnName = strings.TrimSuffix(fnName[strings.LastIndexByte(fnName, '.')+1:], "-fm") + + switch abi.MethodNum(number) { + case builtin.MethodSend: + panic("method 0 is reserved for Send") + case builtin.MethodConstructor: + if fnName != "Constructor" { + panic("method 1 is reserved for Constructor") + } + } + + methods[abi.MethodNum(number)] = MethodMeta{ + Name: fnName, + Params: et.In(1), + Ret: et.Out(0), + } + } + MethodsMap[actor.Code()] = methods + } +} + +func GetReturnType(ctx context.Context, sm *StateManager, to address.Address, method abi.MethodNum, ts *types.TipSet) (cbg.CBORUnmarshaler, error) { + act, err := sm.LoadActor(ctx, to, ts) + if err != nil { + return nil, xerrors.Errorf("(get sset) failed to load miner actor: %w", err) + } + + m, found := MethodsMap[act.Code][method] + if !found { + return nil, fmt.Errorf("unknown method %d for actor %s", method, act.Code) + } + return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil +} + +func GetParamType(actCode cid.Cid, method abi.MethodNum) (cbg.CBORUnmarshaler, error) { + m, found := MethodsMap[actCode][method] + if !found { + return nil, fmt.Errorf("unknown method %d for actor %s", method, actCode) + } + return reflect.New(m.Params.Elem()).Interface().(cbg.CBORUnmarshaler), nil +} + func GetNetworkName(ctx context.Context, sm *StateManager, st cid.Cid) (dtypes.NetworkName, error) { act, err := sm.LoadActorRaw(ctx, init_.Address, st) if err != nil { @@ -60,277 +132,6 @@ func GetNetworkName(ctx context.Context, sm *StateManager, st cid.Cid) (dtypes.N return ias.NetworkName() } -func GetMinerWorkerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (address.Address, error) { - state, err := sm.StateTree(st) - if err != nil { - return address.Undef, xerrors.Errorf("(get sset) failed to load state tree: %w", err) - } - act, err := state.GetActor(maddr) - if err != nil { - return address.Undef, xerrors.Errorf("(get sset) failed to load miner actor: %w", err) - } - mas, err := miner.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return address.Undef, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err) - } - - info, err := mas.Info() - if err != nil { - return address.Undef, xerrors.Errorf("failed to load actor info: %w", err) - } - - return vm.ResolveToKeyAddr(state, sm.cs.ActorStore(ctx), info.Worker) -} - -func GetPower(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (power.Claim, power.Claim, bool, error) { - return GetPowerRaw(ctx, sm, ts.ParentState(), maddr) -} - -func GetPowerRaw(ctx context.Context, sm *StateManager, st cid.Cid, maddr address.Address) (power.Claim, power.Claim, bool, error) { - act, err := sm.LoadActorRaw(ctx, power.Address, st) - if err != nil { - return power.Claim{}, power.Claim{}, false, xerrors.Errorf("(get sset) failed to load power actor state: %w", err) - } - - pas, err := power.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return power.Claim{}, power.Claim{}, false, err - } - - tpow, err := pas.TotalPower() - if err != nil { - return power.Claim{}, power.Claim{}, false, err - } - - var mpow power.Claim - var minpow bool - if maddr != address.Undef { - var found bool - mpow, found, err = pas.MinerPower(maddr) - if err != nil || !found { - return power.Claim{}, tpow, false, err - } - - minpow, err = pas.MinerNominalPowerMeetsConsensusMinimum(maddr) - if err != nil { - return power.Claim{}, power.Claim{}, false, err - } - } - - return mpow, tpow, minpow, nil -} - -func PreCommitInfo(ctx context.Context, sm *StateManager, maddr address.Address, sid abi.SectorNumber, ts *types.TipSet) (*miner.SectorPreCommitOnChainInfo, error) { - act, err := sm.LoadActor(ctx, maddr, ts) - if err != nil { - return nil, xerrors.Errorf("(get sset) failed to load miner actor: %w", err) - } - - mas, err := miner.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err) - } - - return mas.GetPrecommittedSector(sid) -} - -func MinerSectorInfo(ctx context.Context, sm *StateManager, maddr address.Address, sid abi.SectorNumber, ts *types.TipSet) (*miner.SectorOnChainInfo, error) { - act, err := sm.LoadActor(ctx, maddr, ts) - if err != nil { - return nil, xerrors.Errorf("(get sset) failed to load miner actor: %w", err) - } - - mas, err := miner.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, xerrors.Errorf("(get sset) failed to load miner actor state: %w", err) - } - - return mas.GetSector(sid) -} - -func GetSectorsForWinningPoSt(ctx context.Context, nv network.Version, pv ffiwrapper.Verifier, sm *StateManager, st cid.Cid, maddr address.Address, rand abi.PoStRandomness) ([]builtin.SectorInfo, error) { - act, err := sm.LoadActorRaw(ctx, maddr, st) - if err != nil { - return nil, xerrors.Errorf("failed to load miner actor: %w", err) - } - - mas, err := miner.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, xerrors.Errorf("failed to load miner actor state: %w", err) - } - - var provingSectors bitfield.BitField - if nv < network.Version7 { - allSectors, err := miner.AllPartSectors(mas, miner.Partition.AllSectors) - if err != nil { - return nil, xerrors.Errorf("get all sectors: %w", err) - } - - faultySectors, err := miner.AllPartSectors(mas, miner.Partition.FaultySectors) - if err != nil { - return nil, xerrors.Errorf("get faulty sectors: %w", err) - } - - provingSectors, err = bitfield.SubtractBitField(allSectors, faultySectors) - if err != nil { - return nil, xerrors.Errorf("calc proving sectors: %w", err) - } - } else { - provingSectors, err = miner.AllPartSectors(mas, miner.Partition.ActiveSectors) - if err != nil { - return nil, xerrors.Errorf("get active sectors sectors: %w", err) - } - } - - numProvSect, err := provingSectors.Count() - if err != nil { - return nil, xerrors.Errorf("failed to count bits: %w", err) - } - - // TODO(review): is this right? feels fishy to me - if numProvSect == 0 { - return nil, nil - } - - info, err := mas.Info() - if err != nil { - return nil, xerrors.Errorf("getting miner info: %w", err) - } - - mid, err := address.IDFromAddress(maddr) - if err != nil { - return nil, xerrors.Errorf("getting miner ID: %w", err) - } - - proofType, err := miner.WinningPoStProofTypeFromWindowPoStProofType(nv, info.WindowPoStProofType) - if err != nil { - return nil, xerrors.Errorf("determining winning post proof type: %w", err) - } - - ids, err := pv.GenerateWinningPoStSectorChallenge(ctx, proofType, abi.ActorID(mid), rand, numProvSect) - if err != nil { - return nil, xerrors.Errorf("generating winning post challenges: %w", err) - } - - iter, err := provingSectors.BitIterator() - if err != nil { - return nil, xerrors.Errorf("iterating over proving sectors: %w", err) - } - - // Select winning sectors by _index_ in the all-sectors bitfield. - selectedSectors := bitfield.New() - prev := uint64(0) - for _, n := range ids { - sno, err := iter.Nth(n - prev) - if err != nil { - return nil, xerrors.Errorf("iterating over proving sectors: %w", err) - } - selectedSectors.Set(sno) - prev = n - } - - sectors, err := mas.LoadSectors(&selectedSectors) - if err != nil { - return nil, xerrors.Errorf("loading proving sectors: %w", err) - } - - out := make([]builtin.SectorInfo, len(sectors)) - for i, sinfo := range sectors { - out[i] = builtin.SectorInfo{ - SealProof: sinfo.SealProof, - SectorNumber: sinfo.SectorNumber, - SealedCID: sinfo.SealedCID, - } - } - - return out, nil -} - -func GetMinerSlashed(ctx context.Context, sm *StateManager, ts *types.TipSet, maddr address.Address) (bool, error) { - act, err := sm.LoadActor(ctx, power.Address, ts) - if err != nil { - return false, xerrors.Errorf("failed to load power actor: %w", err) - } - - spas, err := power.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return false, xerrors.Errorf("failed to load power actor state: %w", err) - } - - _, ok, err := spas.MinerPower(maddr) - if err != nil { - return false, xerrors.Errorf("getting miner power: %w", err) - } - - if !ok { - return true, nil - } - - return false, nil -} - -func GetStorageDeal(ctx context.Context, sm *StateManager, dealID abi.DealID, ts *types.TipSet) (*api.MarketDeal, error) { - act, err := sm.LoadActor(ctx, market.Address, ts) - if err != nil { - return nil, xerrors.Errorf("failed to load market actor: %w", err) - } - - state, err := market.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, xerrors.Errorf("failed to load market actor state: %w", err) - } - - proposals, err := state.Proposals() - if err != nil { - return nil, err - } - - proposal, found, err := proposals.Get(dealID) - - if err != nil { - return nil, err - } else if !found { - return nil, xerrors.Errorf( - "deal %d not found "+ - "- deal may not have completed sealing before deal proposal "+ - "start epoch, or deal may have been slashed", - dealID) - } - - states, err := state.States() - if err != nil { - return nil, err - } - - st, found, err := states.Get(dealID) - if err != nil { - return nil, err - } - - if !found { - st = market.EmptyDealState() - } - - return &api.MarketDeal{ - Proposal: *proposal, - State: *st, - }, nil -} - -func ListMinerActors(ctx context.Context, sm *StateManager, ts *types.TipSet) ([]address.Address, error) { - act, err := sm.LoadActor(ctx, power.Address, ts) - if err != nil { - return nil, xerrors.Errorf("failed to load power actor: %w", err) - } - - powState, err := power.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, xerrors.Errorf("failed to load power actor state: %w", err) - } - - return powState.ListAllMiners() -} - func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, msgs []*types.Message, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) { if ts == nil { ts = sm.cs.GetHeaviestTipSet() @@ -434,272 +235,6 @@ func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types. return lbts, nextTs.ParentState(), nil } -func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcs beacon.Schedule, tsk types.TipSetKey, round abi.ChainEpoch, maddr address.Address, pv ffiwrapper.Verifier) (*api.MiningBaseInfo, error) { - ts, err := sm.ChainStore().LoadTipSet(tsk) - if err != nil { - return nil, xerrors.Errorf("failed to load tipset for mining base: %w", err) - } - - prev, err := sm.ChainStore().GetLatestBeaconEntry(ts) - if err != nil { - if os.Getenv("LOTUS_IGNORE_DRAND") != "_yes_" { - return nil, xerrors.Errorf("failed to get latest beacon entry: %w", err) - } - - prev = &types.BeaconEntry{} - } - - entries, err := beacon.BeaconEntriesForBlock(ctx, bcs, round, ts.Height(), *prev) - if err != nil { - return nil, err - } - - rbase := *prev - if len(entries) > 0 { - rbase = entries[len(entries)-1] - } - - lbts, lbst, err := GetLookbackTipSetForRound(ctx, sm, ts, round) - if err != nil { - return nil, xerrors.Errorf("getting lookback miner actor state: %w", err) - } - - act, err := sm.LoadActorRaw(ctx, maddr, lbst) - if xerrors.Is(err, types.ErrActorNotFound) { - _, err := sm.LoadActor(ctx, maddr, ts) - if err != nil { - return nil, xerrors.Errorf("loading miner in current state: %w", err) - } - - return nil, nil - } - if err != nil { - return nil, xerrors.Errorf("failed to load miner actor: %w", err) - } - - mas, err := miner.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, xerrors.Errorf("failed to load miner actor state: %w", err) - } - - buf := new(bytes.Buffer) - if err := maddr.MarshalCBOR(buf); err != nil { - return nil, xerrors.Errorf("failed to marshal miner address: %w", err) - } - - prand, err := store.DrawRandomness(rbase.Data, crypto.DomainSeparationTag_WinningPoStChallengeSeed, round, buf.Bytes()) - if err != nil { - return nil, xerrors.Errorf("failed to get randomness for winning post: %w", err) - } - - nv := sm.GetNtwkVersion(ctx, ts.Height()) - - sectors, err := GetSectorsForWinningPoSt(ctx, nv, pv, sm, lbst, maddr, prand) - if err != nil { - return nil, xerrors.Errorf("getting winning post proving set: %w", err) - } - - if len(sectors) == 0 { - return nil, nil - } - - mpow, tpow, _, err := GetPowerRaw(ctx, sm, lbst, maddr) - if err != nil { - return nil, xerrors.Errorf("failed to get power: %w", err) - } - - info, err := mas.Info() - if err != nil { - return nil, err - } - - worker, err := sm.ResolveToKeyAddress(ctx, info.Worker, ts) - if err != nil { - return nil, xerrors.Errorf("resolving worker address: %w", err) - } - - // TODO: Not ideal performance...This method reloads miner and power state (already looked up here and in GetPowerRaw) - eligible, err := MinerEligibleToMine(ctx, sm, maddr, ts, lbts) - if err != nil { - return nil, xerrors.Errorf("determining miner eligibility: %w", err) - } - - return &api.MiningBaseInfo{ - MinerPower: mpow.QualityAdjPower, - NetworkPower: tpow.QualityAdjPower, - Sectors: sectors, - WorkerKey: worker, - SectorSize: info.SectorSize, - PrevBeaconEntry: *prev, - BeaconEntries: entries, - EligibleForMining: eligible, - }, nil -} - -type MethodMeta struct { - Name string - - Params reflect.Type - Ret reflect.Type -} - -var MethodsMap = map[cid.Cid]map[abi.MethodNum]MethodMeta{} - -func init() { - // TODO: combine with the runtime actor registry. - var actors []rt.VMActor - actors = append(actors, exported0.BuiltinActors()...) - actors = append(actors, exported2.BuiltinActors()...) - actors = append(actors, exported3.BuiltinActors()...) - actors = append(actors, exported4.BuiltinActors()...) - actors = append(actors, exported5.BuiltinActors()...) - - for _, actor := range actors { - exports := actor.Exports() - methods := make(map[abi.MethodNum]MethodMeta, len(exports)) - - // Explicitly add send, it's special. - methods[builtin.MethodSend] = MethodMeta{ - Name: "Send", - Params: reflect.TypeOf(new(abi.EmptyValue)), - Ret: reflect.TypeOf(new(abi.EmptyValue)), - } - - // Iterate over exported methods. Some of these _may_ be nil and - // must be skipped. - for number, export := range exports { - if export == nil { - continue - } - - ev := reflect.ValueOf(export) - et := ev.Type() - - // Extract the method names using reflection. These - // method names always match the field names in the - // `builtin.Method*` structs (tested in the specs-actors - // tests). - fnName := runtime.FuncForPC(ev.Pointer()).Name() - fnName = strings.TrimSuffix(fnName[strings.LastIndexByte(fnName, '.')+1:], "-fm") - - switch abi.MethodNum(number) { - case builtin.MethodSend: - panic("method 0 is reserved for Send") - case builtin.MethodConstructor: - if fnName != "Constructor" { - panic("method 1 is reserved for Constructor") - } - } - - methods[abi.MethodNum(number)] = MethodMeta{ - Name: fnName, - Params: et.In(1), - Ret: et.Out(0), - } - } - MethodsMap[actor.Code()] = methods - } -} - -func GetReturnType(ctx context.Context, sm *StateManager, to address.Address, method abi.MethodNum, ts *types.TipSet) (cbg.CBORUnmarshaler, error) { - act, err := sm.LoadActor(ctx, to, ts) - if err != nil { - return nil, xerrors.Errorf("(get sset) failed to load miner actor: %w", err) - } - - m, found := MethodsMap[act.Code][method] - if !found { - return nil, fmt.Errorf("unknown method %d for actor %s", method, act.Code) - } - return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil -} - -func GetParamType(actCode cid.Cid, method abi.MethodNum) (cbg.CBORUnmarshaler, error) { - m, found := MethodsMap[actCode][method] - if !found { - return nil, fmt.Errorf("unknown method %d for actor %s", method, actCode) - } - return reflect.New(m.Params.Elem()).Interface().(cbg.CBORUnmarshaler), nil -} - -func minerHasMinPower(ctx context.Context, sm *StateManager, addr address.Address, ts *types.TipSet) (bool, error) { - pact, err := sm.LoadActor(ctx, power.Address, ts) - if err != nil { - return false, xerrors.Errorf("loading power actor state: %w", err) - } - - ps, err := power.Load(sm.cs.ActorStore(ctx), pact) - if err != nil { - return false, err - } - - return ps.MinerNominalPowerMeetsConsensusMinimum(addr) -} - -func MinerEligibleToMine(ctx context.Context, sm *StateManager, addr address.Address, baseTs *types.TipSet, lookbackTs *types.TipSet) (bool, error) { - hmp, err := minerHasMinPower(ctx, sm, addr, lookbackTs) - - // TODO: We're blurring the lines between a "runtime network version" and a "Lotus upgrade epoch", is that unavoidable? - if sm.GetNtwkVersion(ctx, baseTs.Height()) <= network.Version3 { - return hmp, err - } - - if err != nil { - return false, err - } - - if !hmp { - return false, nil - } - - // Post actors v2, also check MinerEligibleForElection with base ts - - pact, err := sm.LoadActor(ctx, power.Address, baseTs) - if err != nil { - return false, xerrors.Errorf("loading power actor state: %w", err) - } - - pstate, err := power.Load(sm.cs.ActorStore(ctx), pact) - if err != nil { - return false, err - } - - mact, err := sm.LoadActor(ctx, addr, baseTs) - if err != nil { - return false, xerrors.Errorf("loading miner actor state: %w", err) - } - - mstate, err := miner.Load(sm.cs.ActorStore(ctx), mact) - if err != nil { - return false, err - } - - // Non-empty power claim. - if claim, found, err := pstate.MinerPower(addr); err != nil { - return false, err - } else if !found { - return false, err - } else if claim.QualityAdjPower.LessThanEqual(big.Zero()) { - return false, err - } - - // No fee debt. - if debt, err := mstate.FeeDebt(); err != nil { - return false, err - } else if !debt.IsZero() { - return false, err - } - - // No active consensus faults. - if mInfo, err := mstate.Info(); err != nil { - return false, err - } else if baseTs.Height() <= mInfo.ConsensusFaultElapsed { - return false, nil - } - - return true, nil -} - func CheckTotalFIL(ctx context.Context, cs *store.ChainStore, ts *types.TipSet) (abi.TokenAmount, error) { str, err := state.LoadStateTree(cs.ActorStore(ctx), ts.ParentState()) if err != nil { @@ -748,75 +283,3 @@ func (sm *StateManager) ListAllActors(ctx context.Context, ts *types.TipSet) ([] return out, nil } - -func (sm *StateManager) GetPaychState(ctx context.Context, addr address.Address, ts *types.TipSet) (*types.Actor, paych.State, error) { - st, err := sm.ParentState(ts) - if err != nil { - return nil, nil, err - } - - act, err := st.GetActor(addr) - if err != nil { - return nil, nil, err - } - - actState, err := paych.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, nil, err - } - return act, actState, nil -} - -func (sm *StateManager) GetMarketState(ctx context.Context, ts *types.TipSet) (market.State, error) { - st, err := sm.ParentState(ts) - if err != nil { - return nil, err - } - - act, err := st.GetActor(market.Address) - if err != nil { - return nil, err - } - - actState, err := market.Load(sm.cs.ActorStore(ctx), act) - if err != nil { - return nil, err - } - return actState, nil -} - -func (sm *StateManager) MarketBalance(ctx context.Context, addr address.Address, ts *types.TipSet) (api.MarketBalance, error) { - mstate, err := sm.GetMarketState(ctx, ts) - if err != nil { - return api.MarketBalance{}, err - } - - addr, err = sm.LookupID(ctx, addr, ts) - if err != nil { - return api.MarketBalance{}, err - } - - var out api.MarketBalance - - et, err := mstate.EscrowTable() - if err != nil { - return api.MarketBalance{}, err - } - out.Escrow, err = et.Get(addr) - if err != nil { - return api.MarketBalance{}, xerrors.Errorf("getting escrow balance: %w", err) - } - - lt, err := mstate.LockedTable() - if err != nil { - return api.MarketBalance{}, err - } - out.Locked, err = lt.Get(addr) - if err != nil { - return api.MarketBalance{}, xerrors.Errorf("getting locked balance: %w", err) - } - - return out, nil -} - -var _ StateManagerAPI = (*StateManager)(nil) From ae63a4b33bb731a9d1af312a5cbac96cd6155ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Jul 2021 15:49:01 +0200 Subject: [PATCH 7/9] fix lotus-sim build --- chain/stmgr/stmgr.go | 4 ++++ cmd/lotus-sim/simulation/blockbuilder/blockbuilder.go | 2 +- cmd/lotus-sim/simulation/simulation.go | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 09b8284dd..b76dd0faf 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -354,3 +354,7 @@ func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoc } return sm.latestVersion } + +func (sm *StateManager) VMSys() vm.SyscallBuilder { + return sm.syscalls +} diff --git a/cmd/lotus-sim/simulation/blockbuilder/blockbuilder.go b/cmd/lotus-sim/simulation/blockbuilder/blockbuilder.go index 2ffc0bf14..36b9cee75 100644 --- a/cmd/lotus-sim/simulation/blockbuilder/blockbuilder.go +++ b/cmd/lotus-sim/simulation/blockbuilder/blockbuilder.go @@ -83,7 +83,7 @@ func NewBlockBuilder(ctx context.Context, logger *zap.SugaredLogger, sm *stmgr.S Epoch: parentTs.Height() + 1, Rand: r, Bstore: sm.ChainStore().StateBlockstore(), - Syscalls: sm.ChainStore().VMSys(), + Syscalls: sm.VMSys(), CircSupplyCalc: sm.GetVMCirculatingSupply, NtwkVersion: sm.GetNtwkVersion, BaseFee: abi.NewTokenAmount(0), diff --git a/cmd/lotus-sim/simulation/simulation.go b/cmd/lotus-sim/simulation/simulation.go index d91d30eda..83b45f942 100644 --- a/cmd/lotus-sim/simulation/simulation.go +++ b/cmd/lotus-sim/simulation/simulation.go @@ -18,6 +18,8 @@ import ( "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" + "github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/mock" "github.com/filecoin-project/lotus/cmd/lotus-sim/simulation/stages" ) @@ -198,7 +200,7 @@ func (sim *Simulation) SetUpgradeHeight(nv network.Version, epoch abi.ChainEpoch if err != nil { return err } - sm, err := stmgr.NewStateManagerWithUpgradeSchedule(sim.Node.Chainstore, newUpgradeSchedule) + sm, err := stmgr.NewStateManagerWithUpgradeSchedule(sim.Node.Chainstore, vm.Syscalls(mock.Verifier), newUpgradeSchedule) if err != nil { return err } From 05a3710288daf867f712a342d7fc7ae8266ef58d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Jul 2021 15:53:47 +0200 Subject: [PATCH 8/9] chainstore: Fix test build --- chain/stmgr/forks_test.go | 6 +++--- chain/store/store_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/chain/stmgr/forks_test.go b/chain/stmgr/forks_test.go index 9caeee51f..0df6ce397 100644 --- a/chain/stmgr/forks_test.go +++ b/chain/stmgr/forks_test.go @@ -121,7 +121,7 @@ func TestForkHeightTriggers(t *testing.T) { } sm, err := NewStateManagerWithUpgradeSchedule( - cg.ChainStore(), UpgradeSchedule{{ + cg.ChainStore(), cg.StateManager().VMSys(), UpgradeSchedule{{ Network: network.Version1, Height: testForkHeight, Migration: func(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor, @@ -250,7 +250,7 @@ func TestForkRefuseCall(t *testing.T) { } sm, err := NewStateManagerWithUpgradeSchedule( - cg.ChainStore(), UpgradeSchedule{{ + cg.ChainStore(), cg.StateManager().VMSys(), UpgradeSchedule{{ Network: network.Version1, Expensive: true, Height: testForkHeight, @@ -365,7 +365,7 @@ func TestForkPreMigration(t *testing.T) { counter := make(chan struct{}, 10) sm, err := NewStateManagerWithUpgradeSchedule( - cg.ChainStore(), UpgradeSchedule{{ + cg.ChainStore(), cg.StateManager().VMSys(), UpgradeSchedule{{ Network: network.Version1, Height: testForkHeight, Migration: func(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor, diff --git a/chain/store/store_test.go b/chain/store/store_test.go index 898d590d0..2db2f061b 100644 --- a/chain/store/store_test.go +++ b/chain/store/store_test.go @@ -157,7 +157,7 @@ func TestChainExportImportFull(t *testing.T) { t.Fatal("imported chain differed from exported chain") } - sm := stmgr.NewStateManager(cs) + sm := stmgr.NewStateManager(cs, nil) for i := 0; i < 100; i++ { ts, err := cs.GetTipsetByHeight(context.TODO(), abi.ChainEpoch(i), nil, false) if err != nil { From c57c20c6e43a1e86f650a2ddd600dc4b0589afad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 27 Jul 2021 15:58:18 +0200 Subject: [PATCH 9/9] fix lint --- chain/stmgr/execute.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/stmgr/execute.go b/chain/stmgr/execute.go index de7ca2894..3191a45db 100644 --- a/chain/stmgr/execute.go +++ b/chain/stmgr/execute.go @@ -3,13 +3,13 @@ package stmgr import ( "context" "fmt" + "sync/atomic" "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" "go.opencensus.io/stats" "go.opencensus.io/trace" "golang.org/x/xerrors" - "sync/atomic" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big"