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++