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-04-02 21:18:57 +00:00
"time"
2019-07-05 14:29:17 +00:00
2020-02-28 18:01:43 +00:00
"github.com/filecoin-project/specs-actors/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"
2019-10-18 11:39:31 +00:00
blockstore "github.com/ipfs/go-ipfs-blockstore"
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-02-22 13:10:46 +00:00
commcid "github.com/filecoin-project/go-fil-commcid"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/builtin/account"
init_ "github.com/filecoin-project/specs-actors/actors/builtin/init"
"github.com/filecoin-project/specs-actors/actors/crypto"
"github.com/filecoin-project/specs-actors/actors/runtime"
2020-03-03 23:01:35 +00:00
"github.com/filecoin-project/specs-actors/actors/runtime/exitcode"
2020-02-17 21:11:03 +00:00
2019-10-17 08:57:56 +00:00
"github.com/filecoin-project/lotus/chain/actors/aerrors"
"github.com/filecoin-project/lotus/chain/state"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/bufbstore"
2019-07-05 14:29:17 +00:00
)
2019-07-26 19:01:02 +00:00
var log = logging . Logger ( "vm" )
2020-06-16 16:25:48 +00:00
var gasOnActorExec = newGasCharge ( "OnActorExec" , 0 , 0 )
2019-07-26 19:01:02 +00:00
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-02-04 22:19:05 +00:00
func ResolveToKeyAddr ( state types . StateTree , cst cbor . IpldStore , addr address . Address ) ( address . Address , aerrors . ActorError ) {
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-04-23 16:45:52 +00:00
return address . Undef , aerrors . Newf ( exitcode . SysErrInvalidParameters , "failed to find actor: %s" , addr )
2019-08-09 21:41:50 +00:00
}
2020-02-25 20:54:58 +00:00
if act . Code != builtin . AccountActorCodeID {
2020-04-23 16:45:52 +00:00
return address . Undef , aerrors . Newf ( exitcode . SysErrInvalidParameters , "address %s was not for an account actor" , addr )
2019-08-09 21:41:50 +00:00
}
2020-02-12 07:44:20 +00:00
var aast account . State
2019-08-27 00:46:39 +00:00
if err := cst . Get ( context . TODO ( ) , act . Head , & aast ) ; err != nil {
2020-04-23 16:45:52 +00:00
return address . Undef , aerrors . Absorb ( err , exitcode . SysErrInvalidParameters , fmt . Sprintf ( "failed to get account actor state for %s" , addr ) )
2019-08-09 21:41:50 +00:00
}
return aast . Address , nil
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-06-29 17:23:50 +00:00
bs . chargeGas ( newGasCharge ( "OnIpldGetStart" , 0 , 0 ) . WithVirtual ( 40175 , 0 ) )
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-03-19 16:27:42 +00:00
bs . chargeGas ( bs . pricelist . OnIpldGet ( 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-03-20 19:43:08 +00:00
func ( vm * VM ) makeRuntime ( ctx context . Context , msg * types . Message , origin address . Address , originNonce uint64 , usedGas int64 , nac uint64 ) * 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 ,
msg : msg ,
origin : origin ,
originNonce : originNonce ,
height : vm . blockHeight ,
2020-06-12 18:46:04 +00:00
gasUsed : usedGas ,
gasAvailable : msg . GasLimit ,
numActorsCreated : nac ,
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-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-03-19 16:27:42 +00:00
rt . sys = pricedSyscalls {
under : vm . Syscalls ,
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
rt . vmsg = & vmm
2020-03-10 02:24:02 +00:00
return rt
2019-07-05 14:29:17 +00:00
}
type VM struct {
2019-07-25 22:15:03 +00:00
cstate * state . StateTree
2019-07-05 14:29:17 +00:00
base cid . Cid
2020-02-04 22:19:05 +00:00
cst * cbor . BasicIpldStore
2019-07-08 20:29:01 +00:00
buf * bufbstore . BufferedBS
2020-02-08 02:18:32 +00:00
blockHeight abi . ChainEpoch
2020-06-02 14:29:39 +00:00
inv * Invoker
2019-09-20 02:54:52 +00:00
rand Rand
2019-12-06 14:06:42 +00:00
2020-02-18 07:15:30 +00:00
Syscalls runtime . Syscalls
2019-07-05 14:29:17 +00:00
}
2020-04-07 18:26:43 +00:00
func NewVM ( base cid . Cid , height abi . ChainEpoch , r Rand , cbs blockstore . Blockstore , syscalls runtime . Syscalls ) ( * VM , error ) {
2019-10-15 04:33:29 +00:00
buf := bufbstore . NewBufferedBstore ( cbs )
2020-02-04 22:19:05 +00:00
cst := cbor . NewCborStore ( buf )
2019-07-25 22:15:03 +00:00
state , err := state . LoadStateTree ( cst , base )
2019-07-05 14:29:17 +00:00
if err != nil {
return nil , err
}
return & VM {
cstate : state ,
base : base ,
2019-07-26 04:54:22 +00:00
cst : cst ,
2019-07-05 14:29:17 +00:00
buf : buf ,
blockHeight : height ,
2020-01-31 23:51:15 +00:00
inv : NewInvoker ( ) ,
2020-01-13 20:47:27 +00:00
rand : r , // TODO: Probably should be a syscall
Syscalls : syscalls ,
2019-07-05 14:29:17 +00:00
} , nil
}
2019-09-20 02:54:52 +00:00
type Rand interface {
2020-04-08 15:11:42 +00:00
GetRandomness ( 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
Penalty types . BigInt
ExecutionTrace types . ExecutionTrace
Duration time . Duration
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-03-23 06:39:42 +00:00
st := vm . cstate
2020-03-19 16:27:42 +00:00
2020-03-24 08:43:09 +00:00
origin := msg . From
2020-03-17 05:58:00 +00:00
on := msg . Nonce
2020-03-20 19:43:08 +00:00
var nac uint64 = 0
2020-06-11 19:59:39 +00:00
var gasUsed int64
2019-08-15 16:23:28 +00:00
if parent != nil {
2020-06-11 19:59:39 +00:00
gasUsed = parent . gasUsed
2019-08-15 16:23:28 +00:00
origin = parent . origin
2020-03-17 05:58:00 +00:00
on = parent . originNonce
2020-03-20 19:43:08 +00:00
nac = parent . numActorsCreated
2019-08-03 11:26:35 +00:00
}
2020-03-23 20:44:06 +00:00
2020-03-20 19:43:08 +00:00
rt := vm . makeRuntime ( ctx , msg , origin , on , gasUsed , nac )
2020-06-12 18:46:04 +00:00
rt . lastGasChargeTime = start
2019-08-15 16:23:28 +00:00
if parent != nil {
2020-06-11 19:59:39 +00:00
rt . lastGasChargeTime = parent . lastGasChargeTime
rt . lastGasCharge = parent . lastGasCharge
2019-08-15 16:23:28 +00:00
defer func ( ) {
2020-03-10 02:24:02 +00:00
parent . gasUsed = rt . gasUsed
2020-06-11 19:59:39 +00:00
parent . lastGasChargeTime = rt . lastGasChargeTime
parent . lastGasCharge = rt . lastGasCharge
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 ) {
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-03-25 02:25:20 +00:00
2020-06-11 00:47:28 +00:00
toActor , err := st . GetActor ( msg . To )
if err != nil {
if xerrors . Is ( err , init_ . ErrAddressNotFound ) {
a , err := TryCreateAccountActor ( rt , msg . To )
if err != nil {
return nil , aerrors . Wrapf ( err , "could not create account" )
}
toActor = a
} else {
return nil , aerrors . Escalate ( err , "getting actor" )
2020-03-23 06:39:42 +00:00
}
}
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 )
2020-06-16 17:29:33 +00:00
_ = rt . chargeGasSafe ( newGasCharge ( "OnActorExecDone" , 0 , 0 ) )
2020-06-11 00:47:28 +00:00
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
}
if msg . GasPrice == types . EmptyInt {
return xerrors . Errorf ( "message gas no gas price set" )
}
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-04-02 21:18:57 +00:00
start := time . Now ( )
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 ,
Penalty : types . NewInt ( 0 ) ,
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-04-02 21:18:57 +00:00
start := time . Now ( )
2019-07-26 19:01:02 +00:00
ctx , span := trace . StartSpan ( ctx , "vm.ApplyMessage" )
defer span . End ( )
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-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-04-02 21:18:57 +00:00
Penalty : types . BigMul ( msg . GasPrice , types . NewInt ( uint64 ( msgGasCost ) ) ) ,
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-03-25 19:13:09 +00:00
minerPenaltyAmount := types . BigMul ( msg . GasPrice , types . NewInt ( uint64 ( msgGasCost ) ) )
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 ) {
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-04-02 21:18:57 +00:00
Penalty : minerPenaltyAmount ,
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-03-25 07:57:13 +00:00
if ! fromActor . Code . Equals ( builtin . AccountActorCodeID ) {
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-04-02 21:18:57 +00:00
Penalty : minerPenaltyAmount ,
Duration : time . Since ( start ) ,
2020-03-25 07:57:13 +00:00
} , nil
}
2020-05-11 20:19:35 +00:00
// TODO: We should remove this, we might punish miners for no fault of their own
2020-03-10 20:14:50 +00:00
if msg . Nonce != fromActor . Nonce {
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-04-02 21:18:57 +00:00
Penalty : minerPenaltyAmount ,
Duration : time . Since ( start ) ,
2020-03-10 20:14:50 +00:00
} , nil
}
2020-03-18 20:45:37 +00:00
gascost := types . BigMul ( types . NewInt ( uint64 ( msg . GasLimit ) ) , msg . GasPrice )
2019-08-03 11:53:18 +00:00
totalCost := types . BigAdd ( gascost , msg . Value )
2019-09-23 17:11:44 +00:00
if fromActor . Balance . LessThan ( totalCost ) {
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-04-02 21:18:57 +00:00
Penalty : minerPenaltyAmount ,
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
}
gasUsed = rt . gasUsed
if gasUsed < 0 {
gasUsed = 0
}
// refund unused gas
refund := types . BigMul ( types . NewInt ( uint64 ( msg . GasLimit - gasUsed ) ) , msg . GasPrice )
if err := vm . transferFromGasHolder ( msg . From , gasHolder , refund ) ; err != nil {
return nil , xerrors . Errorf ( "failed to refund gas" )
2019-08-03 11:53:18 +00:00
}
2019-08-03 11:26:35 +00:00
2020-03-18 20:45:37 +00:00
gasReward := types . BigMul ( msg . GasPrice , types . NewInt ( uint64 ( gasUsed ) ) )
2020-03-24 03:17:29 +00:00
if err := vm . transferFromGasHolder ( builtin . RewardActorAddr , gasHolder , gasReward ) ; err != nil {
2019-10-11 09:13:04 +00:00
return nil , xerrors . Errorf ( "failed to give miner gas reward: %w" , err )
}
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
2020-06-12 18:46:04 +00:00
rt . finilizeGasTracing ( )
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 ,
Penalty : types . NewInt ( 0 ) ,
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
}
2019-11-16 19:24:11 +00:00
if err := Copy ( 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
}
2019-11-16 19:24:11 +00:00
func linksForObj ( blk block . Block ) ( [ ] cid . Cid , error ) {
switch blk . Cid ( ) . Prefix ( ) . Codec {
case cid . DagCBOR :
return cbg . ScanForLinks ( bytes . NewReader ( blk . RawData ( ) ) )
default :
return nil , xerrors . Errorf ( "vm flush copy method only supports dag cbor" )
}
}
func Copy ( from , to blockstore . Blockstore , root cid . Cid ) error {
2019-11-17 17:50:04 +00:00
var batch [ ] block . Block
batchCp := func ( blk block . Block ) error {
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 {
return err
}
if len ( batch ) > 0 {
if err := to . PutMany ( batch ) ; err != nil {
return xerrors . Errorf ( "batch put in copy: %w" , err )
}
}
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
links , err := linksForObj ( blk )
if err != nil {
return err
}
2019-07-15 22:19:26 +00:00
for _ , link := range links {
2020-02-23 15:50:36 +00:00
if link . Prefix ( ) . MhType == mh . IDENTITY || link . Prefix ( ) . MhType == uint64 ( commcid . FC_SEALED_V1 ) || link . Prefix ( ) . MhType == uint64 ( commcid . FC_UNSEALED_V1 ) {
2019-07-25 22:15:33 +00:00
continue
}
2019-11-16 19:24:11 +00:00
has , err := to . Has ( link )
if err != nil {
2019-07-15 22:19:26 +00:00
return err
2019-11-16 19:24:11 +00:00
}
if has {
2019-07-15 22:19:26 +00:00
continue
}
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 {
2019-07-15 22:19:26 +00:00
return err
}
}
2019-11-16 19:24:11 +00:00
2019-11-17 17:50:04 +00:00
if err := cp ( blk ) ; err != nil {
2019-07-15 22:19:26 +00:00
return err
}
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-03-10 02:24:02 +00:00
trace . StringAttribute ( "to" , rt . Message ( ) . Receiver ( ) . String ( ) ) ,
2019-11-12 19:54:25 +00:00
trace . Int64Attribute ( "method" , int64 ( method ) ) ,
2020-03-10 02:24:02 +00:00
trace . StringAttribute ( "value" , rt . Message ( ) . 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-04-03 00:09:41 +00:00
ret , err := vm . inv . 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-06-02 14:29:39 +00:00
func ( vm * VM ) SetInvoker ( i * Invoker ) {
2020-03-24 03:17:29 +00:00
vm . inv = i
}
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-05-13 21:20:38 +00:00
return aerrors . Newf ( exitcode . SysErrInsufficientFunds , "transfer failed when deducting funds: %s" , 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-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 )
}