Skip to content

Commit

Permalink
Don't remove spent UTXOs from spendable.
Browse files Browse the repository at this point in the history
Slower, yet enables getting the full history of a key's interactions.
Will be useful in the planned personal_getTransactionHistory mentioned
in #292.

Updates the broken spendable test to the latest policies. It now passes.
  • Loading branch information
kayabaNerve committed Apr 28, 2021
1 parent 28dd39e commit 6f8ed08
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 56 deletions.
43 changes: 7 additions & 36 deletions src/Database/Filesystem/DB/TransactionsDB.nim
Original file line number Diff line number Diff line change
Expand Up @@ -362,12 +362,11 @@ proc removeFromSpendable(
o += 33
db.put(SPENDABLE(key), spendable)

#Add the Transaction's outputs to spendable while removing spent inputs.
#Add the Transaction's outputs to spendable.
proc verify*(
db: DB,
tx: Transaction
) {.forceCheck: [].} =
#Add spendable outputs.
if (tx of Claim) or (tx of Send):
for o in 0 ..< tx.outputs.len:
db.addToSpendable(
Expand All @@ -376,50 +375,19 @@ proc verify*(
o
)

if tx of Send:
#Remove spent inputs.
for input in tx.inputs:
var key: EdPublicKey
try:
key = db.loadSendOutput(cast[FundedInput](input)).key
except DBReadError:
panic("Removing a non-existent output.")

db.removeFromSpendable(
key,
input.hash,
cast[FundedInput](input).nonce
)

#Add a inputs back to spendable while removing unverified outputs.
#Removes outputs from spendable.
proc unverify*(
db: DB,
tx: Transaction
) {.forceCheck: [].} =
if (tx of Claim) or (tx of Send):
#Remove outputs.
for o in 0 ..< tx.outputs.len:
db.removeFromSpendable(
cast[SendOutput](tx.outputs[o]).key,
tx.hash,
o
)

#Restore inputs.
if tx of Send:
for input in tx.inputs:
var key: EdPublicKey
try:
key = db.loadSendOutput(cast[FundedInput](input)).key
except DBReadError:
panic("Restoring a non-existent output.")

db.addToSpendable(
key,
input.hash,
cast[FundedInput](input).nonce
)

#Mark a Transaction as beaten.
proc beat*(
db: DB,
Expand Down Expand Up @@ -487,6 +455,9 @@ proc prune*(
discard
db.del(OUTPUT_SPENDERS(newFundedInput(hash, o)))

#If it has no spenders and is tracked by spendable, remove it.
if (spenders == "") and (tx.outputs[o] of SendOutput):
#If it is tracked by spendable, remove it.
#Should be handled by unverify, so I'm not really sure why this is here.
#That said, it doesn't hurt to have, and it's safe to run even if the outputs aren't present in spendable.
#-- Kayaba
if tx.outputs[o] of SendOutput:
db.removeFromSpendable(cast[SendOutput](tx.outputs[o]).key, hash, o)
27 changes: 7 additions & 20 deletions tests/Database/Filesystem/DB/TransactionsDB/SpendableTest.nim
Original file line number Diff line number Diff line change
Expand Up @@ -88,57 +88,44 @@ suite "Spendable":
compare()

#Spend outputs.
var queue: seq[(EdPublicKey, FundedInput)] = @[]
for key in spendable.keys():
if spendable[key].len == 0:
continue

inputs = @[]
var i: int = 0
while true:
while i != spendable[key].len:
if rand(1) == 0:
inputs.add(spendable[key][i])
spendable[key].delete(i)
else:
inc(i)

if i == spendable[key].len:
break

if inputs.len != 0:
var outputKey: EdPublicKey = wallets[rand(10 - 1)].publicKey
send = newSend(inputs, newSendOutput(outputKey, 0))
db.save(send)
db.verify(send)
sends.add(send)

queue.add((outputKey, newFundedInput(send.hash, 0)))
spenders[send.hash.serialize() & char(0)] = outputKey

for output in queue:
if not spendable.hasKey(output[0]):
spendable[output[0]] = @[]
spendable[output[0]].add(output[1])

compare()

#Unverify a Send.
if sends.len != 0:
var s: int = rand(sends.high)
db.unverify(sends[s])
for input in sends[s].inputs:
spendable[
spenders[input.hash.serialize() & char(cast[FundedInput](input).nonce)]
].add(cast[FundedInput](input))

for o1 in 0 ..< sends[s].outputs.len:
var output: SendOutput = cast[SendOutput](sends[s].outputs[o1])
for o2 in 0 ..< spendable[output.key].len:
var
output: SendOutput = cast[SendOutput](sends[s].outputs[o1])
o2: int = 0
while o2 < spendable[output.key].len:
if (
(spendable[output.key][o2].hash == sends[s].hash) and
(spendable[output.key][o2].nonce == o1)
):
spendable[output.key].delete(o2)
break
else:
inc(o2)

compare()

0 comments on commit 6f8ed08

Please sign in to comment.