Merged in colljos-vault-code-clean-up-refactor (pull request #435)

Colljos vault code clean up refactor
This commit is contained in:
Jose Coll 2016-11-02 14:01:24 +00:00
commit 6deaf2c12c
6 changed files with 23 additions and 38 deletions

View File

@ -32,7 +32,7 @@ import java.util.*
import kotlin.test.*
class CashTests {
val defaultRef = OpaqueBytes(ByteArray(1, {1}))
val defaultRef = OpaqueBytes(ByteArray(1, { 1 }))
val defaultIssuer = MEGA_CORP.ref(defaultRef)
val inState = Cash.State(
amount = 1000.DOLLARS `issued by` defaultIssuer,
@ -85,12 +85,6 @@ class CashTests {
}
}
@After
fun tearDown() {
LogHelper.reset(NodeVaultService::class)
dataSource.close()
}
@Test
fun trivial() {
transaction {
@ -264,7 +258,7 @@ class CashTests {
// Include the previously issued cash in a new issuance command
ptx = TransactionType.General.Builder(DUMMY_NOTARY)
ptx.addInputState(tx.tx.outRef<Cash.State>(0))
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY)
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY)
}
@Test
@ -492,7 +486,7 @@ class CashTests {
}
fun makeSpend(amount: Amount<Currency>, dest: PublicKey): WireTransaction {
var tx = TransactionType.General.Builder(DUMMY_NOTARY)
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
databaseTransaction(database) {
vault.generateSpend(tx, amount, dest)
}
@ -618,8 +612,8 @@ class CashTests {
assertEquals(vaultState0.ref, wtx.inputs[0])
assertEquals(vaultState1.ref, wtx.inputs[1])
assertEquals(vaultState2.ref, wtx.inputs[2])
assertEquals(vaultState0.state.data.copy(owner = THEIR_PUBKEY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
assertEquals(vaultState2.state.data.copy(owner = THEIR_PUBKEY_1), wtx.outputs[1].data)
assertEquals(vaultState0.state.data.copy(owner = THEIR_PUBKEY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
assertEquals(vaultState2.state.data.copy(owner = THEIR_PUBKEY_1), wtx.outputs[0].data)
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
}
}

View File

@ -165,7 +165,9 @@ interface VaultService {
}
/**
* Fungible Asset operations
* [InsufficientBalanceException] is thrown when a Cash Spending transaction fails because
* there is insufficient quantity for a given currency (and optionally set of Issuer Parties).
* Note: an [Amount] of [Currency] is only fungible for a given Issuer Party within a [FungibleAsset]
**/
@Throws(InsufficientBalanceException::class)
fun generateSpend(tx: TransactionBuilder,

View File

@ -79,7 +79,8 @@ 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)
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()}")

View File

@ -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)

View File

@ -1,35 +1,32 @@
package com.r3corda.node.services
import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs
import com.r3corda.core.contracts.*
import com.r3corda.core.days
import com.r3corda.core.node.ServiceHub
import com.r3corda.core.node.recordTransactions
import com.r3corda.core.node.services.VaultService
import com.r3corda.core.protocols.ProtocolLogic
import com.r3corda.core.protocols.ProtocolLogicRef
import com.r3corda.core.protocols.ProtocolLogicRefFactory
import com.r3corda.core.serialization.SingletonSerializeAsToken
import com.r3corda.core.transactions.SignedTransaction
import com.r3corda.core.utilities.DUMMY_NOTARY
import com.r3corda.node.services.events.NodeSchedulerService
import com.r3corda.node.services.persistence.DBCheckpointStorage
import com.r3corda.node.services.statemachine.StateMachineManager
import com.r3corda.node.services.vault.NodeVaultService
import com.r3corda.node.utilities.AddOrRemove
import com.r3corda.node.utilities.AffinityExecutor
import com.r3corda.node.utilities.configureDatabase
import com.r3corda.node.utilities.databaseTransaction
import com.r3corda.testing.ALICE_KEY
import com.r3corda.testing.node.*
import com.r3corda.testing.node.InMemoryMessagingNetwork
import com.r3corda.testing.node.MockKeyManagementService
import com.r3corda.testing.node.TestClock
import com.r3corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.exposed.sql.Database
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.io.Closeable
import java.nio.file.FileSystem
import java.security.PublicKey
import java.time.Clock
import java.time.Instant
@ -39,8 +36,6 @@ import java.util.concurrent.TimeUnit
import kotlin.test.assertTrue
class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
// Use an in memory file system for testing attachment storage.
val fs: FileSystem = Jimfs.newFileSystem(Configuration.unix())
val realClock: Clock = Clock.systemUTC()
val stoppedClock = Clock.fixed(realClock.instant(), realClock.zone)
@ -82,19 +77,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
dataSource = dataSourceAndDatabase.first
database = dataSourceAndDatabase.second
// Switched from InMemoryVault usage to NodeVault
databaseTransaction(database) {
val services1 = object : MockServices() {
override val vaultService: VaultService = NodeVaultService(this)
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx)
vaultService.notify(stx.tx)
}
}
}
val kms = MockKeyManagementService(ALICE_KEY)
val mockMessagingService = InMemoryMessagingNetwork(false).InMemoryMessaging(false, InMemoryMessagingNetwork.Handle(0, "None"), AffinityExecutor.ServiceAffinityExecutor("test", 1), database)
services = object : MockServiceHubInternal(overrideClock = testClock, keyManagement = kms, net = mockMessagingService), TestReference {