Skip to content

Commit

Permalink
Merge pull request #81 from HarryR/HarryR/HTLC-improvements
Browse files Browse the repository at this point in the history
Py2->Py3 bug fixes, Travis CI, HTLC improvements.
  • Loading branch information
daragao authored Jun 25, 2018
2 parents 07b14b7 + ef974c9 commit ad92c56
Show file tree
Hide file tree
Showing 28 changed files with 772 additions and 498 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ lextab.py
yacctab.py
*.swp
*.swo
*.pid
10 changes: 10 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
sudo: enabled
language: python
python:
- '3.6'
install:
- make requirements-dev requirements
script:
- make travis
git:
depth: 3
2 changes: 1 addition & 1 deletion Dockerfile.alpine-python
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM jfloff/alpine-python:2.7
FROM jfloff/alpine-python:3.6

COPY . /app/
WORKDIR /app
Expand Down
42 changes: 38 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ROOT_DIR := $(shell dirname $(realpath $(MAKEFILE_LIST)))

SOLC=$(ROOT_DIR)/node_modules/.bin/solcjs
PYTHON=python
PYTHON=python3
NPM=npm
GANACHE=$(ROOT_DIR)/node_modules/.bin/ganache-cli
TRUFFLE=$(ROOT_DIR)/node_modules/.bin/truffle
Expand All @@ -28,7 +28,9 @@ check-prereqs:
clean:
rm -rf build chaindata dist
find . -name '*.pyc' -exec rm '{}' ';'
rm -rf *.pyc *.pdf *.egg-info
find . -name '__pycache__' -exec rm -rf '{}' ';'
rm -rf *.pyc *.pdf *.egg-info *.pid *.log
rm -f lextab.py yacctab.py


#######################################################################
Expand Down Expand Up @@ -76,11 +78,16 @@ solidity-lint:
nodejs-requirements:
$(NPM) install

# Useful shortcut for development, install packages to user path by default
python-pip-user:
mkdir -p $(HOME)/.pip/
echo -e "[global]\nuser = true\n" > $(HOME)/.pip/pip.conf

python-requirements: requirements.txt
$(PYTHON) -mpip install --user -r $<
$(PYTHON) -mpip install -r $<

python-dev-requirements: requirements-dev.txt
$(PYTHON) -mpip install --user -r $<
$(PYTHON) -mpip install -r $<

requirements-dev: nodejs-requirements python-dev-requirements

Expand Down Expand Up @@ -128,15 +135,36 @@ build/%.combined.sol: contracts/%.sol build
# Testing and unit test harnesses
#

# runs an instance of testrpc in background, then waits for it to be ready
travis-testrpc-start: travis-testrpc-stop
$(NPM) run testrpca > .testrpc.log & echo $$! > .testrpc.pid
while true; do echo -n . ; curl http://localhost:8545 &> /dev/null && break || sleep 1; done

# Stops previ
travis-testrpc-stop:
if [ -f .testrpc.pid ]; then kill `cat .testrpc.pid` || true; rm -f .testrpc.pid; fi

travis: travis-testrpc-start truffle-deploy-a contracts test


testrpc:
$(NPM) run testrpca

testrpc-b:
$(NPM) run testrpcb

test-js:
$(NPM) run test

test-unit:
$(PYTHON) -m unittest discover test/

test-coordserver:
$(PYTHON) -mion htlc coordinator --contract 0xd833215cbcc3f914bd1c9ece3ee7bf8b14f841bb

test-coordclient:
PYTHONPATH=. $(PYTHON) ./test/test_coordclient.py

test: test-unit test-js


Expand All @@ -148,5 +176,11 @@ test: test-unit test-js
truffle-deploy:
$(TRUFFLE) deploy

truffle-deploy-a:
$(TRUFFLE) deploy --network testrpca --reset

truffle-deploy-b:
$(TRUFFLE) deployb --network testrpcb --reset

truffle-console:
$(TRUFFLE) console
2 changes: 1 addition & 1 deletion abi/HTLC.abi
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"constant":false,"inputs":[{"name":"inReceiver","type":"address"},{"name":"inSecretHashed","type":"bytes32"},{"name":"inExpiry","type":"uint256"}],"name":"Deposit","outputs":[{"name":"","type":"bytes32"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"inExchGUID","type":"bytes32"}],"name":"Refund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"inExchGUID","type":"bytes32"},{"name":"inSecret","type":"bytes32"}],"name":"Withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"inExchGUID","type":"bytes32"}],"name":"GetState","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"exchanges","outputs":[{"name":"secretHashed","type":"bytes32"},{"name":"sender","type":"address"},{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"},{"name":"expiry","type":"uint256"},{"name":"state","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"exchGUID","type":"bytes32"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":false,"name":"secretHashed","type":"bytes32"},{"indexed":false,"name":"expiry","type":"uint256"}],"name":"OnDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"exchGUID","type":"bytes32"}],"name":"OnRefund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"exchGUID","type":"bytes32"},{"indexed":false,"name":"secret","type":"bytes32"}],"name":"OnWithdraw","type":"event"}]
[{"constant":true,"inputs":[{"name":"inExchGUID","type":"bytes32"}],"name":"GetReceiver","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"inReceiver","type":"address"},{"name":"inSecretHashed","type":"bytes32"},{"name":"inExpiry","type":"uint256"}],"name":"Deposit","outputs":[{"name":"","type":"bytes32"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"inExchGUID","type":"bytes32"}],"name":"GetAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"inExchGUID","type":"bytes32"}],"name":"Refund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"inExchGUID","type":"bytes32"},{"name":"inSecret","type":"bytes32"}],"name":"Withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"inExchGUID","type":"bytes32"}],"name":"GetExpiry","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"inExchGUID","type":"bytes32"}],"name":"GetSender","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"inExchGUID","type":"bytes32"}],"name":"GetState","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"exchanges","outputs":[{"name":"secretHashed","type":"bytes32"},{"name":"sender","type":"address"},{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"},{"name":"expiry","type":"uint256"},{"name":"state","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"inExchGUID","type":"bytes32"}],"name":"GetSecretHashed","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"exchGUID","type":"bytes32"},{"indexed":true,"name":"receiver","type":"address"},{"indexed":false,"name":"secretHashed","type":"bytes32"},{"indexed":false,"name":"expiry","type":"uint256"}],"name":"OnDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"exchGUID","type":"bytes32"}],"name":"OnRefund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"exchGUID","type":"bytes32"},{"indexed":false,"name":"secret","type":"bytes32"}],"name":"OnWithdraw","type":"event"}]
38 changes: 38 additions & 0 deletions contracts/HTLC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,44 @@ contract HTLC {

mapping (bytes32 => Exchange) public exchanges;

function GetExchange ( bytes32 inExchGUID )
internal view returns (Exchange storage)
{
Exchange storage exch = exchanges[inExchGUID];
require( exch.state != ExchangeState.Invalid );
return exch;
}

function GetSender ( bytes32 inExchGUID )
public view returns (address)
{
return GetExchange(inExchGUID).sender;
}

function GetReceiver ( bytes32 inExchGUID )
public view returns (address)
{
return GetExchange(inExchGUID).receiver;
}

function GetSecretHashed ( bytes32 inExchGUID )
public view returns (bytes32)
{
return GetExchange(inExchGUID).secretHashed;
}

function GetExpiry ( bytes32 inExchGUID )
public view returns (uint256)
{
return GetExchange(inExchGUID).expiry;
}

function GetAmount ( bytes32 inExchGUID )
public view returns (uint256)
{
return GetExchange(inExchGUID).amount;
}

function GetState ( bytes32 inExchGUID )
public view returns (ExchangeState)
{
Expand Down
24 changes: 15 additions & 9 deletions example.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash

PYTHON=python3
ACC_A=0x22d491bde2303f2f43325b2108d26f1eaba1e32b
ACC_B=0xffcf8fdee72ac11b5c542428b35eef5769c409f0
TOKEN_ADDR=0x254dffcd3277c0b1660f6d42efbb754edababc2b
Expand All @@ -11,50 +11,56 @@ IP_A=127.0.0.1
IP_B=127.0.0.1
API_PORT_A=8555
API_PORT_B=8556
REF=`openssl rand 32 | sha256sum | cut -f 1 -d ' '`
VALUE=$(( ( RANDOM % 10000 ) + 1000 ))

echo $PYTHON -mion lithium --from-account $ACC_A --to-account $ACC_B --rpc-from $IP_A:$PORT_A --rpc-to $IP_B:$PORT_B --lock $LOCK_ADDR --link $LINK_ADDR --api-port $API_PORT_A
echo $PYTHON -mion lithium --from-account $ACC_A --to-account $ACC_B --rpc-from $IP_B:$PORT_B --rpc-to $IP_A:$PORT_A --lock $LOCK_ADDR --link $LINK_ADDR --api-port $API_PORT_B
read enter

echo "==== Chain A ===="
echo "...Minting"
python -mion ion mint --rpc $IP_A:$PORT_A --account $ACC_A --tkn $TOKEN_ADDR --value 5000
$PYTHON -mion ion mint --rpc $IP_A:$PORT_A --account $ACC_A --tkn $TOKEN_ADDR --value $VALUE
echo ""
echo "Press any key to proceed"
read enter

echo "...Depositing"
python -mion ion deposit --rpc $IP_A:$PORT_A --account $ACC_A --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value 5000 --ref stuff
$PYTHON -mion ion deposit --rpc $IP_A:$PORT_A --account $ACC_A --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value $VALUE --ref $REF
echo ""
echo "Press any key to proceed"
read enter

echo "...Fetching proof"
python -mion ion proof --lithium-port $API_PORT_A --account $ACC_A --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value 5000 --ref stuff
$PYTHON -mion ion proof --lithium-port $API_PORT_A --account $ACC_A --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value $VALUE --ref $REF
echo ""
echo "Press any key to proceed"
read enter

echo "==== Chain B ===="
echo "...Minting"
python -mion ion mint --rpc $IP_B:$PORT_B --account $ACC_B --tkn $TOKEN_ADDR --value 5000
$PYTHON -mion ion mint --rpc $IP_B:$PORT_B --account $ACC_B --tkn $TOKEN_ADDR --value $VALUE
echo ""
echo "Press any key to proceed"
read enter

echo "...Depositing"
python -mion ion deposit --rpc $IP_B:$PORT_B --account $ACC_B --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value 5000 --ref stuff
$PYTHON -mion ion deposit --rpc $IP_B:$PORT_B --account $ACC_B --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value $VALUE --ref $REF
echo ""
echo "Press any key to proceed"
read enter

echo "...Fetching proof"
python -mion ion proof --lithium-port $API_PORT_B --account $ACC_B --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value 5000 --ref stuff
$PYTHON -mion ion proof --lithium-port $API_PORT_B --account $ACC_B --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value $VALUE --ref $REF
echo ""
echo "Press any key to proceed"
read enter

echo "==== Withdrawing from Chain A ===="
python -mion ion withdraw --lithium-port $API_PORT_B --rpc $IP_A:$PORT_A --account $ACC_B --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value 5000 --ref stuff
$PYTHON -mion ion withdraw --lithium-port $API_PORT_B --rpc $IP_A:$PORT_A --account $ACC_B --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value $VALUE --ref $REF
echo ""
echo "Press any key to proceed"
read enter

echo "==== Withdrawing from Chain B ===="
python -mion ion withdraw --lithium-port $API_PORT_A --rpc $IP_B:$PORT_B --account $ACC_A --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value 5000 --ref stuff
$PYTHON -mion ion withdraw --lithium-port $API_PORT_A --rpc $IP_B:$PORT_B --account $ACC_A --lock $LOCK_ADDR --tkn $TOKEN_ADDR --value $VALUE --ref $REF
100 changes: 44 additions & 56 deletions ion/Ion.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
"""
from __future__ import print_function

from binascii import hexlify, unhexlify

import click
import requests
import simplejson
from sha3 import keccak_256

from .merkle import merkle_hash
from ethereum.utils import keccak
from .ethrpc import BadStatusCodeError, BadJsonError, BadResponseError, ConnectionError
from .args import arg_ethrpc, arg_bytes20 #, arg_lithium_api
PRIMITIVE = (int, long, float, str, bool)


PRIMITIVE = (int, float, str, bool, bytes)

def rpc_call_with_exceptions(function, *args):
"""
Expand Down Expand Up @@ -136,27 +139,22 @@ def ionlock_withdraw(lithium_port, rpc, account, lock, tkn, value, ref):
ionlock = rpc.proxy("abi/IonLock.abi", lock, account)
token = rpc.proxy("abi/Token.abi", tkn, account)

joined_data = account.encode('hex') + tkn.encode('hex') + lock.encode('hex') + "{0:0{1}x}".format(value,64) + keccak.new(digest_bits=256).update(str(ref)).hexdigest()
joined_data = hexlify(account) + hexlify(tkn) + hexlify(lock) + "{0:0{1}x}".format(value,64).encode('utf-8') + hexlify(keccak_256(ref.encode('utf-8')).digest())
api_url = 'http://127.0.0.1:' + str(lithium_port)
r = requests.post(api_url + "/api/blockid", json={'leaf': joined_data})

try:
blockid = r.json()['blockid']
r = requests.post(api_url + "/api/proof", json={'leaf': joined_data, 'blockid': blockid})
r = requests.post(api_url + "/api/blockid", json={'leaf': joined_data.decode('ascii')})

path = r.json()['proof']
path = [int(x) for x in path]
hashed_ref = keccak.new(digest_bits=256).update(str(ref)).hexdigest()
blockid = r.json()['blockid']
r = requests.post(api_url + "/api/proof", json={'leaf': joined_data.decode('ascii'), 'blockid': blockid})

result = rpc_call_with_exceptions(ionlock.Withdraw, value, hashed_ref.decode('hex'), int(blockid), path)

result = rpc_call_with_exceptions(token.balanceOf, account)
if result is not None:
print("New balance =", result)
path = r.json()['proof']
path = [int(x) for x in path]
hashed_ref = hexlify(keccak_256(ref.encode('utf-8')).digest())

result = rpc_call_with_exceptions(ionlock.Withdraw, value, unhexlify(hashed_ref), int(blockid), path)

except simplejson.errors.JSONDecodeError as e:
print(e.message)
result = rpc_call_with_exceptions(token.balanceOf, account)
if result is not None:
print("New balance =", result)

return 0

Expand Down Expand Up @@ -186,28 +184,24 @@ def ionlink_verify(lithium_port, rpc, account, link, lock, tkn, value, ref):
"""
ionlink = rpc.proxy("abi/IonLink.abi", link, account)

joined_data = account.encode('hex') + tkn.encode('hex') + lock.encode('hex') + "{0:0{1}x}".format(value,64) + keccak.new(digest_bits=256).update(str(ref)).hexdigest()
joined_data = hexlify(account) + hexlify(tkn) + hexlify(lock) + "{0:0{1}x}".format(value,64).encode('utf-8') + hexlify(keccak_256(ref.encode('utf-8')).digest())
hashed_data = merkle_hash(int(joined_data, 16))
api_url = 'http://127.0.0.1:' + str(lithium_port)
r = requests.post(api_url + "/api/blockid", json={'leaf': joined_data})
r = requests.post(api_url + "/api/blockid", json={'leaf': joined_data.decode('ascii')})

try:
blockid = r.json()['blockid']
r = requests.post(api_url + "/api/proof", json={'leaf': joined_data, 'blockid': blockid})
blockid = r.json()['blockid']
r = requests.post(api_url + "/api/proof", json={'leaf': joined_data.decode('ascii'), 'blockid': blockid})

path = r.json()['proof']
path = [int(x) for x in path]
path = r.json()['proof']
path = [int(x) for x in path]

r = requests.post(api_url + "/api/verify", json={'leaf': joined_data, 'proof': path, 'blockid': blockid})
print("Lithium proof:")
print(r.text)
r = requests.post(api_url + "/api/verify", json={'leaf': joined_data.decode('ascii'), 'proof': path, 'blockid': blockid})
print("Lithium proof:")
print(r.text)

print("IonLink Proof at block id", blockid)
result = rpc_call_with_exceptions(ionlink.Verify, int(blockid), hashed_data, path)
print(result)

except simplejson.errors.JSONDecodeError as e:
print(e.message)
print("IonLink Proof at block id", blockid)
result = rpc_call_with_exceptions(ionlink.Verify, int(blockid), hashed_data, path)
print(result)

return 0

Expand All @@ -231,21 +225,19 @@ def merkle_proof_path(lithium_port, account, lock, tkn, value, ref):
:param ref: str, reference used for the deposit
:return: 0, merkle path is printed to the console
"""
joined_data = account.encode('hex') + tkn.encode('hex') + lock.encode('hex') + "{0:0{1}x}".format(value,64) + keccak.new(digest_bits=256).update(str(ref)).hexdigest()
api_url = 'http://127.0.0.1:' + str(lithium_port)
r = requests.post(api_url + "/api/blockid", json={'leaf': joined_data})
joined_data = hexlify(account) + hexlify(tkn) + hexlify(lock) + "{0:0{1}x}".format(value,64).encode('utf-8') + hexlify(keccak_256(ref.encode('utf-8')).digest())
print("Joined data", joined_data)

try:
blockid = r.json()['blockid']
r = requests.post(api_url + "/api/proof", json={'leaf': joined_data, 'blockid':blockid})
api_url = 'http://127.0.0.1:' + str(lithium_port)
r = requests.post(api_url + "/api/blockid", json={'leaf': joined_data.decode('ascii')})

print("Received proof:")
[print("Path ", r.json()['proof'].index(x), " : ", x) for x in r.json()['proof']]
blockid = r.json()['blockid']
r = requests.post(api_url + "/api/proof", json={'leaf': joined_data.decode('ascii'), 'blockid':blockid})

print("Latest IonLink block",blockid)
print("Received proof:")
[print("Path ", r.json()['proof'].index(x), " : ", x) for x in r.json()['proof']]

except simplejson.errors.JSONDecodeError as e:
print(e.message)
print("Latest IonLink block",blockid)

return 0

Expand All @@ -269,19 +261,15 @@ def merkle_verify(proof, lithium_port, account, lock, tkn, value, ref):
:param ref: str, reference used for the deposit
:return: 0, result is printed to the console
"""
joined_data = account.encode('hex') + tkn.encode('hex') + lock.encode('hex') + "{0:0{1}x}".format(value,64) + keccak.new(digest_bits=256).update(str(ref)).hexdigest()
joined_data = hexlify(account) + hexlify(tkn) + hexlify(lock) + "{0:0{1}x}".format(value,64).encode('utf-8') + hexlify(keccak_256(ref.encode('utf-8')).digest())
proof = [int(x) for x in proof]
api_url = 'http://127.0.0.1:' + str(lithium_port)
r = requests.post(api_url + "/api/blockid", json={'leaf': joined_data})

try:
blockid = r.json()['blockid']
r = requests.post(api_url + "/api/verify", json={'leaf': joined_data, 'proof': proof, 'blockid':blockid})
print("Received proof:")
print(r.text)
r = requests.post(api_url + "/api/blockid", json={'leaf': joined_data.decode('ascii')})

except simplejson.errors.JSONDecodeError as e:
print(e.message)
blockid = r.json()['blockid']
r = requests.post(api_url + "/api/verify", json={'leaf': joined_data.decode('ascii'), 'proof': proof, 'blockid':blockid})
print("Received proof:")
print(r.text)

return 0

Expand Down
Loading

0 comments on commit ad92c56

Please sign in to comment.