Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
120 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
const { assertInvalidOpcode } = require('./helpers/assertThrow') | ||
const { encodeScript } = require('./helpers/evmScript') | ||
const ExecutionTarget = artifacts.require('ExecutionTarget') | ||
const Executor = artifacts.require('Executor') | ||
|
||
contract('EVM call script', accounts => { | ||
let executor, executionTarget = {} | ||
|
||
before(async () => { | ||
executor = await Executor.new() | ||
}) | ||
|
||
beforeEach(async () => { | ||
executionTarget = await ExecutionTarget.new() | ||
}) | ||
|
||
it('executes single action script', async () => { | ||
const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } | ||
const script = encodeScript([action]) | ||
|
||
await executor.execute(script) | ||
|
||
assert.equal(await executionTarget.counter(), 1, 'should have executed action') | ||
}) | ||
|
||
it('executes multi action script', async () => { | ||
const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } | ||
const script = encodeScript([action, action, action, action]) | ||
|
||
await executor.execute(script) | ||
|
||
assert.equal(await executionTarget.counter(), 4, 'should have executed action') | ||
}) | ||
|
||
it('executes multi action script to multiple addresses', async () => { | ||
const executionTarget2 = await ExecutionTarget.new() | ||
|
||
const action = { to: executionTarget.address, calldata: executionTarget.contract.execute.getData() } | ||
const action2 = { to: executionTarget2.address, calldata: executionTarget2.contract.execute.getData() } | ||
|
||
const script = encodeScript([action2, action, action2, action, action2]) | ||
|
||
await executor.execute(script) | ||
|
||
assert.equal(await executionTarget.counter(), 2, 'should have executed action') | ||
assert.equal(await executionTarget2.counter(), 3, 'should have executed action') | ||
}) | ||
|
||
it('executes with parameters', async () => { | ||
const action = { to: executionTarget.address, calldata: executionTarget.contract.setCounter.getData(101) } | ||
const script = encodeScript([action]) | ||
|
||
await executor.execute(script) | ||
|
||
assert.equal(await executionTarget.counter(), 101, 'should have set counter') | ||
}) | ||
|
||
it('execution fails if one call fails', async () => { | ||
const action1 = { to: executionTarget.address, calldata: executionTarget.contract.setCounter.getData(101) } | ||
const action2 = { to: executionTarget.address, calldata: executionTarget.contract.failExecute.getData() } | ||
|
||
const script = encodeScript([action1, action2]) | ||
|
||
return assertInvalidOpcode(async () => { | ||
await executor.execute(script) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
const abi = require('ethereumjs-abi') | ||
|
||
module.exports = { | ||
// Encodes an array of actions ({ to: address, calldata: bytes}) into the EVM script format | ||
// Concatenates [ 20 bytes (address) ] + [ 4 bytes (uint32: calldata length) ] + [ calldataLength bytes (payload) ] | ||
encodeScript: actions => { | ||
return actions.reduce((script, { to, calldata }) => { | ||
const addr = abi.rawEncode(['address'], [to]).toString('hex') | ||
const length = abi.rawEncode(['uint256'], [(calldata.length - 2) / 2]).toString('hex') | ||
|
||
// Remove 12 first 0s of padding for addr and 28 0s for uint32 | ||
return script + addr.slice(24) + length.slice(56) + calldata.slice(2) | ||
}, '0x') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
pragma solidity 0.4.15; | ||
|
||
contract ExecutionTarget { | ||
uint public counter; | ||
|
||
function execute() { | ||
counter += 1; | ||
Executed(counter); | ||
} | ||
|
||
function failExecute() { | ||
revert(); | ||
} | ||
|
||
function setCounter(uint x) { | ||
counter = x; | ||
} | ||
|
||
event Executed(uint x); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
pragma solidity 0.4.15; | ||
|
||
import "../../contracts/common/EVMCallScript.sol"; | ||
|
||
contract Executor is EVMCallScriptRunner, EVMCallScriptDecoder { | ||
function execute(bytes script) { | ||
runScript(script); | ||
} | ||
|
||
function getActionsCount(bytes script) constant returns (uint256) { | ||
return getScriptActionsCount(script); | ||
} | ||
|
||
function getAction(bytes script, uint256 i) constant returns (address, bytes) { | ||
return getScriptAction(script, i); | ||
} | ||
} |