Merge pull request #6889 from filecoin-project/chore/cleanup-chain
Reduce entropy in the chain package
This commit is contained in:
commit
c31e5596e8
@ -233,7 +233,7 @@ func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeS
|
|||||||
return nil, xerrors.Errorf("make genesis block failed: %w", err)
|
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}
|
genfb := &types.FullBlock{Header: genb.Genesis}
|
||||||
gents := store.NewFullTipSet([]*types.FullBlock{genfb})
|
gents := store.NewFullTipSet([]*types.FullBlock{genfb})
|
||||||
@ -247,7 +247,7 @@ func NewGeneratorWithSectorsAndUpgradeSchedule(numSectors int, us stmgr.UpgradeS
|
|||||||
mgen[genesis2.MinerAddress(uint64(i))] = &wppProvider{}
|
mgen[genesis2.MinerAddress(uint64(i))] = &wppProvider{}
|
||||||
}
|
}
|
||||||
|
|
||||||
sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, us)
|
sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, sys, us)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("initing stmgr: %w", err)
|
return nil, xerrors.Errorf("initing stmgr: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -471,7 +471,7 @@ func createMultisigAccount(ctx context.Context, cst cbor.IpldStore, state *state
|
|||||||
return nil
|
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)
|
verifNeeds := make(map[address.Address]abi.PaddedPieceSize)
|
||||||
var sum abi.PaddedPieceSize
|
var sum abi.PaddedPieceSize
|
||||||
|
|
||||||
@ -480,7 +480,7 @@ func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot ci
|
|||||||
Epoch: 0,
|
Epoch: 0,
|
||||||
Rand: &fakeRand{},
|
Rand: &fakeRand{},
|
||||||
Bstore: cs.StateBlockstore(),
|
Bstore: cs.StateBlockstore(),
|
||||||
Syscalls: mkFakedSigSyscalls(cs.VMSys()),
|
Syscalls: mkFakedSigSyscalls(sys),
|
||||||
CircSupplyCalc: nil,
|
CircSupplyCalc: nil,
|
||||||
NtwkVersion: func(_ context.Context, _ abi.ChainEpoch) network.Version {
|
NtwkVersion: func(_ context.Context, _ abi.ChainEpoch) network.Version {
|
||||||
return nv
|
return nv
|
||||||
@ -559,15 +559,15 @@ func MakeGenesisBlock(ctx context.Context, j journal.Journal, bs bstore.Blocksto
|
|||||||
}
|
}
|
||||||
|
|
||||||
// temp chainstore
|
// temp chainstore
|
||||||
cs := store.NewChainStore(bs, bs, datastore.NewMapDatastore(), sys, j)
|
cs := store.NewChainStore(bs, bs, datastore.NewMapDatastore(), j)
|
||||||
|
|
||||||
// Verify PreSealed Data
|
// 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 {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to verify presealed data: %w", err)
|
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 {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("setup miners failed: %w", err)
|
return nil, xerrors.Errorf("setup miners failed: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -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
|
// 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())
|
cst := cbor.NewCborStore(cs.StateBlockstore())
|
||||||
av := actors.VersionForNetwork(nv)
|
av := actors.VersionForNetwork(nv)
|
||||||
@ -92,7 +92,7 @@ func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid
|
|||||||
Epoch: 0,
|
Epoch: 0,
|
||||||
Rand: &fakeRand{},
|
Rand: &fakeRand{},
|
||||||
Bstore: cs.StateBlockstore(),
|
Bstore: cs.StateBlockstore(),
|
||||||
Syscalls: mkFakedSigSyscalls(cs.VMSys()),
|
Syscalls: mkFakedSigSyscalls(sys),
|
||||||
CircSupplyCalc: csc,
|
CircSupplyCalc: csc,
|
||||||
NtwkVersion: func(_ context.Context, _ abi.ChainEpoch) network.Version {
|
NtwkVersion: func(_ context.Context, _ abi.ChainEpoch) network.Version {
|
||||||
return nv
|
return nv
|
||||||
|
551
chain/stmgr/actors.go
Normal file
551
chain/stmgr/actors.go
Normal file
@ -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)
|
@ -64,7 +64,7 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.
|
|||||||
Epoch: pheight + 1,
|
Epoch: pheight + 1,
|
||||||
Rand: store.NewChainRand(sm.cs, ts.Cids()),
|
Rand: store.NewChainRand(sm.cs, ts.Cids()),
|
||||||
Bstore: sm.cs.StateBlockstore(),
|
Bstore: sm.cs.StateBlockstore(),
|
||||||
Syscalls: sm.cs.VMSys(),
|
Syscalls: sm.syscalls,
|
||||||
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
||||||
NtwkVersion: sm.GetNtwkVersion,
|
NtwkVersion: sm.GetNtwkVersion,
|
||||||
BaseFee: types.NewInt(0),
|
BaseFee: types.NewInt(0),
|
||||||
@ -179,7 +179,7 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri
|
|||||||
Epoch: ts.Height() + 1,
|
Epoch: ts.Height() + 1,
|
||||||
Rand: r,
|
Rand: r,
|
||||||
Bstore: sm.cs.StateBlockstore(),
|
Bstore: sm.cs.StateBlockstore(),
|
||||||
Syscalls: sm.cs.VMSys(),
|
Syscalls: sm.syscalls,
|
||||||
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
||||||
NtwkVersion: sm.GetNtwkVersion,
|
NtwkVersion: sm.GetNtwkVersion,
|
||||||
BaseFee: ts.Blocks()[0].ParentBaseFee,
|
BaseFee: ts.Blocks()[0].ParentBaseFee,
|
||||||
|
326
chain/stmgr/execute.go
Normal file
326
chain/stmgr/execute.go
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
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"
|
||||||
|
|
||||||
|
"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.syscalls,
|
||||||
|
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)
|
||||||
|
}
|
1088
chain/stmgr/forks.go
1088
chain/stmgr/forks.go
File diff suppressed because it is too large
Load Diff
@ -121,7 +121,7 @@ func TestForkHeightTriggers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sm, err := NewStateManagerWithUpgradeSchedule(
|
sm, err := NewStateManagerWithUpgradeSchedule(
|
||||||
cg.ChainStore(), UpgradeSchedule{{
|
cg.ChainStore(), cg.StateManager().VMSys(), UpgradeSchedule{{
|
||||||
Network: network.Version1,
|
Network: network.Version1,
|
||||||
Height: testForkHeight,
|
Height: testForkHeight,
|
||||||
Migration: func(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor,
|
Migration: func(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor,
|
||||||
@ -250,7 +250,7 @@ func TestForkRefuseCall(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sm, err := NewStateManagerWithUpgradeSchedule(
|
sm, err := NewStateManagerWithUpgradeSchedule(
|
||||||
cg.ChainStore(), UpgradeSchedule{{
|
cg.ChainStore(), cg.StateManager().VMSys(), UpgradeSchedule{{
|
||||||
Network: network.Version1,
|
Network: network.Version1,
|
||||||
Expensive: true,
|
Expensive: true,
|
||||||
Height: testForkHeight,
|
Height: testForkHeight,
|
||||||
@ -365,7 +365,7 @@ func TestForkPreMigration(t *testing.T) {
|
|||||||
counter := make(chan struct{}, 10)
|
counter := make(chan struct{}, 10)
|
||||||
|
|
||||||
sm, err := NewStateManagerWithUpgradeSchedule(
|
sm, err := NewStateManagerWithUpgradeSchedule(
|
||||||
cg.ChainStore(), UpgradeSchedule{{
|
cg.ChainStore(), cg.StateManager().VMSys(), UpgradeSchedule{{
|
||||||
Network: network.Version1,
|
Network: network.Version1,
|
||||||
Height: testForkHeight,
|
Height: testForkHeight,
|
||||||
Migration: func(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor,
|
Migration: func(ctx context.Context, sm *StateManager, cache MigrationCache, cb ExecMonitor,
|
||||||
|
@ -31,6 +31,14 @@ func (sm *StateManager) ParentState(ts *types.TipSet) (*state.StateTree, error)
|
|||||||
return state, nil
|
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) {
|
func (sm *StateManager) StateTree(st cid.Cid) (*state.StateTree, error) {
|
||||||
cst := cbor.NewCborStore(sm.cs.StateBlockstore())
|
cst := cbor.NewCborStore(sm.cs.StateBlockstore())
|
||||||
state, err := state.LoadStateTree(cst, st)
|
state, err := state.LoadStateTree(cst, st)
|
||||||
|
279
chain/stmgr/searchwait.go
Normal file
279
chain/stmgr/searchwait.go
Normal file
@ -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
|
||||||
|
}
|
1156
chain/stmgr/stmgr.go
1156
chain/stmgr/stmgr.go
File diff suppressed because it is too large
Load Diff
473
chain/stmgr/supply.go
Normal file
473
chain/stmgr/supply.go
Normal file
@ -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
|
||||||
|
}
|
1094
chain/stmgr/upgrades.go
Normal file
1094
chain/stmgr/upgrades.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,540 +1,38 @@
|
|||||||
package stmgr
|
package stmgr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported"
|
"github.com/ipfs/go-cid"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-state-types/big"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/go-state-types/network"
|
|
||||||
|
|
||||||
cid "github.com/ipfs/go-cid"
|
|
||||||
cbg "github.com/whyrusleeping/cbor-gen"
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"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/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"
|
"github.com/filecoin-project/go-state-types/rt"
|
||||||
|
|
||||||
exported0 "github.com/filecoin-project/specs-actors/actors/builtin/exported"
|
exported0 "github.com/filecoin-project/specs-actors/actors/builtin/exported"
|
||||||
exported2 "github.com/filecoin-project/specs-actors/v2/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"
|
exported3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/exported"
|
||||||
exported4 "github.com/filecoin-project/specs-actors/v4/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/api"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init"
|
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/power"
|
|
||||||
"github.com/filecoin-project/lotus/chain/actors/policy"
|
"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/state"
|
||||||
"github.com/filecoin-project/lotus/chain/store"
|
"github.com/filecoin-project/lotus/chain/store"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
"github.com/filecoin-project/lotus/chain/vm"
|
"github.com/filecoin-project/lotus/chain/vm"
|
||||||
"github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper"
|
|
||||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetNetworkName(ctx context.Context, sm *StateManager, st cid.Cid) (dtypes.NetworkName, error) {
|
|
||||||
act, err := sm.LoadActorRaw(ctx, init_.Address, st)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
ias, err := init_.Load(sm.cs.ActorStore(ctx), act)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
base, trace, err := sm.ExecutionTrace(ctx, ts)
|
|
||||||
if err != nil {
|
|
||||||
return cid.Undef, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := ts.Height(); i < height; i++ {
|
|
||||||
// handle state forks
|
|
||||||
base, err = sm.handleStateForks(ctx, base, i, &InvocationTracer{trace: &trace}, ts)
|
|
||||||
if err != nil {
|
|
||||||
return cid.Undef, nil, xerrors.Errorf("error handling state forks: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: should we also run cron here?
|
|
||||||
}
|
|
||||||
|
|
||||||
r := store.NewChainRand(sm.cs, ts.Cids())
|
|
||||||
vmopt := &vm.VMOpts{
|
|
||||||
StateBase: base,
|
|
||||||
Epoch: height,
|
|
||||||
Rand: r,
|
|
||||||
Bstore: sm.cs.StateBlockstore(),
|
|
||||||
Syscalls: sm.cs.VMSys(),
|
|
||||||
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
|
||||||
NtwkVersion: sm.GetNtwkVersion,
|
|
||||||
BaseFee: ts.Blocks()[0].ParentBaseFee,
|
|
||||||
LookbackState: LookbackStateGetterForTipset(sm, ts),
|
|
||||||
}
|
|
||||||
vmi, err := sm.newVM(ctx, vmopt)
|
|
||||||
if err != nil {
|
|
||||||
return cid.Undef, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, msg := range msgs {
|
|
||||||
// TODO: Use the signed message length for secp messages
|
|
||||||
ret, err := vmi.ApplyMessage(ctx, msg)
|
|
||||||
if err != nil {
|
|
||||||
return cid.Undef, nil, xerrors.Errorf("applying message %s: %w", msg.Cid(), err)
|
|
||||||
}
|
|
||||||
if ret.ExitCode != 0 {
|
|
||||||
log.Infof("compute state apply message %d failed (exit: %d): %s", i, ret.ExitCode, ret.ActorErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
root, err := vmi.Flush(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return cid.Undef, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return root, trace, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func LookbackStateGetterForTipset(sm *StateManager, ts *types.TipSet) vm.LookbackStateGetter {
|
|
||||||
return func(ctx context.Context, round abi.ChainEpoch) (*state.StateTree, error) {
|
|
||||||
_, st, err := GetLookbackTipSetForRound(ctx, sm, ts, round)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return sm.StateTree(st)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types.TipSet, round abi.ChainEpoch) (*types.TipSet, cid.Cid, error) {
|
|
||||||
var lbr abi.ChainEpoch
|
|
||||||
lb := policy.GetWinningPoStSectorSetLookback(sm.GetNtwkVersion(ctx, round))
|
|
||||||
if round > lb {
|
|
||||||
lbr = round - lb
|
|
||||||
}
|
|
||||||
|
|
||||||
// more null blocks than our lookback
|
|
||||||
if lbr >= ts.Height() {
|
|
||||||
// This should never happen at this point, but may happen before
|
|
||||||
// network version 3 (where the lookback was only 10 blocks).
|
|
||||||
st, _, err := sm.TipSetState(ctx, ts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, cid.Undef, err
|
|
||||||
}
|
|
||||||
return ts, st, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the tipset after the lookback tipset, or the next non-null one.
|
|
||||||
nextTs, err := sm.ChainStore().GetTipsetByHeight(ctx, lbr+1, ts, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, cid.Undef, xerrors.Errorf("failed to get lookback tipset+1: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if lbr > nextTs.Height() {
|
|
||||||
return nil, cid.Undef, xerrors.Errorf("failed to find non-null tipset %s (%d) which is known to exist, found %s (%d)", ts.Key(), ts.Height(), nextTs.Key(), nextTs.Height())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
lbts, err := sm.ChainStore().GetTipSetFromKey(nextTs.Parents())
|
|
||||||
if err != nil {
|
|
||||||
return nil, cid.Undef, xerrors.Errorf("failed to resolve lookback tipset: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
type MethodMeta struct {
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
@ -621,86 +119,124 @@ func GetParamType(actCode cid.Cid, method abi.MethodNum) (cbg.CBORUnmarshaler, e
|
|||||||
return reflect.New(m.Params.Elem()).Interface().(cbg.CBORUnmarshaler), nil
|
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) {
|
func GetNetworkName(ctx context.Context, sm *StateManager, st cid.Cid) (dtypes.NetworkName, error) {
|
||||||
pact, err := sm.LoadActor(ctx, power.Address, ts)
|
act, err := sm.LoadActorRaw(ctx, init_.Address, st)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, xerrors.Errorf("loading power actor state: %w", err)
|
return "", err
|
||||||
|
}
|
||||||
|
ias, err := init_.Load(sm.cs.ActorStore(ctx), act)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
ps, err := power.Load(sm.cs.ActorStore(ctx), pact)
|
return ias.NetworkName()
|
||||||
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) {
|
func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, msgs []*types.Message, ts *types.TipSet) (cid.Cid, []*api.InvocResult, error) {
|
||||||
hmp, err := minerHasMinPower(ctx, sm, addr, lookbackTs)
|
if ts == nil {
|
||||||
|
ts = sm.cs.GetHeaviestTipSet()
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base, trace, err := sm.ExecutionTrace(ctx, ts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return cid.Undef, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hmp {
|
for i := ts.Height(); i < height; i++ {
|
||||||
return false, nil
|
// handle state forks
|
||||||
|
base, err = sm.handleStateForks(ctx, base, i, &InvocationTracer{trace: &trace}, ts)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, nil, xerrors.Errorf("error handling state forks: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: should we also run cron here?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post actors v2, also check MinerEligibleForElection with base ts
|
r := store.NewChainRand(sm.cs, ts.Cids())
|
||||||
|
vmopt := &vm.VMOpts{
|
||||||
pact, err := sm.LoadActor(ctx, power.Address, baseTs)
|
StateBase: base,
|
||||||
|
Epoch: height,
|
||||||
|
Rand: r,
|
||||||
|
Bstore: sm.cs.StateBlockstore(),
|
||||||
|
Syscalls: sm.syscalls,
|
||||||
|
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
||||||
|
NtwkVersion: sm.GetNtwkVersion,
|
||||||
|
BaseFee: ts.Blocks()[0].ParentBaseFee,
|
||||||
|
LookbackState: LookbackStateGetterForTipset(sm, ts),
|
||||||
|
}
|
||||||
|
vmi, err := sm.newVM(ctx, vmopt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, xerrors.Errorf("loading power actor state: %w", err)
|
return cid.Undef, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pstate, err := power.Load(sm.cs.ActorStore(ctx), pact)
|
for i, msg := range msgs {
|
||||||
|
// TODO: Use the signed message length for secp messages
|
||||||
|
ret, err := vmi.ApplyMessage(ctx, msg)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, nil, xerrors.Errorf("applying message %s: %w", msg.Cid(), err)
|
||||||
|
}
|
||||||
|
if ret.ExitCode != 0 {
|
||||||
|
log.Infof("compute state apply message %d failed (exit: %d): %s", i, ret.ExitCode, ret.ActorErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root, err := vmi.Flush(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return cid.Undef, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mact, err := sm.LoadActor(ctx, addr, baseTs)
|
return root, trace, nil
|
||||||
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, sm *StateManager, ts *types.TipSet) (abi.TokenAmount, error) {
|
func LookbackStateGetterForTipset(sm *StateManager, ts *types.TipSet) vm.LookbackStateGetter {
|
||||||
str, err := state.LoadStateTree(sm.ChainStore().ActorStore(ctx), ts.ParentState())
|
return func(ctx context.Context, round abi.ChainEpoch) (*state.StateTree, error) {
|
||||||
|
_, st, err := GetLookbackTipSetForRound(ctx, sm, ts, round)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sm.StateTree(st)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLookbackTipSetForRound(ctx context.Context, sm *StateManager, ts *types.TipSet, round abi.ChainEpoch) (*types.TipSet, cid.Cid, error) {
|
||||||
|
var lbr abi.ChainEpoch
|
||||||
|
lb := policy.GetWinningPoStSectorSetLookback(sm.GetNtwkVersion(ctx, round))
|
||||||
|
if round > lb {
|
||||||
|
lbr = round - lb
|
||||||
|
}
|
||||||
|
|
||||||
|
// more null blocks than our lookback
|
||||||
|
if lbr >= ts.Height() {
|
||||||
|
// This should never happen at this point, but may happen before
|
||||||
|
// network version 3 (where the lookback was only 10 blocks).
|
||||||
|
st, _, err := sm.TipSetState(ctx, ts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cid.Undef, err
|
||||||
|
}
|
||||||
|
return ts, st, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the tipset after the lookback tipset, or the next non-null one.
|
||||||
|
nextTs, err := sm.ChainStore().GetTipsetByHeight(ctx, lbr+1, ts, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cid.Undef, xerrors.Errorf("failed to get lookback tipset+1: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lbr > nextTs.Height() {
|
||||||
|
return nil, cid.Undef, xerrors.Errorf("failed to find non-null tipset %s (%d) which is known to exist, found %s (%d)", ts.Key(), ts.Height(), nextTs.Key(), nextTs.Height())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
lbts, err := sm.ChainStore().GetTipSetFromKey(nextTs.Parents())
|
||||||
|
if err != nil {
|
||||||
|
return nil, cid.Undef, xerrors.Errorf("failed to resolve lookback tipset: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lbts, nextTs.ParentState(), 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 {
|
if err != nil {
|
||||||
return abi.TokenAmount{}, err
|
return abi.TokenAmount{}, err
|
||||||
}
|
}
|
||||||
@ -729,3 +265,21 @@ func MakeMsgGasCost(msg *types.Message, ret *vm.ApplyRet) api.MsgGasCost {
|
|||||||
TotalCost: big.Sub(msg.RequiredFunds(), ret.GasCosts.Refund),
|
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
|
||||||
|
}
|
||||||
|
@ -31,7 +31,7 @@ func TestIndexSeeks(t *testing.T) {
|
|||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
nbs := blockstore.NewMemorySync()
|
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
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
_, err = cs.Import(bytes.NewReader(gencar))
|
_, err = cs.Import(bytes.NewReader(gencar))
|
||||||
|
303
chain/store/messages.go
Normal file
303
chain/store/messages.go
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
block "github.com/ipfs/go-block-format"
|
||||||
|
cbor "github.com/ipfs/go-ipld-cbor"
|
||||||
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||||
|
|
||||||
|
bstore "github.com/filecoin-project/lotus/blockstore"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain/state"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type storable interface {
|
||||||
|
ToStorageBlock() (block.Block, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PutMessage(bs bstore.Blockstore, m storable) (cid.Cid, error) {
|
||||||
|
b, err := m.ToStorageBlock()
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bs.Put(b); err != nil {
|
||||||
|
return cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Cid(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) PutMessage(m storable) (cid.Cid, error) {
|
||||||
|
return PutMessage(cs.chainBlockstore, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetCMessage(c cid.Cid) (types.ChainMsg, error) {
|
||||||
|
m, err := cs.GetMessage(c)
|
||||||
|
if err == nil {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
if err != bstore.ErrNotFound {
|
||||||
|
log.Warnf("GetCMessage: unexpected error getting unsigned message: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cs.GetSignedMessage(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetMessage(c cid.Cid) (*types.Message, error) {
|
||||||
|
var msg *types.Message
|
||||||
|
err := cs.chainLocalBlockstore.View(c, func(b []byte) (err error) {
|
||||||
|
msg, err = types.DecodeMessage(b)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return msg, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetSignedMessage(c cid.Cid) (*types.SignedMessage, error) {
|
||||||
|
var msg *types.SignedMessage
|
||||||
|
err := cs.chainLocalBlockstore.View(c, func(b []byte) (err error) {
|
||||||
|
msg, err = types.DecodeSignedMessage(b)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return msg, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) readAMTCids(root cid.Cid) ([]cid.Cid, error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
// block headers use adt0, for now.
|
||||||
|
a, err := blockadt.AsArray(cs.ActorStore(ctx), root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("amt load: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
cids []cid.Cid
|
||||||
|
cborCid cbg.CborCid
|
||||||
|
)
|
||||||
|
if err := a.ForEach(&cborCid, func(i int64) error {
|
||||||
|
c := cid.Cid(cborCid)
|
||||||
|
cids = append(cids, c)
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to traverse amt: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint64(len(cids)) != a.Length() {
|
||||||
|
return nil, xerrors.Errorf("found %d cids, expected %d", len(cids), a.Length())
|
||||||
|
}
|
||||||
|
|
||||||
|
return cids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockMessages struct {
|
||||||
|
Miner address.Address
|
||||||
|
BlsMessages []types.ChainMsg
|
||||||
|
SecpkMessages []types.ChainMsg
|
||||||
|
WinCount int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) BlockMsgsForTipset(ts *types.TipSet) ([]BlockMessages, error) {
|
||||||
|
applied := make(map[address.Address]uint64)
|
||||||
|
|
||||||
|
cst := cbor.NewCborStore(cs.stateBlockstore)
|
||||||
|
st, err := state.LoadStateTree(cst, ts.Blocks()[0].ParentStateRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to load state tree")
|
||||||
|
}
|
||||||
|
|
||||||
|
selectMsg := func(m *types.Message) (bool, error) {
|
||||||
|
var sender address.Address
|
||||||
|
if ts.Height() >= build.UpgradeHyperdriveHeight {
|
||||||
|
sender, err = st.LookupID(m.From)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sender = m.From
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first match for a sender is guaranteed to have correct nonce -- the block isn't valid otherwise
|
||||||
|
if _, ok := applied[sender]; !ok {
|
||||||
|
applied[sender] = m.Nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
if applied[sender] != m.Nonce {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
applied[sender]++
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var out []BlockMessages
|
||||||
|
for _, b := range ts.Blocks() {
|
||||||
|
|
||||||
|
bms, sms, err := cs.MessagesForBlock(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to get messages for block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bm := BlockMessages{
|
||||||
|
Miner: b.Miner,
|
||||||
|
BlsMessages: make([]types.ChainMsg, 0, len(bms)),
|
||||||
|
SecpkMessages: make([]types.ChainMsg, 0, len(sms)),
|
||||||
|
WinCount: b.ElectionProof.WinCount,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bmsg := range bms {
|
||||||
|
b, err := selectMsg(bmsg.VMMessage())
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to decide whether to select message for block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b {
|
||||||
|
bm.BlsMessages = append(bm.BlsMessages, bmsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, smsg := range sms {
|
||||||
|
b, err := selectMsg(smsg.VMMessage())
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to decide whether to select message for block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b {
|
||||||
|
bm.SecpkMessages = append(bm.SecpkMessages, smsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, bm)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) MessagesForTipset(ts *types.TipSet) ([]types.ChainMsg, error) {
|
||||||
|
bmsgs, err := cs.BlockMsgsForTipset(ts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var out []types.ChainMsg
|
||||||
|
for _, bm := range bmsgs {
|
||||||
|
for _, blsm := range bm.BlsMessages {
|
||||||
|
out = append(out, blsm)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, secm := range bm.SecpkMessages {
|
||||||
|
out = append(out, secm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type mmCids struct {
|
||||||
|
bls []cid.Cid
|
||||||
|
secpk []cid.Cid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) ReadMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error) {
|
||||||
|
o, ok := cs.mmCache.Get(mmc)
|
||||||
|
if ok {
|
||||||
|
mmcids := o.(*mmCids)
|
||||||
|
return mmcids.bls, mmcids.secpk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cst := cbor.NewCborStore(cs.chainLocalBlockstore)
|
||||||
|
var msgmeta types.MsgMeta
|
||||||
|
if err := cst.Get(context.TODO(), mmc, &msgmeta); err != nil {
|
||||||
|
return nil, nil, xerrors.Errorf("failed to load msgmeta (%s): %w", mmc, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blscids, err := cs.readAMTCids(msgmeta.BlsMessages)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, xerrors.Errorf("loading bls message cids for block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secpkcids, err := cs.readAMTCids(msgmeta.SecpkMessages)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, xerrors.Errorf("loading secpk message cids for block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.mmCache.Add(mmc, &mmCids{
|
||||||
|
bls: blscids,
|
||||||
|
secpk: secpkcids,
|
||||||
|
})
|
||||||
|
|
||||||
|
return blscids, secpkcids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
|
||||||
|
blscids, secpkcids, err := cs.ReadMsgMetaCids(b.Messages)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blsmsgs, err := cs.LoadMessagesFromCids(blscids)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, xerrors.Errorf("loading bls messages for block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secpkmsgs, err := cs.LoadSignedMessagesFromCids(secpkcids)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, xerrors.Errorf("loading secpk messages for block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return blsmsgs, secpkmsgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetParentReceipt(b *types.BlockHeader, i int) (*types.MessageReceipt, error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
// block headers use adt0, for now.
|
||||||
|
a, err := blockadt.AsArray(cs.ActorStore(ctx), b.ParentMessageReceipts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("amt load: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r types.MessageReceipt
|
||||||
|
if found, err := a.Get(uint64(i), &r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !found {
|
||||||
|
return nil, xerrors.Errorf("failed to find receipt %d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) LoadMessagesFromCids(cids []cid.Cid) ([]*types.Message, error) {
|
||||||
|
msgs := make([]*types.Message, 0, len(cids))
|
||||||
|
for i, c := range cids {
|
||||||
|
m, err := cs.GetMessage(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs = append(msgs, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) LoadSignedMessagesFromCids(cids []cid.Cid) ([]*types.SignedMessage, error) {
|
||||||
|
msgs := make([]*types.SignedMessage, 0, len(cids))
|
||||||
|
for i, c := range cids {
|
||||||
|
m, err := cs.GetSignedMessage(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs = append(msgs, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgs, nil
|
||||||
|
}
|
182
chain/store/rand.go
Normal file
182
chain/store/rand.go
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/minio/blake2b-simd"
|
||||||
|
"go.opencensus.io/trace"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/crypto"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/chain/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||||
|
h := blake2b.New256()
|
||||||
|
if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil {
|
||||||
|
return nil, xerrors.Errorf("deriving randomness: %w", err)
|
||||||
|
}
|
||||||
|
VRFDigest := blake2b.Sum256(rbase)
|
||||||
|
_, err := h.Write(VRFDigest[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("hashing VRFDigest: %w", err)
|
||||||
|
}
|
||||||
|
if err := binary.Write(h, binary.BigEndian, round); err != nil {
|
||||||
|
return nil, xerrors.Errorf("deriving randomness: %w", err)
|
||||||
|
}
|
||||||
|
_, err = h.Write(entropy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("hashing entropy: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetBeaconRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||||
|
return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetBeaconRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||||
|
return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetBeaconRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) {
|
||||||
|
_, span := trace.StartSpan(ctx, "store.GetBeaconRandomness")
|
||||||
|
defer span.End()
|
||||||
|
span.AddAttributes(trace.Int64Attribute("round", int64(round)))
|
||||||
|
|
||||||
|
ts, err := cs.LoadTipSet(types.NewTipSetKey(blks...))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if round > ts.Height() {
|
||||||
|
return nil, xerrors.Errorf("cannot draw randomness from the future")
|
||||||
|
}
|
||||||
|
|
||||||
|
searchHeight := round
|
||||||
|
if searchHeight < 0 {
|
||||||
|
searchHeight = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
be, err := cs.GetLatestBeaconEntry(randTs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if at (or just past -- for null epochs) appropriate epoch
|
||||||
|
// or at genesis (works for negative epochs)
|
||||||
|
return DrawRandomness(be.Data, pers, round, entropy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetChainRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||||
|
return cs.GetChainRandomness(ctx, blks, pers, round, entropy, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetChainRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||||
|
return cs.GetChainRandomness(ctx, blks, pers, round, entropy, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetChainRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) {
|
||||||
|
_, span := trace.StartSpan(ctx, "store.GetChainRandomness")
|
||||||
|
defer span.End()
|
||||||
|
span.AddAttributes(trace.Int64Attribute("round", int64(round)))
|
||||||
|
|
||||||
|
ts, err := cs.LoadTipSet(types.NewTipSetKey(blks...))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if round > ts.Height() {
|
||||||
|
return nil, xerrors.Errorf("cannot draw randomness from the future")
|
||||||
|
}
|
||||||
|
|
||||||
|
searchHeight := round
|
||||||
|
if searchHeight < 0 {
|
||||||
|
searchHeight = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mtb := randTs.MinTicketBlock()
|
||||||
|
|
||||||
|
// if at (or just past -- for null epochs) appropriate epoch
|
||||||
|
// or at genesis (works for negative epochs)
|
||||||
|
return DrawRandomness(mtb.Ticket.VRFProof, pers, round, entropy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetLatestBeaconEntry(ts *types.TipSet) (*types.BeaconEntry, error) {
|
||||||
|
cur := ts
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
cbe := cur.Blocks()[0].BeaconEntries
|
||||||
|
if len(cbe) > 0 {
|
||||||
|
return &cbe[len(cbe)-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if cur.Height() == 0 {
|
||||||
|
return nil, xerrors.Errorf("made it back to genesis block without finding beacon entry")
|
||||||
|
}
|
||||||
|
|
||||||
|
next, err := cs.LoadTipSet(cur.Parents())
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to load parents when searching back for latest beacon entry: %w", err)
|
||||||
|
}
|
||||||
|
cur = next
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("LOTUS_IGNORE_DRAND") == "_yes_" {
|
||||||
|
return &types.BeaconEntry{
|
||||||
|
Data: []byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, xerrors.Errorf("found NO beacon entries in the 20 latest tipsets")
|
||||||
|
}
|
||||||
|
|
||||||
|
type chainRand struct {
|
||||||
|
cs *ChainStore
|
||||||
|
blks []cid.Cid
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChainRand(cs *ChainStore, blks []cid.Cid) vm.Rand {
|
||||||
|
return &chainRand{
|
||||||
|
cs: cs,
|
||||||
|
blks: blks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *chainRand) GetChainRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||||
|
return cr.cs.GetChainRandomnessLookingBack(ctx, cr.blks, pers, round, entropy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *chainRand) GetChainRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||||
|
return cr.cs.GetChainRandomnessLookingForward(ctx, cr.blks, pers, round, entropy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *chainRand) GetBeaconRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||||
|
return cr.cs.GetBeaconRandomnessLookingBack(ctx, cr.blks, pers, round, entropy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *chainRand) GetBeaconRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
||||||
|
return cr.cs.GetBeaconRandomnessLookingForward(ctx, cr.blks, pers, round, entropy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) GetTipSetFromKey(tsk types.TipSetKey) (*types.TipSet, error) {
|
||||||
|
if tsk.IsEmpty() {
|
||||||
|
return cs.GetHeaviestTipSet(), nil
|
||||||
|
}
|
||||||
|
return cs.LoadTipSet(tsk)
|
||||||
|
}
|
205
chain/store/snapshot.go
Normal file
205
chain/store/snapshot.go
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/ipld/go-car"
|
||||||
|
carutil "github.com/ipld/go-car/util"
|
||||||
|
cbg "github.com/whyrusleeping/cbor-gen"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
bstore "github.com/filecoin-project/lotus/blockstore"
|
||||||
|
"github.com/filecoin-project/lotus/build"
|
||||||
|
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
||||||
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRoots abi.ChainEpoch, skipOldMsgs bool, w io.Writer) error {
|
||||||
|
h := &car.CarHeader{
|
||||||
|
Roots: ts.Cids(),
|
||||||
|
Version: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := car.WriteHeader(h, w); err != nil {
|
||||||
|
return xerrors.Errorf("failed to write car header: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
unionBs := bstore.Union(cs.stateBlockstore, cs.chainBlockstore)
|
||||||
|
return cs.WalkSnapshot(ctx, ts, inclRecentRoots, skipOldMsgs, true, func(c cid.Cid) error {
|
||||||
|
blk, err := unionBs.Get(c)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("writing object to car, bs.Get: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := carutil.LdWrite(w, c.Bytes(), blk.RawData()); err != nil {
|
||||||
|
return xerrors.Errorf("failed to write block to car output: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) Import(r io.Reader) (*types.TipSet, error) {
|
||||||
|
// TODO: writing only to the state blockstore is incorrect.
|
||||||
|
// At this time, both the state and chain blockstores are backed by the
|
||||||
|
// universal store. When we physically segregate the stores, we will need
|
||||||
|
// to route state objects to the state blockstore, and chain objects to
|
||||||
|
// the chain blockstore.
|
||||||
|
header, err := car.LoadCar(cs.StateBlockstore(), r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("loadcar failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
root, err := cs.LoadTipSet(types.NewTipSetKey(header.Roots...))
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return root, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ChainStore) WalkSnapshot(ctx context.Context, ts *types.TipSet, inclRecentRoots abi.ChainEpoch, skipOldMsgs, skipMsgReceipts bool, cb func(cid.Cid) error) error {
|
||||||
|
if ts == nil {
|
||||||
|
ts = cs.GetHeaviestTipSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
seen := cid.NewSet()
|
||||||
|
walked := cid.NewSet()
|
||||||
|
|
||||||
|
blocksToWalk := ts.Cids()
|
||||||
|
currentMinHeight := ts.Height()
|
||||||
|
|
||||||
|
walkChain := func(blk cid.Cid) error {
|
||||||
|
if !seen.Visit(blk) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cb(blk); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := cs.chainBlockstore.Get(blk)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("getting block: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var b types.BlockHeader
|
||||||
|
if err := b.UnmarshalCBOR(bytes.NewBuffer(data.RawData())); err != nil {
|
||||||
|
return xerrors.Errorf("unmarshaling block header (cid=%s): %w", blk, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentMinHeight > b.Height {
|
||||||
|
currentMinHeight = b.Height
|
||||||
|
if currentMinHeight%builtin.EpochsInDay == 0 {
|
||||||
|
log.Infow("export", "height", currentMinHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var cids []cid.Cid
|
||||||
|
if !skipOldMsgs || b.Height > ts.Height()-inclRecentRoots {
|
||||||
|
if walked.Visit(b.Messages) {
|
||||||
|
mcids, err := recurseLinks(cs.chainBlockstore, walked, b.Messages, []cid.Cid{b.Messages})
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("recursing messages failed: %w", err)
|
||||||
|
}
|
||||||
|
cids = mcids
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Height > 0 {
|
||||||
|
for _, p := range b.Parents {
|
||||||
|
blocksToWalk = append(blocksToWalk, p)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// include the genesis block
|
||||||
|
cids = append(cids, b.Parents...)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := cids
|
||||||
|
|
||||||
|
if b.Height == 0 || b.Height > ts.Height()-inclRecentRoots {
|
||||||
|
if walked.Visit(b.ParentStateRoot) {
|
||||||
|
cids, err := recurseLinks(cs.stateBlockstore, walked, b.ParentStateRoot, []cid.Cid{b.ParentStateRoot})
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("recursing genesis state failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, cids...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !skipMsgReceipts && walked.Visit(b.ParentMessageReceipts) {
|
||||||
|
out = append(out, b.ParentMessageReceipts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range out {
|
||||||
|
if seen.Visit(c) {
|
||||||
|
if c.Prefix().Codec != cid.DagCBOR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cb(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infow("export started")
|
||||||
|
exportStart := build.Clock.Now()
|
||||||
|
|
||||||
|
for len(blocksToWalk) > 0 {
|
||||||
|
next := blocksToWalk[0]
|
||||||
|
blocksToWalk = blocksToWalk[1:]
|
||||||
|
if err := walkChain(next); err != nil {
|
||||||
|
return xerrors.Errorf("walk chain failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infow("export finished", "duration", build.Clock.Now().Sub(exportStart).Seconds())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func recurseLinks(bs bstore.Blockstore, walked *cid.Set, root cid.Cid, in []cid.Cid) ([]cid.Cid, error) {
|
||||||
|
if root.Prefix().Codec != cid.DagCBOR {
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := bs.Get(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("recurse links get (%s) failed: %w", root, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rerr error
|
||||||
|
err = cbg.ScanForLinks(bytes.NewReader(data.RawData()), func(c cid.Cid) {
|
||||||
|
if rerr != nil {
|
||||||
|
// No error return on ScanForLinks :(
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// traversed this already...
|
||||||
|
if !walked.Visit(c) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
in = append(in, c)
|
||||||
|
var err error
|
||||||
|
in, err = recurseLinks(bs, walked, c, in)
|
||||||
|
if err != nil {
|
||||||
|
rerr = err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, xerrors.Errorf("scanning for links failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return in, rerr
|
||||||
|
}
|
@ -1,36 +1,24 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/state"
|
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-state-types/crypto"
|
|
||||||
"github.com/minio/blake2b-simd"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
|
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/api"
|
"github.com/filecoin-project/lotus/api"
|
||||||
bstore "github.com/filecoin-project/lotus/blockstore"
|
bstore "github.com/filecoin-project/lotus/blockstore"
|
||||||
"github.com/filecoin-project/lotus/build"
|
"github.com/filecoin-project/lotus/build"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/adt"
|
"github.com/filecoin-project/lotus/chain/actors/adt"
|
||||||
"github.com/filecoin-project/lotus/chain/actors/builtin"
|
|
||||||
"github.com/filecoin-project/lotus/chain/vm"
|
|
||||||
"github.com/filecoin-project/lotus/journal"
|
"github.com/filecoin-project/lotus/journal"
|
||||||
"github.com/filecoin-project/lotus/metrics"
|
"github.com/filecoin-project/lotus/metrics"
|
||||||
|
|
||||||
@ -48,9 +36,6 @@ import (
|
|||||||
"github.com/ipfs/go-datastore/query"
|
"github.com/ipfs/go-datastore/query"
|
||||||
cbor "github.com/ipfs/go-ipld-cbor"
|
cbor "github.com/ipfs/go-ipld-cbor"
|
||||||
logging "github.com/ipfs/go-log/v2"
|
logging "github.com/ipfs/go-log/v2"
|
||||||
"github.com/ipld/go-car"
|
|
||||||
carutil "github.com/ipld/go-car/util"
|
|
||||||
cbg "github.com/whyrusleeping/cbor-gen"
|
|
||||||
"github.com/whyrusleeping/pubsub"
|
"github.com/whyrusleeping/pubsub"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
)
|
)
|
||||||
@ -134,11 +119,9 @@ type ChainStore struct {
|
|||||||
reorgCh chan<- reorg
|
reorgCh chan<- reorg
|
||||||
reorgNotifeeCh chan ReorgNotifee
|
reorgNotifeeCh chan ReorgNotifee
|
||||||
|
|
||||||
mmCache *lru.ARCCache
|
mmCache *lru.ARCCache // msg meta cache (mh.Messages -> secp, bls []cid)
|
||||||
tsCache *lru.ARCCache
|
tsCache *lru.ARCCache
|
||||||
|
|
||||||
vmcalls vm.SyscallBuilder
|
|
||||||
|
|
||||||
evtTypes [1]journal.EventType
|
evtTypes [1]journal.EventType
|
||||||
journal journal.Journal
|
journal journal.Journal
|
||||||
|
|
||||||
@ -146,7 +129,7 @@ type ChainStore struct {
|
|||||||
wg sync.WaitGroup
|
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)
|
c, _ := lru.NewARC(DefaultMsgMetaCacheSize)
|
||||||
tsc, _ := lru.NewARC(DefaultTipSetCacheSize)
|
tsc, _ := lru.NewARC(DefaultTipSetCacheSize)
|
||||||
if j == nil {
|
if j == nil {
|
||||||
@ -166,7 +149,6 @@ func NewChainStore(chainBs bstore.Blockstore, stateBs bstore.Blockstore, ds dsto
|
|||||||
tipsets: make(map[abi.ChainEpoch][]cid.Cid),
|
tipsets: make(map[abi.ChainEpoch][]cid.Cid),
|
||||||
mmCache: c,
|
mmCache: c,
|
||||||
tsCache: tsc,
|
tsCache: tsc,
|
||||||
vmcalls: vmcalls,
|
|
||||||
cancelFn: cancel,
|
cancelFn: cancel,
|
||||||
journal: j,
|
journal: j,
|
||||||
}
|
}
|
||||||
@ -988,27 +970,6 @@ func (cs *ChainStore) PersistBlockHeaders(b ...*types.BlockHeader) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type storable interface {
|
|
||||||
ToStorageBlock() (block.Block, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PutMessage(bs bstore.Blockstore, m storable) (cid.Cid, error) {
|
|
||||||
b, err := m.ToStorageBlock()
|
|
||||||
if err != nil {
|
|
||||||
return cid.Undef, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bs.Put(b); err != nil {
|
|
||||||
return cid.Undef, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.Cid(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) PutMessage(m storable) (cid.Cid, error) {
|
|
||||||
return PutMessage(cs.chainBlockstore, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) expandTipset(b *types.BlockHeader) (*types.TipSet, error) {
|
func (cs *ChainStore) expandTipset(b *types.BlockHeader) (*types.TipSet, error) {
|
||||||
// Hold lock for the whole function for now, if it becomes a problem we can
|
// Hold lock for the whole function for now, if it becomes a problem we can
|
||||||
// fix pretty easily
|
// fix pretty easily
|
||||||
@ -1080,203 +1041,6 @@ func (cs *ChainStore) GetGenesis() (*types.BlockHeader, error) {
|
|||||||
return cs.GetBlock(c)
|
return cs.GetBlock(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *ChainStore) GetCMessage(c cid.Cid) (types.ChainMsg, error) {
|
|
||||||
m, err := cs.GetMessage(c)
|
|
||||||
if err == nil {
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
if err != bstore.ErrNotFound {
|
|
||||||
log.Warnf("GetCMessage: unexpected error getting unsigned message: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cs.GetSignedMessage(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetMessage(c cid.Cid) (*types.Message, error) {
|
|
||||||
var msg *types.Message
|
|
||||||
err := cs.chainLocalBlockstore.View(c, func(b []byte) (err error) {
|
|
||||||
msg, err = types.DecodeMessage(b)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return msg, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetSignedMessage(c cid.Cid) (*types.SignedMessage, error) {
|
|
||||||
var msg *types.SignedMessage
|
|
||||||
err := cs.chainLocalBlockstore.View(c, func(b []byte) (err error) {
|
|
||||||
msg, err = types.DecodeSignedMessage(b)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return msg, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) readAMTCids(root cid.Cid) ([]cid.Cid, error) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
// block headers use adt0, for now.
|
|
||||||
a, err := blockadt.AsArray(cs.ActorStore(ctx), root)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("amt load: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
cids []cid.Cid
|
|
||||||
cborCid cbg.CborCid
|
|
||||||
)
|
|
||||||
if err := a.ForEach(&cborCid, func(i int64) error {
|
|
||||||
c := cid.Cid(cborCid)
|
|
||||||
cids = append(cids, c)
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to traverse amt: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if uint64(len(cids)) != a.Length() {
|
|
||||||
return nil, xerrors.Errorf("found %d cids, expected %d", len(cids), a.Length())
|
|
||||||
}
|
|
||||||
|
|
||||||
return cids, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BlockMessages struct {
|
|
||||||
Miner address.Address
|
|
||||||
BlsMessages []types.ChainMsg
|
|
||||||
SecpkMessages []types.ChainMsg
|
|
||||||
WinCount int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) BlockMsgsForTipset(ts *types.TipSet) ([]BlockMessages, error) {
|
|
||||||
applied := make(map[address.Address]uint64)
|
|
||||||
|
|
||||||
cst := cbor.NewCborStore(cs.stateBlockstore)
|
|
||||||
st, err := state.LoadStateTree(cst, ts.Blocks()[0].ParentStateRoot)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to load state tree")
|
|
||||||
}
|
|
||||||
|
|
||||||
selectMsg := func(m *types.Message) (bool, error) {
|
|
||||||
var sender address.Address
|
|
||||||
if ts.Height() >= build.UpgradeHyperdriveHeight {
|
|
||||||
sender, err = st.LookupID(m.From)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sender = m.From
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first match for a sender is guaranteed to have correct nonce -- the block isn't valid otherwise
|
|
||||||
if _, ok := applied[sender]; !ok {
|
|
||||||
applied[sender] = m.Nonce
|
|
||||||
}
|
|
||||||
|
|
||||||
if applied[sender] != m.Nonce {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
applied[sender]++
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var out []BlockMessages
|
|
||||||
for _, b := range ts.Blocks() {
|
|
||||||
|
|
||||||
bms, sms, err := cs.MessagesForBlock(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to get messages for block: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bm := BlockMessages{
|
|
||||||
Miner: b.Miner,
|
|
||||||
BlsMessages: make([]types.ChainMsg, 0, len(bms)),
|
|
||||||
SecpkMessages: make([]types.ChainMsg, 0, len(sms)),
|
|
||||||
WinCount: b.ElectionProof.WinCount,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, bmsg := range bms {
|
|
||||||
b, err := selectMsg(bmsg.VMMessage())
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to decide whether to select message for block: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b {
|
|
||||||
bm.BlsMessages = append(bm.BlsMessages, bmsg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, smsg := range sms {
|
|
||||||
b, err := selectMsg(smsg.VMMessage())
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to decide whether to select message for block: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b {
|
|
||||||
bm.SecpkMessages = append(bm.SecpkMessages, smsg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out = append(out, bm)
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) MessagesForTipset(ts *types.TipSet) ([]types.ChainMsg, error) {
|
|
||||||
bmsgs, err := cs.BlockMsgsForTipset(ts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var out []types.ChainMsg
|
|
||||||
for _, bm := range bmsgs {
|
|
||||||
for _, blsm := range bm.BlsMessages {
|
|
||||||
out = append(out, blsm)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, secm := range bm.SecpkMessages {
|
|
||||||
out = append(out, secm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type mmCids struct {
|
|
||||||
bls []cid.Cid
|
|
||||||
secpk []cid.Cid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) ReadMsgMetaCids(mmc cid.Cid) ([]cid.Cid, []cid.Cid, error) {
|
|
||||||
o, ok := cs.mmCache.Get(mmc)
|
|
||||||
if ok {
|
|
||||||
mmcids := o.(*mmCids)
|
|
||||||
return mmcids.bls, mmcids.secpk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cst := cbor.NewCborStore(cs.chainLocalBlockstore)
|
|
||||||
var msgmeta types.MsgMeta
|
|
||||||
if err := cst.Get(context.TODO(), mmc, &msgmeta); err != nil {
|
|
||||||
return nil, nil, xerrors.Errorf("failed to load msgmeta (%s): %w", mmc, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
blscids, err := cs.readAMTCids(msgmeta.BlsMessages)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, xerrors.Errorf("loading bls message cids for block: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
secpkcids, err := cs.readAMTCids(msgmeta.SecpkMessages)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, xerrors.Errorf("loading secpk message cids for block: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cs.mmCache.Add(mmc, &mmCids{
|
|
||||||
bls: blscids,
|
|
||||||
secpk: secpkcids,
|
|
||||||
})
|
|
||||||
|
|
||||||
return blscids, secpkcids, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPath returns the sequence of atomic head change operations that
|
// GetPath returns the sequence of atomic head change operations that
|
||||||
// need to be applied in order to switch the head of the chain from the `from`
|
// need to be applied in order to switch the head of the chain from the `from`
|
||||||
// tipset to the `to` tipset.
|
// tipset to the `to` tipset.
|
||||||
@ -1304,71 +1068,6 @@ func (cs *ChainStore) GetPath(ctx context.Context, from types.TipSetKey, to type
|
|||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *ChainStore) MessagesForBlock(b *types.BlockHeader) ([]*types.Message, []*types.SignedMessage, error) {
|
|
||||||
blscids, secpkcids, err := cs.ReadMsgMetaCids(b.Messages)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
blsmsgs, err := cs.LoadMessagesFromCids(blscids)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, xerrors.Errorf("loading bls messages for block: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
secpkmsgs, err := cs.LoadSignedMessagesFromCids(secpkcids)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, xerrors.Errorf("loading secpk messages for block: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return blsmsgs, secpkmsgs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetParentReceipt(b *types.BlockHeader, i int) (*types.MessageReceipt, error) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
// block headers use adt0, for now.
|
|
||||||
a, err := blockadt.AsArray(cs.ActorStore(ctx), b.ParentMessageReceipts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("amt load: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var r types.MessageReceipt
|
|
||||||
if found, err := a.Get(uint64(i), &r); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if !found {
|
|
||||||
return nil, xerrors.Errorf("failed to find receipt %d", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) LoadMessagesFromCids(cids []cid.Cid) ([]*types.Message, error) {
|
|
||||||
msgs := make([]*types.Message, 0, len(cids))
|
|
||||||
for i, c := range cids {
|
|
||||||
m, err := cs.GetMessage(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgs = append(msgs, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
return msgs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) LoadSignedMessagesFromCids(cids []cid.Cid) ([]*types.SignedMessage, error) {
|
|
||||||
msgs := make([]*types.SignedMessage, 0, len(cids))
|
|
||||||
for i, c := range cids {
|
|
||||||
m, err := cs.GetSignedMessage(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to get message: (%s):%d: %w", c, i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgs = append(msgs, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
return msgs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainBlockstore returns the chain blockstore. Currently the chain and state
|
// ChainBlockstore returns the chain blockstore. Currently the chain and state
|
||||||
// // stores are both backed by the same physical store, albeit with different
|
// // stores are both backed by the same physical store, albeit with different
|
||||||
// // caching policies, but in the future they will segregate.
|
// // caching policies, but in the future they will segregate.
|
||||||
@ -1391,10 +1090,6 @@ func (cs *ChainStore) ActorStore(ctx context.Context) adt.Store {
|
|||||||
return ActorStore(ctx, cs.stateBlockstore)
|
return ActorStore(ctx, cs.stateBlockstore)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *ChainStore) VMSys() vm.SyscallBuilder {
|
|
||||||
return cs.vmcalls
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) {
|
func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) {
|
||||||
var out []*types.FullBlock
|
var out []*types.FullBlock
|
||||||
|
|
||||||
@ -1417,108 +1112,6 @@ func (cs *ChainStore) TryFillTipSet(ts *types.TipSet) (*FullTipSet, error) {
|
|||||||
return NewFullTipSet(out), nil
|
return NewFullTipSet(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DrawRandomness(rbase []byte, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
|
||||||
h := blake2b.New256()
|
|
||||||
if err := binary.Write(h, binary.BigEndian, int64(pers)); err != nil {
|
|
||||||
return nil, xerrors.Errorf("deriving randomness: %w", err)
|
|
||||||
}
|
|
||||||
VRFDigest := blake2b.Sum256(rbase)
|
|
||||||
_, err := h.Write(VRFDigest[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("hashing VRFDigest: %w", err)
|
|
||||||
}
|
|
||||||
if err := binary.Write(h, binary.BigEndian, round); err != nil {
|
|
||||||
return nil, xerrors.Errorf("deriving randomness: %w", err)
|
|
||||||
}
|
|
||||||
_, err = h.Write(entropy)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("hashing entropy: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return h.Sum(nil), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetBeaconRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
|
||||||
return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetBeaconRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
|
||||||
return cs.GetBeaconRandomness(ctx, blks, pers, round, entropy, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetBeaconRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) {
|
|
||||||
_, span := trace.StartSpan(ctx, "store.GetBeaconRandomness")
|
|
||||||
defer span.End()
|
|
||||||
span.AddAttributes(trace.Int64Attribute("round", int64(round)))
|
|
||||||
|
|
||||||
ts, err := cs.LoadTipSet(types.NewTipSetKey(blks...))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if round > ts.Height() {
|
|
||||||
return nil, xerrors.Errorf("cannot draw randomness from the future")
|
|
||||||
}
|
|
||||||
|
|
||||||
searchHeight := round
|
|
||||||
if searchHeight < 0 {
|
|
||||||
searchHeight = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
be, err := cs.GetLatestBeaconEntry(randTs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if at (or just past -- for null epochs) appropriate epoch
|
|
||||||
// or at genesis (works for negative epochs)
|
|
||||||
return DrawRandomness(be.Data, pers, round, entropy)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetChainRandomnessLookingBack(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
|
||||||
return cs.GetChainRandomness(ctx, blks, pers, round, entropy, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetChainRandomnessLookingForward(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
|
||||||
return cs.GetChainRandomness(ctx, blks, pers, round, entropy, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetChainRandomness(ctx context.Context, blks []cid.Cid, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte, lookback bool) ([]byte, error) {
|
|
||||||
_, span := trace.StartSpan(ctx, "store.GetChainRandomness")
|
|
||||||
defer span.End()
|
|
||||||
span.AddAttributes(trace.Int64Attribute("round", int64(round)))
|
|
||||||
|
|
||||||
ts, err := cs.LoadTipSet(types.NewTipSetKey(blks...))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if round > ts.Height() {
|
|
||||||
return nil, xerrors.Errorf("cannot draw randomness from the future")
|
|
||||||
}
|
|
||||||
|
|
||||||
searchHeight := round
|
|
||||||
if searchHeight < 0 {
|
|
||||||
searchHeight = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
randTs, err := cs.GetTipsetByHeight(ctx, searchHeight, ts, lookback)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
mtb := randTs.MinTicketBlock()
|
|
||||||
|
|
||||||
// if at (or just past -- for null epochs) appropriate epoch
|
|
||||||
// or at genesis (works for negative epochs)
|
|
||||||
return DrawRandomness(mtb.Ticket.VRFProof, pers, round, entropy)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTipsetByHeight returns the tipset on the chain behind 'ts' at the given
|
// GetTipsetByHeight returns the tipset on the chain behind 'ts' at the given
|
||||||
// height. In the case that the given height is a null round, the 'prev' flag
|
// height. In the case that the given height is a null round, the 'prev' flag
|
||||||
// selects the tipset before the null round if true, and the tipset following
|
// selects the tipset before the null round if true, and the tipset following
|
||||||
@ -1555,252 +1148,3 @@ func (cs *ChainStore) GetTipsetByHeight(ctx context.Context, h abi.ChainEpoch, t
|
|||||||
|
|
||||||
return cs.LoadTipSet(lbts.Parents())
|
return cs.LoadTipSet(lbts.Parents())
|
||||||
}
|
}
|
||||||
|
|
||||||
func recurseLinks(bs bstore.Blockstore, walked *cid.Set, root cid.Cid, in []cid.Cid) ([]cid.Cid, error) {
|
|
||||||
if root.Prefix().Codec != cid.DagCBOR {
|
|
||||||
return in, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := bs.Get(root)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("recurse links get (%s) failed: %w", root, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var rerr error
|
|
||||||
err = cbg.ScanForLinks(bytes.NewReader(data.RawData()), func(c cid.Cid) {
|
|
||||||
if rerr != nil {
|
|
||||||
// No error return on ScanForLinks :(
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// traversed this already...
|
|
||||||
if !walked.Visit(c) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
in = append(in, c)
|
|
||||||
var err error
|
|
||||||
in, err = recurseLinks(bs, walked, c, in)
|
|
||||||
if err != nil {
|
|
||||||
rerr = err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("scanning for links failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return in, rerr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) Export(ctx context.Context, ts *types.TipSet, inclRecentRoots abi.ChainEpoch, skipOldMsgs bool, w io.Writer) error {
|
|
||||||
h := &car.CarHeader{
|
|
||||||
Roots: ts.Cids(),
|
|
||||||
Version: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := car.WriteHeader(h, w); err != nil {
|
|
||||||
return xerrors.Errorf("failed to write car header: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
unionBs := bstore.Union(cs.stateBlockstore, cs.chainBlockstore)
|
|
||||||
return cs.WalkSnapshot(ctx, ts, inclRecentRoots, skipOldMsgs, true, func(c cid.Cid) error {
|
|
||||||
blk, err := unionBs.Get(c)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("writing object to car, bs.Get: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := carutil.LdWrite(w, c.Bytes(), blk.RawData()); err != nil {
|
|
||||||
return xerrors.Errorf("failed to write block to car output: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) WalkSnapshot(ctx context.Context, ts *types.TipSet, inclRecentRoots abi.ChainEpoch, skipOldMsgs, skipMsgReceipts bool, cb func(cid.Cid) error) error {
|
|
||||||
if ts == nil {
|
|
||||||
ts = cs.GetHeaviestTipSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
seen := cid.NewSet()
|
|
||||||
walked := cid.NewSet()
|
|
||||||
|
|
||||||
blocksToWalk := ts.Cids()
|
|
||||||
currentMinHeight := ts.Height()
|
|
||||||
|
|
||||||
walkChain := func(blk cid.Cid) error {
|
|
||||||
if !seen.Visit(blk) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cb(blk); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := cs.chainBlockstore.Get(blk)
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("getting block: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var b types.BlockHeader
|
|
||||||
if err := b.UnmarshalCBOR(bytes.NewBuffer(data.RawData())); err != nil {
|
|
||||||
return xerrors.Errorf("unmarshaling block header (cid=%s): %w", blk, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if currentMinHeight > b.Height {
|
|
||||||
currentMinHeight = b.Height
|
|
||||||
if currentMinHeight%builtin.EpochsInDay == 0 {
|
|
||||||
log.Infow("export", "height", currentMinHeight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var cids []cid.Cid
|
|
||||||
if !skipOldMsgs || b.Height > ts.Height()-inclRecentRoots {
|
|
||||||
if walked.Visit(b.Messages) {
|
|
||||||
mcids, err := recurseLinks(cs.chainBlockstore, walked, b.Messages, []cid.Cid{b.Messages})
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("recursing messages failed: %w", err)
|
|
||||||
}
|
|
||||||
cids = mcids
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.Height > 0 {
|
|
||||||
for _, p := range b.Parents {
|
|
||||||
blocksToWalk = append(blocksToWalk, p)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// include the genesis block
|
|
||||||
cids = append(cids, b.Parents...)
|
|
||||||
}
|
|
||||||
|
|
||||||
out := cids
|
|
||||||
|
|
||||||
if b.Height == 0 || b.Height > ts.Height()-inclRecentRoots {
|
|
||||||
if walked.Visit(b.ParentStateRoot) {
|
|
||||||
cids, err := recurseLinks(cs.stateBlockstore, walked, b.ParentStateRoot, []cid.Cid{b.ParentStateRoot})
|
|
||||||
if err != nil {
|
|
||||||
return xerrors.Errorf("recursing genesis state failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
out = append(out, cids...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !skipMsgReceipts && walked.Visit(b.ParentMessageReceipts) {
|
|
||||||
out = append(out, b.ParentMessageReceipts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range out {
|
|
||||||
if seen.Visit(c) {
|
|
||||||
if c.Prefix().Codec != cid.DagCBOR {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cb(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infow("export started")
|
|
||||||
exportStart := build.Clock.Now()
|
|
||||||
|
|
||||||
for len(blocksToWalk) > 0 {
|
|
||||||
next := blocksToWalk[0]
|
|
||||||
blocksToWalk = blocksToWalk[1:]
|
|
||||||
if err := walkChain(next); err != nil {
|
|
||||||
return xerrors.Errorf("walk chain failed: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infow("export finished", "duration", build.Clock.Now().Sub(exportStart).Seconds())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) Import(r io.Reader) (*types.TipSet, error) {
|
|
||||||
// TODO: writing only to the state blockstore is incorrect.
|
|
||||||
// At this time, both the state and chain blockstores are backed by the
|
|
||||||
// universal store. When we physically segregate the stores, we will need
|
|
||||||
// to route state objects to the state blockstore, and chain objects to
|
|
||||||
// the chain blockstore.
|
|
||||||
header, err := car.LoadCar(cs.StateBlockstore(), r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("loadcar failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
root, err := cs.LoadTipSet(types.NewTipSetKey(header.Roots...))
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to load root tipset from chainfile: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return root, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetLatestBeaconEntry(ts *types.TipSet) (*types.BeaconEntry, error) {
|
|
||||||
cur := ts
|
|
||||||
for i := 0; i < 20; i++ {
|
|
||||||
cbe := cur.Blocks()[0].BeaconEntries
|
|
||||||
if len(cbe) > 0 {
|
|
||||||
return &cbe[len(cbe)-1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if cur.Height() == 0 {
|
|
||||||
return nil, xerrors.Errorf("made it back to genesis block without finding beacon entry")
|
|
||||||
}
|
|
||||||
|
|
||||||
next, err := cs.LoadTipSet(cur.Parents())
|
|
||||||
if err != nil {
|
|
||||||
return nil, xerrors.Errorf("failed to load parents when searching back for latest beacon entry: %w", err)
|
|
||||||
}
|
|
||||||
cur = next
|
|
||||||
}
|
|
||||||
|
|
||||||
if os.Getenv("LOTUS_IGNORE_DRAND") == "_yes_" {
|
|
||||||
return &types.BeaconEntry{
|
|
||||||
Data: []byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, xerrors.Errorf("found NO beacon entries in the 20 latest tipsets")
|
|
||||||
}
|
|
||||||
|
|
||||||
type chainRand struct {
|
|
||||||
cs *ChainStore
|
|
||||||
blks []cid.Cid
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewChainRand(cs *ChainStore, blks []cid.Cid) vm.Rand {
|
|
||||||
return &chainRand{
|
|
||||||
cs: cs,
|
|
||||||
blks: blks,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cr *chainRand) GetChainRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
|
||||||
return cr.cs.GetChainRandomnessLookingBack(ctx, cr.blks, pers, round, entropy)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cr *chainRand) GetChainRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
|
||||||
return cr.cs.GetChainRandomnessLookingForward(ctx, cr.blks, pers, round, entropy)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cr *chainRand) GetBeaconRandomnessLookingBack(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
|
||||||
return cr.cs.GetBeaconRandomnessLookingBack(ctx, cr.blks, pers, round, entropy)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cr *chainRand) GetBeaconRandomnessLookingForward(ctx context.Context, pers crypto.DomainSeparationTag, round abi.ChainEpoch, entropy []byte) ([]byte, error) {
|
|
||||||
return cr.cs.GetBeaconRandomnessLookingForward(ctx, cr.blks, pers, round, entropy)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ChainStore) GetTipSetFromKey(tsk types.TipSetKey) (*types.TipSet, error) {
|
|
||||||
if tsk.IsEmpty() {
|
|
||||||
return cs.GetHeaviestTipSet(), nil
|
|
||||||
}
|
|
||||||
return cs.LoadTipSet(tsk)
|
|
||||||
}
|
|
||||||
|
@ -70,7 +70,7 @@ func BenchmarkGetRandomness(b *testing.B) {
|
|||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := store.NewChainStore(bs, bs, mds, nil, nil)
|
cs := store.NewChainStore(bs, bs, mds, nil)
|
||||||
defer cs.Close() //nolint:errcheck
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@ -105,7 +105,7 @@ func TestChainExportImport(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nbs := blockstore.NewMemory()
|
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
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
root, err := cs.Import(buf)
|
root, err := cs.Import(buf)
|
||||||
@ -140,7 +140,7 @@ func TestChainExportImportFull(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nbs := blockstore.NewMemory()
|
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
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
root, err := cs.Import(buf)
|
root, err := cs.Import(buf)
|
||||||
@ -157,7 +157,7 @@ func TestChainExportImportFull(t *testing.T) {
|
|||||||
t.Fatal("imported chain differed from exported chain")
|
t.Fatal("imported chain differed from exported chain")
|
||||||
}
|
}
|
||||||
|
|
||||||
sm := stmgr.NewStateManager(cs)
|
sm := stmgr.NewStateManager(cs, nil)
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
ts, err := cs.GetTipsetByHeight(context.TODO(), abi.ChainEpoch(i), nil, false)
|
ts, err := cs.GetTipsetByHeight(context.TODO(), abi.ChainEpoch(i), nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -228,7 +228,7 @@ func DumpActorState(act *types.Actor, b []byte) (interface{}, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
i := NewActorRegistry() // TODO: register builtins in init block
|
i := NewActorRegistry()
|
||||||
|
|
||||||
actInfo, ok := i.actors[act.Code]
|
actInfo, ok := i.actors[act.Code]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -641,15 +641,6 @@ func (vm *VM) ShouldBurn(ctx context.Context, st *state.StateTree, msg *types.Me
|
|||||||
return true, nil
|
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{}
|
type vmFlushKey struct{}
|
||||||
|
|
||||||
func (vm *VM) Flush(ctx context.Context) (cid.Cid, error) {
|
func (vm *VM) Flush(ctx context.Context) (cid.Cid, error) {
|
||||||
|
@ -253,10 +253,10 @@ var importBenchCmd = &cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
metadataDs := datastore.NewMapDatastore()
|
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
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
stm := stmgr.NewStateManager(cs)
|
stm := stmgr.NewStateManager(cs, vm.Syscalls(verifier))
|
||||||
|
|
||||||
var carFile *os.File
|
var carFile *os.File
|
||||||
// open the CAR file if one is provided.
|
// open the CAR file if one is provided.
|
||||||
|
@ -510,13 +510,13 @@ var chainBalanceStateCmd = &cli.Command{
|
|||||||
return err
|
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
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
cst := cbor.NewCborStore(bs)
|
cst := cbor.NewCborStore(bs)
|
||||||
store := adt.WrapStore(ctx, cst)
|
store := adt.WrapStore(ctx, cst)
|
||||||
|
|
||||||
sm := stmgr.NewStateManager(cs)
|
sm := stmgr.NewStateManager(cs, vm.Syscalls(ffiwrapper.ProofVerifier))
|
||||||
|
|
||||||
tree, err := state.LoadStateTree(cst, sroot)
|
tree, err := state.LoadStateTree(cst, sroot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -731,13 +731,13 @@ var chainPledgeCmd = &cli.Command{
|
|||||||
return err
|
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
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
cst := cbor.NewCborStore(bs)
|
cst := cbor.NewCborStore(bs)
|
||||||
store := adt.WrapStore(ctx, cst)
|
store := adt.WrapStore(ctx, cst)
|
||||||
|
|
||||||
sm := stmgr.NewStateManager(cs)
|
sm := stmgr.NewStateManager(cs, vm.Syscalls(ffiwrapper.ProofVerifier))
|
||||||
|
|
||||||
state, err := state.LoadStateTree(cst, sroot)
|
state, err := state.LoadStateTree(cst, sroot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -90,7 +90,7 @@ var exportChainCmd = &cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cs := store.NewChainStore(bs, bs, mds, nil, nil)
|
cs := store.NewChainStore(bs, bs, mds, nil)
|
||||||
defer cs.Close() //nolint:errcheck
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
if err := cs.Load(); err != nil {
|
if err := cs.Load(); err != nil {
|
||||||
|
@ -52,7 +52,7 @@ var genesisVerifyCmd = &cli.Command{
|
|||||||
}
|
}
|
||||||
bs := blockstore.FromDatastore(datastore.NewMapDatastore())
|
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
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
cf := cctx.Args().Get(0)
|
cf := cctx.Args().Get(0)
|
||||||
@ -66,9 +66,7 @@ var genesisVerifyCmd = &cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sm := stmgr.NewStateManager(cs)
|
total, err := stmgr.CheckTotalFIL(context.TODO(), cs, ts)
|
||||||
|
|
||||||
total, err := stmgr.CheckTotalFIL(context.TODO(), sm, ts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,6 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/chain/state"
|
"github.com/filecoin-project/lotus/chain/state"
|
||||||
"github.com/filecoin-project/lotus/chain/store"
|
"github.com/filecoin-project/lotus/chain/store"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"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"
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin"
|
builtin4 "github.com/filecoin-project/specs-actors/v4/actors/builtin"
|
||||||
"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
|
"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
|
||||||
@ -76,7 +74,7 @@ var minerTypesCmd = &cli.Command{
|
|||||||
return err
|
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
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
cst := cbor.NewCborStore(bs)
|
cst := cbor.NewCborStore(bs)
|
||||||
|
@ -13,8 +13,6 @@ import (
|
|||||||
|
|
||||||
badgerbs "github.com/filecoin-project/lotus/blockstore/badger"
|
badgerbs "github.com/filecoin-project/lotus/blockstore/badger"
|
||||||
"github.com/filecoin-project/lotus/chain/store"
|
"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"
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -169,7 +167,7 @@ var stateTreePruneCmd = &cli.Command{
|
|||||||
return nil
|
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
|
defer cs.Close() //nolint:errcheck
|
||||||
|
|
||||||
if err := cs.Load(); err != nil {
|
if err := cs.Load(); err != nil {
|
||||||
|
@ -83,7 +83,7 @@ func NewBlockBuilder(ctx context.Context, logger *zap.SugaredLogger, sm *stmgr.S
|
|||||||
Epoch: parentTs.Height() + 1,
|
Epoch: parentTs.Height() + 1,
|
||||||
Rand: r,
|
Rand: r,
|
||||||
Bstore: sm.ChainStore().StateBlockstore(),
|
Bstore: sm.ChainStore().StateBlockstore(),
|
||||||
Syscalls: sm.ChainStore().VMSys(),
|
Syscalls: sm.VMSys(),
|
||||||
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
CircSupplyCalc: sm.GetVMCirculatingSupply,
|
||||||
NtwkVersion: sm.GetNtwkVersion,
|
NtwkVersion: sm.GetNtwkVersion,
|
||||||
BaseFee: abi.NewTokenAmount(0),
|
BaseFee: abi.NewTokenAmount(0),
|
||||||
|
@ -61,7 +61,7 @@ func NewNode(ctx context.Context, r repo.Repo) (nd *Node, _err error) {
|
|||||||
}
|
}
|
||||||
return &Node{
|
return &Node{
|
||||||
repo: lr,
|
repo: lr,
|
||||||
Chainstore: store.NewChainStore(bs, bs, ds, vm.Syscalls(mock.Verifier), nil),
|
Chainstore: store.NewChainStore(bs, bs, ds, nil),
|
||||||
MetadataDS: ds,
|
MetadataDS: ds,
|
||||||
Blockstore: bs,
|
Blockstore: bs,
|
||||||
}, err
|
}, err
|
||||||
@ -105,7 +105,7 @@ func (nd *Node) LoadSim(ctx context.Context, name string) (*Simulation, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to create upgrade schedule for simulation %s: %w", name, err)
|
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 {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("failed to create state manager for simulation %s: %w", name, err)
|
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{
|
sim := &Simulation{
|
||||||
name: name,
|
name: name,
|
||||||
Node: nd,
|
Node: nd,
|
||||||
StateManager: stmgr.NewStateManager(nd.Chainstore),
|
StateManager: stmgr.NewStateManager(nd.Chainstore, vm.Syscalls(mock.Verifier)),
|
||||||
stages: stages,
|
stages: stages,
|
||||||
}
|
}
|
||||||
if has, err := nd.MetadataDS.Has(sim.key("head")); err != nil {
|
if has, err := nd.MetadataDS.Has(sim.key("head")); err != nil {
|
||||||
|
@ -18,6 +18,8 @@ import (
|
|||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"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"
|
"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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -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)
|
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
|
defer cst.Close() //nolint:errcheck
|
||||||
|
|
||||||
log.Infof("importing chain from %s...", fname)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stm := stmgr.NewStateManager(cst)
|
stm := stmgr.NewStateManager(cst, vm.Syscalls(ffiwrapper.ProofVerifier))
|
||||||
|
|
||||||
if !snapshot {
|
if !snapshot {
|
||||||
log.Infof("validating imported chain...")
|
log.Infof("validating imported chain...")
|
||||||
|
@ -101,8 +101,8 @@ func (d *Driver) ExecuteTipset(bs blockstore.Blockstore, ds ds.Batching, params
|
|||||||
tipset = params.Tipset
|
tipset = params.Tipset
|
||||||
syscalls = vm.Syscalls(ffiwrapper.ProofVerifier)
|
syscalls = vm.Syscalls(ffiwrapper.ProofVerifier)
|
||||||
|
|
||||||
cs = store.NewChainStore(bs, bs, ds, syscalls, nil)
|
cs = store.NewChainStore(bs, bs, ds, nil)
|
||||||
sm = stmgr.NewStateManager(cs)
|
sm = stmgr.NewStateManager(cs, syscalls)
|
||||||
)
|
)
|
||||||
|
|
||||||
if params.Rand == nil {
|
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,
|
// dummy state manager; only to reference the GetNetworkVersion method,
|
||||||
// which does not depend on state.
|
// which does not depend on state.
|
||||||
sm := stmgr.NewStateManager(nil)
|
sm := stmgr.NewStateManager(nil, nil)
|
||||||
|
|
||||||
vmOpts := &vm.VMOpts{
|
vmOpts := &vm.VMOpts{
|
||||||
StateBase: params.Preroot,
|
StateBase: params.Preroot,
|
||||||
|
@ -72,8 +72,8 @@ func MessagePool(lc fx.Lifecycle, mpp messagepool.Provider, ds dtypes.MetadataDS
|
|||||||
return mp, nil
|
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 {
|
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, syscalls, j)
|
chain := store.NewChainStore(cbs, sbs, ds, j)
|
||||||
|
|
||||||
if err := chain.Load(); err != nil {
|
if err := chain.Load(); err != nil {
|
||||||
log.Warnf("loading chain state from disk: %s", err)
|
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
|
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 {
|
if !build.Devnet {
|
||||||
return "testnetnet", nil
|
return "testnetnet", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := helpers.LifecycleCtx(mctx, lc)
|
ctx := helpers.LifecycleCtx(mctx, lc)
|
||||||
|
|
||||||
sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, us)
|
sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, syscalls, us)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
package modules
|
package modules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/filecoin-project/lotus/chain/vm"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/stmgr"
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||||||
"github.com/filecoin-project/lotus/chain/store"
|
"github.com/filecoin-project/lotus/chain/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StateManager(lc fx.Lifecycle, cs *store.ChainStore, us stmgr.UpgradeSchedule) (*stmgr.StateManager, error) {
|
func StateManager(lc fx.Lifecycle, cs *store.ChainStore, sys vm.SyscallBuilder, us stmgr.UpgradeSchedule) (*stmgr.StateManager, error) {
|
||||||
sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, us)
|
sm, err := stmgr.NewStateManagerWithUpgradeSchedule(cs, sys, us)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user