mirror of
https://github.com/corda/corda.git
synced 2025-06-17 06:38:21 +00:00
Fixed subtle bug in Cash Spending when processing for same Issuer with multiple refs.
This commit is contained in:
@ -79,8 +79,9 @@ class ServerRPCOps(
|
||||
val builder: TransactionBuilder = TransactionType.General.Builder(null)
|
||||
// TODO: Have some way of restricting this to states the caller controls
|
||||
try {
|
||||
val (spendTX, keysForSigning) = services.vaultService.generateSpend(builder, req.amount.withoutIssuer(), req.recipient.owningKey,
|
||||
setOf(req.amount.token.issuer))
|
||||
val (spendTX, keysForSigning) = services.vaultService.generateSpend(builder,
|
||||
req.amount.withoutIssuer(), req.recipient.owningKey, setOf(req.amount.token.issuer.party))
|
||||
|
||||
keysForSigning.forEach {
|
||||
val key = services.keyManagementService.keys[it] ?: throw IllegalStateException("Could not find signing key for ${it.toStringShort()}")
|
||||
builder.signWith(KeyPair(it, key))
|
||||
|
@ -120,7 +120,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
|
||||
override fun generateSpend(tx: TransactionBuilder,
|
||||
amount: Amount<Currency>,
|
||||
to: PublicKey,
|
||||
onlyFromIssuers: Set<PartyAndReference>?): Pair<TransactionBuilder, List<PublicKey>> {
|
||||
onlyFromParties: Set<Party>?): Pair<TransactionBuilder, List<PublicKey>> {
|
||||
// Discussion
|
||||
//
|
||||
// This code is analogous to the Wallet.send() set of methods in bitcoinj, and has the same general outline.
|
||||
@ -146,8 +146,8 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
|
||||
val currency = amount.token
|
||||
var acceptableCoins = run {
|
||||
val ofCurrency = assetsStates.filter { it.state.data.amount.token.product == currency }
|
||||
if (onlyFromIssuers != null)
|
||||
ofCurrency.filter { it.state.data.amount.token.issuer in onlyFromIssuers }
|
||||
if (onlyFromParties != null)
|
||||
ofCurrency.filter { it.state.data.amount.token.issuer.party in onlyFromParties }
|
||||
else
|
||||
ofCurrency
|
||||
}
|
||||
@ -170,17 +170,22 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
|
||||
val coins = it.value
|
||||
val totalAmount = coins.map { it.state.data.amount }.sumOrThrow()
|
||||
deriveState(coins.first().state, totalAmount, to)
|
||||
}
|
||||
}.sortedBy { it.data.amount.quantity }
|
||||
|
||||
val outputs = if (change != null) {
|
||||
// Just copy a key across as the change key. In real life of course, this works but leaks private data.
|
||||
// In bitcoinj we derive a fresh key here and then shuffle the outputs to ensure it's hard to follow
|
||||
// value flows through the transaction graph.
|
||||
val changeKey = gathered.first().state.data.owner
|
||||
val existingOwner = gathered.first().state.data.owner
|
||||
// Add a change output and adjust the last output downwards.
|
||||
states.subList(0, states.lastIndex) +
|
||||
states.last().let { deriveState(it, it.data.amount - change, it.data.owner) } +
|
||||
deriveState(gathered.last().state, change, changeKey)
|
||||
states.last().let {
|
||||
val spent = it.data.amount.withoutIssuer() - change.withoutIssuer()
|
||||
deriveState(it, Amount(spent.quantity, it.data.amount.token), it.data.owner)
|
||||
} +
|
||||
states.last().let {
|
||||
deriveState(it, Amount(change.quantity, it.data.amount.token), existingOwner)
|
||||
}
|
||||
} else states
|
||||
|
||||
for (state in gathered) tx.addInputState(state)
|
||||
|
Reference in New Issue
Block a user