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"
2019-09-21 00:22:28 +00:00
"math/big"
2020-02-18 21:37:59 +00:00
"reflect"
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"
hamt "github.com/ipfs/go-hamt-ipld"
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"
2020-01-30 23:48:25 +00:00
"github.com/filecoin-project/lotus/lib/sigs"
2019-07-05 14:29:17 +00:00
)
2019-07-26 19:01:02 +00:00
var log = logging . Logger ( "vm" )
2019-08-15 16:23:28 +00:00
const (
gasFundTransfer = 10
2019-08-29 18:34:40 +00:00
gasInvoke = 5
2019-08-29 18:20:59 +00:00
2019-08-29 18:34:40 +00:00
gasGetObj = 10
2019-08-30 16:05:54 +00:00
gasGetPerByte = 1
2019-08-29 18:34:40 +00:00
gasPutObj = 20
gasPutPerByte = 2
gasCommit = 50
gasPerMessageByte = 2
2019-08-29 18:20:59 +00:00
)
const (
outOfGasErrCode = 200
2019-08-15 16:23:28 +00:00
)
2019-07-05 14:29:17 +00:00
type VMContext struct {
2019-07-26 19:01:02 +00:00
ctx context . Context
2019-07-12 20:56:41 +00:00
vm * VM
2019-07-25 22:15:03 +00:00
state * state . StateTree
2019-07-12 10:43:15 +00:00
msg * types . Message
2020-02-08 02:18:32 +00:00
height abi . ChainEpoch
2020-02-04 22:19:05 +00:00
cst cbor . IpldStore
2019-07-12 03:59:55 +00:00
2019-08-16 12:41:43 +00:00
gasAvailable types . BigInt
2019-08-15 16:23:28 +00:00
gasUsed types . BigInt
2020-02-18 07:15:30 +00:00
sys runtime . Syscalls
2019-12-06 14:06:42 +00:00
2019-07-12 03:59:55 +00:00
// root cid of the state of the actor this invocation will be on
sroot cid . Cid
2019-10-22 08:14:10 +00:00
// address that started invoke chain
2019-07-16 16:40:25 +00:00
origin address . Address
2019-07-05 14:29:17 +00:00
}
// Message is the message that kicked off the current invocation
2019-07-12 10:43:15 +00:00
func ( vmc * VMContext ) Message ( ) * types . Message {
2019-07-05 14:29:17 +00:00
return vmc . msg
}
2020-02-23 20:00:47 +00:00
func ( vmc * VMContext ) GetRandomness ( personalization crypto . DomainSeparationTag , randEpoch abi . ChainEpoch , entropy [ ] byte ) ( [ ] byte , aerrors . ActorError ) {
res , err := vmc . vm . rand . GetRandomness ( vmc . ctx , personalization , int64 ( randEpoch ) , entropy )
2019-09-19 13:32:00 +00:00
if err != nil {
return nil , aerrors . Escalate ( err , "could not get randomness" )
}
return res , nil
}
2020-02-18 07:15:30 +00:00
func ( vmc * VMContext ) Sys ( ) runtime . Syscalls {
2019-12-06 14:06:42 +00:00
return vmc . sys
}
2019-08-27 15:01:17 +00:00
// Storage interface
2019-07-12 03:59:55 +00:00
2019-09-10 19:58:45 +00:00
func ( vmc * VMContext ) Put ( i cbg . CBORMarshaler ) ( cid . Cid , aerrors . ActorError ) {
2019-08-27 20:32:58 +00:00
c , err := vmc . cst . Put ( context . TODO ( ) , i )
2019-07-22 18:17:42 +00:00
if err != nil {
2019-12-18 03:41:58 +00:00
return cid . Undef , aerrors . HandleExternalError ( err , fmt . Sprintf ( "putting object %T" , i ) )
2019-07-22 18:17:42 +00:00
}
return c , nil
2019-07-12 03:59:55 +00:00
}
2019-09-10 19:58:45 +00:00
func ( vmc * VMContext ) Get ( c cid . Cid , out cbg . CBORUnmarshaler ) aerrors . ActorError {
2019-08-29 18:20:59 +00:00
err := vmc . cst . Get ( context . TODO ( ) , c , out )
if err != nil {
2019-12-18 03:41:58 +00:00
return aerrors . HandleExternalError ( err , "getting cid" )
2019-08-27 15:01:17 +00:00
}
2019-08-29 18:20:59 +00:00
return nil
2019-07-12 03:59:55 +00:00
}
2019-08-27 20:32:58 +00:00
func ( vmc * VMContext ) GetHead ( ) cid . Cid {
return vmc . sroot
2019-07-12 03:59:55 +00:00
}
2019-08-27 20:32:58 +00:00
func ( vmc * VMContext ) Commit ( oldh , newh cid . Cid ) aerrors . ActorError {
if err := vmc . ChargeGas ( gasCommit ) ; err != nil {
2019-08-27 15:01:17 +00:00
return aerrors . Wrap ( err , "out of gas" )
}
2019-08-27 20:32:58 +00:00
if vmc . sroot != oldh {
2019-07-22 18:17:42 +00:00
return aerrors . New ( 1 , "failed to update, inconsistent base reference" )
2019-07-12 03:59:55 +00:00
}
2019-08-27 20:32:58 +00:00
vmc . sroot = newh
2019-07-12 03:59:55 +00:00
return nil
}
2019-08-27 15:01:17 +00:00
// End of storage interface
2019-07-05 14:29:17 +00:00
// Storage provides access to the VM storage layer
2019-07-12 03:59:55 +00:00
func ( vmc * VMContext ) Storage ( ) types . Storage {
2019-08-27 15:01:17 +00:00
return vmc
2019-07-05 14:29:17 +00:00
}
2020-02-04 22:19:05 +00:00
func ( vmc * VMContext ) Ipld ( ) cbor . IpldStore {
2019-07-05 14:29:17 +00:00
return vmc . cst
}
2019-07-16 16:40:25 +00:00
func ( vmc * VMContext ) Origin ( ) address . Address {
return vmc . origin
}
2019-07-05 14:29:17 +00:00
// Send allows the current execution context to invoke methods on other actors in the system
2020-02-11 01:43:26 +00:00
func ( vmc * VMContext ) Send ( to address . Address , method abi . MethodNum , value types . BigInt , params [ ] byte ) ( [ ] byte , aerrors . ActorError ) {
2019-08-15 16:23:28 +00:00
ctx , span := trace . StartSpan ( vmc . ctx , "vmc.Send" )
2019-07-26 19:01:02 +00:00
defer span . End ( )
if span . IsRecordingEvents ( ) {
span . AddAttributes (
trace . StringAttribute ( "to" , to . String ( ) ) ,
trace . Int64Attribute ( "method" , int64 ( method ) ) ,
trace . StringAttribute ( "value" , value . String ( ) ) ,
)
}
2019-07-16 11:35:56 +00:00
2019-07-12 20:56:41 +00:00
msg := & types . Message {
2019-08-15 16:23:28 +00:00
From : vmc . msg . To ,
To : to ,
Method : method ,
Value : value ,
Params : params ,
2019-08-16 12:41:43 +00:00
GasLimit : vmc . gasAvailable ,
2019-07-12 20:56:41 +00:00
}
2019-07-15 22:19:26 +00:00
2020-03-05 01:19:15 +00:00
st := vmc . state
if err := st . Snapshot ( ctx ) ; err != nil {
return nil , aerrors . Fatalf ( "snapshot failed: %s" , err )
}
defer st . ClearSnapshot ( )
2019-08-29 18:34:40 +00:00
ret , err , _ := vmc . vm . send ( ctx , msg , vmc , 0 )
2020-03-05 01:19:15 +00:00
if err != nil {
if err := st . Revert ( ) ; err != nil {
return nil , aerrors . Escalate ( err , "failed to revert state tree after failed subcall" )
}
}
2019-08-03 11:53:18 +00:00
return ret , err
2019-07-05 14:29:17 +00:00
}
// BlockHeight returns the height of the block this message was added to the chain in
2020-02-08 02:18:32 +00:00
func ( vmc * VMContext ) BlockHeight ( ) abi . ChainEpoch {
2019-07-05 14:29:17 +00:00
return vmc . height
}
2019-07-12 10:23:05 +00:00
func ( vmc * VMContext ) GasUsed ( ) types . BigInt {
2019-08-15 16:23:28 +00:00
return vmc . gasUsed
}
func ( vmc * VMContext ) ChargeGas ( amount uint64 ) aerrors . ActorError {
toUse := types . NewInt ( amount )
vmc . gasUsed = types . BigAdd ( vmc . gasUsed , toUse )
2019-09-23 17:11:44 +00:00
if vmc . gasUsed . GreaterThan ( vmc . gasAvailable ) {
2020-03-05 18:13:49 +00:00
return aerrors . Newf ( uint8 ( exitcode . SysErrOutOfGas ) , "not enough gas: used=%s, available=%s" , vmc . gasUsed , vmc . gasAvailable )
2019-08-15 16:23:28 +00:00
}
return nil
2019-07-05 14:29:17 +00:00
}
2019-07-22 18:17:42 +00:00
func ( vmc * VMContext ) StateTree ( ) ( types . StateTree , aerrors . ActorError ) {
2020-02-25 20:54:58 +00:00
if vmc . msg . To != builtin . InitActorAddr {
2019-07-22 18:17:42 +00:00
return nil , aerrors . Escalate ( fmt . Errorf ( "only init actor can access state tree directly" ) , "invalid use of StateTree" )
2019-07-12 16:40:58 +00:00
}
return vmc . state , nil
}
2020-02-17 21:11:03 +00:00
func ( vmc * VMContext ) ActorCodeCID ( addr address . Address ) ( ret cid . Cid , err error ) {
act , err := vmc . state . GetActor ( addr )
if err != nil {
return cid . Undef , err
}
return act . Code , nil
}
2020-02-21 16:57:40 +00:00
func ( vmc * VMContext ) LookupID ( a address . Address ) ( address . Address , error ) {
return vmc . state . LookupID ( a )
}
2019-08-21 20:47:13 +00:00
const GasVerifySignature = 50
2020-02-12 23:52:36 +00:00
func ( vmctx * VMContext ) VerifySignature ( sig * crypto . Signature , act address . Address , data [ ] byte ) aerrors . ActorError {
2019-08-21 20:47:13 +00:00
if err := vmctx . ChargeGas ( GasVerifySignature ) ; err != nil {
return err
}
2019-08-09 21:41:50 +00:00
if act . Protocol ( ) == address . ID {
2019-08-27 00:46:39 +00:00
kaddr , err := ResolveToKeyAddr ( vmctx . state , vmctx . cst , act )
2019-08-09 21:41:50 +00:00
if err != nil {
return aerrors . Wrap ( err , "failed to resolve address to key address" )
}
act = kaddr
}
2020-01-30 23:48:25 +00:00
if err := sigs . Verify ( sig , act , data ) ; err != nil {
2019-08-09 21:41:50 +00:00
return aerrors . New ( 2 , "signature verification failed" )
}
return nil
}
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 {
return address . Undef , aerrors . Newf ( 1 , "failed to find actor: %s" , addr )
}
2020-02-25 20:54:58 +00:00
if act . Code != builtin . AccountActorCodeID {
2019-08-09 21:41:50 +00:00
return address . Undef , aerrors . New ( 1 , "address was not for an account actor" )
}
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 {
2019-08-09 21:41:50 +00:00
return address . Undef , aerrors . Escalate ( err , fmt . Sprintf ( "failed to get account actor state for %s" , addr ) )
}
return aast . Address , nil
2019-07-24 05:38:16 +00:00
}
2019-09-12 04:12:35 +00:00
func ( vmctx * VMContext ) GetBalance ( a address . Address ) ( types . BigInt , aerrors . ActorError ) {
act , err := vmctx . state . GetActor ( a )
switch err {
default :
return types . EmptyInt , aerrors . Escalate ( err , "failed to look up actor balance" )
case hamt . ErrNotFound :
return types . NewInt ( 0 ) , nil
case nil :
return act . Balance , nil
}
}
2019-10-13 09:08:34 +00:00
func ( vmctx * VMContext ) Context ( ) context . Context {
return vmctx . ctx
}
2020-02-04 22:19:05 +00:00
var _ cbor . IpldBlockstore = ( * gasChargingBlocks ) ( nil )
2019-08-29 18:20:59 +00:00
type gasChargingBlocks struct {
chargeGas func ( uint64 ) aerrors . ActorError
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 ) {
2019-08-29 18:20:59 +00:00
if err := bs . chargeGas ( gasGetObj ) ; err != nil {
return nil , err
}
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
}
if err := bs . chargeGas ( uint64 ( len ( blk . RawData ( ) ) ) * gasGetPerByte ) ; err != nil {
return nil , err
}
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 {
2019-08-29 18:20:59 +00:00
if err := bs . chargeGas ( gasPutObj + uint64 ( len ( blk . RawData ( ) ) ) * gasPutPerByte ) ; err != nil {
return err
}
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" )
}
return nil
2019-08-29 18:20:59 +00:00
}
2019-08-15 16:23:28 +00:00
func ( vm * VM ) makeVMContext ( ctx context . Context , sroot cid . Cid , msg * types . Message , origin address . Address , usedGas types . BigInt ) * VMContext {
2019-08-29 18:20:59 +00:00
vmc := & VMContext {
2019-07-26 19:01:02 +00:00
ctx : ctx ,
2019-07-12 20:56:41 +00:00
vm : vm ,
state : vm . cstate ,
2019-07-12 03:59:55 +00:00
sroot : sroot ,
2019-07-05 14:29:17 +00:00
msg : msg ,
2019-07-16 16:40:25 +00:00
origin : origin ,
2019-07-12 20:56:41 +00:00
height : vm . blockHeight ,
2019-12-06 14:06:42 +00:00
sys : vm . Syscalls ,
2019-08-15 16:23:28 +00:00
gasUsed : usedGas ,
2019-08-16 12:41:43 +00:00
gasAvailable : msg . GasLimit ,
2019-07-05 14:29:17 +00:00
}
2020-02-04 22:19:05 +00:00
vmc . cst = & cbor . BasicIpldStore {
2019-08-29 18:20:59 +00:00
Blocks : & gasChargingBlocks { vmc . ChargeGas , vm . cst . Blocks } ,
Atlas : vm . cst . Atlas ,
}
return vmc
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
2019-07-05 14:29:17 +00:00
blockMiner address . Address
2019-07-11 15:38:37 +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-02-18 07:15:30 +00:00
func NewVM ( base cid . Cid , height abi . ChainEpoch , r Rand , maddr address . Address , 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 ,
blockMiner : maddr ,
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-02-23 20:00:47 +00:00
GetRandomness ( ctx context . Context , pers crypto . DomainSeparationTag , round int64 , 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
ActorErr aerrors . ActorError
2020-03-03 23:01:35 +00:00
Penalty big . Int
2019-07-31 15:37:00 +00:00
}
2019-08-29 18:34:40 +00:00
func ( vm * VM ) send ( ctx context . Context , msg * types . Message , parent * VMContext ,
gasCharge uint64 ) ( [ ] byte , aerrors . ActorError , * VMContext ) {
2019-08-15 16:23:28 +00:00
2019-08-03 11:26:35 +00:00
st := vm . cstate
2020-03-05 01:19:15 +00:00
2019-08-03 11:26:35 +00:00
fromActor , err := st . GetActor ( msg . From )
if err != nil {
return nil , aerrors . Absorb ( err , 1 , "could not find source actor" ) , nil
}
toActor , err := st . GetActor ( msg . To )
if err != nil {
2020-02-22 13:10:46 +00:00
if xerrors . Is ( err , init_ . ErrAddressNotFound ) {
2019-08-03 11:26:35 +00:00
a , err := TryCreateAccountActor ( st , msg . To )
if err != nil {
return nil , aerrors . Absorb ( err , 1 , "could not create account" ) , nil
}
toActor = a
} else {
return nil , aerrors . Escalate ( err , "getting actor" ) , nil
}
}
2019-08-29 18:34:40 +00:00
gasUsed := types . NewInt ( gasCharge )
2019-08-15 16:23:28 +00:00
origin := msg . From
if parent != nil {
2019-08-29 18:34:40 +00:00
gasUsed = types . BigAdd ( parent . gasUsed , gasUsed )
2019-08-15 16:23:28 +00:00
origin = parent . origin
2019-08-03 11:26:35 +00:00
}
2019-08-15 16:23:28 +00:00
vmctx := vm . makeVMContext ( ctx , toActor . Head , msg , origin , gasUsed )
if parent != nil {
defer func ( ) {
parent . gasUsed = vmctx . gasUsed
} ( )
}
if types . BigCmp ( msg . Value , types . NewInt ( 0 ) ) != 0 {
if aerr := vmctx . ChargeGas ( gasFundTransfer ) ; aerr != nil {
return nil , aerrors . Wrap ( aerr , "sending funds" ) , nil
}
2019-10-11 09:13:04 +00:00
if err := Transfer ( fromActor , toActor , msg . Value ) ; err != nil {
return nil , aerrors . Absorb ( err , 1 , "failed to transfer funds" ) , nil
2019-08-15 16:23:28 +00:00
}
}
2019-08-03 11:26:35 +00:00
if msg . Method != 0 {
ret , err := vm . Invoke ( toActor , vmctx , msg . Method , msg . Params )
2019-10-22 12:37:04 +00:00
if ! aerrors . IsFatal ( err ) {
toActor . Head = vmctx . Storage ( ) . GetHead ( )
}
2019-08-03 11:26:35 +00:00
return ret , err , vmctx
}
return nil , nil , vmctx
}
2019-08-09 21:41:50 +00:00
func checkMessage ( msg * types . Message ) error {
if msg . GasLimit == types . EmptyInt {
return xerrors . Errorf ( "message gas no gas limit set" )
}
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
}
2019-07-31 15:37:00 +00:00
func ( vm * VM ) ApplyMessage ( ctx context . Context , msg * types . Message ) ( * ApplyRet , error ) {
2019-07-26 19:01:02 +00:00
ctx , span := trace . StartSpan ( ctx , "vm.ApplyMessage" )
defer span . End ( )
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-05 18:13:49 +00:00
serMsg , err := msg . Serialize ( )
if err != nil {
return nil , xerrors . Errorf ( "could not serialize message: %w" , err )
}
msgGasCost := uint64 ( len ( serMsg ) ) * gasPerMessageByte
if msgGasCost > msg . GasLimit . Uint64 ( ) {
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
ExitCode : exitcode . SysErrOutOfGas ,
GasUsed : msg . GasLimit ,
} ,
} , nil
}
2019-07-05 14:29:17 +00:00
st := vm . cstate
2020-01-22 19:53:06 +00:00
if err := st . Snapshot ( ctx ) ; err != nil {
2019-07-28 05:35:32 +00:00
return nil , xerrors . Errorf ( "snapshot failed: %w" , err )
}
2020-03-05 01:19:15 +00:00
defer st . ClearSnapshot ( )
2019-07-28 05:35:32 +00:00
2019-07-05 14:29:17 +00:00
fromActor , err := st . GetActor ( msg . From )
if err != nil {
2020-03-03 23:01:35 +00:00
if xerrors . Is ( err , types . ErrActorNotFound ) {
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
ExitCode : exitcode . SysErrActorNotFound ,
GasUsed : msg . GasLimit ,
} ,
} , nil
}
return nil , xerrors . Errorf ( "failed to look up from actor: %w" , err )
2019-07-05 14:29:17 +00:00
}
2019-08-03 11:53:18 +00:00
gascost := types . BigMul ( msg . GasLimit , msg . GasPrice )
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 {
ExitCode : exitcode . SysErrInsufficientFunds ,
GasUsed : msg . GasLimit ,
} ,
} , nil
2019-08-03 11:53:18 +00:00
}
2019-10-11 09:13:04 +00:00
gasHolder := & types . Actor { Balance : types . NewInt ( 0 ) }
if err := Transfer ( fromActor , gasHolder , gascost ) ; err != nil {
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
2019-08-03 11:53:18 +00:00
if msg . Nonce != fromActor . Nonce {
2020-03-03 23:01:35 +00:00
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
ExitCode : exitcode . SysErrInvalidCallSeqNum ,
GasUsed : msg . GasLimit ,
} ,
} , nil
2019-08-03 11:53:18 +00:00
}
fromActor . Nonce ++
2019-08-15 16:23:28 +00:00
2019-08-29 18:34:40 +00:00
ret , actorErr , vmctx := vm . send ( ctx , msg , nil , msgGasCost )
2019-08-03 11:53:18 +00:00
2019-08-29 18:20:59 +00:00
if aerrors . IsFatal ( actorErr ) {
2019-12-19 15:50:18 +00:00
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-29 18:20:59 +00:00
}
2019-09-23 09:45:22 +00:00
if actorErr != nil {
2020-01-16 00:36:16 +00:00
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 ) )
2019-09-23 09:45:22 +00:00
}
2019-08-29 18:20:59 +00:00
2019-08-03 11:53:18 +00:00
var errcode uint8
2019-08-15 16:23:28 +00:00
var gasUsed types . BigInt
2019-08-29 18:20:59 +00:00
2019-08-03 11:53:18 +00:00
if errcode = aerrors . RetCode ( actorErr ) ; errcode != 0 {
2019-08-15 16:23:28 +00:00
gasUsed = msg . GasLimit
2019-08-03 11:53:18 +00:00
// 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
}
2019-08-03 11:26:35 +00:00
} else {
2019-08-03 11:53:18 +00:00
// refund unused gas
2019-08-15 16:23:28 +00:00
gasUsed = vmctx . GasUsed ( )
refund := types . BigMul ( types . BigSub ( msg . GasLimit , gasUsed ) , msg . GasPrice )
2019-10-11 09:13:04 +00:00
if err := Transfer ( gasHolder , fromActor , 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-04 21:21:24 +00:00
rwAct , err := st . GetActor ( builtin . RewardActorAddr )
2019-08-03 11:53:18 +00:00
if err != nil {
2020-03-03 23:01:35 +00:00
return nil , xerrors . Errorf ( "getting burnt funds actor failed: %w" , err )
2019-08-03 11:53:18 +00:00
}
2019-07-05 14:29:17 +00:00
2019-08-15 16:23:28 +00:00
gasReward := types . BigMul ( msg . GasPrice , gasUsed )
2020-03-04 21:21:24 +00:00
if err := Transfer ( gasHolder , rwAct , 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
2019-08-03 11:53:18 +00:00
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
2020-03-03 23:01:35 +00:00
ExitCode : exitcode . 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
} ,
ActorErr : actorErr ,
} , nil
2019-07-05 14:29:17 +00:00
}
2019-09-06 06:26:02 +00:00
func ( vm * VM ) SetBlockMiner ( m address . Address ) {
vm . blockMiner = m
}
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-02-18 21:37:59 +00:00
// vm.MutateState(idAddr, func(cst cbor.IpldStore, st *ActorStateType) error {...})
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-02-11 01:43:26 +00:00
func ( vm * VM ) Invoke ( act * types . Actor , vmctx * VMContext , method abi . MethodNum , params [ ] byte ) ( [ ] byte , aerrors . ActorError ) {
2019-07-26 19:01:02 +00:00
ctx , span := trace . StartSpan ( vmctx . ctx , "vm.Invoke" )
defer span . End ( )
2019-11-12 19:54:25 +00:00
if span . IsRecordingEvents ( ) {
span . AddAttributes (
trace . StringAttribute ( "to" , vmctx . Message ( ) . To . String ( ) ) ,
trace . Int64Attribute ( "method" , int64 ( method ) ) ,
trace . StringAttribute ( "value" , vmctx . Message ( ) . Value . String ( ) ) ,
)
}
2019-10-10 11:13:26 +00:00
2019-07-26 19:01:02 +00:00
var oldCtx context . Context
oldCtx , vmctx . ctx = vmctx . ctx , ctx
defer func ( ) {
vmctx . ctx = oldCtx
} ( )
2019-08-29 18:34:40 +00:00
if err := vmctx . ChargeGas ( gasInvoke ) ; err != nil {
return nil , aerrors . Wrap ( err , "invokeing" )
}
2019-07-11 15:38:37 +00:00
ret , err := vm . inv . Invoke ( act , vmctx , method , params )
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
2019-10-11 09:13:04 +00:00
func Transfer ( from , to * types . Actor , amt types . BigInt ) error {
2019-10-22 11:57:20 +00:00
if from == to {
return nil
}
2019-10-12 06:57:49 +00:00
if amt . LessThan ( types . NewInt ( 0 ) ) {
return xerrors . Errorf ( "attempted to transfer negative value" )
}
2019-10-11 09:13:04 +00:00
if err := deductFunds ( from , amt ) ; err != nil {
return err
}
depositFunds ( to , amt )
return nil
}
2020-01-31 23:51:15 +00:00
func ( vm * VM ) SetInvoker ( i * invoker ) {
vm . inv = i
}
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 )
}