Skip to content

Commit

Permalink
fix Code.getOpcode & Code.toUint, tested
Browse files Browse the repository at this point in the history
  • Loading branch information
peara committed Jun 20, 2019
1 parent cc5c4dd commit 6b9d5e0
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 32 deletions.
79 changes: 49 additions & 30 deletions contracts/EVMCode.slb
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,71 @@ pragma experimental ABIEncoderV2;

library EVMCode {
struct Code {
uint length; // length is zero-based
mapping (uint => bytes32) fragments;
mapping (uint => bool) known;
uint length; // length is zero-based, this is length of fragments array, not the actually length of code
// TODO improve this
// we have to use memory for EVMCode, so this must be an fixed-size array, not a mapping or dynamic-size array
// although 50 should me enough for most cases
RawCode[50] fragments;
}

struct RawCode {
uint pos;
bytes32 value;
uint value;
}

// TODO no more external address
// function fromAddress(address codeAddress) internal view returns (Code memory code) {
// code.codeAddress = codeAddress;
// uint codeSize;
// assembly {
// codeSize := extcodesize(codeAddress)
// }
function fromArray(RawCode[50] memory codes, uint length) internal pure returns (Code memory code) {
code.length = length;
for (uint i = 0; i < length; i++) {
// require to submit array in ascending order
require(i == 0 || codes[i].pos > codes[i-1].pos, "wrong code order");
code.fragments[i] = RawCode(codes[i].pos, codes[i].value);
}
}

// code.length = codeSize;
// }
/**
* @dev find the position of the required word using binary search
*/
function findFragment(Code memory self, uint wordPos) internal view returns (uint pos) {
uint left = 0;
uint right = self.length;
uint mid;

function fromArray(RawCode[] memory codes, uint length) internal pure return (Code memory code) {
code.length = length;
for (uint i = 0; i < codes.length; i++) {
code.fragments[codes[i].pos] = codes[i].value;
code.known[codes[i].pos] = true;
while (left < right) {
mid = (left + right) >> 1;
if (self.fragments[mid].pos == wordPos) {
return mid;
}
if (self.fragments[mid].pos > wordPos) {
right = mid;
} else {
left = mid+1;
}
}
return uint(-1);
}

/**
* @dev return the opcode at position if known. Otherwise return fe, which is invalid opcode
*
* @params uint pos: position of the required byte, from 0 to 31
*/
function getOpcodeAt(Code memory self, uint pos) internal view returns (uint8 opcode) {
uint fragmentPos = pos >> 5;
function getOpcodeAt(Code memory self, uint pos) internal returns (uint8 opcode) {
uint wordPos = pos >> 5;
uint fragmentPos = findFragment(self, wordPos);

if (code.known[pos]) {
if (fragmentPos != uint(-1)) {
// 0x00000000 11111111 22222222 ... (256 bit)
// to get a byte at position x, we need to shift all these byte at position > x + 8
// x = 0 -> shift 248 = (32 - 1) * 8
// x = 1 -> shift 242 = (32 - 2) * 8
// x = 31 -> shift 0 = (32 - 32) * 8
return uint8(code.fragments[fragmentPos] >> ((31 - pos % 32) * 8));
return uint8(self.fragments[fragmentPos].value >> ((31 - (pos % 32)) * 8));
}
return 0xfe;
}

// function toBytes(Code memory self, uint pos, uint numBytes) internal view returns (bytes memory bts) {
function toBytes(Code memory self) internal view returns (bytes memory bts) {
return "0x";
// TODO no more code address
// address codeContractAddress = self.codeAddress;

Expand All @@ -75,29 +91,32 @@ library EVMCode {
// this is the behaviour we want
assert(32 >= numBytes && numBytes > 0);

// TODO no more code address
// 2 cases:
// - return data fit in a fragment
// - return data span 2 fragments
uint fragmentPos = pos >> 5;
uint wordPos = pos >> 5;
uint fragmentPos = findFragment(self, wordPos);
require(fragmentPos != uint(-1), 'Code not found');

// only need to retrieve 32 bytes
if (fragmentPos == ((pos + numBytes) >> 5)) {
if (fragmentPos == ((pos + numBytes - 1) >> 5)) {
// retrieve the word which contains the required data
// shift left to strip unnecessary data on the left
uint temp = code.fragments[fragmentPos] << ((pos % 32) * 8);
uint temp = self.fragments[fragmentPos].value << ((pos % 32) * 8);
// then shift right to strip unnecessary data on the right
return temp >> ((32 - numBytes) * 8));
return temp >> ((32 - numBytes) * 8);
}

// require fetching an additional 32 bytes
require(self.fragments[fragmentPos+1].pos == self.fragments[fragmentPos].pos + 1, 'Code not found 2');
// the left part should be the rightmost part of the first word
// to retrieve: shift left to strip, then shift back to correct position in numBytes
uint left = (code.fragments[fragmentsPos] << ((pos % 32) * 8)) >> ((32 - numBytes) * 8);
uint left = (self.fragments[fragmentPos].value << ((pos % 32) * 8)) >> ((32 - numBytes) * 8);
// the right part should be the leftmost part of the second word
// to retrieve: shift all the way to the right
// 64 - numBytes - (pos % 32) = 32 - (numBytes - (32 - (pos % 32))) = word_length - (required_length - (left_path_length))
// numBytes + (pos % 32) >= 32, if not, then it requires only 1 byte
uint right = (code.fragments[fragmentsPos + 1] >> (64 - numBytes - (pos % 32)) * 8);
uint right = (self.fragments[fragmentPos + 1].value >> (64 - numBytes - (pos % 32)) * 8);

return left | right;
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/EthereumRuntime.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ contract EthereumRuntime is HydratedRuntime {
evm.caller = DEFAULT_CALLER;
evm.target = DEFAULT_CONTRACT_ADDRESS;

evm.code = EVMCode.fromAddress(img.code);
// evm.code = EVMCode.fromAddress(img.code);
evm.stack = EVMStack.fromArray(img.stack);
evm.mem = EVMMemory.fromArray(img.mem);

Expand Down
2 changes: 1 addition & 1 deletion contracts/PlasmaVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ contract PlasmaVerifier is Verifier {
);
evm.data = input.callData;
evm.gas = 0xffffffff;
evm.code = EVMCode.fromAddress(input.spendingCondition);
// evm.code = EVMCode.fromAddress(input.spendingCondition);
evm.stack = EVMStack.newStack();
evm.mem = EVMMemory.newMemory();

Expand Down
57 changes: 57 additions & 0 deletions contracts/mocks/EVMCodeMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
pragma solidity ^0.5.2;
pragma experimental ABIEncoderV2;

import "../EVMCode.slb";
import "../MemOps.slb";


contract EVMCodeMock {
using EVMCode for EVMCode.Code;
// below is 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
uint data0 = 1780731860627700044960722568376592200742329637303199754547598369979440671;
// below is 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f
uint data1 = 14532552714582660066924456880521368950258152170031413196862950297402215317055;
event FoundByte(uint found);

function testFromArrayGetOpcode() public {
EVMCode.Code memory code;
EVMCode.RawCode[50] memory rawCodes;

rawCodes[0] = EVMCode.RawCode(0, data0);
rawCodes[1] = EVMCode.RawCode(1, data1);
code = EVMCode.fromArray(rawCodes, 2);
for (uint i = 0; i < 64; i++) {
uint opcode = code.getOpcodeAt(i);
require(opcode == i, 'get wrong opcode');
}
}

function testError_FromArrayWrongOrder() public {
EVMCode.Code memory code;
EVMCode.RawCode[50] memory rawCodes;
rawCodes[0] = EVMCode.RawCode(2, 1);
rawCodes[1] = EVMCode.RawCode(1, 1);
code = EVMCode.fromArray(rawCodes, 2);
}

function testToUint() public {
EVMCode.Code memory code;
EVMCode.RawCode[50] memory rawCodes;

rawCodes[0] = EVMCode.RawCode(0, data0);
rawCodes[1] = EVMCode.RawCode(1, data1);
code = EVMCode.fromArray(rawCodes, 2);

uint res = 0;
uint got;

for (uint pos = 0; pos < 64; pos++) {
res = 0;
for (uint len = 1; (len <= 32) && (pos + len <= 64); len++) {
res = (res << 8) | (pos + len - 1);
got = code.toUint(pos, len);
require(res == got, 'get wrong uint');
}
}
}
}
24 changes: 24 additions & 0 deletions test/contracts/evmCode.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const { deployContract } = require('./../helpers/utils');

const EVMCodeMock = artifacts.require('EVMCodeMock.sol');
const assert = require('assert');

contract('TestEVMCode', function () {
let evmCode;

before(async () => {
evmCode = await deployContract(EVMCodeMock);
});

it('fromArray', async function () {
await evmCode.testFromArrayGetOpcode();
});

it('fromArray 2 element wrong order', async function () {
await assert.rejects(evmCode.testError_FromArrayWrongOrder());
});

it('toUint', async function () {
await evmCode.testToUint();
});
});

0 comments on commit 6b9d5e0

Please sign in to comment.