Skip to content
Snippets Groups Projects
Commit d5674c3d authored by Vincent Texier's avatar Vincent Texier
Browse files

[fix] #798 add condition evaluation on automatic sources when creating a transaction

This is to avoid using locked sources

Need all the tx referenced by the sources in the db !

Fix the previous commit that add complex and potentially locked sources in db
parent e2313e7d
No related branches found
No related tags found
1 merge request!778Release 0.51.0
...@@ -111,18 +111,21 @@ class Transaction: ...@@ -111,18 +111,21 @@ class Transaction:
Transaction entity Transaction entity
:param str currency: the currency of the transaction :param str currency: the currency of the transaction
:param str pubkey: the pubkey of the issuer
:param str sha_hash: the hash of the transaction :param str sha_hash: the hash of the transaction
:param int written_block: the number of the block :param int written_block: the number of the block
:param duniterpy.documents.BlockUID blockstamp: the blockstamp of the transaction :param duniterpy.documents.BlockUID blockstamp: the blockstamp of the transaction
:param int timestamp: the timestamp of the transaction :param int timestamp: the timestamp of the transaction
:param str signature: the signature :param str signatures: the signature
:param str issuer: the pubkey of the issuer :param tuple issuers: tuple of pubkey of the issuers
:param str receiver: the pubkey of the receiver :param tuple receivers: tuple of pubkey of the receivers
:param int amount: the amount :param int amount: the amount
:param int amount_base: the amount base :param int amount_base: the amount base
:param str comment: a comment :param str comment: a comment
:param str txid: the transaction id to sort transctions :param int txid: the transaction id to sort transctions
:param int state: the state of the transaction :param int state: the state of the transaction
:param bool local: is the transaction local
:param str raw: the raw string of the transaction
""" """
TO_SEND = 0 TO_SEND = 0
......
...@@ -92,7 +92,7 @@ class TransactionsProcessor: ...@@ -92,7 +92,7 @@ class TransactionsProcessor:
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
self._repo.update(tx) self._repo.update(tx)
def find_by_hash(self, pubkey, sha_hash): def find_by_hash(self, pubkey: str, sha_hash: str) -> Transaction:
return self._repo.get_one(pubkey=pubkey, sha_hash=sha_hash) return self._repo.get_one(pubkey=pubkey, sha_hash=sha_hash)
def awaiting(self, currency): def awaiting(self, currency):
......
...@@ -75,7 +75,7 @@ class TransactionsRepo: ...@@ -75,7 +75,7 @@ class TransactionsRepo:
def get_one(self, **search): def get_one(self, **search):
""" """
Get an existing transaction in the database Get an existing transaction in the database
:param dict search: the criterions of the lookup :param ** search: the criterions of the lookup
:rtype: sakia.data.entities.Transaction :rtype: sakia.data.entities.Transaction
""" """
filters = [] filters = []
......
from hashlib import sha256
import jsonschema import jsonschema
import attr import attr
import logging import logging
...@@ -18,7 +20,7 @@ from duniterpy.documents import ( ...@@ -18,7 +20,7 @@ from duniterpy.documents import (
from duniterpy.documents import Identity as IdentityDoc from duniterpy.documents import Identity as IdentityDoc
from duniterpy.documents import Transaction as TransactionDoc from duniterpy.documents import Transaction as TransactionDoc
from duniterpy.documents.transaction import reduce_base from duniterpy.documents.transaction import reduce_base
from duniterpy.grammars.output import Condition, Operator, SIG, CSV from duniterpy.grammars.output import Condition, Operator, SIG, CSV, CLTV, XHX
from duniterpy.api import bma from duniterpy.api import bma
from sakia.data.entities import Identity, Transaction, Source from sakia.data.entities import Identity, Transaction, Source
from sakia.data.processors import ( from sakia.data.processors import (
...@@ -298,13 +300,13 @@ class DocumentsService: ...@@ -298,13 +300,13 @@ class DocumentsService:
return document.signed_raw(), identity return document.signed_raw(), identity
def tx_sources(self, amount, amount_base, currency, pubkey): def tx_sources(self, amount, amount_base, currency, key: SigningKey):
""" """
Get inputs to generate a transaction with a given amount of money Get inputs to generate a transaction with a given amount of money
:param int amount: The amount target value :param int amount: The amount target value
:param int amount_base: The amount base target value :param int amount_base: The amount base target value
:param str currency: The community target of the transaction :param str currency: The community target of the transaction
:param str pubkey: The pubkey owning the sources :param str key: The key owning the sources
:return: The list of inputs to use in the transaction document :return: The list of inputs to use in the transaction document
""" """
...@@ -321,7 +323,7 @@ class DocumentsService: ...@@ -321,7 +323,7 @@ class DocumentsService:
return i return i
amount, amount_base = reduce_base(amount, amount_base) amount, amount_base = reduce_base(amount, amount_base)
available_sources = self._sources_processor.available(currency, pubkey) available_sources = self._sources_processor.available(currency, key.pubkey)
if available_sources: if available_sources:
current_base = max([src.base for src in available_sources]) current_base = max([src.base for src in available_sources])
value = 0 value = 0
...@@ -331,6 +333,12 @@ class DocumentsService: ...@@ -331,6 +333,12 @@ class DocumentsService:
buf_sources = list(available_sources) buf_sources = list(available_sources)
while current_base >= 0: while current_base >= 0:
for s in [src for src in available_sources if src.base == current_base]: for s in [src for src in available_sources if src.base == current_base]:
condition = pypeg2.parse(s.conditions, Condition)
# evaluate the condition
if not self.evaluate_condition(
currency, condition, [key], [], s.identifier
):
continue
test_sources = sources + [s] test_sources = sources + [s]
val = current_value(test_sources, overheads) val = current_value(test_sources, overheads)
# if we have to compute an overhead # if we have to compute an overhead
...@@ -503,7 +511,7 @@ class DocumentsService: ...@@ -503,7 +511,7 @@ class DocumentsService:
forged_tx = [] forged_tx = []
sources = [None] * 41 sources = [None] * 41
while len(sources) > 40: while len(sources) > 40:
result = self.tx_sources(int(amount), amount_base, currency, key.pubkey) result = self.tx_sources(int(amount), amount_base, currency, key)
sources = result[0] sources = result[0]
computed_outputs = result[1] computed_outputs = result[1]
overheads = result[2] overheads = result[2]
...@@ -626,3 +634,97 @@ class DocumentsService: ...@@ -626,3 +634,97 @@ class DocumentsService:
return result return result
except NotEnoughChangeError as e: except NotEnoughChangeError as e:
return (False, str(e)), tx_entities return (False, str(e)), tx_entities
def evaluate_condition(
self,
currency,
condition: Condition,
keys: list,
passwords,
identifier: str,
result: bool = True,
) -> bool:
"""
Evaluate a source lock condition
Support multiple signatures and passwords
:param passwords:
:param str currency: Name of currency
:param Condition condition: Condition instance
:param [SigningKey] keys: Keys to unlock condition (first key is the source owner key)
:param str identifier: Source transaction identifier
:param bool result: result accumulator
:return:
"""
left = False
right = False
# if left param is a condition...
if isinstance(condition.left, Condition):
# evaluate condition
left = self.evaluate_condition(
currency, condition.left, keys, passwords, identifier, result
)
# if right param is a condition...
if isinstance(condition.right, Condition):
# evaluate condition
right = self.evaluate_condition(
currency, condition.right, keys, passwords, identifier, result
)
# if left param is a SIG...
if isinstance(condition.left, SIG) and condition.left.pubkey in (
key.pubkey for key in keys
):
left = True
# if left param is a CSV value...
if isinstance(condition.left, CSV):
# capture transaction of the source
tx = self._transactions_processor.find_by_hash(keys[0].pubkey, identifier)
if tx:
# capture current blockchain time
median_time = self._blockchain_processor.time(currency)
# param is true if tx time + CSV delay <= blockchain time
left = tx.timestamp + int(condition.left.time) <= median_time
# if left param is a CLTV value...
if isinstance(condition.left, CLTV):
# capture current blockchain time
median_time = self._blockchain_processor.time(currency)
# param is true if CL:TV value <= blockchain time
left = int(condition.left.timestamp) <= median_time
# if left param is a XHX value...
if isinstance(condition.left, XHX):
left = condition.left.sha_hash in [
sha256(password).hexdigest().upper() for password in passwords
]
# if no op then stop evaluation...
if not condition.op:
return left
# if right param is a SIG...
if isinstance(condition.right, SIG) and condition.right.pubkey in (
key.pubkey for key in keys
):
right = True
# if right param is a CSV value...
if isinstance(condition.right, CSV):
# capture transaction of the source
tx = self._transactions_processor.find_by_hash(keys[0].pubkey, identifier)
if tx:
# capture current blockchain time
median_time = self._blockchain_processor.time(currency)
# param is true if tx time + CSV delay <= blockchain time
right = tx.timestamp + int(condition.right.time) <= median_time
# if right param is a CLTV value...
if isinstance(condition.right, CLTV):
# capture current blockchain time
median_time = self._blockchain_processor.time(currency)
# param is true if CLTV value <= blockchain time
right = int(condition.right.timestamp) <= median_time
# if right param is a XHX value...
if isinstance(condition.right, XHX):
right = condition.right.sha_hash in [
sha256(password).upper() for password in passwords
]
# if operator AND...
if condition.op == "&&":
return left & right
# operator OR
return left | right
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment