github.com/koko1123/flow-go-1@v0.29.6/fvm/transaction_test.go (about) 1 package fvm_test 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "testing" 7 8 "github.com/onflow/cadence/runtime" 9 "github.com/onflow/cadence/runtime/common" 10 "github.com/onflow/cadence/runtime/sema" 11 "github.com/stretchr/testify/require" 12 13 "github.com/koko1123/flow-go-1/engine/execution/testutil" 14 "github.com/koko1123/flow-go-1/fvm" 15 "github.com/koko1123/flow-go-1/fvm/derived" 16 "github.com/koko1123/flow-go-1/fvm/environment" 17 "github.com/koko1123/flow-go-1/fvm/errors" 18 "github.com/koko1123/flow-go-1/fvm/state" 19 "github.com/koko1123/flow-go-1/fvm/utils" 20 "github.com/koko1123/flow-go-1/model/flow" 21 "github.com/koko1123/flow-go-1/utils/unittest" 22 ) 23 24 func makeTwoAccounts( 25 t *testing.T, 26 aPubKeys []flow.AccountPublicKey, 27 bPubKeys []flow.AccountPublicKey, 28 ) ( 29 flow.Address, 30 flow.Address, 31 *state.TransactionState, 32 ) { 33 34 txnState := state.NewTransactionState( 35 utils.NewSimpleView(), 36 state.DefaultParameters(), 37 ) 38 39 a := flow.HexToAddress("1234") 40 b := flow.HexToAddress("5678") 41 42 // create accounts 43 accounts := environment.NewAccounts(txnState) 44 err := accounts.Create(aPubKeys, a) 45 require.NoError(t, err) 46 err = accounts.Create(bPubKeys, b) 47 require.NoError(t, err) 48 49 return a, b, txnState 50 } 51 52 func TestAccountFreezing(t *testing.T) { 53 54 chain := flow.Mainnet.Chain() 55 serviceAddress := chain.ServiceAddress() 56 57 t.Run("setFrozenAccount can be enabled", func(t *testing.T) { 58 59 address, _, st := makeTwoAccounts(t, nil, nil) 60 accounts := environment.NewAccounts(st) 61 derivedBlockData := derived.NewEmptyDerivedBlockData() 62 63 // account should no be frozen 64 frozen, err := accounts.GetAccountFrozen(address) 65 require.NoError(t, err) 66 require.False(t, frozen) 67 68 code := fmt.Sprintf(` 69 transaction { 70 prepare(auth: AuthAccount) { 71 setAccountFrozen(0x%s, true) 72 } 73 } 74 `, address.String()) 75 76 tx := flow.TransactionBody{Script: []byte(code)} 77 tx.AddAuthorizer(chain.ServiceAddress()) 78 proc := fvm.Transaction(&tx, derivedBlockData.NextTxIndexForTestingOnly()) 79 80 context := fvm.NewContext( 81 fvm.WithChain(chain), 82 fvm.WithAuthorizationChecksEnabled(false), 83 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 84 fvm.WithDerivedBlockData(derivedBlockData)) 85 86 derivedBlockData = derived.NewEmptyDerivedBlockData() 87 derivedTxnData, err := derivedBlockData.NewDerivedTransactionData(0, 0) 88 require.NoError(t, err) 89 90 err = fvm.Run(proc.NewExecutor(context, st, derivedTxnData)) 91 require.NoError(t, err) 92 require.NoError(t, proc.Err) 93 94 // account should be frozen now 95 frozen, err = accounts.GetAccountFrozen(address) 96 require.NoError(t, err) 97 require.True(t, frozen) 98 }) 99 100 t.Run("freezing account triggers program cache eviction", func(t *testing.T) { 101 address, _, st := makeTwoAccounts(t, nil, nil) 102 accounts := environment.NewAccounts(st) 103 derivedBlockData := derived.NewEmptyDerivedBlockData() 104 105 // account should no be frozen 106 frozen, err := accounts.GetAccountFrozen(address) 107 require.NoError(t, err) 108 require.False(t, frozen) 109 110 vm := fvm.NewVirtualMachine() 111 112 // deploy code to account 113 114 whateverContractCode := ` 115 pub contract Whatever { 116 pub fun say() { 117 log("Düsseldorf") 118 } 119 } 120 ` 121 122 deployContract := []byte(fmt.Sprintf( 123 ` 124 transaction { 125 prepare(signer: AuthAccount) { 126 signer.contracts.add(name: "Whatever", code: "%s".decodeHex()) 127 } 128 } 129 `, hex.EncodeToString([]byte(whateverContractCode)), 130 )) 131 132 proc := fvm.Transaction( 133 &flow.TransactionBody{Script: deployContract, Authorizers: []flow.Address{address}, Payer: address}, 134 derivedBlockData.NextTxIndexForTestingOnly()) 135 context := fvm.NewContext( 136 fvm.WithServiceAccount(false), 137 fvm.WithContractDeploymentRestricted(false), 138 fvm.WithCadenceLogging(true), 139 // run with limited processor to test just core of freezing, but still inside FVM 140 fvm.WithAuthorizationChecksEnabled(false), 141 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 142 fvm.WithDerivedBlockData(derivedBlockData)) 143 144 err = vm.Run(context, proc, st.ViewForTestingOnly()) 145 require.NoError(t, err) 146 require.NoError(t, proc.Err) 147 148 // contracts should load now 149 150 code := func(a flow.Address) []byte { 151 return []byte(fmt.Sprintf(` 152 import Whatever from 0x%s 153 154 transaction { 155 execute { 156 Whatever.say() 157 } 158 } 159 `, a.String())) 160 } 161 162 proc = fvm.Transaction( 163 &flow.TransactionBody{Script: code(address)}, 164 derivedBlockData.NextTxIndexForTestingOnly()) 165 err = vm.Run(context, proc, st.ViewForTestingOnly()) 166 require.NoError(t, err) 167 require.NoError(t, proc.Err) 168 require.Len(t, proc.Logs, 1) 169 require.Contains(t, proc.Logs[0], "\"D\\u{fc}sseldorf\"") 170 171 // verify cache is populated 172 173 cadenceAddr := common.AddressLocation{ 174 Address: common.MustBytesToAddress(address[:]), 175 Name: "Whatever", 176 } 177 entry := derivedBlockData.GetProgramForTestingOnly(cadenceAddr) 178 require.NotNil(t, entry) 179 180 // freeze account 181 182 freezeTx := fmt.Sprintf(` 183 transaction { 184 prepare(auth: AuthAccount) { 185 setAccountFrozen(0x%s, true) 186 } 187 } 188 `, 189 address) 190 tx := &flow.TransactionBody{Script: []byte(freezeTx)} 191 tx.AddAuthorizer(chain.ServiceAddress()) 192 193 proc = fvm.Transaction(tx, derivedBlockData.NextTxIndexForTestingOnly()) 194 err = vm.Run(context, proc, st.ViewForTestingOnly()) 195 require.NoError(t, err) 196 require.NoError(t, proc.Err) 197 198 // verify cache is evicted 199 200 entry = derivedBlockData.GetProgramForTestingOnly(cadenceAddr) 201 require.Nil(t, entry) 202 203 // loading code from frozen account triggers error 204 205 proc = fvm.Transaction( 206 &flow.TransactionBody{Script: code(address)}, 207 derivedBlockData.NextTxIndexForTestingOnly()) 208 209 err = vm.Run(context, proc, st.ViewForTestingOnly()) 210 require.NoError(t, err) 211 require.Error(t, proc.Err) 212 213 // find frozen account specific error 214 require.True(t, errors.IsCadenceRuntimeError(proc.Err)) 215 216 var rtErr runtime.Error 217 require.True(t, errors.As(proc.Err, &rtErr)) 218 219 err = rtErr.Err 220 221 require.IsType(t, &runtime.ParsingCheckingError{}, err) 222 err = err.(*runtime.ParsingCheckingError).Err 223 224 require.IsType(t, &sema.CheckerError{}, err) 225 checkerErr := err.(*sema.CheckerError) 226 227 checkerErrors := checkerErr.ChildErrors() 228 229 require.Len(t, checkerErrors, 2) 230 require.IsType(t, &sema.ImportedProgramError{}, checkerErrors[0]) 231 232 importedCheckerError := checkerErrors[0].(*sema.ImportedProgramError).Err 233 accountFrozenError := errors.FrozenAccountError{} 234 235 require.True(t, errors.As(importedCheckerError, &accountFrozenError)) 236 require.Equal(t, address, accountFrozenError.Address()) 237 }) 238 239 t.Run("code from frozen account cannot be loaded", func(t *testing.T) { 240 241 frozenAddress, notFrozenAddress, st := makeTwoAccounts(t, nil, nil) 242 accounts := environment.NewAccounts(st) 243 derivedBlockData := derived.NewEmptyDerivedBlockData() 244 245 vm := fvm.NewVirtualMachine() 246 247 // deploy code to accounts 248 whateverContractCode := ` 249 pub contract Whatever { 250 pub fun say() { 251 log("Düsseldorf") 252 } 253 } 254 ` 255 256 deployContract := []byte(fmt.Sprintf( 257 ` 258 transaction { 259 prepare(signer: AuthAccount) { 260 signer.contracts.add(name: "Whatever", code: "%s".decodeHex()) 261 } 262 } 263 `, hex.EncodeToString([]byte(whateverContractCode)), 264 )) 265 266 procFrozen := fvm.Transaction( 267 &flow.TransactionBody{Script: deployContract, Authorizers: []flow.Address{frozenAddress}, Payer: frozenAddress}, 268 derivedBlockData.NextTxIndexForTestingOnly()) 269 context := fvm.NewContext( 270 fvm.WithServiceAccount(false), 271 fvm.WithContractDeploymentRestricted(false), 272 fvm.WithCadenceLogging(true), 273 // run with limited processor to test just core of freezing, but still inside FVM 274 fvm.WithAuthorizationChecksEnabled(false), 275 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 276 fvm.WithDerivedBlockData(derivedBlockData)) 277 278 err := vm.Run(context, procFrozen, st.ViewForTestingOnly()) 279 require.NoError(t, err) 280 require.NoError(t, procFrozen.Err) 281 282 procNotFrozen := fvm.Transaction( 283 &flow.TransactionBody{Script: deployContract, Authorizers: []flow.Address{notFrozenAddress}, Payer: notFrozenAddress}, 284 derivedBlockData.NextTxIndexForTestingOnly()) 285 err = vm.Run(context, procNotFrozen, st.ViewForTestingOnly()) 286 require.NoError(t, err) 287 require.NoError(t, procNotFrozen.Err) 288 289 // both contracts should load now 290 291 code := func(a flow.Address) []byte { 292 return []byte(fmt.Sprintf(` 293 import Whatever from 0x%s 294 295 transaction { 296 execute { 297 Whatever.say() 298 } 299 } 300 `, a.String())) 301 } 302 303 // code from not frozen loads fine 304 proc := fvm.Transaction( 305 &flow.TransactionBody{Script: code(frozenAddress), Payer: serviceAddress}, 306 derivedBlockData.NextTxIndexForTestingOnly()) 307 308 err = vm.Run(context, proc, st.ViewForTestingOnly()) 309 require.NoError(t, err) 310 require.NoError(t, proc.Err) 311 require.Len(t, proc.Logs, 1) 312 require.Contains(t, proc.Logs[0], "\"D\\u{fc}sseldorf\"") 313 314 proc = fvm.Transaction( 315 &flow.TransactionBody{Script: code(notFrozenAddress)}, 316 derivedBlockData.NextTxIndexForTestingOnly()) 317 err = vm.Run(context, proc, st.ViewForTestingOnly()) 318 require.NoError(t, err) 319 require.NoError(t, proc.Err) 320 require.Len(t, proc.Logs, 1) 321 require.Contains(t, proc.Logs[0], "\"D\\u{fc}sseldorf\"") 322 323 // freeze account 324 325 freezeTx := fmt.Sprintf(` 326 transaction { 327 prepare(auth: AuthAccount) { 328 setAccountFrozen(0x%s, true) 329 } 330 } 331 `, 332 frozenAddress) 333 tx := &flow.TransactionBody{Script: []byte(freezeTx)} 334 tx.AddAuthorizer(chain.ServiceAddress()) 335 336 proc = fvm.Transaction(tx, derivedBlockData.NextTxIndexForTestingOnly()) 337 err = vm.Run(context, proc, st.ViewForTestingOnly()) 338 require.NoError(t, err) 339 require.NoError(t, proc.Err) 340 341 // make sure freeze status is correct 342 frozen, err := accounts.GetAccountFrozen(frozenAddress) 343 require.NoError(t, err) 344 require.True(t, frozen) 345 346 frozen, err = accounts.GetAccountFrozen(notFrozenAddress) 347 require.NoError(t, err) 348 require.False(t, frozen) 349 350 // loading code from frozen account triggers error 351 proc = fvm.Transaction( 352 &flow.TransactionBody{Script: code(frozenAddress)}, 353 derivedBlockData.NextTxIndexForTestingOnly()) 354 355 err = vm.Run(context, proc, st.ViewForTestingOnly()) 356 require.NoError(t, err) 357 require.Error(t, proc.Err) 358 359 // find frozen account specific error 360 require.True(t, errors.IsCadenceRuntimeError(proc.Err)) 361 362 var rtErr runtime.Error 363 require.True(t, errors.As(proc.Err, &rtErr)) 364 365 err = rtErr.Err 366 367 require.IsType(t, &runtime.ParsingCheckingError{}, err) 368 err = err.(*runtime.ParsingCheckingError).Err 369 370 require.IsType(t, &sema.CheckerError{}, err) 371 checkerErr := err.(*sema.CheckerError) 372 373 checkerErrors := checkerErr.ChildErrors() 374 375 require.Len(t, checkerErrors, 2) 376 require.IsType(t, &sema.ImportedProgramError{}, checkerErrors[0]) 377 378 importedCheckerError := checkerErrors[0].(*sema.ImportedProgramError).Err 379 accountFrozenError := errors.FrozenAccountError{} 380 381 require.True(t, errors.As(importedCheckerError, &accountFrozenError)) 382 require.Equal(t, frozenAddress, accountFrozenError.Address()) 383 }) 384 385 t.Run("service account cannot freeze itself", func(t *testing.T) { 386 387 vm := fvm.NewVirtualMachine() 388 // create default context 389 derivedBlockData := derived.NewEmptyDerivedBlockData() 390 context := fvm.NewContext( 391 fvm.WithDerivedBlockData(derivedBlockData)) 392 393 ledger := testutil.RootBootstrappedLedger(vm, context) 394 395 privateKeys, err := testutil.GenerateAccountPrivateKeys(1) 396 require.NoError(t, err) 397 398 // Bootstrap a ledger, creating accounts with the provided private keys and the root account. 399 accounts, err := testutil.CreateAccounts(vm, ledger, derivedBlockData, privateKeys, context.Chain) 400 require.NoError(t, err) 401 402 address := accounts[0] 403 404 codeAccount := fmt.Sprintf(` 405 transaction { 406 prepare(auth: AuthAccount) {} 407 execute { 408 setAccountFrozen(0x%s, true) 409 } 410 } 411 `, address.String()) 412 413 codeService := fmt.Sprintf(` 414 transaction { 415 prepare(auth: AuthAccount) {} 416 execute { 417 setAccountFrozen(0x%s, true) 418 } 419 } 420 `, serviceAddress.String()) 421 422 // sign tx by service account now 423 txBody := &flow.TransactionBody{Script: []byte(codeAccount)} 424 txBody.SetProposalKey(serviceAddress, 0, 0) 425 txBody.SetPayer(serviceAddress) 426 txBody.AddAuthorizer(serviceAddress) 427 428 err = testutil.SignEnvelope(txBody, serviceAddress, unittest.ServiceAccountPrivateKey) 429 require.NoError(t, err) 430 431 tx := fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly()) 432 err = vm.Run(context, tx, ledger) 433 require.NoError(t, err) 434 require.NoError(t, tx.Err) 435 436 accountsService := environment.NewAccounts(state.NewTransactionState( 437 ledger, 438 state.DefaultParameters(), 439 )) 440 441 frozen, err := accountsService.GetAccountFrozen(address) 442 require.NoError(t, err) 443 require.True(t, frozen) 444 445 // make sure service account is not frozen before 446 frozen, err = accountsService.GetAccountFrozen(serviceAddress) 447 require.NoError(t, err) 448 require.False(t, frozen) 449 450 // service account cannot be frozen 451 txBody = &flow.TransactionBody{Script: []byte(codeService)} 452 txBody.SetProposalKey(serviceAddress, 0, 1) 453 txBody.SetPayer(serviceAddress) 454 txBody.AddAuthorizer(serviceAddress) 455 456 err = testutil.SignPayload(txBody, accounts[0], privateKeys[0]) 457 require.NoError(t, err) 458 459 err = testutil.SignEnvelope(txBody, serviceAddress, unittest.ServiceAccountPrivateKey) 460 require.NoError(t, err) 461 462 tx = fvm.Transaction(txBody, derivedBlockData.NextTxIndexForTestingOnly()) 463 err = vm.Run(context, tx, ledger) 464 require.NoError(t, err) 465 require.Error(t, tx.Err) 466 467 accountsService = environment.NewAccounts(state.NewTransactionState( 468 ledger, 469 state.DefaultParameters(), 470 )) 471 472 frozen, err = accountsService.GetAccountFrozen(serviceAddress) 473 require.NoError(t, err) 474 require.False(t, frozen) 475 }) 476 477 t.Run("frozen account fail just tx, not execution", func(t *testing.T) { 478 479 frozenAddress, notFrozenAddress, st := makeTwoAccounts(t, nil, nil) 480 accounts := environment.NewAccounts(st) 481 482 vm := fvm.NewVirtualMachine() 483 484 // deploy code to accounts 485 whateverCode := []byte(` 486 transaction { 487 prepare(auth: AuthAccount) { 488 log("Szczebrzeszyn") 489 } 490 } 491 `) 492 493 derivedBlockData := derived.NewEmptyDerivedBlockData() 494 context := fvm.NewContext( 495 fvm.WithServiceAccount(false), 496 fvm.WithContractDeploymentRestricted(false), 497 fvm.WithCadenceLogging(true), 498 // run with limited processor to test just core of freezing, but still inside FVM 499 fvm.WithAccountKeyWeightThreshold(-1), 500 fvm.WithSequenceNumberCheckAndIncrementEnabled(false), 501 fvm.WithDerivedBlockData(derivedBlockData)) 502 503 // freeze account 504 505 freezeTx := fmt.Sprintf(` 506 transaction { 507 prepare(auth: AuthAccount) { 508 setAccountFrozen(0x%s, true) 509 } 510 } 511 `, 512 frozenAddress) 513 tx := &flow.TransactionBody{Script: []byte(freezeTx)} 514 tx.AddAuthorizer(chain.ServiceAddress()) 515 516 proc := fvm.Transaction(tx, derivedBlockData.NextTxIndexForTestingOnly()) 517 518 derivedTxnData, err := derivedBlockData.NewDerivedTransactionData(0, 0) 519 require.NoError(t, err) 520 521 err = fvm.Run(proc.NewExecutor( 522 fvm.NewContextFromParent( 523 context, 524 fvm.WithAuthorizationChecksEnabled(false), 525 ), 526 st, 527 derivedTxnData)) 528 require.NoError(t, err) 529 require.NoError(t, proc.Err) 530 531 // make sure freeze status is correct 532 var frozen bool 533 frozen, err = accounts.GetAccountFrozen(frozenAddress) 534 require.NoError(t, err) 535 require.True(t, frozen) 536 537 frozen, err = accounts.GetAccountFrozen(notFrozenAddress) 538 require.NoError(t, err) 539 require.False(t, frozen) 540 541 t.Run("authorizer", func(t *testing.T) { 542 543 notFrozenProc := fvm.Transaction( 544 &flow.TransactionBody{ 545 Script: whateverCode, 546 Authorizers: []flow.Address{notFrozenAddress}, 547 ProposalKey: flow.ProposalKey{Address: notFrozenAddress}, 548 Payer: notFrozenAddress}, 549 derivedBlockData.NextTxIndexForTestingOnly()) 550 // tx run OK by nonfrozen account 551 err = vm.Run(context, notFrozenProc, st.ViewForTestingOnly()) 552 require.NoError(t, err) 553 require.NoError(t, notFrozenProc.Err) 554 555 frozenProc := fvm.Transaction( 556 &flow.TransactionBody{ 557 Script: whateverCode, 558 Authorizers: []flow.Address{frozenAddress}, 559 ProposalKey: flow.ProposalKey{Address: notFrozenAddress}, 560 Payer: notFrozenAddress}, 561 derivedBlockData.NextTxIndexForTestingOnly()) 562 err = vm.Run(context, frozenProc, st.ViewForTestingOnly()) 563 require.NoError(t, err) 564 require.Error(t, frozenProc.Err) 565 566 require.Equal( 567 t, 568 errors.ErrCodeFrozenAccountError, 569 frozenProc.Err.Code()) 570 571 // The outer most coded error is a wrapper, not the actual 572 // FrozenAccountError itself. 573 _, ok := frozenProc.Err.(errors.FrozenAccountError) 574 require.False(t, ok) 575 576 // find frozen account specific error 577 var accountFrozenErr errors.FrozenAccountError 578 ok = errors.As(frozenProc.Err, &accountFrozenErr) 579 require.True(t, ok) 580 require.Equal(t, frozenAddress, accountFrozenErr.Address()) 581 }) 582 583 t.Run("proposal", func(t *testing.T) { 584 585 notFrozenProc := fvm.Transaction( 586 &flow.TransactionBody{ 587 Script: whateverCode, 588 Authorizers: []flow.Address{notFrozenAddress}, 589 ProposalKey: flow.ProposalKey{Address: notFrozenAddress}, 590 Payer: notFrozenAddress, 591 }, 592 derivedBlockData.NextTxIndexForTestingOnly()) 593 594 // tx run OK by nonfrozen account 595 err = vm.Run(context, notFrozenProc, st.ViewForTestingOnly()) 596 require.NoError(t, err) 597 require.NoError(t, notFrozenProc.Err) 598 599 frozenProc := fvm.Transaction( 600 &flow.TransactionBody{ 601 Script: whateverCode, 602 Authorizers: []flow.Address{notFrozenAddress}, 603 ProposalKey: flow.ProposalKey{Address: frozenAddress}, 604 Payer: notFrozenAddress, 605 }, 606 derivedBlockData.NextTxIndexForTestingOnly()) 607 err = vm.Run(context, frozenProc, st.ViewForTestingOnly()) 608 require.NoError(t, err) 609 require.Error(t, frozenProc.Err) 610 611 require.Equal( 612 t, 613 errors.ErrCodeFrozenAccountError, 614 frozenProc.Err.Code()) 615 616 // The outer most coded error is a wrapper, not the actual 617 // FrozenAccountError itself. 618 _, ok := frozenProc.Err.(errors.FrozenAccountError) 619 require.False(t, ok) 620 621 // find frozen account specific error 622 var accountFrozenErr errors.FrozenAccountError 623 ok = errors.As(frozenProc.Err, &accountFrozenErr) 624 require.True(t, ok) 625 require.Equal(t, frozenAddress, accountFrozenErr.Address()) 626 }) 627 628 t.Run("payer", func(t *testing.T) { 629 630 notFrozenProc := fvm.Transaction( 631 &flow.TransactionBody{ 632 Script: whateverCode, 633 Authorizers: []flow.Address{notFrozenAddress}, 634 ProposalKey: flow.ProposalKey{Address: notFrozenAddress}, 635 Payer: notFrozenAddress, 636 }, 637 derivedBlockData.NextTxIndexForTestingOnly()) 638 639 // tx run OK by nonfrozen account 640 err = vm.Run(context, notFrozenProc, st.ViewForTestingOnly()) 641 require.NoError(t, err) 642 require.NoError(t, notFrozenProc.Err) 643 644 frozenProc := fvm.Transaction( 645 &flow.TransactionBody{ 646 Script: whateverCode, 647 Authorizers: []flow.Address{notFrozenAddress}, 648 ProposalKey: flow.ProposalKey{Address: notFrozenAddress}, 649 Payer: frozenAddress, 650 }, 651 derivedBlockData.NextTxIndexForTestingOnly()) 652 err = vm.Run(context, frozenProc, st.ViewForTestingOnly()) 653 require.NoError(t, err) 654 require.Error(t, frozenProc.Err) 655 656 require.Equal( 657 t, 658 errors.ErrCodeFrozenAccountError, 659 frozenProc.Err.Code()) 660 661 // The outer most coded error is a wrapper, not the actual 662 // FrozenAccountError itself. 663 _, ok := frozenProc.Err.(errors.FrozenAccountError) 664 require.False(t, ok) 665 666 // find frozen account specific error 667 var accountFrozenErr errors.FrozenAccountError 668 ok = errors.As(frozenProc.Err, &accountFrozenErr) 669 require.True(t, ok) 670 require.Equal(t, frozenAddress, accountFrozenErr.Address()) 671 }) 672 }) 673 }