Skip to content

Commit

Permalink
Support encode/decode functions on hosted abi (#2348)
Browse files Browse the repository at this point in the history
* runtime: export `ethereum_encode`

* runtime: export `ethereum_decode`

* integration-tests: rename `big-decimal` to `host-exports`

* integration-tests: add ethereum abi to host-exports

* integration-tests: refactor ethereum abi and add another case

* integration-tests: bump `graph-ts` to new master branch hash
  • Loading branch information
evaporei authored Apr 14, 2021
1 parent fec450a commit ff6075a
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 72 deletions.
19 changes: 18 additions & 1 deletion runtime/wasm/src/host_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use crate::{
module::IntoTrap,
UnresolvedContractCall,
};
use ethabi::{Address, Token};
use ethabi::param_type::Reader;
use ethabi::{decode, encode, Address, Token};
use graph::bytes::Bytes;
use graph::components::ethereum::*;
use graph::components::store::EntityKey;
Expand Down Expand Up @@ -812,6 +813,22 @@ pub(crate) fn bytes_to_string(logger: &Logger, bytes: Vec<u8>) -> String {
s.trim_end_matches('\u{0000}').to_string()
}

pub(crate) fn ethereum_encode(token: Token) -> Result<Vec<u8>, anyhow::Error> {
Ok(encode(&[token]))
}

pub(crate) fn ethereum_decode(types: String, data: Vec<u8>) -> Result<Token, anyhow::Error> {
let param_types =
Reader::read(&types).or_else(|e| Err(anyhow::anyhow!("Failed to read types: {}", e)))?;

decode(&[param_types], &data)
// The `.pop().unwrap()` here is ok because we're always only passing one
// `param_types` to `decode`, so the returned `Vec` has always size of one.
// We can't do `tokens[0]` because the value can't be moved out of the `Vec`.
.map(|mut tokens| tokens.pop().unwrap())
.context("Failed to decode")
}

#[test]
fn test_string_to_h160_with_0x() {
assert_eq!(
Expand Down
28 changes: 28 additions & 0 deletions runtime/wasm/src/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,9 @@ impl WasmInstance {
})?;
}

link!("ethereum.encode", ethereum_encode, params_ptr);
link!("ethereum.decode", ethereum_decode, params_ptr, data_ptr);

link!("abort", abort, message_ptr, file_name_ptr, line, column);

link!("store.get", store_get, "host_export_store_get", entity, id);
Expand Down Expand Up @@ -1369,6 +1372,31 @@ impl WasmInstanceContext {
self.ctx.host_exports.log_log(&self.ctx.logger, level, msg)
}

/// function encode(token: ethereum.Value): Bytes | null
fn ethereum_encode(
&mut self,
token_ptr: AscPtr<AscEnum<EthereumValueKind>>,
) -> Result<AscPtr<Uint8Array>, DeterministicHostError> {
let data = host_exports::ethereum_encode(self.asc_get(token_ptr)?);
// return `null` if it fails
data.map(|bytes| self.asc_new(&*bytes))
.unwrap_or(Ok(AscPtr::null()))
}

/// function decode(types: String, data: Bytes): ethereum.Value | null
fn ethereum_decode(
&mut self,
types_ptr: AscPtr<AscString>,
data_ptr: AscPtr<Uint8Array>,
) -> Result<AscPtr<AscEnum<EthereumValueKind>>, DeterministicHostError> {
let result =
host_exports::ethereum_decode(self.asc_get(types_ptr)?, self.asc_get(data_ptr)?);
// return `null` if it fails
result
.map(|param| self.asc_new(&param))
.unwrap_or(Ok(AscPtr::null()))
}

/// function arweave.transactionData(txId: string): Bytes | null
fn arweave_transaction_data(
&mut self,
Expand Down
65 changes: 0 additions & 65 deletions tests/integration-tests/big-decimal/src/mapping.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "big-decimal",
"name": "host-exports",
"version": "0.1.0",
"scripts": {
"build-contracts": "../common/build-contracts.sh",
"codegen": "graph codegen",
"test": "yarn build-contracts && truffle test --compile-none --network test",
"create:test": "graph create test/big-decimal --node $GRAPH_NODE_ADMIN_URI",
"deploy:test": "graph deploy test/big-decimal --ipfs $IPFS_URI --node $GRAPH_NODE_ADMIN_URI"
"create:test": "graph create test/host-exports --node $GRAPH_NODE_ADMIN_URI",
"deploy:test": "graph deploy test/host-exports --ipfs $IPFS_URI --node $GRAPH_NODE_ADMIN_URI"
},
"devDependencies": {
"@graphprotocol/graph-cli": "https://github.com/graphprotocol/graph-cli#master",
Expand Down
125 changes: 125 additions & 0 deletions tests/integration-tests/host-exports/src/mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { Trigger } from "../generated/Contract/Contract";
import { Address, BigDecimal, BigInt, ethereum } from "@graphprotocol/graph-ts";

// Test that host exports work in globals.
let one = BigDecimal.fromString("1");

export function handleTrigger(event: Trigger): void {
testBigDecimal();
testEthereumAbi();
}

function testBigDecimal(): void {
// There are 35 digits after the dot.
// big_decimal exponent will be: -35 - 6109 = -6144.
// Minimum exponent is: -6143.
// So 1 digit will be removed, the 8, and the 6 will be rounded to 7.
let small = BigDecimal.fromString("0.99999999999999999999999999999999968");

small.exp -= BigInt.fromI32(6109);

// Round-trip through the node so it truncates.
small = small * new BigDecimal(BigInt.fromI32(1));

assert(small.exp == BigInt.fromI32(-6143), "wrong exp");

// This has 33 nines and the 7 which was rounded from 6.
assert(
small.digits ==
BigDecimal.fromString("9999999999999999999999999999999997").digits,
"wrong digits " + small.digits.toString()
);

// This has 35 nines, but we round to 34 decimal digits.
let big = BigDecimal.fromString("99999999999999999999999999999999999");

// This has 35 zeros.
let rounded = BigDecimal.fromString("100000000000000000000000000000000000");

assert(big == rounded, "big not equal to rounded");

// This has 35 eights, but we round to 34 decimal digits.
let big2 = BigDecimal.fromString("88888888888888888888888888888888888");

// This has 33 eights.
let rounded2 = BigDecimal.fromString("88888888888888888888888888888888890");

assert(big2 == rounded2, "big2 not equal to rounded2 " + big2.toString());

// Test big decimal division.
assert(one / BigDecimal.fromString("10") == BigDecimal.fromString("0.1"));

// Test big int fromString
assert(BigInt.fromString("8888") == BigInt.fromI32(8888));

let bigInt = BigInt.fromString("8888888888888888");

// Test big int bit or
assert(
(bigInt | BigInt.fromI32(42)) == BigInt.fromString("8888888888888890")
);

// Test big int bit and
assert((bigInt & BigInt.fromI32(42)) == BigInt.fromI32(40));

// Test big int left shift
assert(bigInt << 6 == BigInt.fromString("568888888888888832"));

// Test big int right shift
assert(bigInt >> 6 == BigInt.fromString("138888888888888"));
}

function testEthereumAbi(): void {
ethereumAbiSimpleCase();
ethereumAbiComplexCase();
}

function ethereumAbiSimpleCase(): void {
let address = ethereum.Value.fromAddress(Address.fromString("0x0000000000000000000000000000000000000420"));

let encoded = ethereum.encode(address)!;

let decoded = ethereum.decode("address", encoded);

assert(address.toAddress() == decoded.toAddress(), "address ethereum encoded does not equal the decoded value");
}

function ethereumAbiComplexCase(): void {
let address = ethereum.Value.fromAddress(Address.fromString("0x0000000000000000000000000000000000000420"));
let bigInt1 = ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(62));
let bigInt2 = ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(63));
let bool = ethereum.Value.fromBoolean(true);

let fixedSizedArray = ethereum.Value.fromFixedSizedArray([
bigInt1,
bigInt2
]);

let tupleArray: Array<ethereum.Value> = [
fixedSizedArray,
bool
];

let tuple = ethereum.Value.fromTuple(tupleArray as ethereum.Tuple);

let token: Array<ethereum.Value> = [
address,
tuple
];

let encoded = ethereum.encode(ethereum.Value.fromTuple(token as ethereum.Tuple))!;

let decoded = ethereum.decode("(address,(uint256[2],bool))", encoded).toTuple();

let decodedAddress = decoded[0].toAddress();
let decodedTuple = decoded[1].toTuple();
let decodedFixedSizedArray = decodedTuple[0].toArray();
let decodedBigInt1 = decodedFixedSizedArray[0].toBigInt();
let decodedBigInt2 = decodedFixedSizedArray[1].toBigInt();
let decodedBool = decodedTuple[1].toBoolean();

assert(address.toAddress() == decodedAddress, "address ethereum encoded does not equal the decoded value");
assert(bigInt1.toBigInt() == decodedBigInt1, "uint256[0] ethereum encoded does not equal the decoded value");
assert(bigInt2.toBigInt() == decodedBigInt2, "uint256[1] ethereum encoded does not equal the decoded value");
assert(bool.toBoolean() == decodedBool, "boolean ethereum encoded does not equal the decoded value");
}
2 changes: 1 addition & 1 deletion tests/integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"private": true,
"workspaces": [
"arweave-and-3box",
"big-decimal",
"data-source-context",
"data-source-revert",
"fatal-error",
"ganache-reverts",
"host-exports",
"non-fatal-errors",
"overloaded-contract-functions",
"remove-then-update",
Expand Down
2 changes: 1 addition & 1 deletion tests/integration-tests/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@

"@graphprotocol/graph-ts@https://github.com/graphprotocol/graph-ts#master":
version "0.20.0"
resolved "https://github.com/graphprotocol/graph-ts#c18868afe9666a2283cbaa54692ea78608e8a50d"
resolved "https://github.com/graphprotocol/graph-ts#56adb62d9e4233c6fc6c38bc0519a8a566afdd9e"
dependencies:
assemblyscript "https://github.com/AssemblyScript/assemblyscript#36040d5b5312f19a025782b5e36663823494c2f3"

Expand Down
2 changes: 1 addition & 1 deletion tests/tests/parallel_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ const DEFAULT_N_CONCURRENT_TESTS: usize = 15;
/// All integration tests subdirectories to run
pub const INTEGRATION_TESTS_DIRECTORIES: [&str; 9] = [
// "arweave-and-3box",
"big-decimal",
"data-source-context",
"data-source-revert",
"fatal-error",
"ganache-reverts",
"host-exports",
"non-fatal-errors",
"overloaded-contract-functions",
"remove-then-update",
Expand Down

0 comments on commit ff6075a

Please sign in to comment.