Skip to content

Commit 1e639a4

Browse files
authored
Merge pull request #1099 from oasisprotocol/ptrus/feature/parse-precompile-result
analyzer/runtime: Parse precompile results
2 parents b92390b + da0aa13 commit 1e639a4

File tree

5 files changed

+126
-0
lines changed

5 files changed

+126
-0
lines changed

.changelog/1093.feature.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
analyzer/runtime: Parse precompile results

analyzer/runtime/evm/precompile.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package evm
2+
3+
import (
4+
"fmt"
5+
"math/big"
6+
7+
"github.com/ethereum/go-ethereum/accounts/abi"
8+
"github.com/oasisprotocol/oasis-core/go/common/cbor"
9+
10+
"github.com/oasisprotocol/nexus/analyzer/util/addresses"
11+
"github.com/oasisprotocol/nexus/api/v1/types"
12+
)
13+
14+
var (
15+
// 0x0100000000000000000000000000000000000103
16+
subcallPrecompile = addresses.FromBech32("oasis1qzr543mmela3xwflqtz3e0l8jzp8tupf3v59r6qn")
17+
18+
typeUint256, _ = abi.NewType("uint256", "", nil)
19+
typeBytes, _ = abi.NewType("bytes", "", nil)
20+
)
21+
22+
// IsSubcallPrecompile checks if the address is the subcall precompile address.
23+
func IsSubcallPrecompile(address types.Address) bool {
24+
return address == subcallPrecompile
25+
}
26+
27+
// EVMMaybeUnmarshalPrecompileResult tries to unmarshal a precompile result.
28+
func EVMMaybeUnmarshalPrecompileResult(result []byte) (uint32, string, error) {
29+
// Try parsing the output:
30+
// https://github.com/oasisprotocol/oasis-sdk/blob/deca9369166757b3075dc4414d7450340fdaa779/runtime-sdk/modules/evm/src/precompile/subcall.rs#L97-L106
31+
var output []byte
32+
if err := cbor.Unmarshal(result, &output); err != nil {
33+
return 0, "", fmt.Errorf("unmarshal precompile result: %w", err)
34+
}
35+
36+
args := abi.Arguments{
37+
{Type: typeUint256, Name: "status_code"},
38+
{Type: typeBytes, Name: "module"},
39+
}
40+
out, err := args.Unpack(output)
41+
if err != nil {
42+
return 0, "", fmt.Errorf("unpack precompile result: %w", err)
43+
}
44+
if len(out) != 2 {
45+
return 0, "", fmt.Errorf("expected 2 arguments, got %d", len(out))
46+
}
47+
48+
statusCode := out[0].(*big.Int).Uint64()
49+
if statusCode == 0 {
50+
return 0, "", nil
51+
}
52+
53+
return uint32(statusCode), string(out[1].([]byte)), nil
54+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package evm
2+
3+
import (
4+
"encoding/hex"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/oasisprotocol/oasis-core/go/common/cbor"
10+
)
11+
12+
func TestPrecompileResult(t *testing.T) {
13+
testCases := []struct {
14+
name string
15+
result string
16+
statusCode uint32
17+
module string
18+
}{
19+
// https://github.com/oasisprotocol/nexus/issues/1093
20+
{
21+
name: "failed precompile",
22+
result: "000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000004636f726500000000000000000000000000000000000000000000000000000000",
23+
statusCode: 10,
24+
module: "core",
25+
},
26+
{
27+
name: "success precompile",
28+
result: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001f600000000000000000000000000000000000000000000000000000000000000",
29+
statusCode: 0,
30+
module: "",
31+
},
32+
}
33+
34+
for _, tc := range testCases {
35+
t.Run(tc.name, func(t *testing.T) {
36+
// Prepare the precompile result.
37+
result, err := hex.DecodeString(tc.result)
38+
require.NoError(t, err)
39+
40+
// Test unmarshalling.
41+
statusCode, module, err := EVMMaybeUnmarshalPrecompileResult(cbor.Marshal(result))
42+
require.NoError(t, err)
43+
require.Equal(t, tc.statusCode, statusCode)
44+
require.Equal(t, tc.module, module)
45+
})
46+
}
47+
}

analyzer/runtime/extract.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,23 @@ func ExtractRound(blockHeader nodeapi.RuntimeBlockHeader, txrs []nodeapi.Runtime
560560
for _, signer := range blockTransactionData.SignerData {
561561
registerTokenDecrease(blockData.TokenBalanceChanges, evm.NativeRuntimeTokenAddress, signer.Address, reckonedAmount)
562562
}
563+
564+
}
565+
566+
// Subcall precompile.
567+
if evm.IsSubcallPrecompile(to) && txr.Result.Ok != nil {
568+
// Try parsing precompile results.
569+
statusCode, module, err := evm.EVMMaybeUnmarshalPrecompileResult(txr.Result.Ok)
570+
if err != nil {
571+
logger.Error("error unmarshalling precompile result", "err", err)
572+
}
573+
if statusCode != 0 {
574+
blockTransactionData.Success = common.Ptr(false)
575+
blockTransactionData.Error = &TxError{
576+
Code: statusCode,
577+
Module: module,
578+
}
579+
}
563580
}
564581

565582
// TODO: maybe parse known token methods (ERC-20 etc)

analyzer/util/addresses/addresses.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,10 @@ func SliceFromSet(accounts map[apiTypes.Address]struct{}) []apiTypes.Address {
7070
}
7171
return addrs
7272
}
73+
74+
func FromBech32(bech32 string) apiTypes.Address {
75+
address := sdkTypes.NewAddressFromBech32(bech32)
76+
// Address is valid, otherwise the above call panics.
77+
addr, _ := FromSdkAddress(&address)
78+
return addr
79+
}

0 commit comments

Comments
 (0)