Skip to content

Commit 9ffa6f7

Browse files
author
Eela Nagaraj
committed
Merge remote-tracking branch 'upstream/master' into feature/go-native-trace
2 parents a7d11fe + 43eecf0 commit 9ffa6f7

File tree

12 files changed

+429
-27
lines changed

12 files changed

+429
-27
lines changed

core/vm/contracts.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ func (c *transfer) Run(input []byte, caller common.Address, evm *EVM) ([]byte, e
698698
// to: 32 bytes representing the address of the recipient
699699
// value: 32 bytes, a 256 bit integer representing the amount of Celo Gold to transfer
700700
// 3 arguments x 32 bytes each = 96 bytes total input
701-
if len(input) < 96 {
701+
if (evm.chainRules.IsGFork && len(input) != 96) || len(input) < 96 {
702702
return nil, ErrInputLength
703703
}
704704

@@ -781,7 +781,7 @@ func (c *fractionMulExp) Run(input []byte, caller common.Address, evm *EVM) ([]b
781781
// decimals: 32 bytes, 256 bit integer, places of precision
782782
//
783783
// 6 args x 32 bytes each = 192 bytes total input length
784-
if len(input) < 192 {
784+
if (evm.chainRules.IsGFork && len(input) != 192) || len(input) < 192 {
785785
return nil, ErrInputLength
786786
}
787787

@@ -1015,7 +1015,7 @@ func (c *getValidator) Run(input []byte, caller common.Address, evm *EVM) ([]byt
10151015
// input is comprised of two arguments:
10161016
// index: 32 byte integer representing the index of the validator to get
10171017
// blockNumber: 32 byte integer representing the block number to access
1018-
if len(input) < 64 {
1018+
if (evm.chainRules.IsGFork && len(input) != 64) || len(input) < 64 {
10191019
return nil, ErrInputLength
10201020
}
10211021

core/vm/contracts_test.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,14 @@ func BenchmarkPrecompiledEd25519Verify(b *testing.B) { benchJSON("ed25519Verify"
499499

500500
// Tests sample inputs for fractionMulExp
501501
// NOTE: This currently only verifies that inputs of invalid length are rejected
502-
func TestPrecompiledFractionMulExp(t *testing.T) { testJSON("fractionMulExp", "fc", t) }
502+
func TestPrecompiledFractionMulExp(t *testing.T) {
503+
// Post GFork behaviour
504+
mockEVM.chainRules.IsGFork = true
505+
testJSON("fractionMulExp", "fc", t)
506+
// Pre GFork behaviour
507+
mockEVM.chainRules.IsGFork = false
508+
testJSON("fractionMulExpOld", "fc", t)
509+
}
503510

504511
// Tests sample inputs for proofOfPossession
505512
// NOTE: This currently only verifies that inputs of invalid length are rejected

core/vm/testdata/precompiles/fractionMulExp.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
},
77
{
88
"Input": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000f100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000",
9-
"Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002710",
10-
"Name": "input_too_long"
9+
"Expected": "invalid input length",
10+
"Name": "input_too_long",
11+
"ErrorExpected": true
1112
},
1213
{
1314
"Input": "",
1415
"Expected": "invalid input length",
1516
"Name": "empty_input",
1617
"ErrorExpected": true
1718
}
18-
]
19+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"Input": "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000f100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000",
4+
"Expected": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002710",
5+
"Name": "input_too_long"
6+
}
7+
]

e2e_test/e2e_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@ import (
1414
"github.com/celo-org/celo-blockchain/common"
1515
"github.com/celo-org/celo-blockchain/common/hexutil"
1616
"github.com/celo-org/celo-blockchain/core/types"
17+
"github.com/celo-org/celo-blockchain/eth/tracers"
1718
"github.com/celo-org/celo-blockchain/log"
1819
"github.com/celo-org/celo-blockchain/node"
1920
"github.com/celo-org/celo-blockchain/rpc"
2021
"github.com/celo-org/celo-blockchain/test"
2122
"github.com/stretchr/testify/assert"
2223
"github.com/stretchr/testify/require"
24+
25+
// Force-load native and js pacakges, to trigger registration
26+
_ "github.com/celo-org/celo-blockchain/eth/tracers/js"
27+
_ "github.com/celo-org/celo-blockchain/eth/tracers/native"
2328
)
2429

2530
func init() {
@@ -55,6 +60,41 @@ func TestSendCelo(t *testing.T) {
5560
require.NoError(t, err)
5661
}
5762

63+
// This test starts a network, submits a GoldToken.transfer tx, waits for the whole
64+
// network to process the transaction, and traces that tx.
65+
// This tests that CELO transfers made via the transfer precompile work e2e
66+
// and can be useful for debugging these traces.
67+
func TestTraceSendCeloViaGoldToken(t *testing.T) {
68+
ac := test.AccountConfig(3, 2)
69+
gc, ec, err := test.BuildConfig(ac)
70+
require.NoError(t, err)
71+
network, shutdown, err := test.NewNetwork(ac, gc, ec)
72+
require.NoError(t, err)
73+
defer shutdown()
74+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
75+
defer cancel()
76+
77+
accounts := test.Accounts(ac.DeveloperAccounts(), gc.ChainConfig())
78+
// Send 1 wei of CELO from accounts[0] to accounts[1] by calling GoldToken.transfer
79+
tx, err := accounts[0].SendCeloViaGoldToken(ctx, accounts[1].Address, 1, network[0])
80+
require.NoError(t, err)
81+
82+
// Wait for the whole network to process the transaction.
83+
err = network.AwaitTransactions(ctx, tx)
84+
require.NoError(t, err)
85+
c, err := rpc.DialContext(ctx, network[0].WSEndpoint())
86+
require.NoError(t, err)
87+
88+
var result map[string]interface{}
89+
tracerStr := "callTracer"
90+
err = c.CallContext(ctx, &result, "debug_traceTransaction", tx.Hash().String(), tracers.TraceConfig{Tracer: &tracerStr})
91+
92+
require.NoError(t, err)
93+
// Check top level gas values
94+
require.Equal(t, result["gasUsed"], "0x3a46")
95+
require.Equal(t, result["gas"], "0x3ac4")
96+
}
97+
5898
// This test verifies correct behavior in a network of size one, in the case that
5999
// this fails we know that the problem does not lie with our network code.
60100
func TestSingleNodeNetworkManyTxs(t *testing.T) {

eth/protocols/eth/handlers.go

+5
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ func handleGetBlockBodies67(backend Backend, msg Decoder, peer *Peer) error {
137137
return peer.ReplyBlockBodiesRLP(query.RequestId, response)
138138
}
139139

140+
// In Celo the return value of the `GetBlockBodies` query has been changed to include both the block hash
141+
// and the block bodies. This is necessary because received block bodies can not be matched to the header
142+
// directly, as there is body data (i.e. `Randomness` and `EpochSnarkData`) which is not represented in the
143+
// header. That means that that the block fetcher cannot find the corresponding header for given blockdata
144+
// without executing the block contents. This is avoided by passing the block hash with the body data.
140145
func answerGetBlockBodiesQuery(backend Backend, query GetBlockBodiesPacket, peer *Peer) ([]rlp.RawValue, error) {
141146
// Gather blocks until the fetch or network limits is reached
142147
var (

eth/tracers/api_test.go

+214
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"testing"
3030
"time"
3131

32+
"github.com/celo-org/celo-blockchain/accounts/abi"
3233
"github.com/celo-org/celo-blockchain/common"
3334
"github.com/celo-org/celo-blockchain/common/hexutil"
3435
"github.com/celo-org/celo-blockchain/consensus"
@@ -403,6 +404,194 @@ func TestTraceTransactionWithRegistryDeployed(t *testing.T) {
403404
}
404405
}
405406

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: \nhave %+v\nwant %+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: \nhave %+v\nwant %+v", ret, expectedTrace)
592+
}
593+
}
594+
406595
func TestTraceBlock(t *testing.T) {
407596
t.Parallel()
408597

@@ -716,6 +905,11 @@ func newRPCBalance(balance *big.Int) **hexutil.Big {
716905
return &rpcBalance
717906
}
718907

908+
func newRPCUint64(number uint64) *hexutil.Uint64 {
909+
rpcUint64 := hexutil.Uint64(number)
910+
return &rpcUint64
911+
}
912+
719913
func newRPCBytes(bytes []byte) *hexutil.Bytes {
720914
rpcBytes := hexutil.Bytes(bytes)
721915
return &rpcBytes
@@ -731,3 +925,23 @@ func newStates(keys []common.Hash, vals []common.Hash) *map[common.Hash]common.H
731925
}
732926
return &m
733927
}
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

Comments
 (0)