Skip to content
Permalink
Browse files

Hamdi/fee challenge (#87)

* removed extra indent

* added challenge. Need to refactor contract into multiple libraries

* explicit uint sizes

* open-zeppelin to 2.0

* refactors. contract deploys but tests broken

* refactored contracts

* refactored migrations

* tests reflect changes

* added tests. fixed contract bugs

* removed redundant check in finalizeExit. updated docs.

* added stuff to docs

* fee challenge also for deposits. added deposit test

* refactored challenges into one. emmitted events in fee challenge. updated docs and tests

* renamed folder

* changed imports for the rename

* libraries are now internal. Gas cost is sufficient

* some library functions should be private

* fixed typo

* updated docs again

* formatting

* formatting and typo

* docs

* check to make sure challenge is successful

* added check for fee challenge and updated docs

* challenges only need the blknum,txindex

* matching inputs bug
  • Loading branch information...
hamdiallam committed Dec 13, 2018
1 parent 1b58d54 commit 74047018dd36b0bf76adbb9b350e45aa285f1974

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -0,0 +1,53 @@
pragma solidity ^0.4.24;

library BytesUtil {
uint8 constant WORD_SIZE = 32;

// @param _bytes raw bytes that needs to be slices
// @param start start of the slice relative to `_bytes`
// @param len length of the sliced byte array
function slice(bytes _bytes, uint start, uint len)
internal
pure
returns (bytes)
{
require(_bytes.length - start >= len, "slice out of bounds");

if (_bytes.length == len)
return _bytes;

bytes memory result;
uint src;
uint dest;
assembly {
// memory & free memory pointer
result := mload(0x40)
mstore(result, len) // store the size in the prefix
mstore(0x40, add(result, and(add(add(0x20, len), 0x1f), not(0x1f)))) // padding

// pointers
src := add(start, add(0x20, _bytes))
dest := add(0x20, result)
}

// copy as many word sizes as possible
for(; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}

src += WORD_SIZE;
dest += WORD_SIZE;
}

// copy remaining bytes
uint mask = 256 ** (WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask)) // zero out src
let destpart := and(mload(dest), mask) // retrieve the bytes
mstore(dest, or(destpart, srcpart))
}

return result;
}
}
@@ -0,0 +1,9 @@
pragma solidity ^0.4.24;

import "./BytesUtil.sol";

contract BytesUtil_Test {
using BytesUtil for bytes;

function slice(bytes a , uint start, uint len) public pure returns (bytes) { return a.slice(start, len); }
}
@@ -7,25 +7,24 @@ library PriorityQueue {
using SafeMath for uint256;

function insert(uint256[] storage heapList, uint256 k)
public
internal
{
heapList.push(k);
if (heapList.length > 1)
percUp(heapList, heapList.length.sub(1));
}

function getMin(uint256[] storage heapList)
public
internal
view
returns (uint256)
{
require(heapList.length > 0, "empty queue");

return heapList[0];
}

function delMin(uint256[] storage heapList)
public
internal
returns (uint256)
{
require(heapList.length > 0, "empty queue");
@@ -91,12 +90,4 @@ library PriorityQueue {
// place value in the correct child
if (position != i) heapList[i] = value;
}

function currentSize(uint256[] storage heapList)
internal
view
returns (uint256)
{
return heapList.length;
}
}
@@ -11,5 +11,5 @@ contract PriorityQueue_Test {
function insert(uint256 k) public { heapList.insert(k); }
function getMin() public view returns (uint256) { return heapList.getMin(); }
function delMin() public { heapList.delMin(); }
function currentSize() public view returns (uint256) { return heapList.currentSize(); }
function currentSize() public view returns (uint256) { return heapList.length; }
}
@@ -0,0 +1,72 @@
pragma solidity ^0.4.24;

import "./BytesUtil.sol";

// from https://tendermint.com/docs/spec/blockchain/encoding.html#merkle-trees
library TMSimpleMerkleTree {
using BytesUtil for bytes;

// @param leaf a leaf of the tree
// @param index position of this leaf in the tree that is zero indexed
// @param rootHash block header of the merkle tree
// @param proof sequence of 32-byte hashes from the leaf up to, but excluding, the root
// @paramt total total # of leafs in the tree
function checkMembership(bytes32 leaf, uint256 index, bytes32 rootHash, bytes proof, uint256 total)
internal
pure
returns (bool)
{
// variable size Merkle tree, but proof must consist of 32-byte hashes
require(proof.length % 32 == 0, "Incorrect proof length");

bytes32 computedHash = computeHashFromAunts(index, total, leaf, proof);
return computedHash == rootHash;
}

// helper function as described in the tendermint docs
function computeHashFromAunts(uint256 index, uint256 total, bytes32 leaf, bytes innerHashes)
private
pure
returns (bytes32)
{
require(index < total, "Index must be less than total number of leaf nodes");
require(total > 0, "Must have at least one leaf node");

if (total == 1) {
require(innerHashes.length == 0, "Simple Tree with 1 txn should have no innerHashes");
return leaf;
}
require(innerHashes.length != 0, "Simple Tree with > 1 txn should have innerHashes");

uint256 numLeft = (total + 1) / 2;
bytes32 proofElement;

// prepend 0x20 byte literal to hashes
// tendermint prefixes intermediate hashes with 0x20 bytes literals
// before hashing them.
bytes memory b = new bytes(1);
assembly {
let memPtr := add(b, 0x20)
mstore8(memPtr, 0x20)
}

if (index < numLeft) {
bytes32 leftHash = computeHashFromAunts(index, numLeft, leaf, innerHashes.slice( 0, innerHashes.length - 32));
uint innerHashesMemOffset = innerHashes.length - 32;
assembly {
// get the last 32-byte hash from innerHashes array
proofElement := mload(add(add(innerHashes, 0x20), innerHashesMemOffset))
}

return sha256(abi.encodePacked(b, leftHash, b, proofElement));
} else {
bytes32 rightHash = computeHashFromAunts(index-numLeft, total-numLeft, leaf, innerHashes.slice(0, innerHashes.length - 32));
innerHashesMemOffset = innerHashes.length - 32;
assembly {
// get the last 32-byte hash from innerHashes array
proofElement := mload(add(add(innerHashes, 0x20), innerHashesMemOffset))
}
return sha256(abi.encodePacked(b, proofElement, b, rightHash));
}
}
}
@@ -0,0 +1,15 @@
pragma solidity ^0.4.24;

import "./TMSimpleMerkleTree.sol";

contract TMSimpleMerkleTree_Test {
using TMSimpleMerkleTree for bytes32;

function checkMembership(bytes32 leaf, uint256 index, bytes32 rootHash, bytes proof, uint256 total)
public
pure
returns (bool)
{
return leaf.checkMembership(index, rootHash, proof, total);
}
}
@@ -1,79 +1,17 @@
pragma solidity ^0.4.24;

import "openzeppelin-solidity/contracts/ECRecovery.sol";
import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol";
import "./BytesUtil.sol";

library Validator {
uint8 constant WORD_SIZE = 32;

// @param leaf a leaf of the tree
// @param index position of this leaf in the tree that is zero indexed
// @param rootHash block header of the merkle tree
// @param proof sequence of 32-byte hashes from the leaf up to, but excluding, the root
function checkMembership(bytes32 leaf, uint256 index, bytes32 rootHash, bytes proof, uint256 total)
internal
pure
returns (bool)
{
// variable size Merkle tree, but proof must consist of 32-byte hashes
require(proof.length % 32 == 0, "Incorrect proof length");

bytes32 computedHash = computeHashFromAunts(index, total, leaf, proof);
return computedHash == rootHash;
}

// from https://tendermint.com/docs/spec/blockchain/encoding.html#merkle-trees
function computeHashFromAunts(uint256 index, uint256 total, bytes32 leaf, bytes innerHashes)
internal
pure
returns (bytes32)
{
require(index < total, "Index must be less than total number of leaf nodes");
require(total > 0, "Must have at least one leaf node");

if (total == 1) {
require(innerHashes.length == 0, "Simple Tree with 1 txn should have no innerHashes");
return leaf;
}
require(innerHashes.length != 0, "Simple Tree with > 1 txn should have innerHashes");

uint256 numLeft = (total + 1) / 2;
bytes32 proofElement;

// prepend 0x20 byte literal to hashes
// tendermint prefixes intermediate hashes with 0x20 bytes literals
// before hashing them.
bytes memory b = new bytes(1);
assembly {
let memPtr := add(b, 0x20)
mstore8(memPtr, 0x20)
}

if (index < numLeft) {
bytes32 leftHash = computeHashFromAunts(index, numLeft, leaf, slice(innerHashes, 0, innerHashes.length - 32));
uint innerHashesMemOffset = innerHashes.length - 32;
assembly {
// get the last 32-byte hash from innerHashes array
proofElement := mload(add(add(innerHashes, 0x20), innerHashesMemOffset))
}

return sha256(abi.encodePacked(b, leftHash, b, proofElement));
} else {
bytes32 rightHash = computeHashFromAunts(index-numLeft, total-numLeft, leaf, slice(innerHashes, 0, innerHashes.length - 32));
innerHashesMemOffset = innerHashes.length - 32;
assembly {
// get the last 32-byte hash from innerHashes array
proofElement := mload(add(add(innerHashes, 0x20), innerHashesMemOffset))
}
return sha256(abi.encodePacked(b, proofElement, b, rightHash));
}
}
using BytesUtil for bytes;

// @param txHash transaction hash
// @param rootHash block header of the merkle tree
// @param input1 indicator for the second input
// @param sigs transaction signatures
// @notice when one input is present, we require it to be the first input by convention
function checkSigs(bytes32 txHash, bytes32 confirmationHash, bool input1, bytes sig0, bytes sig1, bytes confirmSignatures)
function checkSignatures(bytes32 txHash, bytes32 confirmationHash, bool input1, bytes sig0, bytes sig1, bytes confirmSignatures)
internal
pure
returns (bool)
@@ -86,8 +24,8 @@ library Validator {
address recoveredAddr0 = recover(txHash, sig0);
address recoveredAddr1 = recover(txHash, sig1);

return recoveredAddr0 == recover(confirmationHash, slice(confirmSignatures, 0, 65))
&& recoveredAddr1 == recover(confirmationHash, slice(confirmSignatures, 65, 65))
return recoveredAddr0 == recover(confirmationHash, confirmSignatures.slice(0, 65))
&& recoveredAddr1 == recover(confirmationHash, confirmSignatures.slice(65, 65))
&& recoveredAddr0 != address(0) && recoveredAddr1 != address(0);
}

@@ -103,58 +41,6 @@ library Validator {
pure
returns (address)
{

hash = ECRecovery.toEthSignedMessageHash(hash);
return ECRecovery.recover(hash, sig);
}

/* Helpers */

// @param _bytes raw bytes that needs to be slices
// @param start start of the slice relative to `_bytes`
// @param len length of the sliced byte array
function slice(bytes _bytes, uint start, uint len)
internal
pure
returns (bytes)
{
require(_bytes.length - start >= len, "slice out of bounds");

if (_bytes.length == len)
return _bytes;

bytes memory result;
uint src;
uint dest;
assembly {
// memory & free memory pointer
result := mload(0x40)
mstore(result, len) // store the size in the prefix
mstore(0x40, add(result, and(add(add(0x20, len), 0x1f), not(0x1f)))) // padding

// pointers
src := add(start, add(0x20, _bytes))
dest := add(0x20, result)
}

// copy as many word sizes as possible
for(; len >= WORD_SIZE; len -= WORD_SIZE) {
assembly {
mstore(dest, mload(src))
}

src += WORD_SIZE;
dest += WORD_SIZE;
}

// copy remaining bytes
uint mask = 256 ** (WORD_SIZE - len) - 1;
assembly {
let srcpart := and(mload(src), not(mask)) // zero out src
let destpart := and(mload(dest), mask) // retrieve the bytes
mstore(dest, or(destpart, srcpart))
}

return result;
return ECDSA.recover(ECDSA.toEthSignedMessageHash(hash), sig);
}
}
@@ -7,23 +7,14 @@ import "./Validator.sol";
*/

contract Validator_Test {

using Validator for bytes32;

function checkMembership(bytes32 leaf, uint256 index, bytes32 rootHash, bytes proof, uint256 total)
public
pure
returns (bool)
{
return leaf.checkMembership(index, rootHash, proof, total);
}

function checkSigs(bytes32 txHash, bytes32 confirmationHash, bool input1, bytes sig0, bytes sig1, bytes confirmSignatures)
function checkSignatures(bytes32 txHash, bytes32 confirmationHash, bool input1, bytes sig0, bytes sig1, bytes confirmSignatures)
public
pure
returns (bool)
{
return txHash.checkSigs(confirmationHash, input1, sig0, sig1, confirmSignatures);
return txHash.checkSignatures(confirmationHash, input1, sig0, sig1, confirmSignatures);
}

function recover(bytes32 hash, bytes sig)
@@ -33,12 +24,4 @@ contract Validator_Test {
{
return hash.recover(sig);
}

function slice(bytes _bytes, uint start, uint len)
public
pure
returns (bytes)
{
return Validator.slice(_bytes, start, len);
}
}
Oops, something went wrong.

0 comments on commit 7404701

Please sign in to comment.
You can’t perform that action at this time.