diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..39230dc --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/ccurl b/ccurl new file mode 160000 index 0000000..94d74a4 --- /dev/null +++ b/ccurl @@ -0,0 +1 @@ +Subproject commit 94d74a492a7ff9494c73b7be2dcf80f3ce4d453f diff --git a/entangled b/entangled new file mode 160000 index 0000000..475bcb3 --- /dev/null +++ b/entangled @@ -0,0 +1 @@ +Subproject commit 475bcb3dca9c211950277a755df7385c36877575 diff --git a/examples/with_ccurl.py b/examples/with_ccurl.py new file mode 100644 index 0000000..a29d1b3 --- /dev/null +++ b/examples/with_ccurl.py @@ -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()) diff --git a/examples/with_entangled.py b/examples/with_entangled.py new file mode 100644 index 0000000..81bbb7b --- /dev/null +++ b/examples/with_entangled.py @@ -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()) diff --git a/init.sh b/init.sh new file mode 100755 index 0000000..f0222bf --- /dev/null +++ b/init.sh @@ -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." diff --git a/pow/__init__.py b/pow/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pow/ccurl_interface.py b/pow/ccurl_interface.py new file mode 100644 index 0000000..385c415 --- /dev/null +++ b/pow/ccurl_interface.py @@ -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 diff --git a/pow/entangled_interface.py b/pow/entangled_interface.py new file mode 100644 index 0000000..04bbcd3 --- /dev/null +++ b/pow/entangled_interface.py @@ -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 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..378b811 --- /dev/null +++ b/setup.py @@ -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']}, + ) \ No newline at end of file