From e85dfc7499789c555e49bc4bea5f7fbf5900dd70 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Thu, 23 Jul 2020 15:32:28 -0700 Subject: [PATCH 1/8] add command to print state size statistics of actors --- api/api_full.go | 6 +- cli/state.go | 8 +++ cmd/lotus-shed/main.go | 2 +- cmd/lotus-shed/stateroot-stats.go | 114 +++++++++++++++++++++++++++++- 4 files changed, 126 insertions(+), 4 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index c557cb456..e3a0d8fb4 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -67,7 +67,11 @@ type FullNode interface { // ChainHasObj checks if a given CID exists in the chain blockstore. ChainHasObj(context.Context, cid.Cid) (bool, error) - ChainStatObj(context.Context, cid.Cid, cid.Cid) (ObjStat, error) + + // ChainStatObj returns statistics about the graph referenced by 'obj'. + // If 'base' is also specified, then the returned stat will be a diff + // between the two objects. + ChainStatObj(ctx context.Context, obj cid.Cid, base cid.Cid) (ObjStat, error) // ChainSetHead forcefully sets current chain head. Use with caution. ChainSetHead(context.Context, types.TipSetKey) error diff --git a/cli/state.go b/cli/state.go index 8c5d712d0..d1b9f9b71 100644 --- a/cli/state.go +++ b/cli/state.go @@ -131,7 +131,15 @@ func LoadTipSet(ctx context.Context, cctx *cli.Context, api api.FullNode) (*type return nil, nil } + return ParseTipSetRef(ctx, api, tss) +} + +func ParseTipSetRef(ctx context.Context, api api.FullNode, tss string) (*types.TipSet, error) { if tss[0] == '@' { + if tss == "@head" { + return api.ChainHead(ctx) + } + var h uint64 if _, err := fmt.Sscanf(tss, "@%d", &h); err != nil { return nil, xerrors.Errorf("parsing height tipset ref: %w", err) diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 15cd3cfb3..7a2189c73 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -21,7 +21,7 @@ func main() { keyinfoCmd, noncefix, bigIntParseCmd, - staterootStatsCmd, + staterootCmd, importCarCmd, commpToCidCmd, fetchParamCmd, diff --git a/cmd/lotus-shed/stateroot-stats.go b/cmd/lotus-shed/stateroot-stats.go index 0546e5315..c02e0202a 100644 --- a/cmd/lotus-shed/stateroot-stats.go +++ b/cmd/lotus-shed/stateroot-stats.go @@ -2,17 +2,29 @@ package main import ( "fmt" + "sort" + "github.com/multiformats/go-multihash" "github.com/urfave/cli/v2" "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" ) -var staterootStatsCmd = &cli.Command{ - Name: "stateroot-stats", +var staterootCmd = &cli.Command{ + Name: "stateroot", + Subcommands: []*cli.Command{ + staterootDiffsCmd, + staterootStatCmd, + }, +} + +var staterootDiffsCmd = &cli.Command{ + Name: "diffs", Description: "Walk down the chain and collect stats-obj changes between tipsets", Flags: []cli.Flag{ &cli.StringFlag{ @@ -92,3 +104,101 @@ var staterootStatsCmd = &cli.Command{ return nil }, } + +type statItem struct { + Addr address.Address + Actor *types.Actor + Stat api.ObjStat +} + +var staterootStatCmd = &cli.Command{ + Name: "stat", + Usage: "print statistics for the stateroot of a given block", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "tipset", + Usage: "specify tipset to start from", + }, + }, + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + + defer closer() + ctx := lcli.ReqContext(cctx) + + ts, err := lcli.LoadTipSet(ctx, cctx, api) + if err != nil { + return err + } + + if ts == nil { + ts, err = api.ChainHead(ctx) + if err != nil { + return err + } + } + + var addrs []address.Address + + for _, inp := range cctx.Args().Slice() { + a, err := address.NewFromString(inp) + if err != nil { + return err + } + addrs = append(addrs, a) + } + + if len(addrs) == 0 { + allActors, err := api.StateListActors(ctx, ts.Key()) + if err != nil { + return err + } + addrs = allActors + } + + var infos []statItem + for _, a := range addrs { + act, err := api.StateGetActor(ctx, a, ts.Key()) + if err != nil { + return err + } + + stat, err := api.ChainStatObj(ctx, act.Head, cid.Undef) + if err != nil { + return err + } + + infos = append(infos, statItem{ + Addr: a, + Actor: act, + Stat: stat, + }) + } + + sort.Slice(infos, func(i, j int) bool { + return infos[i].Stat.Size > infos[j].Stat.Size + }) + + outcap := 10 + if cctx.Args().Len() > outcap { + outcap = cctx.Args().Len() + } + if len(infos) < outcap { + outcap = len(infos) + } + + fmt.Print("Addr\tType\tSize\n") + for _, inf := range infos[:outcap] { + cmh, err := multihash.Decode(inf.Actor.Code.Hash()) + if err != nil { + return err + } + + fmt.Printf("%s\t%s\t%d\n", inf.Addr, string(cmh.Digest), inf.Stat.Size) + } + return nil + }, +} From 5d930ed15c1302b348ecc0e67b092c4d60860fac Mon Sep 17 00:00:00 2001 From: hannahhoward Date: Tue, 28 Jul 2020 12:00:53 -0700 Subject: [PATCH 2/8] fix(deps): update to tagged go-fil-markets use 0.5.2 instead of custom commit --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 205b72335..8127456b5 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 github.com/filecoin-project/go-data-transfer v0.5.1 github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f - github.com/filecoin-project/go-fil-markets v0.5.2-0.20200728110539-caae3fda6623 + github.com/filecoin-project/go-fil-markets v0.5.2 github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24 github.com/filecoin-project/go-multistore v0.0.2 github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261 diff --git a/go.sum b/go.sum index fc01bb3ea..c302852bf 100644 --- a/go.sum +++ b/go.sum @@ -244,8 +244,8 @@ github.com/filecoin-project/go-data-transfer v0.5.1/go.mod h1:PRs78hp9u8T4G2Jce5 github.com/filecoin-project/go-fil-commcid v0.0.0-20200208005934-2b8bd03caca5/go.mod h1:JbkIgFF/Z9BDlvrJO1FuKkaWsH673/UdFaiVS6uIHlA= github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f h1:GxJzR3oRIMTPtpZ0b7QF8FKPK6/iPAc7trhlL5k/g+s= github.com/filecoin-project/go-fil-commcid v0.0.0-20200716160307-8f644712406f/go.mod h1:Eaox7Hvus1JgPrL5+M3+h7aSPHc0cVqpSxA+TxIEpZQ= -github.com/filecoin-project/go-fil-markets v0.5.2-0.20200728110539-caae3fda6623 h1:LuX25+rzM56XX+M4413ngRA+RAhm88vlFbsvbVtpcDc= -github.com/filecoin-project/go-fil-markets v0.5.2-0.20200728110539-caae3fda6623/go.mod h1:zDhwmUy/AS/xCJOayW7Cedff9SDuSdGIWRnGXBDjcOk= +github.com/filecoin-project/go-fil-markets v0.5.2 h1:4xW3JoPnPDKP+f4s/tWLmuYaReGdjetHZT7qgXivdiw= +github.com/filecoin-project/go-fil-markets v0.5.2/go.mod h1:zDhwmUy/AS/xCJOayW7Cedff9SDuSdGIWRnGXBDjcOk= github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24 h1:Jc7vkplmZYVuaEcSXGHDwefvZIdoyyaoGDLqSr8Svms= github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24/go.mod h1:j6zV//WXIIY5kky873Q3iIKt/ViOE8rcijovmpxrXzM= github.com/filecoin-project/go-multistore v0.0.2 h1:JZEddnXXt3mMzHi7bi9IH7Yi1NpGLy19J5Lk/xbxBMs= From 859168015a7291142db65755c7193b07bde0e288 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 27 Jul 2020 19:51:30 -0400 Subject: [PATCH 3/8] Use specs actor's MinerNominalPowerMeetsConsensusMinimum --- api/api_full.go | 1 + chain/stmgr/utils.go | 16 +++++++ chain/sub/incoming.go | 97 ++++++++++++++++++++++++++----------------- chain/sync.go | 9 ++++ miner/miner.go | 23 ++-------- 5 files changed, 90 insertions(+), 56 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 2b14c62d8..974e67b4c 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -637,6 +637,7 @@ type MiningBaseInfo struct { SectorSize abi.SectorSize PrevBeaconEntry types.BeaconEntry BeaconEntries []types.BeaconEntry + HasMinPower bool } type BlockTemplate struct { diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index b5e47e2c9..49df52137 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -567,6 +567,11 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcn beacon.RandomBe return nil, xerrors.Errorf("resolving worker address: %w", err) } + hmp, err := MinerHasMinPower(ctx, sm, maddr, lbts) + if err != nil { + return nil, xerrors.Errorf("determining if miner has min power failed: %w", err) + } + return &api.MiningBaseInfo{ MinerPower: mpow.QualityAdjPower, NetworkPower: tpow.QualityAdjPower, @@ -575,6 +580,7 @@ func MinerGetBaseInfo(ctx context.Context, sm *StateManager, bcn beacon.RandomBe SectorSize: info.SectorSize, PrevBeaconEntry: *prev, BeaconEntries: entries, + HasMinPower: hmp, }, nil } @@ -659,3 +665,13 @@ func GetReturnType(ctx context.Context, sm *StateManager, to address.Address, me m := MethodsMap[act.Code][method] return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil } + +func MinerHasMinPower(ctx context.Context, sm *StateManager, addr address.Address, ts *types.TipSet) (bool, error) { + var ps power.State + _, err := sm.LoadActorState(ctx, builtin.StoragePowerActorAddr, &ps, ts) + if err != nil { + return false, xerrors.Errorf("loading power actor state: %w", err) + } + + return ps.MinerNominalPowerMeetsConsensusMinimum(sm.ChainStore().Store(ctx), addr) +} diff --git a/chain/sub/incoming.go b/chain/sub/incoming.go index 875e2658f..b96f9ead2 100644 --- a/chain/sub/incoming.go +++ b/chain/sub/incoming.go @@ -191,14 +191,14 @@ func (bv *BlockValidator) Validate(ctx context.Context, pid peer.ID, msg *pubsub // if we can't find it, we check whether we are (near) synced in the chain. // if we are not synced we cannot validate the block and we must ignore it. // if we are synced and the miner is unknown, then the block is rejcected. - key, err := bv.getMinerWorkerKey(ctx, blk) + key, err := bv.checkPowerAndGetWorkerKey(ctx, blk.Header) if err != nil { if bv.isChainNearSynced() { - log.Warnf("received block message from unknown miner over pubsub; rejecting message") + log.Warnf("received block from unknown miner or miner that doesn't meet min power over pubsub; rejecting message") recordFailure("unknown_miner") return pubsub.ValidationReject } else { - log.Warnf("cannot validate block message; unknown miner in unsynced chain") + log.Warnf("cannot validate block message; unknown miner or miner that doesn't meet min power in unsynced chain") return pubsub.ValidationIgnore } } @@ -283,60 +283,83 @@ func (bv *BlockValidator) validateMsgMeta(ctx context.Context, msg *types.BlockM return nil } -func (bv *BlockValidator) getMinerWorkerKey(ctx context.Context, msg *types.BlockMsg) (address.Address, error) { - addr := msg.Header.Miner +func (bv *BlockValidator) checkPowerAndGetWorkerKey(ctx context.Context, bh *types.BlockHeader) (address.Address, error) { + addr := bh.Miner bv.mx.Lock() key, ok := bv.keycache[addr.String()] bv.mx.Unlock() - if ok { - return key, nil + if !ok { + // TODO I have a feeling all this can be simplified by cleverer DI to use the API + ts := bv.chain.GetHeaviestTipSet() + st, _, err := bv.stmgr.TipSetState(ctx, ts) + if err != nil { + return address.Undef, err + } + buf := bufbstore.NewBufferedBstore(bv.chain.Blockstore()) + cst := cbor.NewCborStore(buf) + state, err := state.LoadStateTree(cst, st) + if err != nil { + return address.Undef, err + } + act, err := state.GetActor(addr) + if err != nil { + return address.Undef, err + } + + blk, err := bv.chain.Blockstore().Get(act.Head) + if err != nil { + return address.Undef, err + } + aso := blk.RawData() + + var mst miner.State + err = mst.UnmarshalCBOR(bytes.NewReader(aso)) + if err != nil { + return address.Undef, err + } + + info, err := mst.GetInfo(adt.WrapStore(ctx, cst)) + if err != nil { + return address.Undef, err + } + + worker := info.Worker + key, err = bv.stmgr.ResolveToKeyAddress(ctx, worker, ts) + if err != nil { + return address.Undef, err + } + + bv.mx.Lock() + bv.keycache[addr.String()] = key + bv.mx.Unlock() } - // TODO I have a feeling all this can be simplified by cleverer DI to use the API - ts := bv.chain.GetHeaviestTipSet() - st, _, err := bv.stmgr.TipSetState(ctx, ts) - if err != nil { - return address.Undef, err - } - buf := bufbstore.NewBufferedBstore(bv.chain.Blockstore()) - cst := cbor.NewCborStore(buf) - state, err := state.LoadStateTree(cst, st) - if err != nil { - return address.Undef, err - } - act, err := state.GetActor(addr) + // we check that the miner met the minimum power at the lookback tipset + + baseTs, err := bv.stmgr.ChainStore().LoadTipSet(types.NewTipSetKey(bh.Parents...)) if err != nil { + log.Warnf("failed to load parent tipset of incoming block") return address.Undef, err } - blk, err := bv.chain.Blockstore().Get(act.Head) - if err != nil { - return address.Undef, err - } - aso := blk.RawData() - - var mst miner.State - err = mst.UnmarshalCBOR(bytes.NewReader(aso)) + lbts, err := stmgr.GetLookbackTipSetForRound(ctx, bv.stmgr, baseTs, bh.Height) if err != nil { + log.Warnf("failed to load lookback tipset for incoming block") return address.Undef, err } - info, err := mst.GetInfo(adt.WrapStore(ctx, cst)) + hmp, err := stmgr.MinerHasMinPower(ctx, bv.stmgr, bh.Miner, lbts) if err != nil { + log.Warnf("failed to determine if incoming block's miner has minimum power") return address.Undef, err } - worker := info.Worker - key, err = bv.stmgr.ResolveToKeyAddress(ctx, worker, ts) - if err != nil { - return address.Undef, err + if !hmp { + log.Warnf("incoming block's miner does not have minimum power") + return address.Undef, xerrors.New("incoming block's miner does not have minimum power") } - bv.mx.Lock() - bv.keycache[addr.String()] = key - bv.mx.Unlock() - return key, nil } diff --git a/chain/sync.go b/chain/sync.go index 22edd4060..4555a980c 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -739,6 +739,15 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock) (er return xerrors.Errorf("block is not claiming to be a winner") } + hp, err := stmgr.MinerHasMinPower(ctx, syncer.sm, h.Miner, lbts) + if err != nil { + return xerrors.Errorf("determining if miner has min power failed: %w", err) + } + + if !hp { + return xerrors.New("block's miner does not meet minimum power threshold") + } + rBeacon := *prevBeacon if len(h.BeaconEntries) != 0 { rBeacon = h.BeaconEntries[len(h.BeaconEntries)-1] diff --git a/miner/miner.go b/miner/miner.go index cc5c85b5a..75edf82d3 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -14,7 +14,6 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/specs-actors/actors/abi" "github.com/filecoin-project/specs-actors/actors/abi/big" - "github.com/filecoin-project/specs-actors/actors/builtin/power" "github.com/filecoin-project/specs-actors/actors/crypto" lru "github.com/hashicorp/golang-lru" @@ -269,15 +268,6 @@ func (m *Miner) GetBestMiningCandidate(ctx context.Context) (*MiningBase, error) return m.lastWork, nil } -func (m *Miner) hasPower(ctx context.Context, addr address.Address, ts *types.TipSet) (bool, error) { - mpower, err := m.api.StateMinerPower(ctx, addr, ts.Key()) - if err != nil { - return false, err - } - - return mpower.MinerPower.QualityAdjPower.GreaterThanEqual(power.ConsensusMinerMinPower), nil -} - // mineOne attempts to mine a single block, and does so synchronously, if and // only if we are eligible to mine. // @@ -301,6 +291,10 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, if mbi == nil { return nil, nil } + if !mbi.HasMinPower { + // slashed or just have no power yet + return nil, nil + } tMBI := build.Clock.Now() @@ -309,15 +303,6 @@ func (m *Miner) mineOne(ctx context.Context, base *MiningBase) (*types.BlockMsg, tDrand := build.Clock.Now() bvals := mbi.BeaconEntries - hasPower, err := m.hasPower(ctx, m.address, base.TipSet) - if err != nil { - return nil, xerrors.Errorf("checking if miner is slashed: %w", err) - } - if !hasPower { - // slashed or just have no power yet - return nil, nil - } - tPowercheck := build.Clock.Now() log.Infof("Time delta between now and our mining base: %ds (nulls: %d)", uint64(build.Clock.Now().Unix())-base.TipSet.MinTimestamp(), base.NullRounds) From de867d7a9a60c129995c1fbdd1ae652ae68d0e3f Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Sat, 25 Jul 2020 18:29:33 -0400 Subject: [PATCH 4/8] Correct calculation of TotalCircSupply --- chain/gen/genesis/genesis.go | 2 +- chain/gen/genesis/miners.go | 6 ++- chain/stmgr/call.go | 4 +- chain/stmgr/forks_test.go | 4 +- chain/stmgr/stmgr.go | 98 +++++++++++++++++++++++++++++++++--- chain/stmgr/utils.go | 4 +- chain/store/weight.go | 42 ---------------- chain/validation/applier.go | 2 +- chain/vm/runtime.go | 34 ++++++++++--- chain/vm/vm.go | 10 +++- 10 files changed, 140 insertions(+), 66 deletions(-) diff --git a/chain/gen/genesis/genesis.go b/chain/gen/genesis/genesis.go index 54d4d9e2c..187b7a507 100644 --- a/chain/gen/genesis/genesis.go +++ b/chain/gen/genesis/genesis.go @@ -321,7 +321,7 @@ func VerifyPreSealedData(ctx context.Context, cs *store.ChainStore, stateroot ci verifNeeds := make(map[address.Address]abi.PaddedPieceSize) var sum abi.PaddedPieceSize - vm, err := vm.NewVM(stateroot, 0, &fakeRand{}, cs.Blockstore(), mkFakedSigSyscalls(cs.VMSys())) + vm, err := vm.NewVM(stateroot, 0, &fakeRand{}, cs.Blockstore(), mkFakedSigSyscalls(cs.VMSys()), nil) if err != nil { return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err) } diff --git a/chain/gen/genesis/miners.go b/chain/gen/genesis/miners.go index a0f0ee4b1..928b847a4 100644 --- a/chain/gen/genesis/miners.go +++ b/chain/gen/genesis/miners.go @@ -56,7 +56,11 @@ func mkFakedSigSyscalls(base vm.SyscallBuilder) vm.SyscallBuilder { } func SetupStorageMiners(ctx context.Context, cs *store.ChainStore, sroot cid.Cid, miners []genesis.Miner) (cid.Cid, error) { - vm, err := vm.NewVM(sroot, 0, &fakeRand{}, cs.Blockstore(), mkFakedSigSyscalls(cs.VMSys())) + vc := func(context.Context, abi.ChainEpoch) (abi.TokenAmount, error) { + return big.Zero(), nil + } + + vm, err := vm.NewVM(sroot, 0, &fakeRand{}, cs.Blockstore(), mkFakedSigSyscalls(cs.VMSys()), vc) if err != nil { return cid.Undef, xerrors.Errorf("failed to create NewVM: %w", err) } diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 4da912614..cd4112365 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -22,7 +22,7 @@ func (sm *StateManager) CallRaw(ctx context.Context, msg *types.Message, bstate ctx, span := trace.StartSpan(ctx, "statemanager.CallRaw") defer span.End() - vmi, err := vm.NewVM(bstate, bheight, r, sm.cs.Blockstore(), sm.cs.VMSys()) + vmi, err := vm.NewVM(bstate, bheight, r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } @@ -106,7 +106,7 @@ func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, pri ) } - vmi, err := vm.NewVM(state, ts.Height(), r, sm.cs.Blockstore(), sm.cs.VMSys()) + vmi, err := vm.NewVM(state, ts.Height(), r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) if err != nil { return nil, xerrors.Errorf("failed to set up vm: %w", err) } diff --git a/chain/stmgr/forks_test.go b/chain/stmgr/forks_test.go index fd597fe8d..c1c9289fa 100644 --- a/chain/stmgr/forks_test.go +++ b/chain/stmgr/forks_test.go @@ -151,8 +151,8 @@ func TestForkHeightTriggers(t *testing.T) { } inv.Register(builtin.PaymentChannelActorCodeID, &testActor{}, &testActorState{}) - sm.SetVMConstructor(func(c cid.Cid, h abi.ChainEpoch, r vm.Rand, b blockstore.Blockstore, s vm.SyscallBuilder) (*vm.VM, error) { - nvm, err := vm.NewVM(c, h, r, b, s) + sm.SetVMConstructor(func(c cid.Cid, h abi.ChainEpoch, r vm.Rand, b blockstore.Blockstore, s vm.SyscallBuilder, vc vm.VestedCalculator) (*vm.VM, error) { + nvm, err := vm.NewVM(c, h, r, b, s, sm.GetVestedFunds) if err != nil { return nil, err } diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 171483f9d..95c911988 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -3,6 +3,7 @@ package stmgr import ( "context" "fmt" + "github.com/filecoin-project/specs-actors/actors/builtin/multisig" "sync" "github.com/filecoin-project/go-address" @@ -35,10 +36,11 @@ var log = logging.Logger("statemgr") type StateManager struct { cs *store.ChainStore - stCache map[string][]cid.Cid - compWait map[string]chan struct{} - stlk sync.Mutex - newVM func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder) (*vm.VM, error) + stCache map[string][]cid.Cid + compWait map[string]chan struct{} + stlk sync.Mutex + newVM func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder, vm.VestedCalculator) (*vm.VM, error) + genesisMsigs []multisig.State } func NewStateManager(cs *store.ChainStore) *StateManager { @@ -147,7 +149,7 @@ type BlockMessages struct { type ExecCallback func(cid.Cid, *types.Message, *vm.ApplyRet) error func (sm *StateManager) ApplyBlocks(ctx context.Context, parentEpoch abi.ChainEpoch, pstate cid.Cid, bms []BlockMessages, epoch abi.ChainEpoch, r vm.Rand, cb ExecCallback) (cid.Cid, cid.Cid, error) { - vmi, err := sm.newVM(pstate, parentEpoch, r, sm.cs.Blockstore(), sm.cs.VMSys()) + vmi, err := sm.newVM(pstate, parentEpoch, r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("instantiating VM failed: %w", err) } @@ -777,6 +779,90 @@ func (sm *StateManager) ValidateChain(ctx context.Context, ts *types.TipSet) err return nil } -func (sm *StateManager) SetVMConstructor(nvm func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder) (*vm.VM, error)) { +func (sm *StateManager) SetVMConstructor(nvm func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder, vm.VestedCalculator) (*vm.VM, error)) { sm.newVM = nvm } + +type GenesisMsigEntry struct { + totalFunds abi.TokenAmount + unitVest abi.TokenAmount +} + +func (sm *StateManager) setupGenesisMsigs(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) + } + + r, err := adt.AsMap(sm.cs.Store(ctx), st) + if err != nil { + return xerrors.Errorf("getting genesis actors: %w", err) + } + + totalsByEpoch := make(map[abi.ChainEpoch]abi.TokenAmount) + var act types.Actor + err = r.ForEach(&act, func(k string) error { + if act.Code == builtin.MultisigActorCodeID { + var s multisig.State + err := sm.cs.Store(ctx).Get(ctx, act.Head, &s) + if err != nil { + return err + } + + if s.StartEpoch != 0 { + return xerrors.New("genesis multisig doesn't start vesting at epoch 0!") + } + + ot, f := totalsByEpoch[s.UnlockDuration] + if f { + totalsByEpoch[s.UnlockDuration] = big.Add(ot, s.InitialBalance) + } else { + totalsByEpoch[s.UnlockDuration] = s.InitialBalance + } + + } + return nil + }) + + if err != nil { + return xerrors.Errorf("error setting up composite genesis multisigs: %w", err) + } + + sm.genesisMsigs = make([]multisig.State, 0, len(totalsByEpoch)) + for k, v := range totalsByEpoch { + ns := multisig.State{ + InitialBalance: v, + UnlockDuration: k, + PendingTxns: cid.Undef, + } + sm.genesisMsigs = append(sm.genesisMsigs, ns) + } + + return nil +} + +func (sm *StateManager) GetVestedFunds(ctx context.Context, height abi.ChainEpoch) (abi.TokenAmount, error) { + + if sm.genesisMsigs == nil { + err := sm.setupGenesisMsigs(ctx) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to setup genesis msig entries: %w", err) + } + } + vf := big.Zero() + for _, v := range sm.genesisMsigs { + au := big.Sub(v.InitialBalance, v.AmountLocked(height)) + vf = big.Add(vf, au) + } + return vf, nil +} diff --git a/chain/stmgr/utils.go b/chain/stmgr/utils.go index b5e47e2c9..c4967ce9f 100644 --- a/chain/stmgr/utils.go +++ b/chain/stmgr/utils.go @@ -440,7 +440,7 @@ func ComputeState(ctx context.Context, sm *StateManager, height abi.ChainEpoch, } r := store.NewChainRand(sm.cs, ts.Cids(), height) - vmi, err := vm.NewVM(base, height, r, sm.cs.Blockstore(), sm.cs.VMSys()) + vmi, err := vm.NewVM(base, height, r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) if err != nil { return cid.Undef, nil, err } @@ -589,7 +589,7 @@ func (sm *StateManager) CirculatingSupply(ctx context.Context, ts *types.TipSet) } r := store.NewChainRand(sm.cs, ts.Cids(), ts.Height()) - vmi, err := vm.NewVM(st, ts.Height(), r, sm.cs.Blockstore(), sm.cs.VMSys()) + vmi, err := vm.NewVM(st, ts.Height(), r, sm.cs.Blockstore(), sm.cs.VMSys(), sm.GetVestedFunds) if err != nil { return big.Zero(), err } diff --git a/chain/store/weight.go b/chain/store/weight.go index eb385f816..adbc076fd 100644 --- a/chain/store/weight.go +++ b/chain/store/weight.go @@ -7,7 +7,6 @@ import ( "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/state" "github.com/filecoin-project/lotus/chain/types" - "github.com/filecoin-project/lotus/chain/vm" big2 "github.com/filecoin-project/specs-actors/actors/abi/big" "github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin/power" @@ -73,44 +72,3 @@ func (cs *ChainStore) Weight(ctx context.Context, ts *types.TipSet) (types.BigIn return types.BigInt{Int: out}, nil } - -// todo: dedupe with state manager -func (cs *ChainStore) call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*types.MessageReceipt, error) { - bstate := ts.ParentState() - - r := NewChainRand(cs, ts.Cids(), ts.Height()) - - vmi, err := vm.NewVM(bstate, ts.Height(), r, cs.bs, cs.vmcalls) - if err != nil { - return nil, xerrors.Errorf("failed to set up vm: %w", err) - } - - if msg.GasLimit == 0 { - msg.GasLimit = 10000000000 - } - if msg.GasPrice == types.EmptyInt { - msg.GasPrice = types.NewInt(0) - } - if msg.Value == types.EmptyInt { - msg.Value = types.NewInt(0) - } - - fromActor, err := vmi.StateTree().GetActor(msg.From) - if err != nil { - return nil, xerrors.Errorf("call raw get actor: %s", err) - } - - msg.Nonce = fromActor.Nonce - - // TODO: maybe just use the invoker directly? - // TODO: use signed message length for secp messages - ret, err := vmi.ApplyMessage(ctx, msg) - if err != nil { - return nil, xerrors.Errorf("apply message failed: %w", err) - } - - if ret.ActorErr != nil { - log.Warnf("chain call failed: %s", ret.ActorErr) - } - return &ret.MessageReceipt, nil -} diff --git a/chain/validation/applier.go b/chain/validation/applier.go index 3044c77a1..e9b77cbfc 100644 --- a/chain/validation/applier.go +++ b/chain/validation/applier.go @@ -135,7 +135,7 @@ func (a *Applier) applyMessage(epoch abi.ChainEpoch, lm types.ChainMsg) (vtypes. ctx := context.TODO() base := a.stateWrapper.Root() - lotusVM, err := vm.NewVM(base, epoch, &vmRand{}, a.stateWrapper.bs, a.syscalls) + lotusVM, err := vm.NewVM(base, epoch, &vmRand{}, a.stateWrapper.bs, a.syscalls, nil) // need to modify the VM invoker to add the puppet actor chainValInvoker := vm.NewInvoker() chainValInvoker.Register(puppet.PuppetActorCodeID, puppet.Actor{}, puppet.State{}) diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index c82f5a84d..aade7a911 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -5,6 +5,7 @@ import ( "context" "encoding/binary" "fmt" + samarket "github.com/filecoin-project/specs-actors/actors/builtin/market" gruntime "runtime" "time" @@ -60,38 +61,55 @@ type Runtime struct { } func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount { - total := types.FromFil(build.TotalFilecoin) + + filVested, err := rt.vm.GetVestedFunds(rt.ctx) + if err != nil { + rt.Abortf(exitcode.ErrIllegalState, "failed to get vested funds for computing total supply: %s", err) + } rew, err := rt.state.GetActor(builtin.RewardActorAddr) if err != nil { rt.Abortf(exitcode.ErrIllegalState, "failed to get reward actor for computing total supply: %s", err) } + filMined := types.BigSub(types.FromFil(build.TotalFilecoin), rew.Balance) + burnt, err := rt.state.GetActor(builtin.BurntFundsActorAddr) if err != nil { rt.Abortf(exitcode.ErrIllegalState, "failed to get reward actor for computing total supply: %s", err) } + filBurned := burnt.Balance + market, err := rt.state.GetActor(builtin.StorageMarketActorAddr) if err != nil { rt.Abortf(exitcode.ErrIllegalState, "failed to get reward actor for computing total supply: %s", err) } + var mst samarket.State + if err := rt.cst.Get(rt.ctx, market.Head, &mst); err != nil { + rt.Abortf(exitcode.ErrIllegalState, "failed to get market state: %s", err) + } + + filMarketLocked := types.BigAdd(mst.TotalClientLockedCollateral, mst.TotalProviderLockedCollateral) + filMarketLocked = types.BigAdd(filMarketLocked, mst.TotalClientStorageFee) + power, err := rt.state.GetActor(builtin.StoragePowerActorAddr) if err != nil { rt.Abortf(exitcode.ErrIllegalState, "failed to get reward actor for computing total supply: %s", err) } - total = types.BigSub(total, rew.Balance) - total = types.BigSub(total, burnt.Balance) - total = types.BigSub(total, market.Balance) - - var st sapower.State - if err := rt.cst.Get(rt.ctx, power.Head, &st); err != nil { + var pst sapower.State + if err := rt.cst.Get(rt.ctx, power.Head, &pst); err != nil { rt.Abortf(exitcode.ErrIllegalState, "failed to get storage power state: %s", err) } - return types.BigSub(total, st.TotalPledgeCollateral) + filLocked := types.BigAdd(filMarketLocked, pst.TotalPledgeCollateral) + + ret := types.BigAdd(filVested, filMined) + ret = types.BigSub(ret, filBurned) + ret = types.BigSub(ret, filLocked) + return ret } func (rt *Runtime) ResolveAddress(addr address.Address) (ret address.Address, ok bool) { diff --git a/chain/vm/vm.go b/chain/vm/vm.go index d9e056be6..097775253 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -138,6 +138,8 @@ func (vm *UnsafeVM) MakeRuntime(ctx context.Context, msg *types.Message, origin return vm.VM.makeRuntime(ctx, msg, origin, originNonce, usedGas, nac) } +type VestedCalculator func(context.Context, abi.ChainEpoch) (abi.TokenAmount, error) + type VM struct { cstate *state.StateTree base cid.Cid @@ -146,11 +148,12 @@ type VM struct { blockHeight abi.ChainEpoch inv *Invoker rand Rand + vc VestedCalculator Syscalls SyscallBuilder } -func NewVM(base cid.Cid, height abi.ChainEpoch, r Rand, cbs blockstore.Blockstore, syscalls SyscallBuilder) (*VM, error) { +func NewVM(base cid.Cid, height abi.ChainEpoch, r Rand, cbs blockstore.Blockstore, syscalls SyscallBuilder, vestedCalc VestedCalculator) (*VM, error) { buf := bufbstore.NewBufferedBstore(cbs) cst := cbor.NewCborStore(buf) state, err := state.LoadStateTree(cst, base) @@ -166,6 +169,7 @@ func NewVM(base cid.Cid, height abi.ChainEpoch, r Rand, cbs blockstore.Blockstor blockHeight: height, inv: NewInvoker(), rand: r, // TODO: Probably should be a syscall + vc: vestedCalc, Syscalls: syscalls, }, nil } @@ -662,6 +666,10 @@ func (vm *VM) SetInvoker(i *Invoker) { vm.inv = i } +func (vm *VM) GetVestedFunds(ctx context.Context) (abi.TokenAmount, error) { + return vm.vc(ctx, vm.blockHeight) +} + func (vm *VM) incrementNonce(addr address.Address) error { return vm.cstate.MutateActor(addr, func(a *types.Actor) error { a.Nonce++ From 7153bda0be8d8af6e90ab5d7f647ecd25d552d30 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 27 Jul 2020 18:50:59 -0400 Subject: [PATCH 5/8] Correct MiningRewardTotal and calculation of filMined --- build/params_shared_vals.go | 2 +- chain/vm/runtime.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/params_shared_vals.go b/build/params_shared_vals.go index 4ecb31054..1c31cbe54 100644 --- a/build/params_shared_vals.go +++ b/build/params_shared_vals.go @@ -60,7 +60,7 @@ const WinningPoStSectorSetLookback = abi.ChainEpoch(10) // Devnet settings const TotalFilecoin = uint64(2_000_000_000) -const MiningRewardTotal = uint64(1_900_000_000) +const MiningRewardTotal = uint64(1_100_000_000) const FilecoinPrecision = uint64(1_000_000_000_000_000_000) diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index aade7a911..f34f13e4b 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -72,7 +72,7 @@ func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount { rt.Abortf(exitcode.ErrIllegalState, "failed to get reward actor for computing total supply: %s", err) } - filMined := types.BigSub(types.FromFil(build.TotalFilecoin), rew.Balance) + filMined := types.BigSub(types.FromFil(build.MiningRewardTotal), rew.Balance) burnt, err := rt.state.GetActor(builtin.BurntFundsActorAddr) if err != nil { From 0b1d80d3e3055ded57fbe02686e732a7a0a5fe21 Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Mon, 27 Jul 2020 19:10:13 -0400 Subject: [PATCH 6/8] Rename constants to match CE spec --- build/params_shared_vals.go | 6 +++--- chain/types/bigint.go | 2 +- chain/vm/runtime.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/params_shared_vals.go b/build/params_shared_vals.go index 1c31cbe54..6ef6e675f 100644 --- a/build/params_shared_vals.go +++ b/build/params_shared_vals.go @@ -59,8 +59,8 @@ const WinningPoStSectorSetLookback = abi.ChainEpoch(10) // ///// // Devnet settings -const TotalFilecoin = uint64(2_000_000_000) -const MiningRewardTotal = uint64(1_100_000_000) +const FilBase = uint64(2_000_000_000) +const FilAllocStorageMining = uint64(1_100_000_000) const FilecoinPrecision = uint64(1_000_000_000_000_000_000) @@ -69,7 +69,7 @@ var InitialRewardBalance *big.Int // TODO: Move other important consts here func init() { - InitialRewardBalance = big.NewInt(int64(MiningRewardTotal)) + InitialRewardBalance = big.NewInt(int64(FilAllocStorageMining)) InitialRewardBalance = InitialRewardBalance.Mul(InitialRewardBalance, big.NewInt(int64(FilecoinPrecision))) } diff --git a/chain/types/bigint.go b/chain/types/bigint.go index a7b25870a..466b9c556 100644 --- a/chain/types/bigint.go +++ b/chain/types/bigint.go @@ -11,7 +11,7 @@ import ( const BigIntMaxSerializedLen = 128 // is this big enough? or too big? -var TotalFilecoinInt = FromFil(build.TotalFilecoin) +var TotalFilecoinInt = FromFil(build.FilBase) var EmptyInt = BigInt{} diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index f34f13e4b..c2ca936a7 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -72,7 +72,7 @@ func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount { rt.Abortf(exitcode.ErrIllegalState, "failed to get reward actor for computing total supply: %s", err) } - filMined := types.BigSub(types.FromFil(build.MiningRewardTotal), rew.Balance) + filMined := types.BigSub(types.FromFil(build.FilAllocStorageMining), rew.Balance) burnt, err := rt.state.GetActor(builtin.BurntFundsActorAddr) if err != nil { From 7b29a1ae73b44e89daea27b92c612e003542ad81 Mon Sep 17 00:00:00 2001 From: whyrusleeping Date: Tue, 28 Jul 2020 16:31:05 -0700 Subject: [PATCH 7/8] only republish the next few messages from your mempool --- chain/messagepool/messagepool.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index c02ea3712..b4ae32b97 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -40,6 +40,8 @@ const futureDebug = false const ReplaceByFeeRatio = 1.25 +const repubMsgLimit = 5 + var ( rbfNum = types.NewInt(uint64((ReplaceByFeeRatio - 1) * 256)) rbfDenom = types.NewInt(256) @@ -269,6 +271,10 @@ func (mp *MessagePool) repubLocal() { log.Infow("republishing local messages", "n", len(outputMsgs)) } + if len(outputMsgs) > repubMsgLimit { + outputMsgs = outputMsgs[:repubMsgLimit] + } + for _, msg := range outputMsgs { msgb, err := msg.Serialize() if err != nil { From 890f56ac38f374a33e2405437ad90dad326d494b Mon Sep 17 00:00:00 2001 From: Aayush Rajasekaran Date: Tue, 28 Jul 2020 17:36:59 -0400 Subject: [PATCH 8/8] Lock before setting up genesis msigs --- build/params_testground.go | 6 +++--- chain/stmgr/stmgr.go | 14 ++++++++------ chain/vm/runtime.go | 7 +++++++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/build/params_testground.go b/build/params_testground.go index ab7c16436..2802943a5 100644 --- a/build/params_testground.go +++ b/build/params_testground.go @@ -49,13 +49,13 @@ var ( TicketRandomnessLookback = abi.ChainEpoch(1) WinningPoStSectorSetLookback = abi.ChainEpoch(10) - TotalFilecoin uint64 = 2_000_000_000 - MiningRewardTotal uint64 = 1_400_000_000 + FilBase uint64 = 2_000_000_000 + FilAllocStorageMining uint64 = 1_400_000_000 FilecoinPrecision uint64 = 1_000_000_000_000_000_000 InitialRewardBalance = func() *big.Int { - v := big.NewInt(int64(MiningRewardTotal)) + v := big.NewInt(int64(FilAllocStorageMining)) v = v.Mul(v, big.NewInt(int64(FilecoinPrecision))) return v }() diff --git a/chain/stmgr/stmgr.go b/chain/stmgr/stmgr.go index 95c911988..1acacba17 100644 --- a/chain/stmgr/stmgr.go +++ b/chain/stmgr/stmgr.go @@ -36,11 +36,12 @@ var log = logging.Logger("statemgr") type StateManager struct { cs *store.ChainStore - stCache map[string][]cid.Cid - compWait map[string]chan struct{} - stlk sync.Mutex - newVM func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder, vm.VestedCalculator) (*vm.VM, error) - genesisMsigs []multisig.State + stCache map[string][]cid.Cid + compWait map[string]chan struct{} + stlk sync.Mutex + genesisMsigLk sync.Mutex + newVM func(cid.Cid, abi.ChainEpoch, vm.Rand, blockstore.Blockstore, vm.SyscallBuilder, vm.VestedCalculator) (*vm.VM, error) + genesisMsigs []multisig.State } func NewStateManager(cs *store.ChainStore) *StateManager { @@ -852,7 +853,8 @@ func (sm *StateManager) setupGenesisMsigs(ctx context.Context) error { } func (sm *StateManager) GetVestedFunds(ctx context.Context, height abi.ChainEpoch) (abi.TokenAmount, error) { - + sm.genesisMsigLk.Lock() + defer sm.genesisMsigLk.Unlock() if sm.genesisMsigs == nil { err := sm.setupGenesisMsigs(ctx) if err != nil { diff --git a/chain/vm/runtime.go b/chain/vm/runtime.go index c2ca936a7..b4f6c6b9a 100644 --- a/chain/vm/runtime.go +++ b/chain/vm/runtime.go @@ -73,6 +73,9 @@ func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount { } filMined := types.BigSub(types.FromFil(build.FilAllocStorageMining), rew.Balance) + if filMined.LessThan(big.Zero()) { + filMined = big.Zero() + } burnt, err := rt.state.GetActor(builtin.BurntFundsActorAddr) if err != nil { @@ -109,6 +112,10 @@ func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount { ret := types.BigAdd(filVested, filMined) ret = types.BigSub(ret, filBurned) ret = types.BigSub(ret, filLocked) + + if ret.LessThan(big.Zero()) { + ret = big.Zero() + } return ret }