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-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"
2020-11-11 16:05:08 +00:00
"github.com/filecoin-project/lotus/metrics"
2020-09-29 04:24:38 +00:00
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"
2020-11-11 16:05:08 +00:00
"go.opencensus.io/stats"
2019-10-17 08:57:56 +00:00
"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
2021-01-29 20:01:00 +00:00
"github.com/filecoin-project/lotus/blockstore"
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-12-15 04:51:43 +00:00
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
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"
2019-07-05 14:29:17 +00:00
)
2020-10-19 17:26:07 +00:00
const MaxCallDepth = 4096
2021-01-14 11:29:57 +00:00
var (
log = logging . Logger ( "vm" )
actorLog = logging . Logger ( "actors" )
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
}
2021-01-14 11:29:57 +00:00
var (
_ cbor . IpldBlockstore = ( * gasChargingBlocks ) ( nil )
_ blockstore . Viewer = ( * 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-11-10 12:40:32 +00:00
func ( bs * gasChargingBlocks ) View ( c cid . Cid , cb func ( [ ] byte ) error ) error {
if v , ok := bs . under . ( blockstore . Viewer ) ; ok {
bs . chargeGas ( bs . pricelist . OnIpldGet ( ) )
return v . View ( c , func ( b [ ] byte ) error {
// we have successfully retrieved the value; charge for it, even if the user-provided function fails.
bs . chargeGas ( newGasCharge ( "OnIpldViewEnd" , 0 , 0 ) . WithExtra ( len ( b ) ) )
bs . chargeGas ( gasOnActorExec )
return cb ( b )
} )
}
// the underlying blockstore doesn't implement the viewer interface, fall back to normal Get behaviour.
blk , err := bs . Get ( c )
2020-11-10 22:34:13 +00:00
if err == nil && blk != nil {
2020-11-10 12:40:32 +00:00
return cb ( blk . RawData ( ) )
}
return err
}
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 ,
2021-08-10 19:50:37 +00:00
pricelist : PricelistByEpoch ( vm . blockHeight ) ,
2020-06-12 18:46:04 +00:00
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 {
2020-11-09 02:13:58 +00:00
// TODO: The version check here should be unnecessary, but we can wait to take it out
if ! parent . allowInternal && rt . NetworkVersion ( ) >= network . Version7 {
rt . Abortf ( exitcode . SysErrForbidden , "internal calls currently disabled" )
}
2020-10-19 17:26:07 +00:00
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-11-10 15:36:03 +00:00
cbb := & gasChargingBlocks { rt . chargeGasFunc ( 2 ) , rt . pricelist , vm . cst . Blocks }
cst := cbor . NewCborStore ( cbb )
cst . Atlas = vm . cst . Atlas // associate the atlas.
rt . cst = cst
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-10-24 09:57:17 +00:00
rt . Syscalls = pricedSyscalls {
under : vm . Syscalls ( ctx , rt ) ,
chargeGas : rt . chargeGasFunc ( 1 ) ,
pl : rt . pricelist ,
}
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
}
2021-01-14 11:29:57 +00:00
type (
CircSupplyCalculator func ( context . Context , abi . ChainEpoch , * state . StateTree ) ( abi . TokenAmount , error )
NtwkVersionGetter func ( context . Context , abi . ChainEpoch ) network . Version
LookbackStateGetter func ( context . Context , abi . ChainEpoch ) ( * state . StateTree , error )
)
2020-07-25 22:29:33 +00:00
2019-07-05 14:29:17 +00:00
type VM struct {
2021-05-15 01:11:23 +00:00
cstate * state . StateTree
// TODO: Is base actually used? Can we delete it?
2020-08-09 22:49:38 +00:00
base cid . Cid
cst * cbor . BasicIpldStore
2021-01-29 20:01:00 +00:00
buf * blockstore . BufferedBlockstore
2020-08-09 22:49:38 +00:00
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
2020-10-24 09:57:17 +00:00
lbStateGet LookbackStateGetter
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
2021-01-29 20:01:00 +00:00
Bstore blockstore . Blockstore
2021-09-02 16:07:23 +00:00
Actors * ActorRegistry
2020-08-09 22:49:38 +00:00
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-10-24 09:57:17 +00:00
LookbackState LookbackStateGetter
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 ) {
2021-01-29 20:01:00 +00:00
buf := blockstore . NewBuffered ( 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 ,
2021-09-02 16:07:23 +00:00
areg : opts . Actors ,
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 ,
2020-10-24 09:57:17 +00:00
lbStateGet : opts . LookbackState ,
2019-07-05 14:29:17 +00:00
} , nil
}
2019-09-20 02:54:52 +00:00
type Rand interface {
2021-09-18 17:57:04 +00:00
GetChainRandomnessV1 ( ctx context . Context , pers crypto . DomainSeparationTag , round abi . ChainEpoch , entropy [ ] byte ) ( [ ] byte , error )
GetChainRandomnessV2 ( ctx context . Context , pers crypto . DomainSeparationTag , round abi . ChainEpoch , entropy [ ] byte ) ( [ ] byte , error )
GetBeaconRandomnessV1 ( ctx context . Context , pers crypto . DomainSeparationTag , round abi . ChainEpoch , entropy [ ] byte ) ( [ ] byte , error )
GetBeaconRandomnessV2 ( ctx context . Context , pers crypto . DomainSeparationTag , round abi . ChainEpoch , entropy [ ] byte ) ( [ ] byte , error )
GetBeaconRandomnessV3 ( ctx context . Context , pers crypto . DomainSeparationTag , filecoinEpoch 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-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 {
2021-11-29 22:26:47 +00:00
if err := vm . transfer ( msg . From , msg . To , msg . Value , vm . ntwkVersion ( ctx , vm . blockHeight ) ) ; err != nil {
2020-06-11 00:47:28 +00:00
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
}
2021-08-10 19:50:37 +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 ) ,
2021-06-21 16:05:48 +00:00
ActorErr : aerrors . Newf ( exitcode . SysErrOutOfGas ,
2021-06-09 20:00:56 +00:00
"message gas limit does not cover on-chain gas costs" ) ,
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-12-15 04:51:43 +00:00
2021-05-27 16:06:15 +00:00
burn , err := vm . ShouldBurn ( ctx , st , msg , errcode )
2020-12-15 04:51:43 +00:00
if err != nil {
return nil , xerrors . Errorf ( "deciding whether should burn failed: %w" , err )
}
gasOutputs := ComputeGasOutputs ( gasUsed , msg . GasLimit , vm . baseFee , msg . GasFeeCap , msg . GasPremium , burn )
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
}
2021-05-27 16:06:15 +00:00
func ( vm * VM ) ShouldBurn ( ctx context . Context , st * state . StateTree , msg * types . Message , errcode exitcode . ExitCode ) ( bool , error ) {
if vm . ntwkVersion ( ctx , vm . blockHeight ) <= network . Version12 {
// Check to see if we should burn funds. We avoid burning on successful
// window post. This won't catch _indirect_ window post calls, but this
// is the best we can get for now.
if vm . blockHeight > build . UpgradeClausHeight && errcode == exitcode . Ok && msg . Method == miner . Methods . SubmitWindowedPoSt {
// Ok, we've checked the _method_, but we still need to check
// the target actor. It would be nice if we could just look at
// the trace, but I'm not sure if that's safe?
if toActor , err := st . GetActor ( msg . To ) ; err != nil {
// If the actor wasn't found, we probably deleted it or something. Move on.
if ! xerrors . Is ( err , types . ErrActorNotFound ) {
// Otherwise, this should never fail and something is very wrong.
return false , xerrors . Errorf ( "failed to lookup target actor: %w" , err )
}
} else if builtin . IsStorageMinerActor ( toActor . Code ) {
// Ok, this is a storage miner and we've processed a window post. Remove the burn.
return false , nil
2020-12-15 04:51:43 +00:00
}
}
2021-05-27 16:06:15 +00:00
return true , nil
2020-12-15 04:51:43 +00:00
}
2021-05-27 16:06:15 +00:00
// Any "don't burn" rules from Network v13 onwards go here, for now we always return true
2020-12-15 04:51:43 +00:00
return true , nil
}
2020-11-17 04:34:09 +00:00
type vmFlushKey struct { }
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-11-17 04:34:09 +00:00
if err := Copy ( context . WithValue ( ctx , vmFlushKey { } , true ) , 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
}
2021-05-19 00:01:30 +00:00
// Get the buffered blockstore associated with the VM. This includes any temporary blocks produced
2021-06-18 18:17:35 +00:00
// during this VM's execution.
2021-05-19 00:01:30 +00:00
func ( vm * VM ) ActorStore ( ctx context . Context ) adt . Store {
return adt . WrapStore ( ctx , vm . cst )
}
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 ( )
2020-11-11 16:05:08 +00:00
start := time . Now ( )
2020-09-24 03:19:20 +00:00
var numBlocks int
var totalCopySize int
2020-11-11 19:42:00 +00:00
const batchSize = 128
const bufCount = 3
freeBufs := make ( chan [ ] block . Block , bufCount )
toFlush := make ( chan [ ] block . Block , bufCount )
for i := 0 ; i < bufCount ; i ++ {
freeBufs <- make ( [ ] block . Block , 0 , batchSize )
}
errFlushChan := make ( chan error )
go func ( ) {
for b := range toFlush {
if err := to . PutMany ( b ) ; err != nil {
close ( freeBufs )
errFlushChan <- xerrors . Errorf ( "batch put in copy: %w" , err )
return
}
freeBufs <- b [ : 0 ]
}
close ( errFlushChan )
close ( freeBufs )
} ( )
2021-01-14 11:29:57 +00:00
batch := <- freeBufs
2019-11-17 17:50:04 +00:00
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 )
2020-11-11 19:42:00 +00:00
if len ( batch ) >= batchSize {
toFlush <- batch
var ok bool
batch , ok = <- freeBufs
if ! ok {
return <- errFlushChan
2019-11-17 17:50:04 +00:00
}
}
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 {
2020-11-11 19:42:00 +00:00
toFlush <- batch
}
close ( toFlush ) // close the toFlush triggering the loop to end
err := <- errFlushChan // get error out or get nil if it was closed
if err != nil {
return err
2019-11-17 17:50:04 +00:00
}
2020-09-24 03:19:20 +00:00
span . AddAttributes (
trace . Int64Attribute ( "numBlocks" , int64 ( numBlocks ) ) ,
trace . Int64Attribute ( "copySize" , int64 ( totalCopySize ) ) ,
)
2020-11-17 04:34:09 +00:00
if yes , ok := ctx . Value ( vmFlushKey { } ) . ( bool ) ; yes && ok {
2020-11-11 16:05:08 +00:00
took := metrics . SinceInMilliseconds ( start )
stats . Record ( ctx , metrics . VMFlushCopyCount . M ( int64 ( numBlocks ) ) , metrics . VMFlushCopyDuration . M ( took ) )
}
2020-09-24 03:19:20 +00:00
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
}
2021-11-29 22:26:47 +00:00
func ( vm * VM ) transfer ( from , to address . Address , amt types . BigInt , networkVersion network . Version ) aerrors . ActorError {
var f * types . Actor
var fromID , toID address . Address
var err error
// switching the order around so that transactions for more than the balance sent to self fail
if networkVersion >= network . Version15 {
if amt . LessThan ( types . NewInt ( 0 ) ) {
return aerrors . Newf ( exitcode . SysErrForbidden , "attempted to transfer negative value: %s" , amt )
}
2019-10-22 11:57:20 +00:00
2021-11-29 22:26:47 +00:00
fromID , err = vm . cstate . LookupID ( from )
if err != nil {
return aerrors . Fatalf ( "transfer failed when resolving sender address: %s" , err )
}
2020-05-28 17:22:32 +00:00
2021-11-29 22:26:47 +00:00
f , err = vm . cstate . GetActor ( fromID )
if err != nil {
return aerrors . Fatalf ( "transfer failed when retrieving sender actor: %s" , err )
}
2020-05-28 17:22:32 +00:00
2021-11-29 22:26:47 +00:00
if f . Balance . LessThan ( amt ) {
return aerrors . Newf ( exitcode . SysErrInsufficientFunds , "transfer failed, insufficient balance in sender actor: %v" , f . Balance )
}
2020-05-28 17:22:32 +00:00
2021-11-29 22:26:47 +00:00
if from == to {
log . Infow ( "sending to same address: noop" , "from/to addr" , from )
return nil
}
2019-10-12 06:57:49 +00:00
2021-11-29 22:26:47 +00:00
toID , err = vm . cstate . LookupID ( to )
if err != nil {
return aerrors . Fatalf ( "transfer failed when resolving receiver address: %s" , err )
}
if fromID == toID {
log . Infow ( "sending to same actor ID: noop" , "from/to actor" , fromID )
return nil
}
} else {
if from == to {
return nil
}
fromID , err = vm . cstate . LookupID ( from )
if err != nil {
return aerrors . Fatalf ( "transfer failed when resolving sender address: %s" , err )
}
toID , err = vm . cstate . LookupID ( to )
if err != nil {
return aerrors . Fatalf ( "transfer failed when resolving receiver address: %s" , err )
}
if fromID == toID {
return nil
}
if amt . LessThan ( types . NewInt ( 0 ) ) {
return aerrors . Newf ( exitcode . SysErrForbidden , "attempted to transfer negative value: %s" , amt )
}
f , err = vm . cstate . GetActor ( fromID )
if err != nil {
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
}
2021-11-29 22:26:47 +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
2021-11-29 22:26:47 +00:00
if err = vm . cstate . SetActor ( fromID , f ) ; err != nil {
return aerrors . Fatalf ( "transfer failed when setting sender actor: %s" , err )
2020-04-03 00:09:41 +00:00
}
2021-11-29 22:26:47 +00:00
if err = vm . cstate . SetActor ( toID , t ) ; err != nil {
return aerrors . Fatalf ( "transfer failed when setting receiver 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 )
}