2019-09-06 06:26:02 +00:00
package stmgr
import (
"context"
2020-09-04 03:28:42 +00:00
"fmt"
2020-07-03 16:57:58 +00:00
"reflect"
2020-09-04 03:28:42 +00:00
"runtime"
"strings"
2020-04-17 23:36:54 +00:00
2021-07-27 13:43:03 +00:00
"github.com/ipfs/go-cid"
2020-04-20 17:43:02 +00:00
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
2020-09-07 03:49:10 +00:00
"github.com/filecoin-project/go-state-types/abi"
2021-07-27 13:43:03 +00:00
"github.com/filecoin-project/go-state-types/big"
2020-09-28 19:48:08 +00:00
"github.com/filecoin-project/go-state-types/rt"
2020-09-22 18:14:55 +00:00
2020-09-28 19:48:08 +00:00
exported0 "github.com/filecoin-project/specs-actors/actors/builtin/exported"
2020-09-30 17:11:48 +00:00
exported2 "github.com/filecoin-project/specs-actors/v2/actors/builtin/exported"
2021-01-21 19:43:36 +00:00
exported3 "github.com/filecoin-project/specs-actors/v3/actors/builtin/exported"
2021-04-23 12:29:46 +00:00
exported4 "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported"
2021-07-27 13:43:03 +00:00
exported5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/exported"
2020-02-08 02:18:32 +00:00
2019-10-18 04:47:41 +00:00
"github.com/filecoin-project/lotus/api"
2020-10-08 20:32:54 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin"
2020-09-14 22:43:12 +00:00
init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init"
2020-10-08 20:32:54 +00:00
"github.com/filecoin-project/lotus/chain/actors/policy"
2020-03-31 23:13:37 +00:00
"github.com/filecoin-project/lotus/chain/state"
2020-01-17 02:33:43 +00:00
"github.com/filecoin-project/lotus/chain/store"
2019-10-18 04:47:41 +00:00
"github.com/filecoin-project/lotus/chain/types"
2020-01-17 02:33:43 +00:00
"github.com/filecoin-project/lotus/chain/vm"
2020-03-31 23:13:37 +00:00
"github.com/filecoin-project/lotus/node/modules/dtypes"
2019-09-06 06:26:02 +00:00
)
2021-07-27 13:43:03 +00:00
type MethodMeta struct {
Name string
2020-05-28 00:06:29 +00:00
2021-07-27 13:43:03 +00:00
Params reflect . Type
Ret reflect . Type
2020-05-28 00:06:29 +00:00
}
2021-07-27 13:43:03 +00:00
var MethodsMap = map [ cid . Cid ] map [ abi . MethodNum ] MethodMeta { }
2020-07-01 04:57:49 +00:00
2021-07-27 13:43:03 +00:00
func init ( ) {
// TODO: combine with the runtime actor registry.
var actors [ ] rt . VMActor
actors = append ( actors , exported0 . BuiltinActors ( ) ... )
actors = append ( actors , exported2 . BuiltinActors ( ) ... )
actors = append ( actors , exported3 . BuiltinActors ( ) ... )
actors = append ( actors , exported4 . BuiltinActors ( ) ... )
actors = append ( actors , exported5 . BuiltinActors ( ) ... )
2020-09-20 21:04:45 +00:00
2021-07-27 13:43:03 +00:00
for _ , actor := range actors {
exports := actor . Exports ( )
methods := make ( map [ abi . MethodNum ] MethodMeta , len ( exports ) )
2020-09-20 21:04:45 +00:00
2021-07-27 13:43:03 +00:00
// Explicitly add send, it's special.
methods [ builtin . MethodSend ] = MethodMeta {
Name : "Send" ,
Params : reflect . TypeOf ( new ( abi . EmptyValue ) ) ,
Ret : reflect . TypeOf ( new ( abi . EmptyValue ) ) ,
2020-11-05 17:16:22 +00:00
}
2020-07-01 04:57:49 +00:00
2021-07-27 13:43:03 +00:00
// Iterate over exported methods. Some of these _may_ be nil and
// must be skipped.
for number , export := range exports {
if export == nil {
continue
}
2020-04-17 14:47:19 +00:00
2021-07-27 13:43:03 +00:00
ev := reflect . ValueOf ( export )
et := ev . Type ( )
2020-07-01 04:57:49 +00:00
2021-07-27 13:43:03 +00:00
// Extract the method names using reflection. These
// method names always match the field names in the
// `builtin.Method*` structs (tested in the specs-actors
// tests).
fnName := runtime . FuncForPC ( ev . Pointer ( ) ) . Name ( )
fnName = strings . TrimSuffix ( fnName [ strings . LastIndexByte ( fnName , '.' ) + 1 : ] , "-fm" )
2020-09-17 06:34:15 +00:00
2021-07-27 13:43:03 +00:00
switch abi . MethodNum ( number ) {
case builtin . MethodSend :
panic ( "method 0 is reserved for Send" )
case builtin . MethodConstructor :
if fnName != "Constructor" {
panic ( "method 1 is reserved for Constructor" )
}
}
2020-07-01 04:57:49 +00:00
2021-07-27 13:43:03 +00:00
methods [ abi . MethodNum ( number ) ] = MethodMeta {
Name : fnName ,
Params : et . In ( 1 ) ,
Ret : et . Out ( 0 ) ,
}
2020-04-17 14:47:19 +00:00
}
2021-07-27 13:43:03 +00:00
MethodsMap [ actor . Code ( ) ] = methods
2020-04-17 14:47:19 +00:00
}
2020-04-17 05:39:55 +00:00
}
2021-07-27 13:43:03 +00:00
func GetReturnType ( ctx context . Context , sm * StateManager , to address . Address , method abi . MethodNum , ts * types . TipSet ) ( cbg . CBORUnmarshaler , error ) {
act , err := sm . LoadActor ( ctx , to , ts )
2020-04-21 14:32:17 +00:00
if err != nil {
2021-07-27 13:43:03 +00:00
return nil , xerrors . Errorf ( "(get sset) failed to load miner actor: %w" , err )
2020-03-02 01:09:38 +00:00
}
2020-04-16 20:52:40 +00:00
2021-07-27 13:43:03 +00:00
m , found := MethodsMap [ act . Code ] [ method ]
if ! found {
return nil , fmt . Errorf ( "unknown method %d for actor %s" , method , act . Code )
2020-03-02 01:09:38 +00:00
}
2021-07-27 13:43:03 +00:00
return reflect . New ( m . Ret . Elem ( ) ) . Interface ( ) . ( cbg . CBORUnmarshaler ) , nil
2019-11-15 01:01:53 +00:00
}
2021-07-27 13:43:03 +00:00
func GetParamType ( actCode cid . Cid , method abi . MethodNum ) ( cbg . CBORUnmarshaler , error ) {
m , found := MethodsMap [ actCode ] [ method ]
2020-05-06 00:04:25 +00:00
if ! found {
2021-07-27 13:43:03 +00:00
return nil , fmt . Errorf ( "unknown method %d for actor %s" , method , actCode )
2020-05-06 00:04:25 +00:00
}
2021-07-27 13:43:03 +00:00
return reflect . New ( m . Params . Elem ( ) ) . Interface ( ) . ( cbg . CBORUnmarshaler ) , nil
2019-11-07 07:57:10 +00:00
}
2021-07-27 13:43:03 +00:00
func GetNetworkName ( ctx context . Context , sm * StateManager , st cid . Cid ) ( dtypes . NetworkName , error ) {
act , err := sm . LoadActorRaw ( ctx , init_ . Address , st )
2020-09-15 11:04:45 +00:00
if err != nil {
2021-07-27 13:43:03 +00:00
return "" , err
2020-09-15 11:04:45 +00:00
}
2021-07-27 13:43:03 +00:00
ias , err := init_ . Load ( sm . cs . ActorStore ( ctx ) , act )
2020-09-15 11:04:45 +00:00
if err != nil {
2021-07-27 13:43:03 +00:00
return "" , err
2019-12-17 06:51:41 +00:00
}
2021-07-27 13:43:03 +00:00
return ias . NetworkName ( )
2019-09-17 22:43:47 +00:00
}
2020-01-17 02:33:43 +00:00
2020-03-08 02:31:36 +00:00
func ComputeState ( ctx context . Context , sm * StateManager , height abi . ChainEpoch , msgs [ ] * types . Message , ts * types . TipSet ) ( cid . Cid , [ ] * api . InvocResult , error ) {
2020-01-17 02:33:43 +00:00
if ts == nil {
ts = sm . cs . GetHeaviestTipSet ( )
}
2020-03-08 02:31:36 +00:00
base , trace , err := sm . ExecutionTrace ( ctx , ts )
2020-01-17 02:33:43 +00:00
if err != nil {
2020-03-08 02:31:36 +00:00
return cid . Undef , nil , err
2020-01-17 02:33:43 +00:00
}
2020-09-24 21:30:11 +00:00
for i := ts . Height ( ) ; i < height ; i ++ {
// handle state forks
2021-06-04 00:44:38 +00:00
base , err = sm . handleStateForks ( ctx , base , i , & InvocationTracer { trace : & trace } , ts )
2020-09-24 21:30:11 +00:00
if err != nil {
return cid . Undef , nil , xerrors . Errorf ( "error handling state forks: %w" , err )
}
// TODO: should we also run cron here?
}
2020-09-01 19:48:16 +00:00
r := store . NewChainRand ( sm . cs , ts . Cids ( ) )
2020-08-06 17:09:03 +00:00
vmopt := & vm . VMOpts {
2020-08-09 22:49:38 +00:00
StateBase : base ,
Epoch : height ,
Rand : r ,
2021-02-28 22:48:36 +00:00
Bstore : sm . cs . StateBlockstore ( ) ,
2021-07-27 13:30:23 +00:00
Syscalls : sm . syscalls ,
2020-10-11 22:17:28 +00:00
CircSupplyCalc : sm . GetVMCirculatingSupply ,
2020-09-07 20:01:09 +00:00
NtwkVersion : sm . GetNtwkVersion ,
2020-08-09 22:49:38 +00:00
BaseFee : ts . Blocks ( ) [ 0 ] . ParentBaseFee ,
2020-10-24 09:57:17 +00:00
LookbackState : LookbackStateGetterForTipset ( sm , ts ) ,
2020-08-06 17:09:03 +00:00
}
2020-10-07 22:26:08 +00:00
vmi , err := sm . newVM ( ctx , vmopt )
2020-01-17 02:33:43 +00:00
if err != nil {
2020-03-08 02:31:36 +00:00
return cid . Undef , nil , err
2020-01-17 02:33:43 +00:00
}
for i , msg := range msgs {
2020-03-25 11:29:35 +00:00
// TODO: Use the signed message length for secp messages
2020-03-25 19:13:09 +00:00
ret , err := vmi . ApplyMessage ( ctx , msg )
2020-01-17 02:33:43 +00:00
if err != nil {
2020-03-08 02:31:36 +00:00
return cid . Undef , nil , xerrors . Errorf ( "applying message %s: %w" , msg . Cid ( ) , err )
2020-01-17 02:33:43 +00:00
}
if ret . ExitCode != 0 {
2020-01-17 06:14:00 +00:00
log . Infof ( "compute state apply message %d failed (exit: %d): %s" , i , ret . ExitCode , ret . ActorErr )
2020-01-17 02:33:43 +00:00
}
}
2020-03-08 02:31:36 +00:00
root , err := vmi . Flush ( ctx )
if err != nil {
return cid . Undef , nil , err
}
return root , trace , nil
2020-01-17 02:33:43 +00:00
}
2020-04-02 01:10:28 +00:00
2020-10-24 09:57:17 +00:00
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 )
}
}
2020-10-23 21:52:26 +00:00
func GetLookbackTipSetForRound ( ctx context . Context , sm * StateManager , ts * types . TipSet , round abi . ChainEpoch ) ( * types . TipSet , cid . Cid , error ) {
2020-04-17 23:36:54 +00:00
var lbr abi . ChainEpoch
2020-10-06 09:26:25 +00:00
lb := policy . GetWinningPoStSectorSetLookback ( sm . GetNtwkVersion ( ctx , round ) )
if round > lb {
lbr = round - lb
2020-04-17 23:36:54 +00:00
}
// more null blocks than our lookback
2020-10-23 21:52:26 +00:00
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 ( ) )
2020-04-17 23:36:54 +00:00
}
2020-10-23 21:52:26 +00:00
lbts , err := sm . ChainStore ( ) . GetTipSetFromKey ( nextTs . Parents ( ) )
2020-04-17 23:36:54 +00:00
if err != nil {
2020-10-23 21:52:26 +00:00
return nil , cid . Undef , xerrors . Errorf ( "failed to resolve lookback tipset: %w" , err )
2020-04-17 23:36:54 +00:00
}
2020-10-23 21:52:26 +00:00
return lbts , nextTs . ParentState ( ) , nil
2020-04-17 23:36:54 +00:00
}
2021-07-27 13:30:23 +00:00
func CheckTotalFIL ( ctx context . Context , cs * store . ChainStore , ts * types . TipSet ) ( abi . TokenAmount , error ) {
str , err := state . LoadStateTree ( cs . ActorStore ( ctx ) , ts . ParentState ( ) )
2020-08-12 22:20:32 +00:00
if err != nil {
return abi . TokenAmount { } , err
}
sum := types . NewInt ( 0 )
err = str . ForEach ( func ( a address . Address , act * types . Actor ) error {
sum = types . BigAdd ( sum , act . Balance )
return nil
} )
if err != nil {
return abi . TokenAmount { } , err
}
return sum , nil
}
2020-10-15 22:48:51 +00:00
func MakeMsgGasCost ( msg * types . Message , ret * vm . ApplyRet ) api . MsgGasCost {
return api . MsgGasCost {
Message : msg . Cid ( ) ,
GasUsed : big . NewInt ( ret . GasUsed ) ,
BaseFeeBurn : ret . GasCosts . BaseFeeBurn ,
OverEstimationBurn : ret . GasCosts . OverEstimationBurn ,
MinerPenalty : ret . GasCosts . MinerPenalty ,
MinerTip : ret . GasCosts . MinerTip ,
Refund : ret . GasCosts . Refund ,
TotalCost : big . Sub ( msg . RequiredFunds ( ) , ret . GasCosts . Refund ) ,
}
}
2021-07-27 13:15:50 +00:00
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
}