Skip to content

Commit

Permalink
Add new Solidity function type support. Resolve #604
Browse files Browse the repository at this point in the history
  • Loading branch information
Nashatyrev committed Mar 2, 2017
1 parent f748aa9 commit 14a19cb
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 4 deletions.
Expand Up @@ -200,6 +200,8 @@ public static Function fromSignature(String funcName, String[] paramTypes, Strin
}
return ret;
}


}

public static class Contract {
Expand Down
Expand Up @@ -44,6 +44,7 @@ public static SolidityType getType(String typeName) {
if ("address".equals(typeName)) return new AddressType();
if ("string".equals(typeName)) return new StringType();
if ("bytes".equals(typeName)) return new BytesType();
if ("function".equals(typeName)) return new FunctionType();
if (typeName.startsWith("bytes")) return new Bytes32Type(typeName);
throw new RuntimeException("Unknown type: " + typeName);
}
Expand Down Expand Up @@ -404,4 +405,16 @@ public Object decode(byte[] encoded, int offset) {
}
}

public static class FunctionType extends Bytes32Type {
public FunctionType() {
super("function");
}

@Override
public byte[] encode(Object value) {
if (!(value instanceof byte[])) throw new RuntimeException("Expected byte[] value for FunctionType");
if (((byte[]) value).length != 24) throw new RuntimeException("Expected byte[24] for FunctionType");
return super.encode(ByteUtil.merge((byte[]) value, new byte[8]));
}
}
}
Expand Up @@ -59,6 +59,12 @@ public interface SolidityContract extends Contract {
*/
Object[] callConstFunction(Block callBlock, String functionName, Object... args);

/**
* Gets the contract function. This object can be passed as a call argument for another
* function with a function type parameter
*/
SolidityFunction getFunction(String name);

/**
* Returns the Solidity JSON ABI (Application Binary Interface)
*/
Expand Down
@@ -0,0 +1,13 @@
package org.ethereum.util.blockchain;

import org.ethereum.core.CallTransaction;

/**
* Created by Anton Nashatyrev on 02.03.2017.
*/
public interface SolidityFunction {

SolidityContract getContract();

CallTransaction.Function getInterface();
}
Expand Up @@ -486,6 +486,26 @@ private BlockchainImpl createBlockchain(Genesis genesis) {
return blockchain;
}

public class SolidityFunctionImpl implements SolidityFunction {
SolidityContractImpl contract;
CallTransaction.Function abi;

public SolidityFunctionImpl(SolidityContractImpl contract, CallTransaction.Function abi) {
this.contract = contract;
this.abi = abi;
}

@Override
public SolidityContract getContract() {
return contract;
}

@Override
public CallTransaction.Function getInterface() {
return abi;
}
}

public class SolidityContractImpl implements SolidityContract {
byte[] address;
public CompilationResult.ContractMetadata compiled;
Expand Down Expand Up @@ -525,7 +545,7 @@ public SolidityCallResult callFunction(String functionName, Object... args) {
@Override
public SolidityCallResult callFunction(long value, String functionName, Object... args) {
CallTransaction.Function function = contract.getByName(functionName);
byte[] data = function.encode(args);
byte[] data = function.encode(convertArgs(args));
SolidityCallResult res = new SolidityCallResultImpl(this, function);
submitNewTx(new PendingTx(null, BigInteger.valueOf(value), data, null, this, res));
return res;
Expand All @@ -542,7 +562,7 @@ public Object[] callConstFunction(Block callBlock, String functionName, Object..
CallTransaction.Function func = contract.getByName(functionName);
if (func == null) throw new RuntimeException("No function with name '" + functionName + "'");
Transaction tx = CallTransaction.createCallTransaction(0, 0, 100000000000000L,
Hex.toHexString(getAddress()), 0, func, args);
Hex.toHexString(getAddress()), 0, func, convertArgs(args));
tx.sign(new byte[32]);

Repository repository = getBlockchain().getRepository().getSnapshotTo(callBlock.getStateRoot()).startTracking();
Expand All @@ -564,6 +584,19 @@ public Object[] callConstFunction(Block callBlock, String functionName, Object..
}
}

private Object[] convertArgs(Object[] args) {
Object[] ret = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof SolidityFunction) {
SolidityFunction f = (SolidityFunction) args[i];
ret[i] = ByteUtil.merge(f.getContract().getAddress(), f.getInterface().encodeSignature());
} else {
ret[i] = args[i];
}
}
return ret;
}

@Override
public SolidityStorage getStorage() {
return new SolidityStorageImpl(getAddress());
Expand All @@ -585,6 +618,11 @@ public void call(byte[] callData) {
// Abstract and Solidity specific
throw new UnsupportedOperationException();
}

@Override
public SolidityFunction getFunction(String name) {
return new SolidityFunctionImpl(this, contract.getByName(name));
}
}

public class SolidityCallResultImpl extends SolidityCallResult {
Expand Down
Expand Up @@ -841,14 +841,42 @@ public void ecRecoverTest() throws Exception {
ByteUtil.bigIntegerToBytes(signature.r, 32),
ByteUtil.bigIntegerToBytes(signature.s, 32));

Assert.assertArrayEquals(key.getAddress(), (byte[])ret[0]);
Assert.assertArrayEquals(key.getAddress(), (byte[]) ret[0]);

ret = a.callConstFunction("f", hash,
ByteUtil.merge(new byte[] {1}, new byte[30], new byte[]{signature.v}),
ByteUtil.bigIntegerToBytes(signature.r, 32),
ByteUtil.bigIntegerToBytes(signature.s, 32));

Assert.assertArrayEquals(new byte[20], (byte[])ret[0]);
Assert.assertArrayEquals(new byte[20], (byte[]) ret[0]);
}

@Test
public void functionTypeTest() throws IOException, InterruptedException {
String contractA =
"contract A {" +
" int public res;" +
" function calc(int b, function (int a) external returns (int) f) external returns (int) {" +
" return f(b);" +
" }" +
" function fInc(int a) external returns (int) { return a + 1;}" +
" function fDec(int a) external returns (int) { return a - 1;}" +
" function test() {" +
" res = this.calc(111, this.fInc);" +
" }" +
"}";

StandaloneBlockchain bc = new StandaloneBlockchain();
SolidityContract a = bc.submitNewContract(contractA);
bc.createBlock();
a.callFunction("test");
bc.createBlock();
Assert.assertEquals(a.callConstFunction("res")[0], BigInteger.valueOf(112));

BigInteger r1 = (BigInteger) a.callConstFunction("calc", 222, a.getFunction("fInc"))[0];
Assert.assertEquals(223, r1.intValue());
BigInteger r2 = (BigInteger) a.callConstFunction("calc", 222, a.getFunction("fDec"))[0];
Assert.assertEquals(221, r2.intValue());
}

public static BlockchainImpl createBlockchain(Genesis genesis) {
Expand Down

0 comments on commit 14a19cb

Please sign in to comment.