forked from LBRYCommunity/lbry-sdk
added is_spent attribute to transaction outputs
This commit is contained in:
parent
cc69faa1fd
commit
c255c606a7
6 changed files with 53 additions and 12 deletions
|
@ -25,6 +25,7 @@ def encode_txo_doc():
|
||||||
'address': "address of who can spend the txo",
|
'address': "address of who can spend the txo",
|
||||||
'confirmations': "number of confirmed blocks",
|
'confirmations': "number of confirmed blocks",
|
||||||
'is_change': "payment to change address, only available when it can be determined",
|
'is_change': "payment to change address, only available when it can be determined",
|
||||||
|
'is_spent': "true if txo is spent, false or None if it could not be determined",
|
||||||
'is_mine': "payment to one of your accounts, only available when it can be determined",
|
'is_mine': "payment to one of your accounts, only available when it can be determined",
|
||||||
'type': "one of 'claim', 'support' or 'purchase'",
|
'type': "one of 'claim', 'support' or 'purchase'",
|
||||||
'name': "when type is 'claim' or 'support', this is the claim name",
|
'name': "when type is 'claim' or 'support', this is the claim name",
|
||||||
|
@ -168,6 +169,8 @@ class JSONResponseEncoder(JSONEncoder):
|
||||||
}
|
}
|
||||||
if txo.is_change is not None:
|
if txo.is_change is not None:
|
||||||
output['is_change'] = txo.is_change
|
output['is_change'] = txo.is_change
|
||||||
|
if txo.is_spent is not None:
|
||||||
|
output['is_spent'] = txo.is_spent
|
||||||
if txo.is_my_account is not None:
|
if txo.is_my_account is not None:
|
||||||
output['is_mine'] = txo.is_my_account
|
output['is_mine'] = txo.is_my_account
|
||||||
|
|
||||||
|
|
|
@ -548,6 +548,13 @@ class CommandTestCase(IntegrationTestCase):
|
||||||
self.daemon.jsonrpc_support_create(claim_id, bid, **kwargs), confirm
|
self.daemon.jsonrpc_support_create(claim_id, bid, **kwargs), confirm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def support_abandon(self, *args, confirm=True, **kwargs):
|
||||||
|
if 'blocking' not in kwargs:
|
||||||
|
kwargs['blocking'] = False
|
||||||
|
return await self.confirm_and_render(
|
||||||
|
self.daemon.jsonrpc_support_abandon(*args, **kwargs), confirm
|
||||||
|
)
|
||||||
|
|
||||||
async def resolve(self, uri):
|
async def resolve(self, uri):
|
||||||
return (await self.out(self.daemon.jsonrpc_resolve(uri)))[uri]
|
return (await self.out(self.daemon.jsonrpc_resolve(uri)))[uri]
|
||||||
|
|
||||||
|
|
|
@ -488,6 +488,8 @@ class Database(SQLiteMixin):
|
||||||
*query(f"SELECT {cols} FROM tx", **constraints)
|
*query(f"SELECT {cols} FROM tx", **constraints)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TXO_NOT_MINE = Output(None, None, is_my_account=False)
|
||||||
|
|
||||||
async def get_transactions(self, wallet=None, **constraints):
|
async def get_transactions(self, wallet=None, **constraints):
|
||||||
tx_rows = await self.select_transactions(
|
tx_rows = await self.select_transactions(
|
||||||
'txid, raw, height, position, is_verified',
|
'txid, raw, height, position, is_verified',
|
||||||
|
@ -538,7 +540,7 @@ class Database(SQLiteMixin):
|
||||||
if _txo:
|
if _txo:
|
||||||
txo.update_annotations(_txo)
|
txo.update_annotations(_txo)
|
||||||
else:
|
else:
|
||||||
txo.update_annotations(None)
|
txo.update_annotations(self.TXO_NOT_MINE)
|
||||||
|
|
||||||
for tx in txs:
|
for tx in txs:
|
||||||
txos = tx.outputs
|
txos = tx.outputs
|
||||||
|
@ -577,7 +579,7 @@ class Database(SQLiteMixin):
|
||||||
tx.txid, raw, tx.height, tx.position, tx.is_verified, txo.position, amount, script, (
|
tx.txid, raw, tx.height, tx.position, tx.is_verified, txo.position, amount, script, (
|
||||||
select group_concat(account||"|"||chain) from account_address
|
select group_concat(account||"|"||chain) from account_address
|
||||||
where account_address.address=txo.address
|
where account_address.address=txo.address
|
||||||
)
|
), exists(select txoid from txi where txi.txoid=txo.txoid)
|
||||||
""",
|
""",
|
||||||
**constraints
|
**constraints
|
||||||
)
|
)
|
||||||
|
@ -599,6 +601,7 @@ class Database(SQLiteMixin):
|
||||||
txo = txs[row[0]].outputs[row[5]]
|
txo = txs[row[0]].outputs[row[5]]
|
||||||
row_accounts = dict(a.split('|') for a in row[8].split(','))
|
row_accounts = dict(a.split('|') for a in row[8].split(','))
|
||||||
account_match = set(row_accounts) & my_accounts
|
account_match = set(row_accounts) & my_accounts
|
||||||
|
txo.is_spent = bool(row[9])
|
||||||
if account_match:
|
if account_match:
|
||||||
txo.is_my_account = True
|
txo.is_my_account = True
|
||||||
txo.is_change = row_accounts[account_match.pop()] == '1'
|
txo.is_change = row_accounts[account_match.pop()] == '1'
|
||||||
|
|
|
@ -844,7 +844,8 @@ class Ledger(metaclass=LedgerRegistry):
|
||||||
'amount': dewies_to_lbc(txo.amount),
|
'amount': dewies_to_lbc(txo.amount),
|
||||||
'claim_id': txo.claim_id,
|
'claim_id': txo.claim_id,
|
||||||
'claim_name': txo.claim_name,
|
'claim_name': txo.claim_name,
|
||||||
'nout': txo.position
|
'nout': txo.position,
|
||||||
|
'is_spent': txo.is_spent,
|
||||||
})
|
})
|
||||||
for txo in tx.my_update_outputs:
|
for txo in tx.my_update_outputs:
|
||||||
if is_my_inputs: # updating my own claim
|
if is_my_inputs: # updating my own claim
|
||||||
|
@ -863,7 +864,8 @@ class Ledger(metaclass=LedgerRegistry):
|
||||||
'amount': dewies_to_lbc(txo.amount),
|
'amount': dewies_to_lbc(txo.amount),
|
||||||
'claim_id': txo.claim_id,
|
'claim_id': txo.claim_id,
|
||||||
'claim_name': txo.claim_name,
|
'claim_name': txo.claim_name,
|
||||||
'nout': txo.position
|
'nout': txo.position,
|
||||||
|
'is_spent': txo.is_spent,
|
||||||
})
|
})
|
||||||
else: # someone sent us their claim
|
else: # someone sent us their claim
|
||||||
item['update_info'].append({
|
item['update_info'].append({
|
||||||
|
@ -872,7 +874,8 @@ class Ledger(metaclass=LedgerRegistry):
|
||||||
'amount': dewies_to_lbc(txo.amount),
|
'amount': dewies_to_lbc(txo.amount),
|
||||||
'claim_id': txo.claim_id,
|
'claim_id': txo.claim_id,
|
||||||
'claim_name': txo.claim_name,
|
'claim_name': txo.claim_name,
|
||||||
'nout': txo.position
|
'nout': txo.position,
|
||||||
|
'is_spent': txo.is_spent,
|
||||||
})
|
})
|
||||||
for txo in tx.my_support_outputs:
|
for txo in tx.my_support_outputs:
|
||||||
item['support_info'].append({
|
item['support_info'].append({
|
||||||
|
@ -882,7 +885,8 @@ class Ledger(metaclass=LedgerRegistry):
|
||||||
'claim_id': txo.claim_id,
|
'claim_id': txo.claim_id,
|
||||||
'claim_name': txo.claim_name,
|
'claim_name': txo.claim_name,
|
||||||
'is_tip': not is_my_inputs,
|
'is_tip': not is_my_inputs,
|
||||||
'nout': txo.position
|
'nout': txo.position,
|
||||||
|
'is_spent': txo.is_spent,
|
||||||
})
|
})
|
||||||
if is_my_inputs:
|
if is_my_inputs:
|
||||||
for txo in tx.other_support_outputs:
|
for txo in tx.other_support_outputs:
|
||||||
|
@ -893,7 +897,8 @@ class Ledger(metaclass=LedgerRegistry):
|
||||||
'claim_id': txo.claim_id,
|
'claim_id': txo.claim_id,
|
||||||
'claim_name': txo.claim_name,
|
'claim_name': txo.claim_name,
|
||||||
'is_tip': is_my_inputs,
|
'is_tip': is_my_inputs,
|
||||||
'nout': txo.position
|
'nout': txo.position,
|
||||||
|
'is_spent': txo.is_spent,
|
||||||
})
|
})
|
||||||
for txo in tx.my_abandon_outputs:
|
for txo in tx.my_abandon_outputs:
|
||||||
item['abandon_info'].append({
|
item['abandon_info'].append({
|
||||||
|
@ -910,7 +915,8 @@ class Ledger(metaclass=LedgerRegistry):
|
||||||
'balance_delta': dewies_to_lbc(txo.amount if not is_my_inputs else -txo.amount),
|
'balance_delta': dewies_to_lbc(txo.amount if not is_my_inputs else -txo.amount),
|
||||||
'amount': dewies_to_lbc(txo.amount),
|
'amount': dewies_to_lbc(txo.amount),
|
||||||
'claim_id': txo.purchased_claim_id,
|
'claim_id': txo.purchased_claim_id,
|
||||||
'nout': txo.position
|
'nout': txo.position,
|
||||||
|
'is_spent': txo.is_spent,
|
||||||
})
|
})
|
||||||
history.append(item)
|
history.append(item)
|
||||||
return history
|
return history
|
||||||
|
|
|
@ -207,7 +207,7 @@ class OutputEffectiveAmountEstimator:
|
||||||
class Output(InputOutput):
|
class Output(InputOutput):
|
||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'amount', 'script', 'is_change', 'is_my_account',
|
'amount', 'script', 'is_change', 'is_spent', 'is_my_account',
|
||||||
'channel', 'private_key', 'meta',
|
'channel', 'private_key', 'meta',
|
||||||
'purchase', 'purchased_claim', 'purchase_receipt',
|
'purchase', 'purchased_claim', 'purchase_receipt',
|
||||||
'reposted_claim', 'claims',
|
'reposted_claim', 'claims',
|
||||||
|
@ -215,13 +215,15 @@ class Output(InputOutput):
|
||||||
|
|
||||||
def __init__(self, amount: int, script: OutputScript,
|
def __init__(self, amount: int, script: OutputScript,
|
||||||
tx_ref: TXRef = None, position: int = None,
|
tx_ref: TXRef = None, position: int = None,
|
||||||
is_change: Optional[bool] = None, is_my_account: Optional[bool] = None,
|
is_change: Optional[bool] = None, is_spent: Optional[bool] = None,
|
||||||
|
is_my_account: Optional[bool] = None,
|
||||||
channel: Optional['Output'] = None, private_key: Optional[str] = None
|
channel: Optional['Output'] = None, private_key: Optional[str] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(tx_ref, position)
|
super().__init__(tx_ref, position)
|
||||||
self.amount = amount
|
self.amount = amount
|
||||||
self.script = script
|
self.script = script
|
||||||
self.is_change = is_change
|
self.is_change = is_change
|
||||||
|
self.is_spent = is_spent
|
||||||
self.is_my_account = is_my_account
|
self.is_my_account = is_my_account
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
self.private_key = private_key
|
self.private_key = private_key
|
||||||
|
@ -234,10 +236,12 @@ class Output(InputOutput):
|
||||||
|
|
||||||
def update_annotations(self, annotated):
|
def update_annotations(self, annotated):
|
||||||
if annotated is None:
|
if annotated is None:
|
||||||
self.is_change = False
|
self.is_change = None
|
||||||
self.is_my_account = False
|
self.is_spent = None
|
||||||
|
self.is_my_account = None
|
||||||
else:
|
else:
|
||||||
self.is_change = annotated.is_change
|
self.is_change = annotated.is_change
|
||||||
|
self.is_spent = annotated.is_spent
|
||||||
self.is_my_account = annotated.is_my_account
|
self.is_my_account = annotated.is_my_account
|
||||||
self.channel = annotated.channel if annotated else None
|
self.channel = annotated.channel if annotated else None
|
||||||
self.private_key = annotated.private_key if annotated else None
|
self.private_key = annotated.private_key if annotated else None
|
||||||
|
|
|
@ -1369,6 +1369,7 @@ class StreamCommands(ClaimTestCase):
|
||||||
self.assertEqual(txs[0]['confirmations'], 1)
|
self.assertEqual(txs[0]['confirmations'], 1)
|
||||||
self.assertEqual(txs[0]['claim_info'][0]['balance_delta'], '-2.5')
|
self.assertEqual(txs[0]['claim_info'][0]['balance_delta'], '-2.5')
|
||||||
self.assertEqual(txs[0]['claim_info'][0]['claim_id'], claim_id)
|
self.assertEqual(txs[0]['claim_info'][0]['claim_id'], claim_id)
|
||||||
|
self.assertFalse(txs[0]['claim_info'][0]['is_spent'])
|
||||||
self.assertEqual(txs[0]['value'], '0.0')
|
self.assertEqual(txs[0]['value'], '0.0')
|
||||||
self.assertEqual(txs[0]['fee'], '-0.020107')
|
self.assertEqual(txs[0]['fee'], '-0.020107')
|
||||||
await self.assertBalance(self.account, '7.479893')
|
await self.assertBalance(self.account, '7.479893')
|
||||||
|
@ -1382,6 +1383,8 @@ class StreamCommands(ClaimTestCase):
|
||||||
self.assertEqual(len(txs[0]['update_info']), 1)
|
self.assertEqual(len(txs[0]['update_info']), 1)
|
||||||
self.assertEqual(txs[0]['update_info'][0]['balance_delta'], '1.5')
|
self.assertEqual(txs[0]['update_info'][0]['balance_delta'], '1.5')
|
||||||
self.assertEqual(txs[0]['update_info'][0]['claim_id'], claim_id)
|
self.assertEqual(txs[0]['update_info'][0]['claim_id'], claim_id)
|
||||||
|
self.assertFalse(txs[0]['update_info'][0]['is_spent'])
|
||||||
|
self.assertTrue(txs[1]['claim_info'][0]['is_spent'])
|
||||||
self.assertEqual(txs[0]['value'], '0.0')
|
self.assertEqual(txs[0]['value'], '0.0')
|
||||||
self.assertEqual(txs[0]['fee'], '-0.0002165')
|
self.assertEqual(txs[0]['fee'], '-0.0002165')
|
||||||
await self.assertBalance(self.account, '8.9796765')
|
await self.assertBalance(self.account, '8.9796765')
|
||||||
|
@ -1391,6 +1394,9 @@ class StreamCommands(ClaimTestCase):
|
||||||
self.assertEqual(len(txs[0]['abandon_info']), 1)
|
self.assertEqual(len(txs[0]['abandon_info']), 1)
|
||||||
self.assertEqual(txs[0]['abandon_info'][0]['balance_delta'], '1.0')
|
self.assertEqual(txs[0]['abandon_info'][0]['balance_delta'], '1.0')
|
||||||
self.assertEqual(txs[0]['abandon_info'][0]['claim_id'], claim_id)
|
self.assertEqual(txs[0]['abandon_info'][0]['claim_id'], claim_id)
|
||||||
|
self.assertTrue(txs[0]['abandon_info'][0]['is_spent'])
|
||||||
|
self.assertTrue(txs[1]['update_info'][0]['is_spent'])
|
||||||
|
self.assertTrue(txs[2]['claim_info'][0]['is_spent'])
|
||||||
self.assertEqual(txs[0]['value'], '0.0')
|
self.assertEqual(txs[0]['value'], '0.0')
|
||||||
self.assertEqual(txs[0]['fee'], '-0.000107')
|
self.assertEqual(txs[0]['fee'], '-0.000107')
|
||||||
await self.assertBalance(self.account, '9.9795695')
|
await self.assertBalance(self.account, '9.9795695')
|
||||||
|
@ -1494,6 +1500,7 @@ class SupportCommands(CommandTestCase):
|
||||||
self.assertEqual(txs[0]['support_info'][0]['balance_delta'], '1.0')
|
self.assertEqual(txs[0]['support_info'][0]['balance_delta'], '1.0')
|
||||||
self.assertEqual(txs[0]['support_info'][0]['claim_id'], claim_id)
|
self.assertEqual(txs[0]['support_info'][0]['claim_id'], claim_id)
|
||||||
self.assertTrue(txs[0]['support_info'][0]['is_tip'])
|
self.assertTrue(txs[0]['support_info'][0]['is_tip'])
|
||||||
|
self.assertFalse(txs[0]['support_info'][0]['is_spent'])
|
||||||
self.assertEqual(txs[0]['value'], '1.0')
|
self.assertEqual(txs[0]['value'], '1.0')
|
||||||
self.assertEqual(txs[0]['fee'], '0.0')
|
self.assertEqual(txs[0]['fee'], '0.0')
|
||||||
|
|
||||||
|
@ -1505,6 +1512,7 @@ class SupportCommands(CommandTestCase):
|
||||||
self.assertEqual(txs2[0]['support_info'][0]['balance_delta'], '-1.0')
|
self.assertEqual(txs2[0]['support_info'][0]['balance_delta'], '-1.0')
|
||||||
self.assertEqual(txs2[0]['support_info'][0]['claim_id'], claim_id)
|
self.assertEqual(txs2[0]['support_info'][0]['claim_id'], claim_id)
|
||||||
self.assertTrue(txs2[0]['support_info'][0]['is_tip'])
|
self.assertTrue(txs2[0]['support_info'][0]['is_tip'])
|
||||||
|
self.assertFalse(txs2[0]['support_info'][0]['is_spent'])
|
||||||
self.assertEqual(txs2[0]['value'], '-1.0')
|
self.assertEqual(txs2[0]['value'], '-1.0')
|
||||||
self.assertEqual(txs2[0]['fee'], '-0.0001415')
|
self.assertEqual(txs2[0]['fee'], '-0.0001415')
|
||||||
|
|
||||||
|
@ -1525,9 +1533,19 @@ class SupportCommands(CommandTestCase):
|
||||||
self.assertEqual(txs2[0]['support_info'][0]['balance_delta'], '-2.0')
|
self.assertEqual(txs2[0]['support_info'][0]['balance_delta'], '-2.0')
|
||||||
self.assertEqual(txs2[0]['support_info'][0]['claim_id'], claim_id)
|
self.assertEqual(txs2[0]['support_info'][0]['claim_id'], claim_id)
|
||||||
self.assertFalse(txs2[0]['support_info'][0]['is_tip'])
|
self.assertFalse(txs2[0]['support_info'][0]['is_tip'])
|
||||||
|
self.assertFalse(txs2[0]['support_info'][0]['is_spent'])
|
||||||
self.assertEqual(txs2[0]['value'], '0.0')
|
self.assertEqual(txs2[0]['value'], '0.0')
|
||||||
self.assertEqual(txs2[0]['fee'], '-0.0001415')
|
self.assertEqual(txs2[0]['fee'], '-0.0001415')
|
||||||
|
|
||||||
|
# abandoning the tip increases balance and shows tip as spent
|
||||||
|
await self.support_abandon(claim_id)
|
||||||
|
await self.assertBalance(self.account, '4.979662')
|
||||||
|
txs = (await self.out(self.daemon.jsonrpc_transaction_list(self.account.id)))['items']
|
||||||
|
self.assertEqual(len(txs[0]['abandon_info']), 1)
|
||||||
|
self.assertEqual(len(txs[1]['support_info']), 1)
|
||||||
|
self.assertTrue(txs[1]['support_info'][0]['is_tip'])
|
||||||
|
self.assertTrue(txs[1]['support_info'][0]['is_spent'])
|
||||||
|
|
||||||
|
|
||||||
class CollectionCommands(CommandTestCase):
|
class CollectionCommands(CommandTestCase):
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue