Skip to content

Commit

Permalink
Add an endpoint for precise circulating supply
Browse files Browse the repository at this point in the history
  • Loading branch information
arajasek committed Oct 11, 2020
1 parent 00620aa commit 4762430
Show file tree
Hide file tree
Showing 14 changed files with 223 additions and 61 deletions.
8 changes: 6 additions & 2 deletions api/api_full.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,12 @@ type FullNode interface {
// can issue. It takes the deal size and verified status as parameters.
StateDealProviderCollateralBounds(context.Context, abi.PaddedPieceSize, bool, types.TipSetKey) (DealCollateralBounds, error)

// StateCirculatingSupply returns the circulating supply of Filecoin at the given tipset
StateCirculatingSupply(context.Context, types.TipSetKey) (CirculatingSupply, error)
// StateCirculatingSupply returns the exact circulating supply of Filecoin at the given tipset.
// This is not used anywhere in the protocol itself, and is only for external consumption.
StateCirculatingSupply(context.Context, types.TipSetKey) (abi.TokenAmount, error)
// StateVMCirculatingSupply returns an approximation of the circulating supply of Filecoin at the given tipset.
// This is the value reported by the runtime interface to actors code.
StateVMCirculatingSupply(context.Context, types.TipSetKey) (CirculatingSupply, error)
// StateNetworkVersion returns the network version at the given tipset
StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error)

Expand Down
9 changes: 7 additions & 2 deletions api/apistruct/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ type FullNodeStruct struct {
StateVerifiedClientStatus func(context.Context, address.Address, types.TipSetKey) (*abi.StoragePower, error) `perm:"read"`
StateVerifiedRegistryRootKey func(ctx context.Context, tsk types.TipSetKey) (address.Address, error) `perm:"read"`
StateDealProviderCollateralBounds func(context.Context, abi.PaddedPieceSize, bool, types.TipSetKey) (api.DealCollateralBounds, error) `perm:"read"`
StateCirculatingSupply func(context.Context, types.TipSetKey) (api.CirculatingSupply, error) `perm:"read"`
StateCirculatingSupply func(context.Context, types.TipSetKey) (abi.TokenAmount, error) `perm:"read"`
StateVMCirculatingSupply func(context.Context, types.TipSetKey) (api.CirculatingSupply, error) `perm:"read"`
StateNetworkVersion func(context.Context, types.TipSetKey) (stnetwork.Version, error) `perm:"read"`

MsigGetAvailableBalance func(context.Context, address.Address, types.TipSetKey) (types.BigInt, error) `perm:"read"`
Expand Down Expand Up @@ -963,10 +964,14 @@ func (c *FullNodeStruct) StateDealProviderCollateralBounds(ctx context.Context,
return c.Internal.StateDealProviderCollateralBounds(ctx, size, verified, tsk)
}

func (c *FullNodeStruct) StateCirculatingSupply(ctx context.Context, tsk types.TipSetKey) (api.CirculatingSupply, error) {
func (c *FullNodeStruct) StateCirculatingSupply(ctx context.Context, tsk types.TipSetKey) (abi.TokenAmount, error) {
return c.Internal.StateCirculatingSupply(ctx, tsk)
}

func (c *FullNodeStruct) StateVMCirculatingSupply(ctx context.Context, tsk types.TipSetKey) (api.CirculatingSupply, error) {
return c.Internal.StateVMCirculatingSupply(ctx, tsk)
}

func (c *FullNodeStruct) StateNetworkVersion(ctx context.Context, tsk types.TipSetKey) (stnetwork.Version, error) {
return c.Internal.StateNetworkVersion(ctx, tsk)
}
Expand Down
9 changes: 6 additions & 3 deletions chain/actors/builtin/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@ package builtin

import (
"github.com/filecoin-project/go-address"
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
smoothing2 "github.com/filecoin-project/specs-actors/v2/actors/util/smoothing"
"github.com/ipfs/go-cid"
"golang.org/x/xerrors"

builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"

"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/cbor"

"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/types"

builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
miner0 "github.com/filecoin-project/specs-actors/actors/builtin/miner"
proof0 "github.com/filecoin-project/specs-actors/actors/runtime/proof"
smoothing0 "github.com/filecoin-project/specs-actors/actors/util/smoothing"
builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin"
smoothing2 "github.com/filecoin-project/specs-actors/v2/actors/util/smoothing"
)

var SystemActorAddr = builtin0.SystemActorAddr
var BurntFundsActorAddr = builtin0.BurntFundsActorAddr
var CronActorAddr = builtin0.CronActorAddr
var SaftAddress = makeAddress("t0122")
var ReserveAddress = makeAddress("t090")
var RootVerifierAddress = makeAddress("t080")

Expand Down
5 changes: 5 additions & 0 deletions chain/actors/builtin/miner/miner.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package miner

import (
"github.com/filecoin-project/go-state-types/big"
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p-core/peer"
cbg "github.com/whyrusleeping/cbor-gen"
Expand Down Expand Up @@ -182,3 +183,7 @@ type LockedFunds struct {
InitialPledgeRequirement abi.TokenAmount
PreCommitDeposits abi.TokenAmount
}

func (lf LockedFunds) TotalLockedFunds() abi.TokenAmount {
return big.Add(lf.VestingFunds, big.Add(lf.InitialPledgeRequirement, lf.PreCommitDeposits))
}
12 changes: 10 additions & 2 deletions chain/actors/builtin/miner/v0.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,16 @@ type partition0 struct {
store adt.Store
}

func (s *state0) AvailableBalance(bal abi.TokenAmount) (abi.TokenAmount, error) {
return s.GetAvailableBalance(bal), nil
func (s *state0) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmount, err error) {
defer func() {
if r := recover(); r != nil {
err = xerrors.Errorf("failed to get available balance: %w", r)
available = abi.NewTokenAmount(0)
}
}()
// this panics if the miner doesnt have enough funds to cover their locked pledge
available = s.GetAvailableBalance(bal)
return available, err
}

func (s *state0) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) {
Expand Down
12 changes: 10 additions & 2 deletions chain/actors/builtin/miner/v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,16 @@ type partition2 struct {
store adt.Store
}

func (s *state2) AvailableBalance(bal abi.TokenAmount) (abi.TokenAmount, error) {
return s.GetAvailableBalance(bal)
func (s *state2) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmount, err error) {
defer func() {
if r := recover(); r != nil {
err = xerrors.Errorf("failed to get available balance: %w", r)
available = abi.NewTokenAmount(0)
}
}()
// this panics if the miner doesnt have enough funds to cover their locked pledge
available, err = s.GetAvailableBalance(bal)
return available, err
}

func (s *state2) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) {
Expand Down
26 changes: 8 additions & 18 deletions chain/stmgr/forks.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,6 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
return cid.Undef, xerrors.Errorf("loading state tree failed: %w", err)
}

ReserveAddress, err := address.NewFromString("t090")
if err != nil {
return cid.Undef, xerrors.Errorf("failed to parse reserve address: %w", err)
}

tree, err := sm.StateTree(root)
if err != nil {
return cid.Undef, xerrors.Errorf("getting state tree: %w", err)
Expand All @@ -292,7 +287,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
if !sysAcc {
transfers = append(transfers, transfer{
From: addr,
To: ReserveAddress,
To: builtin.ReserveAddress,
Amt: act.Balance,
})
}
Expand All @@ -316,7 +311,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal

transfers = append(transfers, transfer{
From: addr,
To: ReserveAddress,
To: builtin.ReserveAddress,
Amt: available,
})
}
Expand Down Expand Up @@ -367,7 +362,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
nbalance := big.Min(prevBalance, AccountCap)
if nbalance.Sign() != 0 {
transfersBack = append(transfersBack, transfer{
From: ReserveAddress,
From: builtin.ReserveAddress,
To: addr,
Amt: nbalance,
})
Expand All @@ -394,7 +389,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal

mfunds := minerFundsAlloc(power, totalPower)
transfersBack = append(transfersBack, transfer{
From: ReserveAddress,
From: builtin.ReserveAddress,
To: minfo.Worker,
Amt: mfunds,
})
Expand All @@ -414,7 +409,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal

if lbsectors.Length() > 0 {
transfersBack = append(transfersBack, transfer{
From: ReserveAddress,
From: builtin.ReserveAddress,
To: minfo.Worker,
Amt: BaseMinerBalance,
})
Expand All @@ -441,7 +436,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
if err != nil {
return cid.Undef, xerrors.Errorf("failed to load burnt funds actor: %w", err)
}
if err := doTransfer(cb, tree, builtin0.BurntFundsActorAddr, ReserveAddress, burntAct.Balance); err != nil {
if err := doTransfer(cb, tree, builtin0.BurntFundsActorAddr, builtin.ReserveAddress, burntAct.Balance); err != nil {
return cid.Undef, xerrors.Errorf("failed to unburn funds: %w", err)
}

Expand All @@ -457,7 +452,7 @@ func UpgradeFaucetBurnRecovery(ctx context.Context, sm *StateManager, cb ExecCal
}

difference := types.BigSub(DesiredReimbursementBalance, reimb.Balance)
if err := doTransfer(cb, tree, ReserveAddress, reimbAddr, difference); err != nil {
if err := doTransfer(cb, tree, builtin.ReserveAddress, reimbAddr, difference); err != nil {
return cid.Undef, xerrors.Errorf("failed to top up reimbursement account: %w", err)
}

Expand Down Expand Up @@ -542,12 +537,7 @@ func UpgradeRefuel(ctx context.Context, sm *StateManager, cb ExecCallback, root
return cid.Undef, xerrors.Errorf("getting state tree: %w", err)
}

addr, err := address.NewFromString("t0122")
if err != nil {
return cid.Undef, xerrors.Errorf("getting address: %w", err)
}

err = resetMultisigVesting(ctx, store, tree, addr, 0, 0, big.Zero())
err = resetMultisigVesting(ctx, store, tree, builtin.SaftAddress, 0, 0, big.Zero())
if err != nil {
return cid.Undef, xerrors.Errorf("tweaking msig vesting: %w", err)
}
Expand Down
92 changes: 88 additions & 4 deletions chain/stmgr/stmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import (
"sync"

"github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/actors/builtin/verifreg"

_init "github.com/filecoin-project/lotus/chain/actors/builtin/init"

"github.com/filecoin-project/lotus/chain/actors/builtin/miner"

builtin0 "github.com/filecoin-project/specs-actors/actors/builtin"
msig0 "github.com/filecoin-project/specs-actors/actors/builtin/multisig"
Expand Down Expand Up @@ -1294,7 +1299,7 @@ func GetFilBurnt(ctx context.Context, st *state.StateTree) (abi.TokenAmount, err
return burnt.Balance, nil
}

func (sm *StateManager) GetCirculatingSupplyDetailed(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (api.CirculatingSupply, error) {
func (sm *StateManager) GetVMCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (api.CirculatingSupply, error) {
sm.genesisMsigLk.Lock()
defer sm.genesisMsigLk.Unlock()
if sm.preIgnitionGenInfos == nil {
Expand Down Expand Up @@ -1357,12 +1362,91 @@ func (sm *StateManager) GetCirculatingSupplyDetailed(ctx context.Context, height
}

func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.ChainEpoch, st *state.StateTree) (abi.TokenAmount, error) {
csi, err := sm.GetCirculatingSupplyDetailed(ctx, height, st)
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.Store(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.Store(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.Store(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 big.Zero(), err
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 csi.FilCirculating, nil
return circ, nil
}

func (sm *StateManager) GetNtwkVersion(ctx context.Context, height abi.ChainEpoch) network.Version {
Expand Down
37 changes: 27 additions & 10 deletions cli/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -1688,7 +1688,14 @@ func parseParamsForMethod(act cid.Cid, method uint64, args []string) ([]byte, er

var stateCircSupplyCmd = &cli.Command{
Name: "circulating-supply",
Usage: "Get the current circulating supply of filecoin",
Usage: "Get the exact current circulating supply of Filecoin",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "vm-supply",
Usage: "calculates the approximation of the circulating supply used internally by the VM (instead of the exact amount)",
Value: false,
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := GetFullNodeAPI(cctx)
if err != nil {
Expand All @@ -1703,16 +1710,26 @@ var stateCircSupplyCmd = &cli.Command{
return err
}

circ, err := api.StateCirculatingSupply(ctx, ts.Key())
if err != nil {
return err
}
if cctx.IsSet("vm-supply") {
circ, err := api.StateVMCirculatingSupply(ctx, ts.Key())
if err != nil {
return err
}

fmt.Println("Circulating supply: ", types.FIL(circ.FilCirculating))
fmt.Println("Mined: ", types.FIL(circ.FilMined))
fmt.Println("Vested: ", types.FIL(circ.FilVested))
fmt.Println("Burnt: ", types.FIL(circ.FilBurnt))
fmt.Println("Locked: ", types.FIL(circ.FilLocked))
} else {
circ, err := api.StateCirculatingSupply(ctx, ts.Key())
if err != nil {
return err
}

fmt.Println("Circulating supply: ", types.FIL(circ.FilCirculating))
fmt.Println("Mined: ", types.FIL(circ.FilMined))
fmt.Println("Vested: ", types.FIL(circ.FilVested))
fmt.Println("Burnt: ", types.FIL(circ.FilBurnt))
fmt.Println("Locked: ", types.FIL(circ.FilLocked))
fmt.Println("Exact circulating supply: ", types.FIL(circ))
return nil
}

return nil
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/lotus-chainwatch/syncer/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ limit 1
}

func (s *Syncer) storeCirculatingSupply(ctx context.Context, tipset *types.TipSet) error {
supply, err := s.node.StateCirculatingSupply(ctx, tipset.Key())
supply, err := s.node.StateVMCirculatingSupply(ctx, tipset.Key())
if err != nil {
return err
}
Expand Down

0 comments on commit 4762430

Please sign in to comment.