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"
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" )
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
)
2020-03-04 01:33:55 +00:00
type ExecutionResult struct {
2020-03-08 00:46:12 +00:00
Msg * types . Message
MsgRct * types . MessageReceipt
Error string
2020-03-10 04:38:31 +00:00
Subcalls [ ] * ExecutionResult
2020-03-04 01:33:55 +00:00
}
2019-08-27 15:01:17 +00:00
// Storage interface
2019-07-12 03:59:55 +00:00
2019-08-27 15:01:17 +00:00
// End of storage interface
2019-07-05 14:29:17 +00:00
// Send allows the current execution context to invoke methods on other actors in the system
2019-08-09 21:41:50 +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 {
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
}
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-03-18 20:45:37 +00:00
chargeGas func ( int64 )
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 ) {
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 ( ) ) ) )
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" )
}
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-03-20 19:43:08 +00:00
gasUsed : usedGas ,
gasAvailable : msg . GasLimit ,
numActorsCreated : nac ,
pricelist : PricelistByEpoch ( vm . blockHeight ) ,
2019-07-05 14:29:17 +00:00
}
2020-03-10 02:24:02 +00:00
rt . cst = & cbor . BasicIpldStore {
2020-03-19 16:27:42 +00:00
Blocks : & gasChargingBlocks { rt . ChargeGas , 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 ,
chargeGas : rt . ChargeGas ,
pl : rt . pricelist ,
}
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
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
2020-03-08 00:46:12 +00:00
ActorErr aerrors . ActorError
Penalty big . Int
2020-03-04 01:33:55 +00:00
InternalExecutions [ ] * ExecutionResult
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-03-18 20:45:37 +00:00
gasCharge int64 ) ( [ ] byte , aerrors . ActorError , * Runtime ) {
2020-03-23 06:39:42 +00:00
st := vm . cstate
2020-03-19 16:27:42 +00:00
gasUsed := gasCharge
2020-03-23 08:17:45 +00:00
var origin address . Address
2020-03-17 05:58:00 +00:00
on := msg . Nonce
2020-03-20 19:43:08 +00:00
var nac uint64 = 0
2019-08-15 16:23:28 +00:00
if parent != nil {
2020-03-18 20:45:37 +00:00
gasUsed = parent . gasUsed + 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
2020-03-23 08:17:45 +00:00
} else {
var aerr aerrors . ActorError
origin , aerr = ResolveToKeyAddr ( vm . cstate , vm . cst , msg . From )
if aerr != nil {
return nil , aerr , nil
}
2019-08-03 11:26:35 +00:00
}
2020-03-20 19:43:08 +00:00
rt := vm . makeRuntime ( ctx , msg , origin , on , gasUsed , nac )
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-03-23 06:39:42 +00:00
fromActor , err := st . GetActor ( msg . From )
if err != nil {
return nil , aerrors . Absorb ( err , 1 , "could not find source actor" ) , rt
}
toActor , err := st . GetActor ( msg . To )
if err != nil {
if xerrors . Is ( err , init_ . ErrAddressNotFound ) {
a , err := TryCreateAccountActor ( st , msg . To )
if err != nil {
return nil , aerrors . Absorb ( err , 1 , "could not create account" ) , rt
}
toActor = a
gasUsed += PricelistByEpoch ( vm . blockHeight ) . OnCreateActor ( )
} else {
return nil , aerrors . Escalate ( err , "getting actor" ) , rt
}
}
2020-03-23 08:17:45 +00:00
aerr := rt . chargeGasSafe ( rt . Pricelist ( ) . OnMethodInvocation ( msg . Value , msg . Method ) )
2020-03-19 16:27:42 +00:00
if aerr != nil {
return nil , aerr , rt
}
2019-08-15 16:23:28 +00:00
2020-03-19 16:27:42 +00:00
if types . BigCmp ( msg . Value , types . NewInt ( 0 ) ) != 0 {
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 {
2020-03-10 02:24:02 +00:00
ret , err := vm . Invoke ( toActor , rt , msg . Method , msg . Params )
return ret , err , rt
2019-08-03 11:26:35 +00:00
}
2020-03-10 02:24:02 +00:00
return nil , nil , 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
}
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-19 16:27:42 +00:00
pl := PricelistByEpoch ( vm . blockHeight )
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 )
}
2020-03-19 16:27:42 +00:00
msgGasCost := pl . OnChainMessage ( len ( serMsg ) )
// TODO: charge miner??
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 {
ExitCode : exitcode . SysErrOutOfGas ,
GasUsed : msg . GasLimit ,
} ,
} , nil
}
2019-07-05 14:29:17 +00:00
st := vm . cstate
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
}
2020-03-10 20:14:50 +00:00
if msg . Nonce != fromActor . Nonce {
return & ApplyRet {
MessageReceipt : types . MessageReceipt {
ExitCode : exitcode . SysErrInvalidCallSeqNum ,
2020-03-11 16:39:49 +00:00
GasUsed : 0 ,
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 {
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
fromActor . Nonce ++
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-03-10 02:24:02 +00:00
ret , actorErr , rt := vm . send ( ctx , msg , nil , msgGasCost )
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-03-19 16:27:42 +00:00
{
actorErr2 := rt . chargeGasSafe ( rt . Pricelist ( ) . OnChainReturnValue ( len ( ret ) ) )
if actorErr == nil {
//TODO: Ambigous what to do in this case
actorErr = actorErr2
}
}
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
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 {
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 {
2020-03-10 02:24:02 +00:00
gasUsed = rt . gasUsed
2020-03-19 16:27:42 +00:00
if gasUsed < 0 {
gasUsed = 0
}
2019-08-03 11:53:18 +00:00
// refund unused gas
2020-03-18 20:45:37 +00:00
refund := types . BigMul ( types . NewInt ( uint64 ( 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
2020-03-18 20:45:37 +00:00
gasReward := types . BigMul ( msg . GasPrice , types . NewInt ( uint64 ( 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
} ,
2020-03-08 00:46:12 +00:00
ActorErr : actorErr ,
2020-03-10 02:24:02 +00:00
InternalExecutions : rt . internalExecutions ,
2019-08-03 11:53:18 +00:00
} , 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-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-03-10 02:24:02 +00:00
ret , err := vm . inv . Invoke ( act , 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
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 )
}