@@ -29,6 +29,7 @@ import (
29
29
"testing"
30
30
"time"
31
31
32
+ "github.com/celo-org/celo-blockchain/accounts/abi"
32
33
"github.com/celo-org/celo-blockchain/common"
33
34
"github.com/celo-org/celo-blockchain/common/hexutil"
34
35
"github.com/celo-org/celo-blockchain/consensus"
@@ -403,6 +404,194 @@ func TestTraceTransactionWithRegistryDeployed(t *testing.T) {
403
404
}
404
405
}
405
406
407
+ // Use the callTracer to trace a native CELO transfer after the
408
+ // registry has been deployed, as above.
409
+ func TestCallTraceTransactionNativeTransfer (t * testing.T ) {
410
+ t .Parallel ()
411
+
412
+ // Initialize test accounts
413
+ accounts := newAccounts (2 )
414
+ genesis := & core.Genesis {Alloc : core.GenesisAlloc {
415
+ accounts [0 ].addr : {Balance : big .NewInt (params .Ether )},
416
+ accounts [1 ].addr : {Balance : big .NewInt (params .Ether )},
417
+ common .HexToAddress ("0xce10" ): { // Registry Proxy
418
+ Code : testutil .RegistryProxyOpcodes ,
419
+ Storage : map [common.Hash ]common.Hash {
420
+ // Hashes represent the storage slot for Registry.sol's `registry` mapping
421
+ // which is stored in the RegistryProxy's storage.
422
+ // Hashes are computed by taking: keccack(packed(params.GoldTokenRegistryId, 1)),
423
+ // where 1 is the storage offset)
424
+ common .HexToHash ("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc" ): common .HexToHash ("0xce11" ), // Registry Implementation
425
+ common .HexToHash ("0x91646b8507bf2e54d7c3de9155442ba111546b81af1cbdd1f68eeb6926b98d58" ): common .HexToHash ("0xd023" ), // Governance Proxy
426
+ },
427
+ Balance : big .NewInt (0 ),
428
+ },
429
+ common .HexToAddress ("0xce11" ): { // Registry Implementation
430
+ Code : testutil .RegistryOpcodes ,
431
+ Balance : big .NewInt (0 ),
432
+ },
433
+ }}
434
+
435
+ target := common.Hash {}
436
+ signer := types.HomesteadSigner {}
437
+ transferVal := big .NewInt (1000 )
438
+ api := NewAPI (newTestBackend (t , 1 , genesis , func (i int , b * core.BlockGen ) {
439
+ // Transfer from account[0] to account[1]
440
+ // value: 1000 wei
441
+ // fee: 0 wei
442
+ tx , _ := types .SignTx (types .NewTransaction (uint64 (i ), accounts [1 ].addr , transferVal , params .TxGas , nil , nil , nil , nil , nil ), signer , accounts [0 ].key )
443
+ b .AddTx (tx )
444
+ target = tx .Hash ()
445
+ }))
446
+ tracerStr := "callTracer"
447
+ result , err := api .TraceTransaction (context .Background (), target , & TraceConfig {Tracer : & tracerStr })
448
+ if err != nil {
449
+ t .Errorf ("Failed to trace transaction %v" , err )
450
+ }
451
+
452
+ ret := new (callTrace )
453
+ if err := json .Unmarshal (result .(json.RawMessage ), ret ); err != nil {
454
+ t .Fatalf ("failed to unmarshal trace result: %v" , err )
455
+ }
456
+ expectedTrace := & callTrace {
457
+ Type : "CALL" ,
458
+ From : accounts [0 ].addr ,
459
+ To : accounts [1 ].addr ,
460
+ Input : hexutil .Bytes (common .Hex2Bytes ("0x" )),
461
+ Output : hexutil .Bytes (common .Hex2Bytes ("0x" )),
462
+ Gas : newRPCUint64 (0 ),
463
+ GasUsed : newRPCUint64 (0 ),
464
+ Value : (* hexutil .Big )(transferVal ),
465
+ }
466
+ if ! jsonEqual (ret , expectedTrace ) {
467
+ t .Fatalf ("trace mismatch: \n have %+v\n want %+v" , ret , expectedTrace )
468
+ }
469
+ }
470
+
471
+ // Use the callTracer to trace a CELO transfer made via the transfer
472
+ // precompile (by sending this from the registered GoldToken contract).
473
+ func TestCallTraceTransactionPrecompileTransfer (t * testing.T ) {
474
+ // Invoke the transfer precompile by sending a transfer from an account
475
+ // registered as GoldToken in the mock registry
476
+ t .Parallel ()
477
+ // Initialize test accounts
478
+ accounts := newAccounts (3 )
479
+ goldToken := accounts [0 ]
480
+ registryProxyAddr := common .HexToAddress ("0xce10" )
481
+ registryImplAddr := common .HexToAddress ("0xce11" )
482
+ genesis := & core.Genesis {Alloc : core.GenesisAlloc {
483
+ goldToken .addr : {Balance : big .NewInt (params .Ether )},
484
+ accounts [1 ].addr : {Balance : big .NewInt (params .Ether )},
485
+ accounts [2 ].addr : {Balance : big .NewInt (params .Ether )},
486
+ registryProxyAddr : { // Registry Proxy
487
+ Code : testutil .RegistryProxyOpcodes ,
488
+ Storage : map [common.Hash ]common.Hash {
489
+ common .HexToHash ("0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc" ): common .HexToHash ("0xce11" ), // Registry Implementation
490
+ common .HexToHash ("0x91646b8507bf2e54d7c3de9155442ba111546b81af1cbdd1f68eeb6926b98d58" ): common .HexToHash ("0xd023" ), // Governance Proxy
491
+ common .HexToHash ("0x773cc8652456781771d48fb3d560d7ba3d6d1882654492b68beec583d2c590aa" ): goldToken .addr .Hash (), // GoldToken Implementation
492
+ common .HexToHash ("0x2131a4338f6fb8d4507e234a7c72af8efefbbf2f1817ed570bce33eb6667feb9" ): common .HexToHash ("0xd007" ), // Reserve Implementation
493
+ },
494
+ Balance : big .NewInt (0 ),
495
+ },
496
+ registryImplAddr : { // Registry Implementation
497
+ Code : testutil .RegistryOpcodes ,
498
+ Balance : big .NewInt (0 ),
499
+ },
500
+ }}
501
+
502
+ target := common.Hash {}
503
+ signer := types.HomesteadSigner {}
504
+ transferVal := big .NewInt (1000 )
505
+
506
+ // Construct and pack data for transfer precompile representing [from, to, value]
507
+ addressTy , err := abi .NewType ("address" , "" , nil )
508
+ if err != nil {
509
+ t .Fatalf ("failed to create address type: %v" , err )
510
+ }
511
+ uint256Ty , err := abi .NewType ("uint256" , "" , nil )
512
+ if err != nil {
513
+ t .Fatalf ("failed to create uint256 type: %v" , err )
514
+ }
515
+
516
+ transferArgs := abi.Arguments {
517
+ {
518
+ Type : addressTy ,
519
+ },
520
+ {
521
+ Type : addressTy ,
522
+ },
523
+ {
524
+ Type : uint256Ty ,
525
+ },
526
+ }
527
+ data , err := transferArgs .Pack (accounts [1 ].addr , accounts [2 ].addr , transferVal )
528
+ if err != nil {
529
+ t .Fatalf ("failed to pack args: %v" , err )
530
+ }
531
+ transferPrecompile := common .HexToAddress ("0xfd" )
532
+ gas := params .TxGas * 2
533
+ api := NewAPI (newTestBackend (t , 1 , genesis , func (i int , b * core.BlockGen ) {
534
+ // Transfer via transfer precompile by sending tx from GoldToken addr
535
+ tx , _ := types .SignTx (types .NewTransaction (uint64 (i ), transferPrecompile , big .NewInt (0 ), gas , big .NewInt (3 ), nil , nil , nil , data ), signer , goldToken .key )
536
+ b .AddTx (tx )
537
+ target = tx .Hash ()
538
+ }))
539
+ tracerStr := "callTracer"
540
+ result , err := api .TraceTransaction (context .Background (), target , & TraceConfig {Tracer : & tracerStr })
541
+
542
+ if err != nil {
543
+ t .Errorf ("Failed to trace transaction %v" , err )
544
+ }
545
+
546
+ ret := new (callTrace )
547
+ if err := json .Unmarshal (result .(json.RawMessage ), ret ); err != nil {
548
+ t .Fatalf ("failed to unmarshal trace result: %v" , err )
549
+ }
550
+
551
+ // Registry ABI packed version of Registry.getAddressFor("GoldToken")
552
+ packedGetAddressForGoldToken := common .FromHex ("0xdd927233d7e89ade8430819f08bf97a087285824af3351ee12d72a2d132b0c6c0687bfaf" )
553
+ expectedTrace := & callTrace {
554
+ // Outer transaction call
555
+ Type : "CALL" ,
556
+ From : goldToken .addr ,
557
+ To : transferPrecompile ,
558
+ Input : data ,
559
+ Output : data ,
560
+ Gas : newRPCUint64 (20112 ),
561
+ // Note that top-level traces do not currently include intrinsic gas
562
+ GasUsed : newRPCUint64 (params .CallValueTransferGas ),
563
+ Value : (* hexutil .Big )(big .NewInt (0 )),
564
+ Calls : []callTrace {
565
+ {
566
+ // Lookup of GoldToken contract address with capped gas
567
+ Type : "STATICCALL" ,
568
+ From : common .ZeroAddress ,
569
+ To : registryProxyAddr ,
570
+ Input : packedGetAddressForGoldToken ,
571
+ Output : common .LeftPadBytes (goldToken .addr .Bytes (), 32 ),
572
+ Gas : newRPCUint64 (params .MaxGasForGetAddressFor ),
573
+ GasUsed : newRPCUint64 (0 ),
574
+ Calls : []callTrace {
575
+ {
576
+ // RegistryProxy -> RegistryImpl delegate call to
577
+ // retrieve GoldToken address
578
+ Type : "DELEGATECALL" ,
579
+ From : registryProxyAddr ,
580
+ To : registryImplAddr ,
581
+ Input : packedGetAddressForGoldToken ,
582
+ Output : common .LeftPadBytes (goldToken .addr .Bytes (), 32 ),
583
+ Gas : newRPCUint64 (0 ),
584
+ GasUsed : newRPCUint64 (0 ),
585
+ },
586
+ },
587
+ },
588
+ },
589
+ }
590
+ if ! jsonEqual (ret , expectedTrace ) {
591
+ t .Fatalf ("trace mismatch: \n have %+v\n want %+v" , ret , expectedTrace )
592
+ }
593
+ }
594
+
406
595
func TestTraceBlock (t * testing.T ) {
407
596
t .Parallel ()
408
597
@@ -716,6 +905,11 @@ func newRPCBalance(balance *big.Int) **hexutil.Big {
716
905
return & rpcBalance
717
906
}
718
907
908
+ func newRPCUint64 (number uint64 ) * hexutil.Uint64 {
909
+ rpcUint64 := hexutil .Uint64 (number )
910
+ return & rpcUint64
911
+ }
912
+
719
913
func newRPCBytes (bytes []byte ) * hexutil.Bytes {
720
914
rpcBytes := hexutil .Bytes (bytes )
721
915
return & rpcBytes
@@ -731,3 +925,23 @@ func newStates(keys []common.Hash, vals []common.Hash) *map[common.Hash]common.H
731
925
}
732
926
return & m
733
927
}
928
+
929
+ // jsonEqual is similar to reflect.DeepEqual, but does a 'bounce' via json prior to
930
+ // comparison
931
+ // Note: this is duplicated from calltrace_test due to keep the diff minimal
932
+ // after package restructuring in go-ethereum.
933
+ func jsonEqual (x , y interface {}) bool {
934
+ xTrace := new (callTrace )
935
+ yTrace := new (callTrace )
936
+ if xj , err := json .Marshal (x ); err == nil {
937
+ json .Unmarshal (xj , xTrace )
938
+ } else {
939
+ return false
940
+ }
941
+ if yj , err := json .Marshal (y ); err == nil {
942
+ json .Unmarshal (yj , yTrace )
943
+ } else {
944
+ return false
945
+ }
946
+ return reflect .DeepEqual (xTrace , yTrace )
947
+ }
0 commit comments