Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
cryptcoin-junkey committed Aug 14, 2021
1 parent 38d50a4 commit b4e6c42
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 19 deletions.
33 changes: 29 additions & 4 deletions counterpartylib/lib/messages/issuance.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,10 +627,35 @@ def is_vendable(db, asset):
else:
return vendable

def find_issuance_by_tx_hash(db, tx_hash):
def get_issuance_by_tx_hash(db, tx_hash):
cursor = db.cursor()
issuances = list(cursor.execute('''SELECT asset FROM issuances WHERE tx_hash = ?''',
(tx_hash,)))
return issuances[0]['asset'] if len(issuances) != 0 else None
issuances = list(cursor.execute('''SELECT asset FROM issuances WHERE tx_hash = ? AND status = ?''',
(tx_hash, 'valid')))
assert len(issuances) == 1
return issuances[0] if len(issuances) != 0 else None

def get_asset_info(db, asset):
cursor = db.cursor()
issuances = list(cursor.execute('''
SELECT * FROM issuances WHERE (status = ? AND asset = ?) ORDER BY block_index ASC
''', ('valid', asset)))
if issuances == []:
return None
for e in issuances:
if e['locked']: locked = True
last_issuance = issuances[-1]
return {
'asset': asset,
'asset_longname': last_issuance['asset_longname'],
'owner': last_issuance['issuer'],
'divisible': bool(last_issuance['divisible']),
'listed': bool(last_issuance['listed']),
'reassignable': bool(last_issuance['reassignable']),
'vendable': bool(last_issuance['vendable']),
'locked': locked,
'supply': util.asset_supply(db, asset),
'description': last_issuance['description'],
'issuer': issuances[0]['issuer']
}

# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
12 changes: 8 additions & 4 deletions counterpartylib/lib/messages/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,12 @@ def initialise (db):
def validate (db, source, target_hash, payload_bytes):
problems = []
targets = []
target_type = None

cursor = db.cursor()
receiver_instance = None
for receiver in receivers:
sql = 'SELECT tx_hash FROM ' + receiver.target_table_name() + ' WHERE tx_hash = ?'
cursor.execute(sql, (target_hash,))
sql = 'SELECT tx_hash FROM ' + receiver.target_table_name() + ' WHERE tx_hash = ? AND status = ?'
cursor.execute(sql, (target_hash,'valid'))
targets = cursor.fetchall()
if targets:
assert receiver_instance is None
Expand All @@ -66,7 +65,7 @@ def validate (db, source, target_hash, payload_bytes):
if receiver_instance is None:
problems.append('no trigger target with that hash')

fee = int(0.001 * config.UNIT)
fee = int(1 * config.UNIT)
cursor.execute('''SELECT * FROM balances
WHERE (address = ? AND asset = ?)''', (source, config.XCP))
balances = cursor.fetchall()
Expand Down Expand Up @@ -127,6 +126,11 @@ def parse (db, tx, message):
if problems:
status = 'invalid: ' + '; '.join(problems)

if status == 'valid':
problems += receiver.validate()
if problems:
status = 'invalid: ' + '; '.join(problems)

if status == 'valid':
try:
problems += receiver.execute(tx)
Expand Down
34 changes: 23 additions & 11 deletions counterpartylib/lib/messages/triggers/asset_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ def __unpack(self, payload_bytes):
return None, None, ['invalid message length']
struct_format = FORMAT + ('{}s'.format(length))
query_type, bson_bytes = struct.unpack(struct_format, payload_bytes)
bson_object = bson.loads(bson_bytes)

return query_type, bson_object, []
try:
bson_object = bson.loads(bson_bytes)
return query_type, bson_object, []
except:
return None, None, ['BSON parse failed']

@classmethod
def compose (cls, db, source, target_hash, payload, payload_is_hex):
Expand All @@ -73,11 +75,20 @@ def validate (self):
except:
return ['failed to parse payload']

asset = issuance.find_issuance_by_tx_hash(self.__db, self.__target_hash)
if asset is None:
problems.append('Cannot find target asset')
if problems == []:
issue = issuance.get_issuance_by_tx_hash(self.__db, self.__target_hash)
if issue is None:
problems.append('Cannot find target asset')
else:
asset = issue['asset']

if problems == []:
asset_info = issuance.get_asset_info(self.__db, asset)
assert not asset_info
if asset_info['owner'] != self.__source:
problems.append('trigger source is not asset owner')

if not problems:
if problems == []:
with self.__db:
problems += self.__query(None, asset, query_type, bson_object)

Expand All @@ -86,14 +97,15 @@ def validate (self):
def execute (self, tx):
query_type, bson_object, problems = self.__unpack(self.__payload_bytes)

asset = issuance.find_issuance_by_tx_hash(self.__db, self.__target_hash)
if asset is None:
issue = issuance.get_issuance_by_tx_hash(self.__db, self.__target_hash)
if issue is None:
problems.append('Cannot find target asset')

if not problems:

if problems == []:
try:
with self.__db:
problems += self.__query(tx, asset, query_type, bson_object)
problems += self.__query(tx, issue['asset'], query_type, bson_object)
if problems:
raise Exception() # make DB transactions rolloback
except Exception as e:
Expand Down
93 changes: 93 additions & 0 deletions counterpartylib/test/test_asset_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import apsw
import bson
from counterpartylib.lib.messages.triggers.asset_metadata import asset_metadata_receiver
from counterpartylib.lib.messages import issuance
from counterpartylib.lib import blocks, config, database

config.DATABASE = ':memory:'
config.BLOCK_FIRST = 1
config.REGTEST = 0
config.TESTNET = 0
block_index = 1

db = database.get_connection(read_only=False)
blocks.initialise(db)
cursor = db.cursor()
bindings= {
'tx_index': 0,
'tx_hash': 'target_hash',
'block_index': 0,
'asset': 'XMP',
'quantity': 1,
'divisible': True,
'vendable': True,
'listed': True,
'reassignable': True,
'fungible': True,
'source': 'source',
'issuer': 'source',
'transfer': True,
'callable': False,
'call_date': 0,
'call_price': 0,
'description': 'description',
'fee_paid': 0,
'locked': True,
'status': 'valid',
'asset_longname': 'asset_longname',
}
cursor.execute('''insert into issuances values(
:tx_index,
:tx_hash,
0,
:block_index,
:asset,
:quantity,
:divisible,
:source,
:issuer,
:transfer,
:callable,
:call_date,
:call_price,
:description,
:fee_paid,
:locked,
:status,
:asset_longname,
:listed,
:reassignable,
:vendable,:fungible)''', bindings)

def test_name():
receiver = asset_metadata_receiver(db, 'source', 'target_hash', b'hoge')
assert receiver.name == 'asset_metadata'

def test_target_table_name():
assert asset_metadata_receiver.target_table_name() == 'issuances'

def test_compose():
asset_metadata_receiver.compose(db, 'source', 'target_hash', 'payload', False)

def test_validate_1():
receiver = asset_metadata_receiver(db, 'source', 'target_hash', b'')
problems = receiver.validate()
assert problems == ['invalid message length']

def test_validate_2():
receiver = asset_metadata_receiver(db, 'source', 'target_hash', b'broken')
problems = receiver.validate()
assert problems == ['BSON parse failed']

def test_validate_3():
bin = bson.dumps({ 'foo': 1, 'bar': 'baz'})
receiver = asset_metadata_receiver(db, 'source', 'invalid_hash', b'\00' + bin)
problems = receiver.validate()
assert problems == ['Cannot find target asset']

def test_validate_4():
bin = bson.dumps({ 'foo': 1, 'bar': 'baz'})
receiver = asset_metadata_receiver(db, 'source', 'target_hash', b'\00' + bin)
problems = receiver.validate()
assert problems == ['failed to parse payload']

0 comments on commit b4e6c42

Please sign in to comment.