Skip to content

Commit

Permalink
evm: adjust unpacked types for json
Browse files Browse the repository at this point in the history
  • Loading branch information
pro-wh committed May 26, 2023
1 parent 72844a1 commit e90ef11
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 0 deletions.
31 changes: 31 additions & 0 deletions api/logic/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,42 @@ package logic

import (
"fmt"
"reflect"

"github.com/ethereum/go-ethereum/accounts/abi"
ethCommon "github.com/ethereum/go-ethereum/common"
)

// evmPreMarshal converts v to a type that gives us the JSON serialization that we like:
// - large integers are JSON strings instead of JSON numbers
// - byte array types are JSON strings of base64 instead of JSON arrays of numbers
func evmPreMarshal(v interface{}, t abi.Type) interface{} {
switch t.T {
case abi.IntTy, abi.UintTy:
if t.Size > 32 {
return fmt.Sprint(v)
}
case abi.SliceTy, abi.ArrayTy:
rv := reflect.ValueOf(v)
slice := make([]interface{}, 0, rv.Len())
for i := 0; i < rv.Len(); i++ {
slice = append(slice, evmPreMarshal(rv.Index(i).Interface(), *t.Elem))
}
return slice
case abi.TupleTy:
rv := reflect.ValueOf(v)
m := map[string]interface{}{}
for i, fieldName := range t.TupleRawNames {
m[fieldName] = evmPreMarshal(rv.Field(i).Interface(), *t.TupleElems[i])
}
case abi.FixedBytesTy, abi.FunctionTy:
c := reflect.New(t.GetType()).Elem()
c.Set(reflect.ValueOf(v))
return c.Bytes()
}
return v
}

func EVMParseData(data []byte, contractABI *abi.ABI) (*abi.Method, []interface{}, error) {
if len(data) < 4 {
return nil, nil, fmt.Errorf("data (%dB) too short to have method ID", len(data))
Expand Down
46 changes: 46 additions & 0 deletions api/logic/evm_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,62 @@
package logic

import (
_ "embed"
"encoding/hex"
"encoding/json"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/accounts/abi"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"

"github.com/oasisprotocol/oasis-indexer/analyzer/evmabi"
)

//go:embed test_contracts/artifacts/Varied.json
var artifactVariedJSON []byte
var Varied *abi.ABI

func init() {
type artifact struct {
ABI *abi.ABI
}
var artifactVaried artifact
if err := json.Unmarshal(artifactVariedJSON, &artifactVaried); err != nil {
panic(err)
}
Varied = artifactVaried.ABI
}

func TestEVMParseTypes(t *testing.T) {
data, err := hex.DecodeString("a85acffaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010101010101010101010101010101010101010101010101010101010101010101000000000000000000000000010101010101010101010101010101010101010101010101010101010101010101010101010101010202020200000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err)
method, args, err := EVMParseData(data, Varied)
require.Equal(t, Varied.Methods["test"], *method)
jsonExpected := []string{
"-1", // int8
"1", // uint8
"\"-1\"", // int256
"\"1\"", // uint256
"true", // bool
"\"AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE=\"", // bytes32
"\"0x0101010101010101010101010101010101010101\"", // address
"\"AQEBAQEBAQEBAQEBAQEBAQEBAQECAgIC\"", // function (uint16) external returns (uint16)
"[1,1]", // uint16[2]
"\"AQ==\"", // bytes
"\"a\"", // string
"[1]", // uint16[]
"{\"n\":1,\"s\":\"a\"}", // O
}
for i, input := range method.Inputs {
transducedArg := evmPreMarshal(args[i], input.Type)
jsonBytesArg, err1 := json.Marshal(transducedArg)
require.NoError(t, err1)
require.Equal(t, jsonExpected[i], string(jsonBytesArg))
}
}

func TestEVMParseData(t *testing.T) {
// https://explorer.emerald.oasis.dev/tx/0x1ac7521df4cda38c87cff56b1311ee9362168bd794230415a37f2aff3a554a5f/internal-transactions
data, err := hex.DecodeString("095ea7b3000000000000000000000000250d48c5e78f1e85f7ab07fec61e93ba703ae6680000000000000000000000000000000000000000000000003782dace9d900000")
Expand Down
29 changes: 29 additions & 0 deletions api/logic/test_contracts/Varied.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

struct O {
uint16 n;
string s;
}

interface Varied {
function test(
int8 i8,
uint8 u8,
int256 i,
uint256 u,
bool b,
// not supported in go-ethereum
// fixed128x18 f18,
// ufixed128x18 uf18,
bytes32 b32,
address a,
function (uint16) external returns (uint16) f,
uint16[2] calldata xy,
bytes calldata buf,
string calldata s,
uint16[] calldata l,
O calldata o
) external;
}
142 changes: 142 additions & 0 deletions api/logic/test_contracts/artifacts/Varied.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{
"deploy": {
"VM:-": {
"linkReferences": {},
"autoDeployLib": true
},
"main:1": {
"linkReferences": {},
"autoDeployLib": true
},
"ropsten:3": {
"linkReferences": {},
"autoDeployLib": true
},
"rinkeby:4": {
"linkReferences": {},
"autoDeployLib": true
},
"kovan:42": {
"linkReferences": {},
"autoDeployLib": true
},
"goerli:5": {
"linkReferences": {},
"autoDeployLib": true
},
"Custom": {
"linkReferences": {},
"autoDeployLib": true
}
},
"data": {
"bytecode": {
"functionDebugData": {},
"generatedSources": [],
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"deployedBytecode": {
"functionDebugData": {},
"generatedSources": [],
"immutableReferences": {},
"linkReferences": {},
"object": "",
"opcodes": "",
"sourceMap": ""
},
"gasEstimates": null,
"methodIdentifiers": {
"test(int8,uint8,int256,uint256,bool,bytes32,address,function,uint16[2],bytes,string,uint16[],(uint16,string))": "a85acffa"
}
},
"abi": [
{
"inputs": [
{
"internalType": "int8",
"name": "i8",
"type": "int8"
},
{
"internalType": "uint8",
"name": "u8",
"type": "uint8"
},
{
"internalType": "int256",
"name": "i",
"type": "int256"
},
{
"internalType": "uint256",
"name": "u",
"type": "uint256"
},
{
"internalType": "bool",
"name": "b",
"type": "bool"
},
{
"internalType": "bytes32",
"name": "b32",
"type": "bytes32"
},
{
"internalType": "address",
"name": "a",
"type": "address"
},
{
"internalType": "function (uint16) external returns (uint16)",
"name": "f",
"type": "function"
},
{
"internalType": "uint16[2]",
"name": "xy",
"type": "uint16[2]"
},
{
"internalType": "bytes",
"name": "buf",
"type": "bytes"
},
{
"internalType": "string",
"name": "s",
"type": "string"
},
{
"internalType": "uint16[]",
"name": "l",
"type": "uint16[]"
},
{
"components": [
{
"internalType": "uint16",
"name": "n",
"type": "uint16"
},
{
"internalType": "string",
"name": "s",
"type": "string"
}
],
"internalType": "struct O",
"name": "o",
"type": "tuple"
}
],
"name": "test",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
}

0 comments on commit e90ef11

Please sign in to comment.