2019-07-26 04:54:22 +00:00
package vm
2019-07-05 14:29:17 +00:00
import (
2019-11-16 19:24:11 +00:00
"bytes"
2019-07-05 14:29:17 +00:00
"context"
"fmt"
2020-02-18 21:37:59 +00:00
"reflect"
2020-09-24 11:35:45 +00:00
"sync/atomic"
2020-04-02 21:18:57 +00:00
"time"
2019-07-05 14:29:17 +00:00
2020-09-29 04:24:38 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin"
2019-08-29 18:20:59 +00:00
block "github.com/ipfs/go-block-format"
2019-07-05 14:29:17 +00:00
cid "github.com/ipfs/go-cid"
2020-02-04 22:19:05 +00:00
cbor "github.com/ipfs/go-ipld-cbor"
2020-01-08 19:10:57 +00:00
logging "github.com/ipfs/go-log/v2"
2020-02-19 19:39:00 +00:00
mh "github.com/multiformats/go-multihash"
2019-10-17 08:57:56 +00:00
cbg "github.com/whyrusleeping/cbor-gen"
"go.opencensus.io/trace"
2019-07-16 16:07:14 +00:00
"golang.org/x/xerrors"
2019-10-17 08:57:56 +00:00
2019-12-19 20:13:17 +00:00
"github.com/filecoin-project/go-address"
2020-09-07 03:49:10 +00:00
"github.com/filecoin-project/go-state-types/abi"
2020-09-14 11:45:20 +00:00
"github.com/filecoin-project/go-state-types/big"
2020-09-07 03:49:10 +00:00
"github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/go-state-types/exitcode"
2020-09-14 11:45:20 +00:00
"github.com/filecoin-project/go-state-types/network"
2020-02-17 21:11:03 +00:00
2020-07-10 14:43:14 +00:00
"github.com/filecoin-project/lotus/build"
2020-09-18 21:20:53 +00:00
"github.com/filecoin-project/lotus/chain/actors/adt"
2019-10-17 08:57:56 +00:00
"github.com/filecoin-project/lotus/chain/actors/aerrors"
2020-09-18 21:20:53 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin/account"
2020-09-23 06:18:52 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin/reward"
2019-10-17 08:57:56 +00:00
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/types"
2020-07-23 02:05:11 +00:00
"github.com/filecoin-project/lotus/lib/blockstore"
2020-09-14 11:45:20 +00:00
bstore "github.com/filecoin-project/lotus/lib/blockstore"
2019-10-17 08:57:56 +00:00
"github.com/filecoin-project/lotus/lib/bufbstore"
2019-07-05 14:29:17 +00:00
)
2020-10-19 17:26:07 +00:00
const MaxCallDepth = 4096
2019-07-26 19:01:02 +00:00
var log = logging . Logger ( "vm" )
2020-07-13 10:29:27 +00:00
var actorLog = logging . Logger ( "actors" )
2020-06-16 16:25:48 +00:00
var gasOnActorExec = newGasCharge ( "OnActorExec" , 0 , 0 )
2019-07-26 19:01:02 +00:00
2020-09-24 11:35:45 +00:00
// stat counters
var (
StatSends uint64
StatApplied uint64
)
2020-02-18 18:10:42 +00:00
// ResolveToKeyAddr returns the public key type of address (`BLS`/`SECP256K1`) of an account actor identified by `addr`.
2020-08-20 05:28:42 +00:00
func ResolveToKeyAddr ( state types . StateTree , cst cbor . IpldStore , addr address . Address ) ( address . Address , error ) {
2019-08-09 21:41:50 +00:00
if addr . Protocol ( ) == address . BLS || addr . Protocol ( ) == address . SECP256K1 {
return addr , nil
}
2019-08-27 00:46:39 +00:00
act , err := state . GetActor ( addr )
2019-08-09 21:41:50 +00:00
if err != nil {
2020-08-20 05:28:42 +00:00
return address . Undef , xerrors . Errorf ( "failed to find actor: %s" , addr )
2019-08-09 21:41:50 +00:00
}
2020-09-18 21:20:53 +00:00
aast , err := account . Load ( adt . WrapStore ( context . TODO ( ) , cst ) , act )
if err != nil {
2020-08-20 05:28:42 +00:00
return address . Undef , xerrors . Errorf ( "failed to get account actor state for %s: %w" , addr , err )
2019-08-09 21:41:50 +00:00
}
2020-09-18 21:20:53 +00:00
return aast . PubkeyAddress ( )
2019-07-24 05:38:16 +00:00
}
2020-02-04 22:19:05 +00:00
var _ cbor . IpldBlockstore = ( * gasChargingBlocks ) ( nil )
2019-08-29 18:20:59 +00:00
type gasChargingBlocks struct {
2020-06-11 18:37:14 +00:00
chargeGas func ( GasCharge )
2020-03-19 16:27:42 +00:00
pricelist Pricelist
2020-02-04 22:19:05 +00:00
under cbor . IpldBlockstore
2019-08-29 18:20:59 +00:00
}
2020-02-04 22:19:05 +00:00
func ( bs * gasChargingBlocks ) Get ( c cid . Cid ) ( block . Block , error ) {
2020-07-15 19:40:49 +00:00
bs . chargeGas ( bs . pricelist . OnIpldGet ( ) )
2020-02-04 22:19:05 +00:00
blk , err := bs . under . Get ( c )
2019-08-30 16:05:54 +00:00
if err != nil {
2020-01-08 03:46:55 +00:00
return nil , aerrors . Escalate ( err , "failed to get block from blockstore" )
2019-08-30 16:05:54 +00:00
}
2020-07-15 19:40:49 +00:00
bs . chargeGas ( newGasCharge ( "OnIpldGetEnd" , 0 , 0 ) . WithExtra ( len ( blk . RawData ( ) ) ) )
2020-06-16 16:25:48 +00:00
bs . chargeGas ( gasOnActorExec )
2019-08-30 16:05:54 +00:00
return blk , nil
2019-08-29 18:20:59 +00:00
}
2020-02-04 22:19:05 +00:00
func ( bs * gasChargingBlocks ) Put ( blk block . Block ) error {
2020-03-19 16:27:42 +00:00
bs . chargeGas ( bs . pricelist . OnIpldPut ( len ( blk . RawData ( ) ) ) )
2020-03-10 02:24:02 +00:00
2020-02-04 22:19:05 +00:00
if err := bs . under . Put ( blk ) ; err != nil {
2020-01-08 03:46:55 +00:00
return aerrors . Escalate ( err , "failed to write data to disk" )
}
2020-06-16 16:25:48 +00:00
bs . chargeGas ( gasOnActorExec )
2020-01-08 03:46:55 +00:00
return nil
2019-08-29 18:20:59 +00:00
}
2020-10-19 17:26:07 +00:00
func ( vm * VM ) makeRuntime ( ctx context . Context , msg * types . Message , parent * Runtime ) * Runtime {
2020-03-10 02:24:02 +00:00
rt := & Runtime {
2020-03-17 05:58:00 +00:00
ctx : ctx ,
vm : vm ,
state : vm . cstate ,
2020-10-19 17:26:07 +00:00
origin : msg . From ,
originNonce : msg . Nonce ,
2020-03-17 05:58:00 +00:00
height : vm . blockHeight ,
2020-10-19 17:26:07 +00:00
gasUsed : 0 ,
2020-06-12 18:46:04 +00:00
gasAvailable : msg . GasLimit ,
2020-10-19 17:26:07 +00:00
depth : 0 ,
numActorsCreated : 0 ,
2020-06-12 18:46:04 +00:00
pricelist : PricelistByEpoch ( vm . blockHeight ) ,
allowInternal : true ,
callerValidated : false ,
executionTrace : types . ExecutionTrace { Msg : msg } ,
2019-07-05 14:29:17 +00:00
}
2020-04-03 21:38:11 +00:00
2020-10-19 17:26:07 +00:00
if parent != nil {
rt . gasUsed = parent . gasUsed
rt . origin = parent . origin
rt . originNonce = parent . originNonce
rt . numActorsCreated = parent . numActorsCreated
rt . depth = parent . depth + 1
}
if rt . depth > MaxCallDepth && rt . NetworkVersion ( ) >= network . Version6 {
rt . Abortf ( exitcode . SysErrForbidden , "message execution exceeds call depth" )
}
2020-03-10 02:24:02 +00:00
rt . cst = & cbor . BasicIpldStore {
2020-06-12 16:49:29 +00:00
Blocks : & gasChargingBlocks { rt . chargeGasFunc ( 2 ) , rt . pricelist , vm . cst . Blocks } ,
2019-08-29 18:20:59 +00:00
Atlas : vm . cst . Atlas ,
}
2020-09-14 11:45:20 +00:00
rt . Syscalls = pricedSyscalls {
2020-07-18 13:46:47 +00:00
under : vm . Syscalls ( ctx , vm . cstate , rt . cst ) ,
2020-06-12 16:49:29 +00:00
chargeGas : rt . chargeGasFunc ( 1 ) ,
2020-03-19 16:27:42 +00:00
pl : rt . pricelist ,
}
2020-03-10 02:24:02 +00:00
2020-04-03 21:38:11 +00:00
vmm := * msg
resF , ok := rt . ResolveAddress ( msg . From )
if ! ok {
rt . Abortf ( exitcode . SysErrInvalidReceiver , "resolve msg.From address failed" )
}
vmm . From = resF
2020-10-06 05:25:55 +00:00
if vm . ntwkVersion ( ctx , vm . blockHeight ) <= network . Version3 {
rt . Message = & vmm
} else {
resT , _ := rt . ResolveAddress ( msg . To )
// may be set to undef if recipient doesn't exist yet
vmm . To = resT
rt . Message = & Message { msg : vmm }
}
2020-04-03 21:38:11 +00:00
2020-03-10 02:24:02 +00:00
return rt
2019-07-05 14:29:17 +00:00
}
2020-06-26 13:08:03 +00:00
type UnsafeVM struct {
VM * VM
}
2020-10-19 17:26:07 +00:00
func ( vm * UnsafeVM ) MakeRuntime ( ctx context . Context , msg * types . Message ) * Runtime {
return vm . VM . makeRuntime ( ctx , msg , nil )
2020-06-26 13:08:03 +00:00
}
2020-08-09 22:49:38 +00:00
type CircSupplyCalculator func ( context . Context , abi . ChainEpoch , * state . StateTree ) ( abi . TokenAmount , error )
2020-09-14 11:45:20 +00:00
type NtwkVersionGetter func ( context . Context , abi . ChainEpoch ) network . Version
2020-07-25 22:29:33 +00:00
2019-07-05 14:29:17 +00:00
type VM struct {
2020-08-09 22:49:38 +00:00
cstate * state . StateTree
base cid . Cid
cst * cbor . BasicIpldStore
buf * bufbstore . BufferedBS
blockHeight abi . ChainEpoch
2020-09-25 00:51:34 +00:00
areg * ActorRegistry
2020-08-09 22:49:38 +00:00
rand Rand
circSupplyCalc CircSupplyCalculator
2020-09-07 20:01:09 +00:00
ntwkVersion NtwkVersionGetter
2020-08-09 22:49:38 +00:00
baseFee abi . TokenAmount
2019-12-06 14:06:42 +00:00
2020-07-18 13:46:47 +00:00
Syscalls SyscallBuilder
2019-07-05 14:29:17 +00:00
}
2020-08-06 17:09:03 +00:00
type VMOpts struct {
2020-08-09 22:49:38 +00:00
StateBase cid . Cid
Epoch abi . ChainEpoch
Rand Rand
Bstore bstore . Blockstore
Syscalls SyscallBuilder
CircSupplyCalc CircSupplyCalculator
2020-09-14 12:17:45 +00:00
NtwkVersion NtwkVersionGetter // TODO: stebalien: In what cases do we actually need this? It seems like even when creating new networks we want to use the 'global'/build-default version getter
2020-08-09 22:49:38 +00:00
BaseFee abi . TokenAmount
2020-08-06 17:09:03 +00:00
}
2020-09-14 11:45:20 +00:00
func NewVM ( ctx context . Context , opts * VMOpts ) ( * VM , error ) {
2020-08-06 17:09:03 +00:00
buf := bufbstore . NewBufferedBstore ( opts . Bstore )
2020-02-04 22:19:05 +00:00
cst := cbor . NewCborStore ( buf )
2020-09-14 22:43:12 +00:00
state , err := state . LoadStateTree ( cst , opts . StateBase )
2019-07-05 14:29:17 +00:00
if err != nil {
return nil , err
}
return & VM {
2020-08-09 22:49:38 +00:00
cstate : state ,
base : opts . StateBase ,
cst : cst ,
buf : buf ,
blockHeight : opts . Epoch ,
2020-09-25 00:51:34 +00:00
areg : NewActorRegistry ( ) ,
2020-08-09 22:49:38 +00:00
rand : opts . Rand , // TODO: Probably should be a syscall
circSupplyCalc : opts . CircSupplyCalc ,
2020-09-07 20:01:09 +00:00
ntwkVersion : opts . NtwkVersion ,
2020-08-09 22:49:38 +00:00
Syscalls : opts . Syscalls ,
baseFee : opts . BaseFee ,
2019-07-05 14:29:17 +00:00
} , nil
}
2019-09-20 02:54:52 +00:00
type Rand interface {
2020-08-12 02:47:45 +00:00
GetChainRandomness ( ctx context . Context , pers crypto . DomainSeparationTag , round abi . ChainEpoch , entropy [ ] byte ) ( [ ] byte , error )
GetBeaconRandomness ( ctx context . Context , pers crypto . DomainSeparationTag , round abi . ChainEpoch , entropy [ ] byte ) ( [ ] byte , error )
2019-09-20 02:54:52 +00:00
}
2019-07-31 15:37:00 +00:00
type ApplyRet struct {
types . MessageReceipt
2020-06-11 00:47:28 +00:00
ActorErr aerrors . ActorError
ExecutionTrace types . ExecutionTrace
Duration time . Duration
2020-10-16 01:12:38 +00:00
GasCosts * GasOutputs
2019-07-31 15:37:00 +00:00
}
2020-03-10 02:24:02 +00:00
func ( vm * VM ) send ( ctx context . Context , msg * types . Message , parent * Runtime ,
2020-06-12 18:46:04 +00:00
gasCharge * GasCharge , start time . Time ) ( [ ] byte , aerrors . ActorError , * Runtime ) {
2020-06-11 00:47:28 +00:00
2020-09-24 11:35:45 +00:00
defer atomic . AddUint64 ( & StatSends , 1 )
2020-03-23 06:39:42 +00:00
st := vm . cstate
2020-03-19 16:27:42 +00:00
2020-10-19 17:26:07 +00:00
rt := vm . makeRuntime ( ctx , msg , parent )
2020-10-07 14:52:03 +00:00
if EnableGasTracing {
2020-09-21 22:46:31 +00:00
rt . lastGasChargeTime = start
if parent != nil {
rt . lastGasChargeTime = parent . lastGasChargeTime
rt . lastGasCharge = parent . lastGasCharge
defer func ( ) {
parent . lastGasChargeTime = rt . lastGasChargeTime
parent . lastGasCharge = rt . lastGasCharge
} ( )
}
}
2019-08-15 16:23:28 +00:00
if parent != nil {
defer func ( ) {
2020-03-10 02:24:02 +00:00
parent . gasUsed = rt . gasUsed
2019-08-15 16:23:28 +00:00
} ( )
}
2020-06-11 19:59:39 +00:00
if gasCharge != nil {
if err := rt . chargeGasSafe ( * gasCharge ) ; err != nil {
// this should never happen
return nil , aerrors . Wrap ( err , "not enough gas for initial message charge, this should not happen" ) , rt
}
}
2020-06-11 00:47:28 +00:00
ret , err := func ( ) ( [ ] byte , aerrors . ActorError ) {
2020-07-03 13:13:23 +00:00
_ = rt . chargeGasSafe ( newGasCharge ( "OnGetActor" , 0 , 0 ) )
2020-06-11 00:47:28 +00:00
toActor , err := st . GetActor ( msg . To )
if err != nil {
2020-08-12 02:47:24 +00:00
if xerrors . Is ( err , types . ErrActorNotFound ) {
2020-09-06 07:48:45 +00:00
a , aid , err := TryCreateAccountActor ( rt , msg . To )
2020-06-11 00:47:28 +00:00
if err != nil {
return nil , aerrors . Wrapf ( err , "could not create account" )
}
toActor = a
2020-10-06 05:25:55 +00:00
if vm . ntwkVersion ( ctx , vm . blockHeight ) <= network . Version3 {
// Leave the rt.Message as is
} else {
nmsg := Message {
msg : types . Message {
To : aid ,
From : rt . Message . Caller ( ) ,
Value : rt . Message . ValueReceived ( ) ,
} ,
}
rt . Message = & nmsg
2020-09-06 07:48:45 +00:00
}
2020-06-11 00:47:28 +00:00
} else {
return nil , aerrors . Escalate ( err , "getting actor" )
2020-03-23 06:39:42 +00:00
}
}
2020-07-03 13:13:23 +00:00
if aerr := rt . chargeGasSafe ( rt . Pricelist ( ) . OnMethodInvocation ( msg . Value , msg . Method ) ) ; aerr != nil {
return nil , aerrors . Wrap ( aerr , "not enough gas for method invocation" )
}
2020-08-20 04:49:10 +00:00
// not charging any gas, just logging
//nolint:errcheck
2020-07-03 13:13:23 +00:00
defer rt . chargeGasSafe ( newGasCharge ( "OnMethodInvocationDone" , 0 , 0 ) )
2020-06-11 00:47:28 +00:00
if types . BigCmp ( msg . Value , types . NewInt ( 0 ) ) != 0 {
if err := vm . transfer ( msg . From , msg . To , msg . Value ) ; err != nil {
return nil , aerrors . Wrap ( err , "failed to transfer funds" )
}
2019-08-15 16:23:28 +00:00
}
2020-06-11 00:47:28 +00:00
if msg . Method != 0 {
var ret [ ] byte
2020-06-16 16:25:48 +00:00
_ = rt . chargeGasSafe ( gasOnActorExec )
2020-06-11 00:47:28 +00:00
ret , err := vm . Invoke ( toActor , rt , msg . Method , msg . Params )
return ret , err
}
return nil , nil
} ( )
mr := types . MessageReceipt {
ExitCode : aerrors . RetCode ( err ) ,
Return : ret ,
GasUsed : rt . gasUsed ,
}
rt . executionTrace . MsgRct = & mr
rt . executionTrace . Duration = time . Since ( start )
if err != nil {
rt . executionTrace . Error = err . Error ( )
2019-08-03 11:26:35 +00:00
}
2020-06-11 00:47:28 +00:00
return ret , err , rt
2019-08-03 11:26:35 +00:00
}
2019-08-09 21:41:50 +00:00
func checkMessage ( msg * types . Message ) error {
2020-03-18 20:45:37 +00:00
if msg . GasLimit == 0 {
return xerrors . Errorf ( "message has no gas limit set" )
}
if msg . GasLimit < 0 {
return xerrors . Errorf ( "message has negative gas limit" )
2019-08-09 21:41:50 +00:00
}
2020-08-06 19:45:03 +00:00
if msg . GasFeeCap == types . EmptyInt {
return xerrors . Errorf ( "message fee cap not set" )
}
if msg . GasPremium == types . EmptyInt {
return xerrors . Errorf ( "message gas premium not set" )
2019-08-09 21:41:50 +00:00
}
if msg . Value == types . EmptyInt {
2019-08-15 16:23:28 +00:00
return xerrors . Errorf ( "message no value set" )
2019-08-09 21:41:50 +00:00
}
return nil
}
2020-03-25 08:46:42 +00:00
func ( vm * VM ) ApplyImplicitMessage ( ctx context . Context , msg * types . Message ) ( * ApplyRet , error ) {
2020-07-10 14:43:14 +00:00
start := build . Clock . Now ( )
2020-09-24 11:35:45 +00:00
defer atomic . AddUint64 ( & StatApplied , 1 )
2020-06-12 18:46:04 +00:00
ret , actorErr , rt := vm . send ( ctx , msg , nil , nil , start )
rt . finilizeGasTracing ( )
2020-03-25 08:46:42 +00:00
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
2020-05-27 20:53:20 +00:00
ExitCode : aerrors . RetCode ( actorErr ) ,
2020-03-25 08:46:42 +00:00
Return : ret ,
GasUsed : 0 ,
} ,
2020-06-11 00:47:28 +00:00
ActorErr : actorErr ,
ExecutionTrace : rt . executionTrace ,
2020-10-16 01:12:38 +00:00
GasCosts : nil ,
2020-06-11 00:47:28 +00:00
Duration : time . Since ( start ) ,
2020-03-25 08:46:42 +00:00
} , actorErr
}
2020-03-25 19:13:09 +00:00
func ( vm * VM ) ApplyMessage ( ctx context . Context , cmsg types . ChainMsg ) ( * ApplyRet , error ) {
2020-07-10 14:43:14 +00:00
start := build . Clock . Now ( )
2019-07-26 19:01:02 +00:00
ctx , span := trace . StartSpan ( ctx , "vm.ApplyMessage" )
defer span . End ( )
2020-09-24 11:35:45 +00:00
defer atomic . AddUint64 ( & StatApplied , 1 )
2020-03-25 19:13:09 +00:00
msg := cmsg . VMMessage ( )
2019-10-10 11:13:26 +00:00
if span . IsRecordingEvents ( ) {
span . AddAttributes (
trace . StringAttribute ( "to" , msg . To . String ( ) ) ,
trace . Int64Attribute ( "method" , int64 ( msg . Method ) ) ,
trace . StringAttribute ( "value" , msg . Value . String ( ) ) ,
)
}
2019-07-26 19:01:02 +00:00
2019-08-09 21:41:50 +00:00
if err := checkMessage ( msg ) ; err != nil {
return nil , err
}
2020-03-19 16:27:42 +00:00
pl := PricelistByEpoch ( vm . blockHeight )
2020-03-26 21:11:06 +00:00
2020-06-11 19:59:39 +00:00
msgGas := pl . OnChainMessage ( cmsg . ChainLength ( ) )
msgGasCost := msgGas . Total ( )
2020-05-11 20:19:35 +00:00
// this should never happen, but is currently still exercised by some tests
2020-03-18 20:45:37 +00:00
if msgGasCost > msg . GasLimit {
2020-09-16 09:53:15 +00:00
gasOutputs := ZeroGasOutputs ( )
gasOutputs . MinerPenalty = types . BigMul ( vm . baseFee , abi . NewTokenAmount ( msgGasCost ) )
2020-03-05 18:13:49 +00:00
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
2020-03-27 00:19:34 +00:00
ExitCode : exitcode . SysErrOutOfGas ,
2020-03-25 07:57:13 +00:00
GasUsed : 0 ,
2020-03-05 18:13:49 +00:00
} ,
2020-10-16 01:12:38 +00:00
GasCosts : & gasOutputs ,
2020-04-02 21:18:57 +00:00
Duration : time . Since ( start ) ,
2020-03-05 18:13:49 +00:00
} , nil
}
2019-07-05 14:29:17 +00:00
st := vm . cstate
2019-07-28 05:35:32 +00:00
2020-08-06 19:05:16 +00:00
minerPenaltyAmount := types . BigMul ( vm . baseFee , abi . NewTokenAmount ( msg . GasLimit ) )
2019-07-05 14:29:17 +00:00
fromActor , err := st . GetActor ( msg . From )
2020-05-11 20:19:35 +00:00
// this should never happen, but is currently still exercised by some tests
2019-07-05 14:29:17 +00:00
if err != nil {
2020-03-03 23:01:35 +00:00
if xerrors . Is ( err , types . ErrActorNotFound ) {
2020-09-16 09:53:15 +00:00
gasOutputs := ZeroGasOutputs ( )
gasOutputs . MinerPenalty = minerPenaltyAmount
2020-03-03 23:01:35 +00:00
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
2020-03-26 00:01:49 +00:00
ExitCode : exitcode . SysErrSenderInvalid ,
2020-03-25 07:57:13 +00:00
GasUsed : 0 ,
2020-03-03 23:01:35 +00:00
} ,
2020-07-21 21:59:15 +00:00
ActorErr : aerrors . Newf ( exitcode . SysErrSenderInvalid , "actor not found: %s" , msg . From ) ,
2020-10-16 01:12:38 +00:00
GasCosts : & gasOutputs ,
2020-04-02 21:18:57 +00:00
Duration : time . Since ( start ) ,
2020-03-03 23:01:35 +00:00
} , nil
}
return nil , xerrors . Errorf ( "failed to look up from actor: %w" , err )
2019-07-05 14:29:17 +00:00
}
2020-05-11 20:19:35 +00:00
// this should never happen, but is currently still exercised by some tests
2020-09-29 04:24:38 +00:00
if ! builtin . IsAccountActor ( fromActor . Code ) {
2020-09-16 09:53:15 +00:00
gasOutputs := ZeroGasOutputs ( )
gasOutputs . MinerPenalty = minerPenaltyAmount
2020-03-25 07:57:13 +00:00
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
2020-03-26 00:01:49 +00:00
ExitCode : exitcode . SysErrSenderInvalid ,
2020-03-25 07:57:13 +00:00
GasUsed : 0 ,
} ,
2020-07-21 21:59:15 +00:00
ActorErr : aerrors . Newf ( exitcode . SysErrSenderInvalid , "send from not account actor: %s" , fromActor . Code ) ,
2020-10-16 01:12:38 +00:00
GasCosts : & gasOutputs ,
2020-04-02 21:18:57 +00:00
Duration : time . Since ( start ) ,
2020-03-25 07:57:13 +00:00
} , nil
}
2020-03-10 20:14:50 +00:00
if msg . Nonce != fromActor . Nonce {
2020-09-16 09:53:15 +00:00
gasOutputs := ZeroGasOutputs ( )
gasOutputs . MinerPenalty = minerPenaltyAmount
2020-03-10 20:14:50 +00:00
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
2020-03-26 00:01:49 +00:00
ExitCode : exitcode . SysErrSenderStateInvalid ,
2020-03-11 16:39:49 +00:00
GasUsed : 0 ,
2020-03-10 20:14:50 +00:00
} ,
2020-07-21 21:59:15 +00:00
ActorErr : aerrors . Newf ( exitcode . SysErrSenderStateInvalid ,
"actor nonce invalid: msg:%d != state:%d" , msg . Nonce , fromActor . Nonce ) ,
2020-09-13 05:26:44 +00:00
2020-10-16 01:12:38 +00:00
GasCosts : & gasOutputs ,
2020-04-02 21:18:57 +00:00
Duration : time . Since ( start ) ,
2020-03-10 20:14:50 +00:00
} , nil
}
2020-08-06 19:05:16 +00:00
gascost := types . BigMul ( types . NewInt ( uint64 ( msg . GasLimit ) ) , msg . GasFeeCap )
2020-07-30 14:23:30 +00:00
if fromActor . Balance . LessThan ( gascost ) {
2020-09-16 09:53:15 +00:00
gasOutputs := ZeroGasOutputs ( )
gasOutputs . MinerPenalty = minerPenaltyAmount
2020-03-03 23:01:35 +00:00
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
2020-03-26 00:01:49 +00:00
ExitCode : exitcode . SysErrSenderStateInvalid ,
2020-03-25 07:57:13 +00:00
GasUsed : 0 ,
2020-03-03 23:01:35 +00:00
} ,
2020-07-21 21:59:15 +00:00
ActorErr : aerrors . Newf ( exitcode . SysErrSenderStateInvalid ,
2020-07-30 14:23:30 +00:00
"actor balance less than needed: %s < %s" , types . FIL ( fromActor . Balance ) , types . FIL ( gascost ) ) ,
2020-10-16 01:12:38 +00:00
GasCosts : & gasOutputs ,
2020-04-02 21:18:57 +00:00
Duration : time . Since ( start ) ,
2020-03-03 23:01:35 +00:00
} , nil
2019-08-03 11:53:18 +00:00
}
2019-10-11 09:13:04 +00:00
gasHolder := & types . Actor { Balance : types . NewInt ( 0 ) }
2020-03-24 03:17:29 +00:00
if err := vm . transferToGasHolder ( msg . From , gasHolder , gascost ) ; err != nil {
2019-10-11 09:13:04 +00:00
return nil , xerrors . Errorf ( "failed to withdraw gas funds: %w" , err )
2019-08-03 11:53:18 +00:00
}
2019-07-22 18:17:42 +00:00
2020-03-24 03:17:29 +00:00
if err := vm . incrementNonce ( msg . From ) ; err != nil {
return nil , err
}
2019-08-15 16:23:28 +00:00
2020-03-18 05:55:05 +00:00
if err := st . Snapshot ( ctx ) ; err != nil {
return nil , xerrors . Errorf ( "snapshot failed: %w" , err )
}
defer st . ClearSnapshot ( )
2020-06-12 18:46:04 +00:00
ret , actorErr , rt := vm . send ( ctx , msg , nil , & msgGas , start )
2020-03-23 06:39:42 +00:00
if aerrors . IsFatal ( actorErr ) {
return nil , xerrors . Errorf ( "[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w" , msg . From , msg . To , msg . Nonce , msg . Method , vm . blockHeight , actorErr )
}
2019-08-03 11:53:18 +00:00
2020-05-06 20:53:32 +00:00
if actorErr != nil {
log . Warnw ( "Send actor error" , "from" , msg . From , "to" , msg . To , "nonce" , msg . Nonce , "method" , msg . Method , "height" , vm . blockHeight , "error" , fmt . Sprintf ( "%+v" , actorErr ) )
}
2020-04-29 20:05:53 +00:00
2020-05-06 20:53:32 +00:00
if actorErr != nil && len ( ret ) != 0 {
// This should not happen, something is wonky
return nil , xerrors . Errorf ( "message invocation errored, but had a return value anyway: %w" , actorErr )
2020-03-19 16:27:42 +00:00
}
2020-05-06 20:53:32 +00:00
if rt == nil {
return nil , xerrors . Errorf ( "send returned nil runtime, send error was: %s" , actorErr )
}
if len ( ret ) != 0 {
// safely override actorErr since it must be nil
actorErr = rt . chargeGasSafe ( rt . Pricelist ( ) . OnChainReturnValue ( len ( ret ) ) )
if actorErr != nil {
ret = nil
}
2019-09-23 09:45:22 +00:00
}
2019-08-29 18:20:59 +00:00
2020-04-02 18:24:38 +00:00
var errcode exitcode . ExitCode
2020-03-18 20:45:37 +00:00
var gasUsed int64
2019-08-29 18:20:59 +00:00
2019-08-03 11:53:18 +00:00
if errcode = aerrors . RetCode ( actorErr ) ; errcode != 0 {
// revert all state changes since snapshot
if err := st . Revert ( ) ; err != nil {
return nil , xerrors . Errorf ( "revert state failed: %w" , err )
2019-07-05 14:29:17 +00:00
}
2020-03-25 07:57:13 +00:00
}
2020-07-03 13:13:23 +00:00
rt . finilizeGasTracing ( )
2020-03-25 07:57:13 +00:00
gasUsed = rt . gasUsed
if gasUsed < 0 {
gasUsed = 0
}
2020-08-06 19:05:16 +00:00
gasOutputs := ComputeGasOutputs ( gasUsed , msg . GasLimit , vm . baseFee , msg . GasFeeCap , msg . GasPremium )
2020-08-01 20:01:23 +00:00
2020-08-06 19:05:16 +00:00
if err := vm . transferFromGasHolder ( builtin . BurntFundsActorAddr , gasHolder ,
gasOutputs . BaseFeeBurn ) ; err != nil {
return nil , xerrors . Errorf ( "failed to burn base fee: %w" , err )
2019-08-03 11:53:18 +00:00
}
2019-08-03 11:26:35 +00:00
2020-09-23 06:18:52 +00:00
if err := vm . transferFromGasHolder ( reward . Address , gasHolder , gasOutputs . MinerTip ) ; err != nil {
2020-08-06 19:05:16 +00:00
return nil , xerrors . Errorf ( "failed to give miner gas reward: %w" , err )
2020-08-01 20:01:23 +00:00
}
2020-08-06 19:05:16 +00:00
if err := vm . transferFromGasHolder ( builtin . BurntFundsActorAddr , gasHolder ,
gasOutputs . OverEstimationBurn ) ; err != nil {
return nil , xerrors . Errorf ( "failed to burn overestimation fee: %w" , err )
}
// refund unused gas
if err := vm . transferFromGasHolder ( msg . From , gasHolder , gasOutputs . Refund ) ; err != nil {
return nil , xerrors . Errorf ( "failed to refund gas: %w" , err )
2019-10-11 09:13:04 +00:00
}
if types . BigCmp ( types . NewInt ( 0 ) , gasHolder . Balance ) != 0 {
return nil , xerrors . Errorf ( "gas handling math is wrong" )
}
2019-07-05 14:29:17 +00:00
2019-08-03 11:53:18 +00:00
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
2020-05-27 20:53:20 +00:00
ExitCode : errcode ,
2019-08-03 11:53:18 +00:00
Return : ret ,
2019-08-15 16:23:28 +00:00
GasUsed : gasUsed ,
2019-08-03 11:53:18 +00:00
} ,
2020-06-11 00:47:28 +00:00
ActorErr : actorErr ,
ExecutionTrace : rt . executionTrace ,
2020-10-16 01:12:38 +00:00
GasCosts : & gasOutputs ,
2020-06-11 00:47:28 +00:00
Duration : time . Since ( start ) ,
2019-08-03 11:53:18 +00:00
} , nil
2019-07-05 14:29:17 +00:00
}
2019-08-09 21:41:50 +00:00
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
}
2019-07-05 14:29:17 +00:00
func ( vm * VM ) Flush ( ctx context . Context ) ( cid . Cid , error ) {
2019-12-05 02:04:09 +00:00
_ , span := trace . StartSpan ( ctx , "vm.Flush" )
2019-10-12 09:44:56 +00:00
defer span . End ( )
2019-11-16 19:24:11 +00:00
from := vm . buf
to := vm . buf . Read ( )
2019-07-05 14:29:17 +00:00
2020-01-22 19:53:06 +00:00
root , err := vm . cstate . Flush ( ctx )
2019-07-05 14:29:17 +00:00
if err != nil {
2019-07-15 20:27:45 +00:00
return cid . Undef , xerrors . Errorf ( "flushing vm: %w" , err )
2019-07-05 14:29:17 +00:00
}
2020-09-24 03:19:20 +00:00
if err := Copy ( ctx , from , to , root ) ; err != nil {
2019-07-15 20:27:45 +00:00
return cid . Undef , xerrors . Errorf ( "copying tree: %w" , err )
2019-07-05 14:29:17 +00:00
}
return root , nil
}
2020-06-02 14:29:39 +00:00
// MutateState usage: MutateState(ctx, idAddr, func(cst cbor.IpldStore, st *ActorStateType) error {...})
2020-02-18 21:37:59 +00:00
func ( vm * VM ) MutateState ( ctx context . Context , addr address . Address , fn interface { } ) error {
act , err := vm . cstate . GetActor ( addr )
if err != nil {
return xerrors . Errorf ( "actor not found: %w" , err )
}
st := reflect . New ( reflect . TypeOf ( fn ) . In ( 1 ) . Elem ( ) )
if err := vm . cst . Get ( ctx , act . Head , st . Interface ( ) ) ; err != nil {
return xerrors . Errorf ( "read actor head: %w" , err )
}
out := reflect . ValueOf ( fn ) . Call ( [ ] reflect . Value { reflect . ValueOf ( vm . cst ) , st } )
if ! out [ 0 ] . IsNil ( ) && out [ 0 ] . Interface ( ) . ( error ) != nil {
return out [ 0 ] . Interface ( ) . ( error )
}
head , err := vm . cst . Put ( ctx , st . Interface ( ) )
if err != nil {
return xerrors . Errorf ( "put new actor head: %w" , err )
}
act . Head = head
if err := vm . cstate . SetActor ( addr , act ) ; err != nil {
return xerrors . Errorf ( "set actor: %w" , err )
}
return nil
}
2020-07-14 11:45:45 +00:00
func linksForObj ( blk block . Block , cb func ( cid . Cid ) ) error {
2019-11-16 19:24:11 +00:00
switch blk . Cid ( ) . Prefix ( ) . Codec {
case cid . DagCBOR :
2020-07-14 11:45:45 +00:00
err := cbg . ScanForLinks ( bytes . NewReader ( blk . RawData ( ) ) , cb )
if err != nil {
return xerrors . Errorf ( "cbg.ScanForLinks: %w" , err )
}
return nil
2020-07-24 06:15:26 +00:00
case cid . Raw :
// We implicitly have all children of raw blocks.
return nil
2019-11-16 19:24:11 +00:00
default :
2020-07-14 11:45:45 +00:00
return xerrors . Errorf ( "vm flush copy method only supports dag cbor" )
2019-11-16 19:24:11 +00:00
}
}
2020-09-24 03:19:20 +00:00
func Copy ( ctx context . Context , from , to blockstore . Blockstore , root cid . Cid ) error {
2020-09-24 09:56:54 +00:00
ctx , span := trace . StartSpan ( ctx , "vm.Copy" ) // nolint
2020-09-24 03:19:20 +00:00
defer span . End ( )
var numBlocks int
var totalCopySize int
2019-11-17 17:50:04 +00:00
var batch [ ] block . Block
batchCp := func ( blk block . Block ) error {
2020-09-24 03:19:20 +00:00
numBlocks ++
totalCopySize += len ( blk . RawData ( ) )
2019-11-17 17:50:04 +00:00
batch = append ( batch , blk )
if len ( batch ) > 100 {
if err := to . PutMany ( batch ) ; err != nil {
return xerrors . Errorf ( "batch put in copy: %w" , err )
}
batch = batch [ : 0 ]
}
return nil
}
if err := copyRec ( from , to , root , batchCp ) ; err != nil {
2020-07-14 11:45:45 +00:00
return xerrors . Errorf ( "copyRec: %w" , err )
2019-11-17 17:50:04 +00:00
}
if len ( batch ) > 0 {
if err := to . PutMany ( batch ) ; err != nil {
return xerrors . Errorf ( "batch put in copy: %w" , err )
}
}
2020-09-24 03:19:20 +00:00
span . AddAttributes (
trace . Int64Attribute ( "numBlocks" , int64 ( numBlocks ) ) ,
trace . Int64Attribute ( "copySize" , int64 ( totalCopySize ) ) ,
)
2019-11-17 17:50:04 +00:00
return nil
}
func copyRec ( from , to blockstore . Blockstore , root cid . Cid , cp func ( block . Block ) error ) error {
2019-07-15 22:19:26 +00:00
if root . Prefix ( ) . MhType == 0 {
// identity cid, skip
return nil
}
2019-11-16 19:24:11 +00:00
blk , err := from . Get ( root )
2019-07-15 22:19:26 +00:00
if err != nil {
2019-07-16 16:07:14 +00:00
return xerrors . Errorf ( "get %s failed: %w" , root , err )
2019-07-15 22:19:26 +00:00
}
2019-11-16 19:24:11 +00:00
2020-07-14 11:45:45 +00:00
var lerr error
err = linksForObj ( blk , func ( link cid . Cid ) {
if lerr != nil {
// Theres no erorr return on linksForObj callback :(
return
}
2019-11-16 19:24:11 +00:00
2020-07-24 06:15:26 +00:00
prefix := link . Prefix ( )
if prefix . Codec == cid . FilCommitmentSealed || prefix . Codec == cid . FilCommitmentUnsealed {
2020-07-14 11:45:45 +00:00
return
2019-07-25 22:15:33 +00:00
}
2019-11-16 19:24:11 +00:00
2020-07-24 06:15:26 +00:00
// We always have blocks inlined into CIDs, but we may not have their children.
if prefix . MhType == mh . IDENTITY {
// Unless the inlined block has no children.
if prefix . Codec == cid . Raw {
return
}
} else {
// If we have an object, we already have its children, skip the object.
has , err := to . Has ( link )
if err != nil {
lerr = xerrors . Errorf ( "has: %w" , err )
return
}
if has {
return
}
2019-07-15 22:19:26 +00:00
}
2019-11-16 19:24:11 +00:00
2019-11-17 17:50:04 +00:00
if err := copyRec ( from , to , link , cp ) ; err != nil {
2020-07-14 11:45:45 +00:00
lerr = err
return
2019-07-15 22:19:26 +00:00
}
2020-07-14 11:45:45 +00:00
} )
if err != nil {
return xerrors . Errorf ( "linksForObj (%x): %w" , blk . RawData ( ) , err )
}
if lerr != nil {
return lerr
2019-07-15 22:19:26 +00:00
}
2019-11-16 19:24:11 +00:00
2019-11-17 17:50:04 +00:00
if err := cp ( blk ) ; err != nil {
2020-07-14 11:45:45 +00:00
return xerrors . Errorf ( "copy: %w" , err )
2019-07-15 22:19:26 +00:00
}
return nil
}
2019-08-15 02:30:21 +00:00
func ( vm * VM ) StateTree ( ) types . StateTree {
return vm . cstate
}
2020-02-08 02:18:32 +00:00
func ( vm * VM ) SetBlockHeight ( h abi . ChainEpoch ) {
2019-08-09 21:41:50 +00:00
vm . blockHeight = h
}
2020-03-10 02:24:02 +00:00
func ( vm * VM ) Invoke ( act * types . Actor , rt * Runtime , method abi . MethodNum , params [ ] byte ) ( [ ] byte , aerrors . ActorError ) {
ctx , span := trace . StartSpan ( rt . ctx , "vm.Invoke" )
2019-07-26 19:01:02 +00:00
defer span . End ( )
2019-11-12 19:54:25 +00:00
if span . IsRecordingEvents ( ) {
span . AddAttributes (
2020-09-14 11:45:20 +00:00
trace . StringAttribute ( "to" , rt . Receiver ( ) . String ( ) ) ,
2019-11-12 19:54:25 +00:00
trace . Int64Attribute ( "method" , int64 ( method ) ) ,
2020-09-14 11:45:20 +00:00
trace . StringAttribute ( "value" , rt . ValueReceived ( ) . String ( ) ) ,
2019-11-12 19:54:25 +00:00
)
}
2019-10-10 11:13:26 +00:00
2019-07-26 19:01:02 +00:00
var oldCtx context . Context
2020-03-10 02:24:02 +00:00
oldCtx , rt . ctx = rt . ctx , ctx
2019-07-26 19:01:02 +00:00
defer func ( ) {
2020-03-10 02:24:02 +00:00
rt . ctx = oldCtx
2019-07-26 19:01:02 +00:00
} ( )
2020-09-25 00:51:34 +00:00
ret , err := vm . areg . Invoke ( act . Code , rt , method , params )
2019-07-11 15:38:37 +00:00
if err != nil {
2019-07-22 18:17:42 +00:00
return nil , err
2019-07-11 15:38:37 +00:00
}
2019-07-22 18:17:42 +00:00
return ret , nil
2019-07-12 03:59:55 +00:00
}
2019-07-26 04:54:22 +00:00
2020-09-25 00:51:34 +00:00
func ( vm * VM ) SetInvoker ( i * ActorRegistry ) {
vm . areg = i
2020-03-24 03:17:29 +00:00
}
2020-09-14 11:45:20 +00:00
func ( vm * VM ) GetNtwkVersion ( ctx context . Context , ce abi . ChainEpoch ) network . Version {
2020-09-07 20:01:09 +00:00
return vm . ntwkVersion ( ctx , ce )
}
2020-08-09 22:49:38 +00:00
func ( vm * VM ) GetCircSupply ( ctx context . Context ) ( abi . TokenAmount , error ) {
return vm . circSupplyCalc ( ctx , vm . blockHeight , vm . cstate )
2020-07-25 22:29:33 +00:00
}
2020-03-24 03:17:29 +00:00
func ( vm * VM ) incrementNonce ( addr address . Address ) error {
2020-04-03 00:09:41 +00:00
return vm . cstate . MutateActor ( addr , func ( a * types . Actor ) error {
a . Nonce ++
return nil
} )
2020-03-24 03:17:29 +00:00
}
2020-05-13 21:20:38 +00:00
func ( vm * VM ) transfer ( from , to address . Address , amt types . BigInt ) aerrors . ActorError {
2019-10-22 11:57:20 +00:00
if from == to {
return nil
}
2020-06-02 14:29:39 +00:00
fromID , err := vm . cstate . LookupID ( from )
2020-05-28 17:22:32 +00:00
if err != nil {
return aerrors . Fatalf ( "transfer failed when resolving sender address: %s" , err )
}
2020-06-02 14:29:39 +00:00
toID , err := vm . cstate . LookupID ( to )
2020-05-28 17:22:32 +00:00
if err != nil {
return aerrors . Fatalf ( "transfer failed when resolving receiver address: %s" , err )
}
2020-06-02 14:29:39 +00:00
if fromID == toID {
2020-05-28 17:22:32 +00:00
return nil
}
2019-10-12 06:57:49 +00:00
if amt . LessThan ( types . NewInt ( 0 ) ) {
2020-05-14 17:45:40 +00:00
return aerrors . Newf ( exitcode . SysErrForbidden , "attempted to transfer negative value: %s" , amt )
2019-10-12 06:57:49 +00:00
}
2020-06-02 14:29:39 +00:00
f , err := vm . cstate . GetActor ( fromID )
2020-03-24 03:17:29 +00:00
if err != nil {
2020-05-14 17:45:40 +00:00
return aerrors . Fatalf ( "transfer failed when retrieving sender actor: %s" , err )
2020-03-24 03:17:29 +00:00
}
2020-06-02 14:29:39 +00:00
t , err := vm . cstate . GetActor ( toID )
2020-03-24 03:17:29 +00:00
if err != nil {
2020-05-14 17:45:40 +00:00
return aerrors . Fatalf ( "transfer failed when retrieving receiver actor: %s" , err )
2020-03-24 03:17:29 +00:00
}
if err := deductFunds ( f , amt ) ; err != nil {
2020-07-17 21:34:18 +00:00
return aerrors . Newf ( exitcode . SysErrInsufficientFunds , "transfer failed when deducting funds (%s): %s" , types . FIL ( amt ) , err )
2019-10-11 09:13:04 +00:00
}
2020-03-24 03:17:29 +00:00
depositFunds ( t , amt )
2020-04-03 00:09:41 +00:00
2020-06-02 14:29:39 +00:00
if err := vm . cstate . SetActor ( fromID , f ) ; err != nil {
2020-05-14 17:45:40 +00:00
return aerrors . Fatalf ( "transfer failed when setting receiver actor: %s" , err )
2020-04-03 00:09:41 +00:00
}
2020-06-02 14:29:39 +00:00
if err := vm . cstate . SetActor ( toID , t ) ; err != nil {
2020-05-14 17:45:40 +00:00
return aerrors . Fatalf ( "transfer failed when setting sender actor: %s" , err )
2020-04-03 00:09:41 +00:00
}
2019-10-11 09:13:04 +00:00
return nil
}
2020-03-24 03:17:29 +00:00
func ( vm * VM ) transferToGasHolder ( addr address . Address , gasHolder * types . Actor , amt types . BigInt ) error {
if amt . LessThan ( types . NewInt ( 0 ) ) {
return xerrors . Errorf ( "attempted to transfer negative value to gas holder" )
}
2020-04-03 00:09:41 +00:00
return vm . cstate . MutateActor ( addr , func ( a * types . Actor ) error {
if err := deductFunds ( a , amt ) ; err != nil {
return err
}
depositFunds ( gasHolder , amt )
return nil
} )
2020-03-24 03:17:29 +00:00
}
func ( vm * VM ) transferFromGasHolder ( addr address . Address , gasHolder * types . Actor , amt types . BigInt ) error {
if amt . LessThan ( types . NewInt ( 0 ) ) {
return xerrors . Errorf ( "attempted to transfer negative value from gas holder" )
}
2020-08-06 19:05:16 +00:00
if amt . Equals ( big . NewInt ( 0 ) ) {
return nil
}
2020-04-03 00:09:41 +00:00
return vm . cstate . MutateActor ( addr , func ( a * types . Actor ) error {
if err := deductFunds ( gasHolder , amt ) ; err != nil {
return err
}
depositFunds ( a , amt )
return nil
} )
2020-01-31 23:51:15 +00:00
}
2019-10-11 09:13:04 +00:00
func deductFunds ( act * types . Actor , amt types . BigInt ) error {
2019-09-23 17:11:44 +00:00
if act . Balance . LessThan ( amt ) {
2019-07-26 04:54:22 +00:00
return fmt . Errorf ( "not enough funds" )
}
act . Balance = types . BigSub ( act . Balance , amt )
return nil
}
2019-10-11 09:13:04 +00:00
func depositFunds ( act * types . Actor , amt types . BigInt ) {
2019-07-26 04:54:22 +00:00
act . Balance = types . BigAdd ( act . Balance , amt )
}