github.com/filecoin-project/specs-actors/v4@v4.0.2/support/vm/vm.go (about) 1 package vm_test 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/filecoin-project/go-address" 8 "github.com/filecoin-project/go-state-types/abi" 9 "github.com/filecoin-project/go-state-types/big" 10 "github.com/filecoin-project/go-state-types/cbor" 11 "github.com/filecoin-project/go-state-types/exitcode" 12 "github.com/filecoin-project/go-state-types/network" 13 "github.com/filecoin-project/go-state-types/rt" 14 vm2 "github.com/filecoin-project/specs-actors/v2/support/vm" 15 "github.com/ipfs/go-cid" 16 "github.com/pkg/errors" 17 18 "github.com/filecoin-project/specs-actors/v4/actors/builtin" 19 init_ "github.com/filecoin-project/specs-actors/v4/actors/builtin/init" 20 "github.com/filecoin-project/specs-actors/v4/actors/runtime" 21 "github.com/filecoin-project/specs-actors/v4/actors/states" 22 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" 23 ) 24 25 // VM is a simplified message execution framework for the purposes of testing inter-actor communication. 26 // The VM maintains actor state and can be used to simulate message validation for a single block or tipset. 27 // The VM does not track gas charges, provide working syscalls, validate message nonces and many other things 28 // that a compliant VM needs to do. 29 type VM struct { 30 ctx context.Context 31 store adt.Store 32 33 currentEpoch abi.ChainEpoch 34 networkVersion network.Version 35 36 ActorImpls ActorImplLookup 37 stateRoot cid.Cid // The last committed root. 38 actors *adt.Map // The current (not necessarily committed) root node. 39 actorsDirty bool 40 41 emptyObject cid.Cid 42 callSequence uint64 43 44 logs []string 45 invocationStack []*Invocation 46 invocations []*Invocation 47 48 statsSource StatsSource 49 statsByMethod StatsByCall 50 51 circSupply abi.TokenAmount 52 } 53 54 // VM types 55 56 // type ActorImplLookup map[cid.Cid]runtime.VMActor 57 type ActorImplLookup vm2.ActorImplLookup 58 59 type InternalMessage struct { 60 from address.Address 61 to address.Address 62 value abi.TokenAmount 63 method abi.MethodNum 64 params interface{} 65 } 66 67 type Invocation struct { 68 Msg *InternalMessage 69 Exitcode exitcode.ExitCode 70 Ret cbor.Marshaler 71 SubInvocations []*Invocation 72 } 73 74 // NewVM creates a new runtime for executing messages. 75 func NewVM(ctx context.Context, actorImpls ActorImplLookup, store adt.Store) *VM { 76 actors, err := adt.MakeEmptyMap(store, builtin.DefaultHamtBitwidth) 77 if err != nil { 78 panic(err) 79 } 80 actorRoot, err := actors.Root() 81 if err != nil { 82 panic(err) 83 } 84 85 emptyObject, err := store.Put(context.TODO(), []struct{}{}) 86 if err != nil { 87 panic(err) 88 } 89 90 return &VM{ 91 ctx: ctx, 92 ActorImpls: actorImpls, 93 store: store, 94 actors: actors, 95 stateRoot: actorRoot, 96 actorsDirty: false, 97 emptyObject: emptyObject, 98 networkVersion: network.VersionMax, 99 statsByMethod: make(StatsByCall), 100 circSupply: big.Mul(big.NewInt(1e9), big.NewInt(1e18)), 101 } 102 } 103 104 // NewVM creates a new runtime for executing messages. 105 func NewVMAtEpoch(ctx context.Context, actorImpls ActorImplLookup, store adt.Store, stateRoot cid.Cid, epoch abi.ChainEpoch) (*VM, error) { 106 actors, err := adt.AsMap(store, stateRoot, builtin.DefaultHamtBitwidth) 107 if err != nil { 108 return nil, err 109 } 110 111 emptyObject, err := store.Put(context.TODO(), []struct{}{}) 112 if err != nil { 113 panic(err) 114 } 115 116 return &VM{ 117 ctx: ctx, 118 ActorImpls: actorImpls, 119 currentEpoch: epoch, 120 store: store, 121 actors: actors, 122 stateRoot: stateRoot, 123 actorsDirty: false, 124 emptyObject: emptyObject, 125 networkVersion: network.VersionMax, 126 statsByMethod: make(StatsByCall), 127 circSupply: big.Mul(big.NewInt(1e9), big.NewInt(1e18)), 128 }, nil 129 } 130 131 func (vm *VM) WithEpoch(epoch abi.ChainEpoch) (*VM, error) { 132 _, err := vm.checkpoint() 133 if err != nil { 134 return nil, err 135 } 136 137 actors, err := adt.AsMap(vm.store, vm.stateRoot, builtin.DefaultHamtBitwidth) 138 if err != nil { 139 return nil, err 140 } 141 142 return &VM{ 143 ctx: vm.ctx, 144 ActorImpls: vm.ActorImpls, 145 store: vm.store, 146 actors: actors, 147 stateRoot: vm.stateRoot, 148 actorsDirty: false, 149 emptyObject: vm.emptyObject, 150 currentEpoch: epoch, 151 networkVersion: vm.networkVersion, 152 statsSource: vm.statsSource, 153 statsByMethod: make(StatsByCall), 154 circSupply: vm.circSupply, 155 }, nil 156 } 157 158 func (vm *VM) WithNetworkVersion(nv network.Version) (*VM, error) { 159 _, err := vm.checkpoint() 160 if err != nil { 161 return nil, err 162 } 163 164 actors, err := adt.AsMap(vm.store, vm.stateRoot, builtin.DefaultHamtBitwidth) 165 if err != nil { 166 return nil, err 167 } 168 169 return &VM{ 170 ctx: vm.ctx, 171 ActorImpls: vm.ActorImpls, 172 store: vm.store, 173 actors: actors, 174 stateRoot: vm.stateRoot, 175 actorsDirty: false, 176 emptyObject: vm.emptyObject, 177 currentEpoch: vm.currentEpoch, 178 networkVersion: nv, 179 statsSource: vm.statsSource, 180 statsByMethod: make(StatsByCall), 181 circSupply: vm.circSupply, 182 }, nil 183 } 184 185 func (vm *VM) rollback(root cid.Cid) error { 186 var err error 187 vm.actors, err = adt.AsMap(vm.store, root, builtin.DefaultHamtBitwidth) 188 if err != nil { 189 return errors.Wrapf(err, "failed to load node for %s", root) 190 } 191 192 // reset the root node 193 vm.stateRoot = root 194 vm.actorsDirty = false 195 return nil 196 } 197 198 func (vm *VM) GetActor(a address.Address) (*states.Actor, bool, error) { 199 na, found := vm.NormalizeAddress(a) 200 if !found { 201 return nil, false, nil 202 } 203 var act states.Actor 204 found, err := vm.actors.Get(abi.AddrKey(na), &act) 205 return &act, found, err 206 } 207 208 // SetActor sets the the actor to the given value whether it previously existed or not. 209 // 210 // This method will not check if the actor previously existed, it will blindly overwrite it. 211 func (vm *VM) setActor(_ context.Context, key address.Address, a *states.Actor) error { 212 if err := vm.actors.Put(abi.AddrKey(key), a); err != nil { 213 return errors.Wrap(err, "setting actor in state tree failed") 214 } 215 vm.actorsDirty = true 216 return nil 217 } 218 219 // SetActorState stores the state and updates the addressed actor 220 func (vm *VM) SetActorState(ctx context.Context, key address.Address, state cbor.Marshaler) error { 221 stateCid, err := vm.store.Put(ctx, state) 222 if err != nil { 223 return err 224 } 225 a, found, err := vm.GetActor(key) 226 if err != nil { 227 return err 228 } 229 if !found { 230 return errors.Errorf("could not find actor %s to set state", key) 231 } 232 a.Head = stateCid 233 return vm.setActor(ctx, key, a) 234 } 235 236 // deleteActor remove the actor from the storage. 237 // 238 // This method will NOT return an error if the actor was not found. 239 // This behaviour is based on a principle that some store implementations might not be able to determine 240 // whether something exists before deleting it. 241 func (vm *VM) deleteActor(_ context.Context, key address.Address) error { 242 found, err := vm.actors.TryDelete(abi.AddrKey(key)) 243 vm.actorsDirty = found 244 return err 245 } 246 247 func (vm *VM) checkpoint() (cid.Cid, error) { 248 // commit the vm state 249 root, err := vm.actors.Root() 250 if err != nil { 251 return cid.Undef, err 252 } 253 vm.stateRoot = root 254 vm.actorsDirty = false 255 256 return root, nil 257 } 258 259 func (vm *VM) NormalizeAddress(addr address.Address) (address.Address, bool) { 260 // short-circuit if the address is already an ID address 261 if addr.Protocol() == address.ID { 262 return addr, true 263 } 264 265 // resolve the target address via the InitActor, and attempt to load state. 266 initActorEntry, found, err := vm.GetActor(builtin.InitActorAddr) 267 if err != nil { 268 panic(errors.Wrapf(err, "failed to load init actor")) 269 } 270 if !found { 271 panic(errors.Wrapf(err, "no init actor")) 272 } 273 274 // get a view into the actor state 275 var state init_.State 276 if err := vm.store.Get(vm.ctx, initActorEntry.Head, &state); err != nil { 277 panic(err) 278 } 279 280 idAddr, found, err := state.ResolveAddress(vm.store, addr) 281 if err != nil { 282 panic(err) 283 } 284 return idAddr, found 285 } 286 287 // ApplyMessage applies the message to the current state. 288 func (vm *VM) ApplyMessage(from, to address.Address, value abi.TokenAmount, method abi.MethodNum, params interface{}) (cbor.Marshaler, exitcode.ExitCode) { 289 // This method does not actually execute the message itself, 290 // but rather deals with the pre/post processing of a message. 291 // (see: `invocationContext.invoke()` for the dispatch and execution) 292 293 // load actor from global state 294 fromID, ok := vm.NormalizeAddress(from) 295 if !ok { 296 return nil, exitcode.SysErrSenderInvalid 297 } 298 299 fromActor, found, err := vm.GetActor(fromID) 300 if err != nil { 301 panic(err) 302 } 303 if !found { 304 // Execution error; sender does not exist at time of message execution. 305 return nil, exitcode.SysErrSenderInvalid 306 } 307 308 // checkpoint state 309 // Even if the message fails, the following accumulated changes will be applied: 310 // - CallSeqNumber increment 311 // - sender balance withheld 312 priorRoot, err := vm.checkpoint() 313 if err != nil { 314 panic(err) 315 } 316 317 // send 318 // 1. build internal message 319 // 2. build invocation context 320 // 3. process the msg 321 322 topLevel := topLevelContext{ 323 originatorStableAddress: from, 324 // this should be nonce, but we only care that it creates a unique stable address 325 originatorCallSeq: vm.callSequence, 326 newActorAddressCount: 0, 327 statsSource: vm.statsSource, 328 circSupply: vm.circSupply, 329 } 330 vm.callSequence++ 331 332 // build internal msg 333 imsg := InternalMessage{ 334 from: fromID, 335 to: to, 336 value: value, 337 method: method, 338 params: params, 339 } 340 341 // build invocation context 342 ctx := newInvocationContext(vm, &topLevel, imsg, fromActor, vm.emptyObject) 343 344 // 3. invoke 345 ret, exitCode := ctx.invoke() 346 347 // record stats 348 vm.statsByMethod.MergeStats(ctx.toActor.Code, imsg.method, ctx.stats) 349 350 // Roll back all state if the receipt's exit code is not ok. 351 // This is required in addition to rollback within the invocation context since top level messages can fail for 352 // more reasons than internal ones. Invocation context still needs its own rollback so actors can recover and 353 // proceed from a nested call failure. 354 if exitCode != exitcode.Ok { 355 if err := vm.rollback(priorRoot); err != nil { 356 panic(err) 357 } 358 } else { 359 // persist changes from final invocation if call is ok 360 if _, err := vm.checkpoint(); err != nil { 361 panic(err) 362 } 363 364 } 365 366 return ret.inner, exitCode 367 } 368 369 func (vm *VM) StateRoot() cid.Cid { 370 return vm.stateRoot 371 } 372 373 func (vm *VM) GetState(addr address.Address, out cbor.Unmarshaler) error { 374 act, found, err := vm.GetActor(addr) 375 if err != nil { 376 return err 377 } 378 if !found { 379 return errors.Errorf("actor %v not found", addr) 380 } 381 return vm.store.Get(vm.ctx, act.Head, out) 382 } 383 384 func (vm *VM) GetStateTree() (*states.Tree, error) { 385 root, err := vm.checkpoint() 386 if err != nil { 387 return nil, err 388 } 389 390 return states.LoadTree(vm.store, root) 391 } 392 393 func (vm *VM) GetTotalActorBalance() (abi.TokenAmount, error) { 394 tree, err := vm.GetStateTree() 395 if err != nil { 396 return big.Zero(), err 397 } 398 total := big.Zero() 399 err = tree.ForEach(func(_ address.Address, actor *states.Actor) error { 400 total = big.Add(total, actor.Balance) 401 return nil 402 }) 403 if err != nil { 404 return big.Zero(), err 405 } 406 return total, nil 407 } 408 409 func (vm *VM) Store() adt.Store { 410 return vm.store 411 } 412 413 // Get the chain epoch for this vm 414 func (vm *VM) GetEpoch() abi.ChainEpoch { 415 return vm.currentEpoch 416 } 417 418 // Get call stats 419 func (vm *VM) GetCallStats() map[MethodKey]*CallStats { 420 return vm.statsByMethod 421 } 422 423 // Set the FIL circulating supply passed to actors through runtime 424 func (vm *VM) SetCirculatingSupply(supply abi.TokenAmount) { 425 vm.circSupply = supply 426 } 427 428 // Set the FIL circulating supply passed to actors through runtime 429 func (vm *VM) GetCirculatingSupply() abi.TokenAmount { 430 return vm.circSupply 431 } 432 433 func (vm *VM) GetActorImpls() map[cid.Cid]rt.VMActor { 434 return vm.ActorImpls 435 } 436 437 // transfer debits money from one account and credits it to another. 438 // avoid calling this method with a zero amount else it will perform unnecessary actor loading. 439 // 440 // WARNING: this method will panic if the the amount is negative, accounts dont exist, or have inssuficient funds. 441 // 442 // Note: this is not idiomatic, it follows the Spec expectations for this method. 443 func (vm *VM) transfer(debitFrom address.Address, creditTo address.Address, amount abi.TokenAmount) (*states.Actor, *states.Actor) { 444 // allow only for positive amounts 445 if amount.LessThan(abi.NewTokenAmount(0)) { 446 panic("unreachable: negative funds transfer not allowed") 447 } 448 449 ctx := context.Background() 450 451 // retrieve debit account 452 fromActor, found, err := vm.GetActor(debitFrom) 453 if err != nil { 454 panic(err) 455 } 456 if !found { 457 panic(fmt.Errorf("unreachable: debit account not found. %s", err)) 458 } 459 460 // check that account has enough balance for transfer 461 if fromActor.Balance.LessThan(amount) { 462 panic("unreachable: insufficient balance on debit account") 463 } 464 465 // debit funds 466 fromActor.Balance = big.Sub(fromActor.Balance, amount) 467 if err := vm.setActor(ctx, debitFrom, fromActor); err != nil { 468 panic(err) 469 } 470 471 // retrieve credit account 472 toActor, found, err := vm.GetActor(creditTo) 473 if err != nil { 474 panic(err) 475 } 476 if !found { 477 panic(fmt.Errorf("unreachable: credit account not found. %s", err)) 478 } 479 480 // credit funds 481 toActor.Balance = big.Add(toActor.Balance, amount) 482 if err := vm.setActor(ctx, creditTo, toActor); err != nil { 483 panic(err) 484 } 485 return toActor, fromActor 486 } 487 488 func (vm *VM) getActorImpl(code cid.Cid) runtime.VMActor { 489 actorImpl, ok := vm.ActorImpls[code] 490 if !ok { 491 vm.Abortf(exitcode.SysErrInvalidReceiver, "actor implementation not found for Exitcode %v", code) 492 } 493 return actorImpl 494 } 495 496 // 497 // stats 498 // 499 500 func (vm *VM) SetStatsSource(s StatsSource) { 501 vm.statsSource = s 502 } 503 504 func (vm *VM) GetStatsSource() StatsSource { 505 return vm.statsSource 506 } 507 508 func (vm *VM) StoreReads() uint64 { 509 if vm.statsSource != nil { 510 return vm.statsSource.ReadCount() 511 } 512 return 0 513 } 514 515 func (vm *VM) StoreWrites() uint64 { 516 if vm.statsSource != nil { 517 return vm.statsSource.WriteCount() 518 } 519 return 0 520 } 521 522 func (vm *VM) StoreReadBytes() uint64 { 523 if vm.statsSource != nil { 524 return vm.statsSource.ReadSize() 525 } 526 return 0 527 } 528 529 func (vm *VM) StoreWriteBytes() uint64 { 530 if vm.statsSource != nil { 531 return vm.statsSource.WriteSize() 532 } 533 return 0 534 } 535 536 // 537 // invocation tracking 538 // 539 540 func (vm *VM) startInvocation(msg *InternalMessage) { 541 invocation := Invocation{Msg: msg} 542 if len(vm.invocationStack) > 0 { 543 parent := vm.invocationStack[len(vm.invocationStack)-1] 544 parent.SubInvocations = append(parent.SubInvocations, &invocation) 545 } else { 546 vm.invocations = append(vm.invocations, &invocation) 547 } 548 vm.invocationStack = append(vm.invocationStack, &invocation) 549 } 550 551 func (vm *VM) endInvocation(code exitcode.ExitCode, ret cbor.Marshaler) { 552 curIndex := len(vm.invocationStack) - 1 553 current := vm.invocationStack[curIndex] 554 current.Exitcode = code 555 current.Ret = ret 556 557 vm.invocationStack = vm.invocationStack[:curIndex] 558 } 559 560 func (vm *VM) Invocations() []*Invocation { 561 return vm.invocations 562 } 563 564 func (vm *VM) LastInvocation() *Invocation { 565 return vm.invocations[len(vm.invocations)-1] 566 } 567 568 // 569 // implement runtime.Runtime for VM 570 // 571 572 func (vm *VM) Log(_ rt.LogLevel, msg string, args ...interface{}) { 573 vm.logs = append(vm.logs, fmt.Sprintf(msg, args...)) 574 } 575 576 func (vm *VM) GetLogs() []string { 577 return vm.logs 578 } 579 580 type abort struct { 581 code exitcode.ExitCode 582 msg string 583 } 584 585 func (vm *VM) Abortf(errExitCode exitcode.ExitCode, msg string, args ...interface{}) { 586 panic(abort{errExitCode, fmt.Sprintf(msg, args...)}) 587 } 588 589 // 590 // implement runtime.MessageInfo for InternalMessage 591 // 592 593 var _ runtime.Message = (*InternalMessage)(nil) 594 595 // ValueReceived implements runtime.MessageInfo. 596 func (msg InternalMessage) ValueReceived() abi.TokenAmount { 597 return msg.value 598 } 599 600 // Caller implements runtime.MessageInfo. 601 func (msg InternalMessage) Caller() address.Address { 602 return msg.from 603 } 604 605 // Receiver implements runtime.MessageInfo. 606 func (msg InternalMessage) Receiver() address.Address { 607 return msg.to 608 }