github.com/filecoin-project/specs-actors/v4@v4.0.2/support/vm/testing.go (about) 1 package vm_test 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "testing" 8 9 "github.com/filecoin-project/go-address" 10 "github.com/filecoin-project/go-state-types/abi" 11 "github.com/filecoin-project/go-state-types/big" 12 "github.com/filecoin-project/go-state-types/cbor" 13 "github.com/filecoin-project/go-state-types/dline" 14 "github.com/filecoin-project/go-state-types/exitcode" 15 "github.com/ipfs/go-cid" 16 ipldcbor "github.com/ipfs/go-ipld-cbor" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 20 "github.com/filecoin-project/specs-actors/v4/actors/builtin" 21 "github.com/filecoin-project/specs-actors/v4/actors/builtin/account" 22 "github.com/filecoin-project/specs-actors/v4/actors/builtin/cron" 23 "github.com/filecoin-project/specs-actors/v4/actors/builtin/exported" 24 initactor "github.com/filecoin-project/specs-actors/v4/actors/builtin/init" 25 "github.com/filecoin-project/specs-actors/v4/actors/builtin/market" 26 "github.com/filecoin-project/specs-actors/v4/actors/builtin/miner" 27 "github.com/filecoin-project/specs-actors/v4/actors/builtin/power" 28 "github.com/filecoin-project/specs-actors/v4/actors/builtin/reward" 29 "github.com/filecoin-project/specs-actors/v4/actors/builtin/system" 30 "github.com/filecoin-project/specs-actors/v4/actors/builtin/verifreg" 31 "github.com/filecoin-project/specs-actors/v4/actors/runtime" 32 "github.com/filecoin-project/specs-actors/v4/actors/states" 33 "github.com/filecoin-project/specs-actors/v4/actors/util/adt" 34 "github.com/filecoin-project/specs-actors/v4/actors/util/smoothing" 35 actor_testing "github.com/filecoin-project/specs-actors/v4/support/testing" 36 ) 37 38 var FIL = big.NewInt(1e18) 39 var VerifregRoot address.Address 40 41 func init() { 42 var err error 43 VerifregRoot, err = address.NewIDAddress(80) 44 if err != nil { 45 panic("could not create id address 80") 46 } 47 } 48 49 // 50 // Genesis like setup 51 // 52 53 // Creates a new VM and initializes all singleton actors plus a root verifier account. 54 func NewVMWithSingletons(ctx context.Context, t testing.TB, bs ipldcbor.IpldBlockstore) *VM { 55 lookup := map[cid.Cid]runtime.VMActor{} 56 for _, ba := range exported.BuiltinActors() { 57 lookup[ba.Code()] = ba 58 } 59 60 store := adt.WrapBlockStore(ctx, bs) 61 vm := NewVM(ctx, lookup, store) 62 63 initializeActor(ctx, t, vm, &system.State{}, builtin.SystemActorCodeID, builtin.SystemActorAddr, big.Zero()) 64 65 initState, err := initactor.ConstructState(store, "scenarios") 66 require.NoError(t, err) 67 initializeActor(ctx, t, vm, initState, builtin.InitActorCodeID, builtin.InitActorAddr, big.Zero()) 68 69 rewardState := reward.ConstructState(abi.NewStoragePower(0)) 70 initializeActor(ctx, t, vm, rewardState, builtin.RewardActorCodeID, builtin.RewardActorAddr, reward.StorageMiningAllocationCheck) 71 72 cronState := cron.ConstructState(cron.BuiltInEntries()) 73 initializeActor(ctx, t, vm, cronState, builtin.CronActorCodeID, builtin.CronActorAddr, big.Zero()) 74 75 powerState, err := power.ConstructState(store) 76 require.NoError(t, err) 77 initializeActor(ctx, t, vm, powerState, builtin.StoragePowerActorCodeID, builtin.StoragePowerActorAddr, big.Zero()) 78 79 marketState, err := market.ConstructState(store) 80 require.NoError(t, err) 81 initializeActor(ctx, t, vm, marketState, builtin.StorageMarketActorCodeID, builtin.StorageMarketActorAddr, big.Zero()) 82 83 // this will need to be replaced with the address of a multisig actor for the verified registry to be tested accurately 84 initializeActor(ctx, t, vm, &account.State{Address: VerifregRoot}, builtin.AccountActorCodeID, VerifregRoot, big.Zero()) 85 vrState, err := verifreg.ConstructState(store, VerifregRoot) 86 require.NoError(t, err) 87 initializeActor(ctx, t, vm, vrState, builtin.VerifiedRegistryActorCodeID, builtin.VerifiedRegistryActorAddr, big.Zero()) 88 89 // burnt funds 90 initializeActor(ctx, t, vm, &account.State{Address: builtin.BurntFundsActorAddr}, builtin.AccountActorCodeID, builtin.BurntFundsActorAddr, big.Zero()) 91 92 _, err = vm.checkpoint() 93 require.NoError(t, err) 94 95 return vm 96 } 97 98 // Creates n account actors in the VM with the given balance 99 func CreateAccounts(ctx context.Context, t testing.TB, vm *VM, n int, balance abi.TokenAmount, seed int64) []address.Address { 100 var initState initactor.State 101 err := vm.GetState(builtin.InitActorAddr, &initState) 102 require.NoError(t, err) 103 104 addrPairs := make([]addrPair, n) 105 for i := range addrPairs { 106 addr := actor_testing.NewBLSAddr(t, seed+int64(i)) 107 idAddr, err := initState.MapAddressToNewID(vm.store, addr) 108 require.NoError(t, err) 109 110 addrPairs[i] = addrPair{ 111 pubAddr: addr, 112 idAddr: idAddr, 113 } 114 } 115 err = vm.SetActorState(ctx, builtin.InitActorAddr, &initState) 116 require.NoError(t, err) 117 118 pubAddrs := make([]address.Address, len(addrPairs)) 119 for i, addrPair := range addrPairs { 120 st := &account.State{Address: addrPair.pubAddr} 121 initializeActor(ctx, t, vm, st, builtin.AccountActorCodeID, addrPair.idAddr, balance) 122 pubAddrs[i] = addrPair.pubAddr 123 } 124 return pubAddrs 125 } 126 127 // 128 // Invocation expectations 129 // 130 131 // ExpectInvocation is a pattern for a message invocation within the VM. 132 // The To and Method fields must be supplied. Exitcode defaults to exitcode.Ok. 133 // All other field are optional, where a nil or Undef value indicates that any value will match. 134 // SubInvocations will be matched recursively. 135 type ExpectInvocation struct { 136 To address.Address 137 Method abi.MethodNum 138 139 // optional 140 Exitcode exitcode.ExitCode 141 From address.Address 142 Value *abi.TokenAmount 143 Params *objectExpectation 144 Ret *objectExpectation 145 SubInvocations []ExpectInvocation 146 } 147 148 func (ei ExpectInvocation) Matches(t *testing.T, invocations *Invocation) { 149 ei.matches(t, "", invocations) 150 } 151 152 func (ei ExpectInvocation) matches(t *testing.T, breadcrumb string, invocation *Invocation) { 153 identifier := fmt.Sprintf("%s[%s:%d]", breadcrumb, invocation.Msg.to, invocation.Msg.method) 154 155 // mismatch of to or method probably indicates skipped message or messages out of order. halt. 156 require.Equal(t, ei.To, invocation.Msg.to, "%s unexpected 'to' address", identifier) 157 require.Equal(t, ei.Method, invocation.Msg.method, "%s unexpected method", identifier) 158 159 // other expectations are optional 160 if address.Undef != ei.From { 161 assert.Equal(t, ei.From, invocation.Msg.from, "%s unexpected from address", identifier) 162 } 163 if ei.Value != nil { 164 assert.Equal(t, *ei.Value, invocation.Msg.value, "%s unexpected value", identifier) 165 } 166 if ei.Params != nil { 167 assert.True(t, ei.Params.matches(invocation.Msg.params), "%s params aren't equal (%v != %v)", identifier, ei.Params.val, invocation.Msg.params) 168 } 169 if ei.SubInvocations != nil { 170 for i, invk := range invocation.SubInvocations { 171 subidentifier := fmt.Sprintf("%s%d:", identifier, i) 172 // attempt match only if methods match 173 require.True(t, len(ei.SubInvocations) > i && ei.SubInvocations[i].To == invk.Msg.to && ei.SubInvocations[i].Method == invk.Msg.method, 174 "%s unexpected subinvocation [%s:%d]\nexpected:\n%s\nactual:\n%s", 175 subidentifier, invk.Msg.to, invk.Msg.method, ei.listSubinvocations(), listInvocations(invocation.SubInvocations)) 176 ei.SubInvocations[i].matches(t, subidentifier, invk) 177 } 178 missingInvocations := len(ei.SubInvocations) - len(invocation.SubInvocations) 179 if missingInvocations > 0 { 180 missingIndex := len(invocation.SubInvocations) 181 missingExpect := ei.SubInvocations[missingIndex] 182 require.Failf(t, "missing expected invocations", "%s%d: expected invocation [%s:%d]\nexpected:\n%s\nactual:\n%s", 183 identifier, missingIndex, missingExpect.To, missingExpect.Method, ei.listSubinvocations(), listInvocations(invocation.SubInvocations)) 184 } 185 } 186 187 // expect results 188 assert.Equal(t, ei.Exitcode, invocation.Exitcode, "%s unexpected exitcode", identifier) 189 if ei.Ret != nil { 190 assert.True(t, ei.Ret.matches(invocation.Ret), "%s unexpected return value (%v != %v)", identifier, ei.Ret, invocation.Ret) 191 } 192 } 193 194 func (ei ExpectInvocation) listSubinvocations() string { 195 if len(ei.SubInvocations) == 0 { 196 return "[no invocations]\n" 197 } 198 list := "" 199 for i, si := range ei.SubInvocations { 200 list = fmt.Sprintf("%s%2d: [%s:%d]\n", list, i, si.To, si.Method) 201 } 202 return list 203 } 204 205 func listInvocations(invocations []*Invocation) string { 206 if len(invocations) == 0 { 207 return "[no invocations]\n" 208 } 209 list := "" 210 for i, si := range invocations { 211 list = fmt.Sprintf("%s%2d: [%s:%d]\n", list, i, si.Msg.to, si.Msg.method) 212 } 213 return list 214 } 215 216 // helpers to simplify pointer creation 217 func ExpectAttoFil(amount big.Int) *big.Int { return &amount } 218 func ExpectBytes(b []byte) *objectExpectation { return ExpectObject(builtin.CBORBytes(b)) } 219 func ExpectExitCode(code exitcode.ExitCode) *exitcode.ExitCode { return &code } 220 221 func ExpectObject(v cbor.Marshaler) *objectExpectation { 222 return &objectExpectation{v} 223 } 224 225 // distinguishes a non-expectation from an expectation of nil 226 type objectExpectation struct { 227 val cbor.Marshaler 228 } 229 230 // match by cbor encoding to avoid inconsistencies in internal representations of effectively equal objects 231 func (oe objectExpectation) matches(obj interface{}) bool { 232 if oe.val == nil || obj == nil { 233 return oe.val == nil && obj == nil 234 } 235 236 paramBuf1 := new(bytes.Buffer) 237 oe.val.MarshalCBOR(paramBuf1) // nolint: errcheck 238 marshaller, ok := obj.(cbor.Marshaler) 239 if !ok { 240 return false 241 } 242 paramBuf2 := new(bytes.Buffer) 243 if marshaller != nil { 244 marshaller.MarshalCBOR(paramBuf2) // nolint: errcheck 245 } 246 return bytes.Equal(paramBuf1.Bytes(), paramBuf2.Bytes()) 247 } 248 249 var okExitCode = exitcode.Ok 250 var ExpectOK = &okExitCode 251 252 func ParamsForInvocation(t *testing.T, vm *VM, idxs ...int) interface{} { 253 invocations := vm.Invocations() 254 var invocation *Invocation 255 for _, idx := range idxs { 256 require.Greater(t, len(invocations), idx) 257 invocation = invocations[idx] 258 invocations = invocation.SubInvocations 259 } 260 require.NotNil(t, invocation) 261 return invocation.Msg.params 262 } 263 264 func ValueForInvocation(t *testing.T, vm *VM, idxs ...int) abi.TokenAmount { 265 invocations := vm.Invocations() 266 var invocation *Invocation 267 for _, idx := range idxs { 268 require.Greater(t, len(invocations), idx) 269 invocation = invocations[idx] 270 invocations = invocation.SubInvocations 271 } 272 require.NotNil(t, invocation) 273 return invocation.Msg.value 274 } 275 276 // 277 // Advancing Time while updating state 278 // 279 280 type advanceDeadlinePredicate func(dlInfo *dline.Info) bool 281 282 func MinerDLInfo(t *testing.T, v *VM, minerIDAddr address.Address) *dline.Info { 283 var minerState miner.State 284 err := v.GetState(minerIDAddr, &minerState) 285 require.NoError(t, err) 286 287 return miner.NewDeadlineInfoFromOffsetAndEpoch(minerState.ProvingPeriodStart, v.GetEpoch()) 288 } 289 290 func NextMinerDLInfo(t *testing.T, v *VM, minerIDAddr address.Address) *dline.Info { 291 var minerState miner.State 292 err := v.GetState(minerIDAddr, &minerState) 293 require.NoError(t, err) 294 295 return miner.NewDeadlineInfoFromOffsetAndEpoch(minerState.ProvingPeriodStart, v.GetEpoch()+1) 296 } 297 298 // AdvanceByDeadline creates a new VM advanced to an epoch specified by the predicate while keeping the 299 // miner state upu-to-date by running a cron at the end of each deadline period. 300 func AdvanceByDeadline(t *testing.T, v *VM, minerIDAddr address.Address, predicate advanceDeadlinePredicate) (*VM, *dline.Info) { 301 dlInfo := MinerDLInfo(t, v, minerIDAddr) 302 var err error 303 for predicate(dlInfo) { 304 v, err = v.WithEpoch(dlInfo.Last()) 305 require.NoError(t, err) 306 307 _, code := v.ApplyMessage(builtin.SystemActorAddr, builtin.CronActorAddr, big.Zero(), builtin.MethodsCron.EpochTick, nil) 308 require.Equal(t, exitcode.Ok, code) 309 310 dlInfo = NextMinerDLInfo(t, v, minerIDAddr) 311 } 312 return v, dlInfo 313 } 314 315 // Advances by deadline until e is contained within the deadline period represented by the returned deadline info. 316 // The VM returned will be set to the last deadline close, not at e. 317 func AdvanceByDeadlineTillEpoch(t *testing.T, v *VM, minerIDAddr address.Address, e abi.ChainEpoch) (*VM, *dline.Info) { 318 return AdvanceByDeadline(t, v, minerIDAddr, func(dlInfo *dline.Info) bool { 319 return dlInfo.Close <= e 320 }) 321 } 322 323 // Advances by deadline until the deadline index matches the given index. 324 // The vm returned will be set to the close epoch of the previous deadline. 325 func AdvanceByDeadlineTillIndex(t *testing.T, v *VM, minerIDAddr address.Address, i uint64) (*VM, *dline.Info) { 326 return AdvanceByDeadline(t, v, minerIDAddr, func(dlInfo *dline.Info) bool { 327 return dlInfo.Index != i 328 }) 329 } 330 331 // Advance to the epoch when the sector is due to be proven. 332 // Returns the deadline info for proving deadline for sector, partition index of sector, and a VM at the opening of 333 // the deadline (ready for SubmitWindowedPoSt). 334 func AdvanceTillProvingDeadline(t *testing.T, v *VM, minerIDAddress address.Address, sectorNumber abi.SectorNumber) (*dline.Info, uint64, *VM) { 335 dlIdx, pIdx := SectorDeadline(t, v, minerIDAddress, sectorNumber) 336 337 // advance time to next proving period 338 v, dlInfo := AdvanceByDeadlineTillIndex(t, v, minerIDAddress, dlIdx) 339 v, err := v.WithEpoch(dlInfo.Open) 340 require.NoError(t, err) 341 return dlInfo, pIdx, v 342 } 343 344 // find the proving deadline and partition index of a miner's sector 345 func SectorDeadline(t *testing.T, v *VM, minerIDAddress address.Address, sectorNumber abi.SectorNumber) (uint64, uint64) { 346 var minerState miner.State 347 err := v.GetState(minerIDAddress, &minerState) 348 require.NoError(t, err) 349 350 dlIdx, pIdx, err := minerState.FindSector(v.Store(), sectorNumber) 351 require.NoError(t, err) 352 return dlIdx, pIdx 353 } 354 355 /// 356 // state abstraction 357 // 358 359 type MinerBalances struct { 360 AvailableBalance abi.TokenAmount 361 VestingBalance abi.TokenAmount 362 InitialPledge abi.TokenAmount 363 PreCommitDeposit abi.TokenAmount 364 } 365 366 func GetMinerBalances(t *testing.T, vm *VM, minerIdAddr address.Address) MinerBalances { 367 var state miner.State 368 a, found, err := vm.GetActor(minerIdAddr) 369 require.NoError(t, err) 370 require.True(t, found) 371 372 err = vm.GetState(minerIdAddr, &state) 373 require.NoError(t, err) 374 375 return MinerBalances{ 376 AvailableBalance: big.Subtract(a.Balance, state.PreCommitDeposits, state.InitialPledge, state.LockedFunds, state.FeeDebt), 377 PreCommitDeposit: state.PreCommitDeposits, 378 VestingBalance: state.LockedFunds, 379 InitialPledge: state.InitialPledge, 380 } 381 } 382 383 func PowerForMinerSector(t *testing.T, vm *VM, minerIdAddr address.Address, sectorNumber abi.SectorNumber) miner.PowerPair { 384 var state miner.State 385 err := vm.GetState(minerIdAddr, &state) 386 require.NoError(t, err) 387 388 sector, found, err := state.GetSector(vm.store, sectorNumber) 389 require.NoError(t, err) 390 require.True(t, found) 391 392 sectorSize, err := sector.SealProof.SectorSize() 393 require.NoError(t, err) 394 return miner.PowerForSector(sectorSize, sector) 395 } 396 397 func MinerPower(t *testing.T, vm *VM, minerIdAddr address.Address) miner.PowerPair { 398 var state power.State 399 err := vm.GetState(builtin.StoragePowerActorAddr, &state) 400 require.NoError(t, err) 401 402 claim, found, err := state.GetClaim(vm.store, minerIdAddr) 403 require.NoError(t, err) 404 require.True(t, found) 405 406 return miner.NewPowerPair(claim.RawBytePower, claim.QualityAdjPower) 407 } 408 409 type NetworkStats struct { 410 power.State 411 TotalRawBytePower abi.StoragePower 412 TotalBytesCommitted abi.StoragePower 413 TotalQualityAdjPower abi.StoragePower 414 TotalQABytesCommitted abi.StoragePower 415 TotalPledgeCollateral abi.TokenAmount 416 ThisEpochRawBytePower abi.StoragePower 417 ThisEpochQualityAdjPower abi.StoragePower 418 ThisEpochPledgeCollateral abi.TokenAmount 419 MinerCount int64 420 MinerAboveMinPowerCount int64 421 ThisEpochReward abi.TokenAmount 422 ThisEpochRewardSmoothed smoothing.FilterEstimate 423 ThisEpochBaselinePower abi.StoragePower 424 TotalStoragePowerReward abi.TokenAmount 425 TotalClientLockedCollateral abi.TokenAmount 426 TotalProviderLockedCollateral abi.TokenAmount 427 TotalClientStorageFee abi.TokenAmount 428 } 429 430 func GetNetworkStats(t *testing.T, vm *VM) NetworkStats { 431 var powerState power.State 432 err := vm.GetState(builtin.StoragePowerActorAddr, &powerState) 433 require.NoError(t, err) 434 435 var rewardState reward.State 436 err = vm.GetState(builtin.RewardActorAddr, &rewardState) 437 require.NoError(t, err) 438 439 var marketState market.State 440 err = vm.GetState(builtin.StorageMarketActorAddr, &marketState) 441 require.NoError(t, err) 442 443 return NetworkStats{ 444 TotalRawBytePower: powerState.TotalRawBytePower, 445 TotalBytesCommitted: powerState.TotalBytesCommitted, 446 TotalQualityAdjPower: powerState.TotalQualityAdjPower, 447 TotalQABytesCommitted: powerState.TotalQABytesCommitted, 448 TotalPledgeCollateral: powerState.TotalPledgeCollateral, 449 ThisEpochRawBytePower: powerState.ThisEpochRawBytePower, 450 ThisEpochQualityAdjPower: powerState.ThisEpochQualityAdjPower, 451 ThisEpochPledgeCollateral: powerState.ThisEpochPledgeCollateral, 452 MinerCount: powerState.MinerCount, 453 MinerAboveMinPowerCount: powerState.MinerAboveMinPowerCount, 454 ThisEpochReward: rewardState.ThisEpochReward, 455 ThisEpochRewardSmoothed: rewardState.ThisEpochRewardSmoothed, 456 ThisEpochBaselinePower: rewardState.ThisEpochBaselinePower, 457 TotalStoragePowerReward: rewardState.TotalStoragePowerReward, 458 TotalClientLockedCollateral: marketState.TotalClientLockedCollateral, 459 TotalProviderLockedCollateral: marketState.TotalProviderLockedCollateral, 460 TotalClientStorageFee: marketState.TotalClientStorageFee, 461 } 462 } 463 464 func GetDealState(t *testing.T, vm *VM, dealID abi.DealID) (*market.DealState, bool) { 465 var marketState market.State 466 err := vm.GetState(builtin.StorageMarketActorAddr, &marketState) 467 require.NoError(t, err) 468 469 states, err := market.AsDealStateArray(vm.store, marketState.States) 470 require.NoError(t, err) 471 472 state, found, err := states.Get(dealID) 473 require.NoError(t, err) 474 475 return state, found 476 } 477 478 // 479 // Misc. helpers 480 // 481 482 func ApplyOk(t *testing.T, v *VM, from, to address.Address, value abi.TokenAmount, method abi.MethodNum, params interface{}) cbor.Marshaler { 483 ret, code := v.ApplyMessage(from, to, value, method, params) 484 require.Equal(t, exitcode.Ok, code) 485 return ret 486 } 487 488 // 489 // internal stuff 490 // 491 492 func initializeActor(ctx context.Context, t testing.TB, vm *VM, state cbor.Marshaler, code cid.Cid, a address.Address, balance abi.TokenAmount) { 493 stateCID, err := vm.store.Put(ctx, state) 494 require.NoError(t, err) 495 actor := &states.Actor{ 496 Head: stateCID, 497 Code: code, 498 Balance: balance, 499 } 500 err = vm.setActor(ctx, a, actor) 501 require.NoError(t, err) 502 } 503 504 type addrPair struct { 505 pubAddr address.Address 506 idAddr address.Address 507 }