Fix double spending of inputs issue on ledger level in test dsl. (#15)

* Fix double spending of inputs issue on ledger level in test dsl.

* Address PR comments.
This commit is contained in:
kasiastreich 2016-12-08 10:15:33 +00:00 committed by GitHub
parent 365bc58fc2
commit 036b1b4964
3 changed files with 51 additions and 5 deletions

View File

@ -107,7 +107,6 @@ sealed class TransactionVerificationException(val tx: LedgerTransaction, cause:
override val message: String?
get() = "Missing required encumbrance ${missing} in ${inOut}"
}
enum class Direction {
INPUT,
OUTPUT

View File

@ -717,4 +717,39 @@ class CashTests {
// Test that summing everything fails because we're mixing units
states.sumCash()
}
// Double spend.
@Test
fun chainCashDoubleSpendFailsWith() {
ledger {
unverifiedTransaction {
output("MEGA_CORP cash") {
Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP_PUBKEY
)
}
}
transaction {
input("MEGA_CORP cash")
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
this.verifies()
}
tweak {
transaction {
input("MEGA_CORP cash")
// We send it to another pubkey so that the transaction is not identical to the previous one
output("MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE_PUBKEY))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
this.verifies()
}
this.fails()
}
this.verifies()
}
}
}

View File

@ -79,6 +79,7 @@ sealed class EnforceVerifyOrFail {
}
class DuplicateOutputLabel(label: String) : Exception("Output label '$label' already used")
class DoubleSpentInputs(ids: List<SecureHash>) : Exception("Transactions spend the same input. Conflicting transactions ids: '$ids'")
class AttachmentResolutionException(attachmentId: SecureHash) : Exception("Attachment with id $attachmentId not found")
/**
@ -167,7 +168,7 @@ data class TestLedgerDSLInterpreter private constructor (
companion object {
private fun getCallerLocation(): String? {
val stackTrace = Thread.currentThread().stackTrace
for (i in 1 .. stackTrace.size) {
for (i in 1..stackTrace.size) {
val stackTraceElement = stackTrace[i]
if (!stackTraceElement.fileName.contains("DSL")) {
return stackTraceElement.toString()
@ -182,8 +183,10 @@ data class TestLedgerDSLInterpreter private constructor (
val transaction: WireTransaction,
val location: String?
)
class VerifiesFailed(transactionName: String, cause: Throwable) :
Exception("Transaction ($transactionName) didn't verify: $cause", cause)
class TypeMismatch(requested: Class<*>, actual: Class<*>) :
Exception("Actual type $actual is not a subtype of requested type $requested")
@ -249,7 +252,6 @@ data class TestLedgerDSLInterpreter private constructor (
}
labelToOutputStateAndRefs[label] = wireTransaction.outRef(index)
}
transactionMap[wireTransaction.id] =
WireTransactionWithLocation(transactionLabel, wireTransaction, transactionLocation)
@ -279,10 +281,20 @@ data class TestLedgerDSLInterpreter private constructor (
override fun verifies(): EnforceVerifyOrFail {
try {
val usedInputs = mutableSetOf<StateRef>()
services.recordTransactions(transactionsUnverified.map { SignedTransaction(it.serialized, listOf(NullSignature), it.id) })
for ((key, value) in transactionWithLocations) {
value.transaction.toLedgerTransaction(services).verify()
services.recordTransactions(SignedTransaction(value.transaction.serialized, listOf(NullSignature), value.transaction.id))
val wtx = value.transaction
val ltx = wtx.toLedgerTransaction(services)
ltx.verify()
val doubleSpend = wtx.inputs.intersect(usedInputs)
if (!doubleSpend.isEmpty()) {
val txIds = mutableListOf(wtx.id)
doubleSpend.mapTo(txIds) { it.txhash }
throw DoubleSpentInputs(txIds)
}
usedInputs.addAll(wtx.inputs)
services.recordTransactions(SignedTransaction(wtx.serialized, listOf(NullSignature), wtx.id))
}
return EnforceVerifyOrFail.Token
} catch (exception: TransactionVerificationException) {