Commit 3d1e4eba authored by Pascal Engélibert's avatar Pascal Engélibert

Send TX (not tested)

parent 5ef6a65c
......@@ -13,7 +13,7 @@ It would be great to have a logo (maybe with a mixer blade and an onion in a Ğ)
Install dependances:
sudo pip3 install duniterpy
sudo pip3 install duniterpy silkaj
Create config files: (change `default.ini` and `peers`, so you can run different
servers with different configs) (note that each server must have different config &
......
......@@ -22,9 +22,9 @@ https://www.ietf.org/rfc/rfc3092.txt <- Very important RFC, please read it
"""
import sys, os, time, re, socket, json, asyncio, getpass, secrets
from duniterpy.key import SigningKey
from duniterpy.key import PublicKey
import libnacl.sign
from duniterpy.key import SigningKey, PublicKey
import silkaj.money, silkaj.tx, silkaj.auth
RECBUF = 1024
p_clen = re.compile("\r?\ncontent-length: *(\d+)\r?\n?", re.IGNORECASE)
......@@ -94,6 +94,12 @@ def sdata(host, mode, url="/", data=b""):
def genKeys():
return secrets.token_urlsafe(), secrets.token_urlsafe()
def sendTransaction(sender_keys, receiver_pubkey, amount, comment):
sender_amount = silkaj.money.get_amount_from_pubkey(sender_keys.pubkey)[0]
assert sender_amount >= amount
silkaj.tx.generate_and_send_transaction(sender_keys.hex_seed().decode(), sender_keys.pubkey, amount, [receiver_pubkey], comment)
def mix(amount, base, sender, path, host):
start_time = time.time()
......@@ -176,27 +182,39 @@ def mix(amount, base, sender, path, host):
return
if (i == 0 and confirm.sender_pubkey != sender.pubkey) or (i > 0 and confirm.sender_pubkey != path[i-1]):
print("Bad sender_pubkey #"+str(i))
return
if confirm.receiver_pubkey != path[i+1]:
print("Bad receiver_pubkey #"+str(i))
# TODO check comments
return
if not re.match("^[a-zA-Z0-9_-]{64}$", confirm.in_comment):
print("Bad in_comment #"+str(i))
return
if not re.match("^[a-zA-Z0-9_-]{64}$", confirm.out_comment):
print("Bad out_comment #"+str(i))
return
i += 1
print("OK, total duration = "+str(round(time.time()-start_time, 2))+"s")
exit()
async def test1():
salt = "salt"
password = "psw"
salt = getpass.getpass("Salt: ")
password = getpass.getpass("Psw: ")
keys = SigningKey.from_credentials(salt, password) # sender
print(keys.pubkey)
if input("OK? [yn]: ").lower() != "y":
return
amount = 1000 # 10 G1 with 2 decimals
host1 = ("localhost", 10951) # input node address
pubkey1 = "DCovzCEnQm9GUWe6mr8u42JR1JAuoj3HbQUGdCkfTzSr" # salt=test psw=test # onion router 1 (input node)
pubkey2 = "EiZ8LNJmtwCDQa8W8PcnB9n8Q7QreMHsB24kS14iV5vV" # salt=foo psw=bar # onion router 2 (middle node)
pubkey3 = "ENSD6KikoZAcUVGBWJNKFREG2ryrGaHUD68GCYsiY2pK" # salt=123 psw=456 # onion router 3 (output node)
pubkey4 = "9tjqtDT38Coj9QPMYosPJ2LC4yLyhXJes31q3uiLTSxG" # salt=baz psw=qux # receiver
pubkey1 = "4QRZj4bHpXTdJajfX8mTf2RmYQhKX7BtiMiZL3z5Lo8F" # onion router 1 (input node)
pubkey2 = "FE4p3w7LvfDtat7pwNky5vZ9SPGKRrFid8gS2bg8Pjka" # onion router 2 (middle node)
pubkey3 = "Fn7C5jHyDc6D354CwaXS2eVxHLCqqM2ND3zJjj1Rarvd" # onion router 3 (output node)
pubkey4 = "EEGevmgQcgzXou2ucaf1S9pCMvwKfu56ukRRLPn4D3y9" # receiver
mix(amount, 0, keys, [pubkey1, pubkey2, pubkey3, pubkey4], host1)
asyncio.run(test1())
if __name__ == "__main__":
asyncio.run(test1())
......@@ -18,11 +18,12 @@
"""
import sys, os, socket, re, time, configparser, json, asyncio, getpass, secrets
from threading import Thread
import libnacl.sign
import duniterpy.api.bma as bma
from duniterpy.api.client import Client
from duniterpy.key import SigningKey, PublicKey
from threading import Thread
import libnacl.sign
import silkaj.money, silkaj.tx, silkaj.auth
VERSION = "0.1.0"
AUTHORS = ["Pascal Engélibert <tuxmain@zettascript.org>"]
......@@ -234,6 +235,12 @@ def sdata(host, mode, url="/", data=b""):
return header, content
def sendTransaction(sender_keys, receiver_pubkey, amount, comment):
sender_amount = silkaj.money.get_amount_from_pubkey(sender_keys.pubkey)[0]
assert sender_amount >= amount
silkaj.tx.generate_and_send_transaction(sender_keys.hex_seed().decode(), sender_keys.pubkey, amount, [receiver_pubkey], comment)
class ServerThread(Thread):
def __init__(self, conf, peers, peers_index, keys, pool):
Thread.__init__(self)
......@@ -417,7 +424,6 @@ class ServerThread(Thread):
sendResponse(client, "401 Unauthorized", {"error": "bad_comment"})
continue
print(len(data[64:]))
tx.confirms = data[64:]# TODO check size
logPrint("Rec confirm "+tx.sender_pubkey[:8]+" -> "+tx.receiver_pubkey[:8]+" = "+str(tx.in_amount)+":"+str(tx.in_base)+" -> "+str(tx.out_amount)+":"+str(tx.out_base), LOG_TRACE)
......@@ -539,6 +545,7 @@ class ClientThread(Thread):
logPrint("Up "+str(peer), LOG_TRACE)
try:
message = json.loads(content.decode())
assert "peers" in message
assert type(message["peers"]) == list
except (json.decoder.JSONDecodeError, AssertionError):
logPrint("Bad json from "+str(peer), LOG_ERROR)
......@@ -563,6 +570,54 @@ class ClientThread(Thread):
logPrint("Add "+str(new_peer), LOG_TRACE)
logPrint("Finished peers detection", LOG_TRACE)
async def mix(self):
logPrint("Starting mix", LOG_TRACE)
t = time.time()
client = None
for bma_endpoint in self.bma_endpoints:
client = Client(bma_endpoint)
try:
await client(bma.node.summary)
break
except:
logPrint("BMA down: "+bma_endpoint, LOG_WARN)
await client.close()
client = None
if client:
logPrint("BMA up: "+bma_endpoint, LOG_TRACE)
ready_txs = []
# Get txs history
history = await client(bma.tx.times, self.keys.pubkey, int(t-int(self.conf["Mix"]["MixReqAgeMax"])), int(t))
await client.close()
for tx in self.pool:
if tx.need_send or tx.need_confirm:
continue
wanted_output = tx.in_amount +":"+ tx.in_base +":SIG("+ self.keys.pubkey +")"
ok = False
for in_tx in history["history"]["received"]:
if in_tx["comment"] == tx.in_comment \
or wanted_output in in_tx["output"]:
ok = True
if ok:
ready_txs.append(tx)
print(wanted_output)
if len(ready_txs) >= int(self.conf["Mix"]["MixMinTxs"]):
random.shuffle(ready_txs)
for tx in ready_txs:
try:
sendTransaction(self.keys, tx.receiver_pubkey, tx.out_amount, tx.out_comment)
except Exception as e:
logPrint("Error when sending tx:\n"+e, LOG_ERROR)
else:
logPrint("Not enough ready txs to mix (only "+str(len(ready_txs))+")", LOG_TRACE)
logPrint("Mix finished", LOG_TRACE)
else:
logPrint("All BMA endpoints down: cannot mix!", LOG_ERROR)
def run(self):
#asyncio.get_event_loop().run_until_complete(self.startClient())
asyncio.run(self.startClient())
......@@ -581,41 +636,7 @@ class ClientThread(Thread):
# Mix
if t > next_mix:
logPrint("Starting mix", LOG_TRACE)
client = None
for bma_endpoint in self.bma_endpoints:
client = Client(bma_endpoint)
try:
await client(bma.node.summary)
break
except:
logPrint("BMA down: "+bma_endpoint, LOG_WARN)
await client.close()
client = None
if client:
logPrint("BMA up: "+bma_endpoint, LOG_TRACE)
ready_txs = []
# Get txs history
history = await client(bma.tx.times, self.keys.pubkey, int(t-int(self.conf["Mix"]["MixReqAgeMax"])), int(t))
for tx in self.pool:
if tx.need_send or tx.need_confirm:
continue
wanted_output = tx.in_amount +":"+ tx.in_base +":SIG("+ self.keys.pubkey +")"
ok = False
for in_tx in history["history"]["received"]:
if in_tx["comment"] == tx.in_comment \
or wanted_output in in_tx["output"]:
ok = True
ready_txs.append(tx)
# TODO finish that
await client.close()
logPrint("Mix finished", LOG_TRACE)
else:
logPrint("All BMA endpoints down: cannot mix!", LOG_ERROR)
await self.mix()
next_mix = time.time() + int(self.conf["Mix"]["MixInterval"])
for tx in self.pool:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment