This repository has been archived on 2020-12-03. You can view files and clone it, but cannot push or open issues or pull requests.
py-gva/lib/paylib.py

173 lines
6.2 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import sys, re, os.path, json, ast
from termcolor import colored
2020-11-24 07:41:23 +01:00
from lib.natools import fmt, sign, get_privkey
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport
2020-11-22 06:27:26 +01:00
PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}"
2020-11-17 06:06:45 +01:00
2020-11-19 02:51:35 +01:00
class Transaction:
2020-11-24 06:28:26 +01:00
def __init__(self, dunikey, node, recipient, amount, comment='', useMempool=False, verbose=False):
2020-11-22 06:27:26 +01:00
self.dunikey = dunikey
2020-11-17 06:06:45 +01:00
self.recipient = recipient
2020-11-27 06:04:11 +01:00
self.amount = int(amount*100)
2020-11-17 06:06:45 +01:00
self.comment = comment
self.issuer = get_privkey(dunikey, "pubsec").pubkey
2020-11-24 06:28:26 +01:00
self.useMempool = useMempool
2020-11-22 06:27:26 +01:00
self.verbose = verbose
2020-11-25 08:02:21 +01:00
self.node = node
2020-11-24 06:28:26 +01:00
self._isChange = False
2020-11-17 06:06:45 +01:00
2020-11-25 08:02:21 +01:00
try:
if not re.match(PUBKEY_REGEX, recipient) or len(recipient) > 45:
raise ValueError("La clé publique n'est pas au bon format.")
except:
sys.stderr.write("La clé publique n'est pas au bon format.\n")
2020-11-25 08:02:21 +01:00
raise
2020-11-17 06:06:45 +01:00
2020-11-25 08:02:21 +01:00
try:
if recipient == self.issuer:
raise ValueError('Le destinataire ne peut pas être vous même.')
except:
2020-11-22 06:27:26 +01:00
sys.stderr.write("Le destinataire ne peut pas être vous même.\n")
2020-11-25 08:02:21 +01:00
raise
2020-11-22 06:27:26 +01:00
# Define Duniter GVA node
transport = AIOHTTPTransport(url=node)
self.client = Client(transport=transport, fetch_schema_from_transport=True)
2020-11-17 08:20:47 +01:00
def genDoc(self):
2020-11-17 06:06:45 +01:00
# Build TX generation document
2020-11-27 03:26:14 +01:00
if self.verbose: print("useMempool:", str(self.useMempool))
2020-11-17 06:06:45 +01:00
queryBuild = gql(
"""
query ($recipient: String!, $issuer: String!, $amount: Int!, $comment: String!, $useMempool: Boolean!){ genTx(
2020-11-17 06:06:45 +01:00
amount: $amount
comment: $comment
issuer: $issuer
recipient: $recipient
2020-11-24 06:28:26 +01:00
useMempoolSources: $useMempool
2020-11-17 06:06:45 +01:00
)
}
"""
)
paramsBuild = {
"recipient": self.recipient,
"issuer": self.issuer,
2020-11-25 08:02:21 +01:00
"amount": int(self.amount),
2020-11-19 02:51:35 +01:00
"comment": self.comment,
2020-11-24 06:28:26 +01:00
"useMempool": self.useMempool
2020-11-17 06:06:45 +01:00
}
# Send TX document
try:
# self.txDoc = []
self.txDoc = self.client.execute(queryBuild, variable_values=paramsBuild)['genTx']
2020-11-27 03:26:14 +01:00
if self.verbose: print(self.txDoc[0])
2020-11-17 06:06:45 +01:00
return self.txDoc
except Exception as e:
message = ast.literal_eval(str(e))["message"]
sys.stderr.write("Echec de la génération du document:\n" + message + "\n")
2020-11-25 08:02:21 +01:00
raise
2020-11-17 06:06:45 +01:00
# Check document
def checkTXDoc(self):
issuerRaw=[];outAmount=[];outPubkey=[];commentRaw=[]
2020-11-27 03:26:14 +01:00
for docs in self.txDoc:
docList = docs.splitlines()
2020-11-27 03:26:14 +01:00
for i, line in enumerate(docList):
if re.search("Issuers:", line):
2020-11-27 03:26:14 +01:00
issuerRaw.append(docList[(i + 1) % len(docList)])
if re.search("Outputs:", line):
2020-11-27 03:26:14 +01:00
outputRaw = docList[(i + 1) % len(docList)].split(":")
outAmount.append(int(outputRaw[0]))
outPubkey.append(outputRaw[2].split("SIG(")[1].replace(')',''))
if re.search("Comment:", line):
commentRaw.append(line.split(': ', 1)[1])
2020-11-25 08:02:21 +01:00
# Check if it's only a change transaction
if all(i == self.issuer for i in outPubkey):
2020-11-22 06:27:26 +01:00
print("Le document contient une transaction de change")
2020-11-19 02:51:35 +01:00
self.isChange = True
# Check validity of the document
elif all(i != self.issuer for i in issuerRaw) or sum(outAmount) != self.amount or all(i != self.recipient for i in outPubkey) or all(i != self.comment for i in commentRaw):
2020-11-25 08:02:21 +01:00
sys.stderr.write(colored("Le document généré est corrompu !\nLe noeud " + self.node + "a peut être un dysfonctionnement.\n", 'red'))
sys.stderr.write(colored(issuerRaw[0] + " envoi " + str(outAmount[0]) + " vers " + outPubkey[0] + " with comment: " + commentRaw[0] + "\n", "yellow"))
2020-11-25 08:02:21 +01:00
raise ValueError('Le document généré est corrompu !')
2020-11-17 06:06:45 +01:00
else:
print("Le document généré est conforme.")
2020-11-19 02:51:35 +01:00
self.isChange = False
2020-11-17 06:06:45 +01:00
return self.txDoc
def signDoc(self):
# Sign TX documents
signature=[]
self.signedDoc=[]
for i, docs in enumerate(self.txDoc):
2020-11-22 06:27:26 +01:00
signature.append(fmt["64"](sign(docs.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(docs.encode())]))
self.signedDoc.append(docs + signature[i].decode())
2020-11-17 06:06:45 +01:00
return self.signedDoc
def sendTXDoc(self):
# Build TX documents
txResult=[]
for docs in self.signedDoc:
querySign = gql(
"""
mutation ($signedDoc: String!){ tx(
rawTx: $signedDoc
) {
version
issuers
outputs
}
}
2020-11-17 06:06:45 +01:00
"""
)
paramsSign = {
"signedDoc": docs
2020-11-17 06:06:45 +01:00
}
# Send TX Signed document
try:
2020-11-22 06:27:26 +01:00
txResult.append(str(self.client.execute(querySign, variable_values=paramsSign)))
except Exception as e:
message = ast.literal_eval(str(e))["message"]
sys.stderr.write("Echec de la transaction:\n" + message + "\n")
2020-11-22 06:27:26 +01:00
if self.verbose:
sys.stderr.write("Document final:\n" + docs)
2020-11-25 08:02:21 +01:00
raise ValueError(message)
2020-11-19 02:51:35 +01:00
else:
if self.isChange:
self.send()
else:
print(colored("Transaction effectué avec succès !", "green"))
2020-11-22 06:27:26 +01:00
if self.verbose:
print(docs)
break
return txResult
2020-11-19 02:51:35 +01:00
2020-11-24 06:28:26 +01:00
def _getIsChange(self):
return self._isChange
def _setIsChange(self, newChange):
2020-11-27 03:26:14 +01:00
if self.verbose: print("_setIsChange: ", str(newChange))
2020-11-24 06:28:26 +01:00
self._isChange = newChange
if newChange: self.useMempool == True
isChange = property(_getIsChange, _setIsChange)
2020-11-17 06:06:45 +01:00
def send(self):
result = self.genDoc()
result = self.checkTXDoc()
result = self.signDoc()
result = self.sendTXDoc()
return result
2020-11-17 03:39:13 +01:00