Skip to content
This repository has been archived by the owner on Jun 7, 2023. It is now read-only.

Commit

Permalink
Initial commit, PoC
Browse files Browse the repository at this point in the history
Provide an interface module to execute
Proof Of Work for IOTA locally in PyOTA.

Wrapper modules use the C port of the CCurl library
and the Entangled repo implementation.

Attention!!!
Before installing the modules with 'pip install -e .',
you need to build binaries from the two repo. Use the
'init.sh' script to do so. CMake and Bazel are  build
dependencies.
  • Loading branch information
lzpap committed Oct 4, 2019
1 parent 64121a9 commit 90252f0
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "ccurl"]
path = ccurl
url = https://github.com/iotaledger/ccurl.git
[submodule "entangled"]
path = entangled
url = https://github.com/iotaledger/entangled.git
1 change: 1 addition & 0 deletions ccurl
Submodule ccurl added at 94d74a
1 change: 1 addition & 0 deletions entangled
Submodule entangled added at 475bcb
56 changes: 56 additions & 0 deletions examples/with_ccurl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import iota
from pprint import *
from datetime import datetime
from pow import ccurl_interface

# Generate seed
myseed = iota.crypto.types.Seed.random()
pprint('Generated seed is:')
pprint(myseed)

#Generate two addresses
addres_generator = iota.crypto.addresses.AddressGenerator(myseed)
addys = addres_generator.get_addresses(1, count=2)
pprint('Generated addresses are:')
pprint(addys)

NowIs = datetime.now() # get a actual date & time - just to have some meaningfull info

# preparing transactions
pt = iota.ProposedTransaction(address = iota.Address(addys[0]), # 81 trytes long address
message = iota.TryteString.from_unicode('Hey hey, trying to figure this thing out. This is tx1, now is %s' % (NowIs)),
tag = iota.Tag(b'LOCALATTACHINTERFACE99999'), # Up to 27 trytes
value = 0)

pt2 = iota.ProposedTransaction(address = iota.Address(addys[1]), # 81 trytes long address
message = iota.TryteString.from_unicode('Hey hey, trying to figure this thing out. This is tx2, now is %s' % (NowIs)),
tag = iota.Tag(b'LOCALATTACHINTERFACE99999'), # Up to 27 trytes
value = 0)
# besides the given attributes, library also adds a transaction timestamp

# preparing bundle that consists of both transactions prepared in the previous example
pb = iota.ProposedBundle(transactions=[pt2,pt]) # list of prepared transactions is needed at least

# generate bundle hash using sponge/absorb function + normalize bundle hash + copy bundle hash into each transaction / bundle is finalized
pb.finalize()

#bundle is finalized, let's print it
print("\nGenerated bundle hash: %s" % (pb.hash))
print("\nTail Transaction in the Bundle is a transaction #%s." % (pb.tail_transaction.current_index))

api = iota.Iota("https://nodes.thetangle.org:443") # selecting IOTA node

gta = api.get_transactions_to_approve(depth=3) # get tips to be approved by your bundle

mwm = 14 # target is mainnet

bundle = ccurl_interface.local_attach_to_tangle(pb, gta['trunkTransaction'], gta['branchTransaction'], mwm)

bundle_trytes = [ x.as_tryte_string() for x in pb._transactions ]

# Broadcast transactions on the Tangle
broadcasted = api.broadcast_and_store(bundle_trytes)

bundle_broadcasted =iota.Bundle.from_tryte_strings(broadcasted['trytes'])
pprint('Local pow broadcasted transactions are:')
pprint(bundle_broadcasted.as_json_compatible())
56 changes: 56 additions & 0 deletions examples/with_entangled.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import iota
from pprint import *
from datetime import datetime
from pow import entangled_interface

# Generate seed
myseed = iota.crypto.types.Seed.random()
pprint('Generated seed is:')
pprint(myseed)

#Generate two addresses
addres_generator = iota.crypto.addresses.AddressGenerator(myseed)
addys = addres_generator.get_addresses(1, count=2)
pprint('Generated addresses are:')
pprint(addys)

NowIs = datetime.now() # get a actual date & time - just to have some meaningfull info

# preparing transactions
pt = iota.ProposedTransaction(address = iota.Address(addys[0]), # 81 trytes long address
message = iota.TryteString.from_unicode('Hey hey, trying to figure this thing out. This is tx1, now is %s' % (NowIs)),
tag = iota.Tag(b'LOCALATTACHINTERFACE99999'), # Up to 27 trytes
value = 0)

pt2 = iota.ProposedTransaction(address = iota.Address(addys[1]), # 81 trytes long address
message = iota.TryteString.from_unicode('Hey hey, trying to figure this thing out. This is tx2, now is %s' % (NowIs)),
tag = iota.Tag(b'LOCALATTACHINTERFACE99999'), # Up to 27 trytes
value = 0)
# besides the given attributes, library also adds a transaction timestamp

# preparing bundle that consists of both transactions prepared in the previous example
pb = iota.ProposedBundle(transactions=[pt2,pt]) # list of prepared transactions is needed at least

# generate bundle hash using sponge/absorb function + normalize bundle hash + copy bundle hash into each transaction / bundle is finalized
pb.finalize()

#bundle is finalized, let's print it
print("\nGenerated bundle hash: %s" % (pb.hash))
print("\nTail Transaction in the Bundle is a transaction #%s." % (pb.tail_transaction.current_index))

api = iota.Iota("https://nodes.thetangle.org:443") # selecting IOTA node

gta = api.get_transactions_to_approve(depth=3) # get tips to be approved by your bundle

mwm = 14 # target is mainnet

bundle = entangled_interface.local_attach_to_tangle(pb, gta['trunkTransaction'], gta['branchTransaction'], mwm)

bundle_trytes = [ x.as_tryte_string() for x in pb._transactions ]

# Broadcast transactions on the Tangle
broadcasted = api.broadcast_and_store(bundle_trytes)

bundle_broadcasted =iota.Bundle.from_tryte_strings(broadcasted['trytes'])
pprint('Local pow broadcasted transactions are:')
pprint(bundle_broadcasted.as_json_compatible())
32 changes: 32 additions & 0 deletions init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/bash

# Updating ccurl submodule
git submodule update --init --recursive
rm -f src/libccurl.so
rm -f src/helpers.dll
WD=$(pwd)

# Bulding using cmake
echo "Building ccurl library with cmake..."
cd ccurl && mkdir -p build && cd build && cmake .. && cmake --build .
cd ../..
LIB=$(find . -name "*.so")

echo "The built library is at:"
echo $LIB

echo "Copying shared library file to the src directory..."
cp $LIB pow/

echo "Done."

# Bulding using bazel
echo "Building entangled/common/helpers library with bazel..."
cd entangled
bazel build //common/helpers:helpers.dll

LIB="bazel-bin/common/helpers/helpers.dll"
echo "Copying shared library file to the src directory..."
cp $LIB $WD/pow

echo "Done."
Empty file added pow/__init__.py
Empty file.
60 changes: 60 additions & 0 deletions pow/ccurl_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from ctypes import *
import iota
import math
import time

from pkg_resources import resource_filename
libccurl_path = resource_filename("pow","libccurl.so")

_libccurl = CDLL(libccurl_path)

# calling function in helpers.dll to calculate nonce based on transaction trytes
def get_powed_tx_trytes( trytes, mwm ):
return _libccurl.ccurl_pow(c_char_p(trytes.encode('utf-8')), c_int(mwm))

# calling function in helpers.dll to calculate tx hash based on transaction trytes (with nonce included)
def get_hash_trytes(trytes):
return _libccurl.ccurl_digest_transaction(c_char_p(trytes.encode('utf-8')))

# Takes a bundle object, calculates the pow, attaches tx hash
def local_attach_to_tangle(bundle,
trunk_transaction_hash,
branch_transaction_hash,
mwm):
previoustx = None

for txn in reversed(bundle._transactions):
txn.attachment_timestamp = int(round(time.time() * 1000))
txn.attachment_timestamp_upper_bound = (math.pow(3,27) - 1) // 2

if (not previoustx): # this is the tail transaction
if txn.current_index == txn.last_index:
txn.branch_transaction_hash = branch_transaction_hash
txn.trunk_transaction_hash = trunk_transaction_hash
else:
raise ValueError('Something is not right, the last this should be the last tx in the bundle')

else: # It is not a tail transaction
txn.branch_transaction_hash = trunk_transaction_hash
txn.trunk_transaction_hash = previoustx # the previous transaction

# Let's do the pow locally
txn_string = txn.as_tryte_string().__str__()
# returns a reference to a char array
powed_pointer = get_powed_tx_trytes(txn_string, mwm)
# derive char array values
powed_txn_string = c_char_p(powed_pointer).value.decode('utf-8')
# construct trytestring from python string
powed_txn_trytes = iota.TryteString(powed_txn_string)
# Create powed txn object
powed_txn = iota.Transaction.from_tryte_string(powed_txn_trytes)

hash_pointer = get_hash_trytes(powed_txn_string)
hash_string = c_char_p(hash_pointer).value.decode('utf-8')
hash_trytes = iota.TryteString(hash_string)
powed_txn.hash = iota.TransactionHash(hash_trytes)
powed_txn.hash_ = iota.TransactionHash(hash_trytes)
previoustx = powed_txn.hash
# put that back in the bundle
bundle._transactions[txn.current_index] = powed_txn
return bundle
66 changes: 66 additions & 0 deletions pow/entangled_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from ctypes import *
import iota
import math
import time

# Load the compiled C library
from pkg_resources import resource_filename
libpow_path = resource_filename("pow","helpers.dll")

_libpow = CDLL(libpow_path)

# calling function in helpers.dll to calculate nonce based on transaction trytes
def get_nonce( trytes, mwm ):
return _libpow.iota_pow_trytes(c_char_p(trytes.encode('utf-8')), c_int(mwm))

# calling function in helpers.dll to calculate tx hash based on transaction trytes (with nonce included)
def get_hash(trytes):
return _libpow.iota_digest(c_char_p(trytes.encode('utf-8')))

# Takes a bundle object, calculates the pow, attaches tx hash
def local_attach_to_tangle(bundle,
trunk_transaction_hash,
branch_transaction_hash,
mwm):
previoustx = None

# Iterate thorugh transactions in the bundle, starting from the tail
for txn in reversed(bundle._transactions):
# Fill out attachment timestamp, field is used to calculate nonce
txn.attachment_timestamp = int(round(time.time() * 1000))
txn.attachment_timestamp_upper_bound = (math.pow(3,27) - 1) // 2

if (not previoustx): # this is the tail transaction
if txn.current_index == txn.last_index:
txn.branch_transaction_hash = branch_transaction_hash
txn.trunk_transaction_hash = trunk_transaction_hash
else:
raise ValueError('Something is not right, the last this should be the last tx in the bundle')

else: # It is not a tail transaction
txn.branch_transaction_hash = trunk_transaction_hash
txn.trunk_transaction_hash = previoustx # the previous transaction

# Let's do the pow locally (calculate nonce)
txn_string = txn.as_tryte_string().__str__()
# returns a reference to a char array
nonce_pointer = get_nonce(txn_string, mwm)
# derive char array values
nonce_string = c_char_p(nonce_pointer).value.decode('utf-8')
# construct trytestring from python string
nonce_trytes = iota.TryteString(nonce_string)
# assign it to the tx
txn.nonce = iota.Nonce(nonce_trytes)

# Calculate transaction hash
hash_pointer = get_hash(txn.as_tryte_string().__str__())
hash_string = c_char_p(hash_pointer).value.decode('utf-8')
hash_trytes = iota.TryteString(hash_string)
# Assign hash to transaction object
txn.hash = iota.TransactionHash(hash_trytes)
txn.hash_ = iota.TransactionHash(hash_trytes)

previoustx = txn.hash
# put that back in the bundle
bundle._transactions[txn.current_index] = txn
return bundle
9 changes: 9 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from setuptools import setup, find_packages

setup(
name='pow',
version='1.0',
packages=['pow'],
package_dir={'pow': 'pow'},
package_data={'pow': ['helpers.dll','libccurl.so']},
)

0 comments on commit 90252f0

Please sign in to comment.