Adds helper methods to grab a LedgerTx or verify a SignedTx. Deprecates builder signing methods.

This commit is contained in:
Joel Dudley 2017-07-03 15:53:48 +01:00 committed by GitHub
parent 562b186a65
commit 7df8c50167
21 changed files with 349 additions and 324 deletions

View File

@ -136,7 +136,6 @@ interface ServiceHub : ServicesForResolution {
return builder.toSignedTransaction(false) return builder.toSignedTransaction(false)
} }
/** /**
* Helper method to construct an initial partially signed transaction from a TransactionBuilder * Helper method to construct an initial partially signed transaction from a TransactionBuilder
* using the default identity key contained in the node. * using the default identity key contained in the node.
@ -146,7 +145,6 @@ interface ServiceHub : ServicesForResolution {
*/ */
fun signInitialTransaction(builder: TransactionBuilder): SignedTransaction = signInitialTransaction(builder, legalIdentityKey) fun signInitialTransaction(builder: TransactionBuilder): SignedTransaction = signInitialTransaction(builder, legalIdentityKey)
/** /**
* Helper method to construct an initial partially signed transaction from a [TransactionBuilder] * Helper method to construct an initial partially signed transaction from a [TransactionBuilder]
* using a set of keys all held in this node. * using a set of keys all held in this node.

View File

@ -3,9 +3,11 @@ package net.corda.core.transactions
import net.corda.core.contracts.AttachmentResolutionException import net.corda.core.contracts.AttachmentResolutionException
import net.corda.core.contracts.NamedByHash import net.corda.core.contracts.NamedByHash
import net.corda.core.contracts.TransactionResolutionException import net.corda.core.contracts.TransactionResolutionException
import net.corda.core.contracts.TransactionVerificationException
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.keys
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
@ -136,17 +138,46 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
operator fun plus(sigList: Collection<DigitalSignature.WithKey>) = withAdditionalSignatures(sigList) operator fun plus(sigList: Collection<DigitalSignature.WithKey>) = withAdditionalSignatures(sigList)
/** /**
* Calls [verifySignatures] to check all required signatures are present, and then calls * Checks the transaction's signatures are valid, optionally calls [verifySignatures] to check
* [WireTransaction.toLedgerTransaction] with the passed in [ServiceHub] to resolve the dependencies, * all required signatures are present, and then calls [WireTransaction.toLedgerTransaction]
* returning an unverified LedgerTransaction. * with the passed in [ServiceHub] to resolve the dependencies, returning an unverified
* LedgerTransaction.
*
* This allows us to perform validation over the entirety of the transaction's contents.
* WireTransaction only contains StateRef for the inputs and hashes for the attachments,
* rather than ContractState instances for the inputs and Attachment instances for the attachments.
* *
* @throws AttachmentResolutionException if a required attachment was not found in storage. * @throws AttachmentResolutionException if a required attachment was not found in storage.
* @throws TransactionResolutionException if an input points to a transaction not found in storage. * @throws TransactionResolutionException if an input points to a transaction not found in storage.
* @throws SignatureException if any signatures were invalid or unrecognised * @throws SignatureException if any signatures were invalid or unrecognised
* @throws SignaturesMissingException if any signatures that should have been present are missing. * @throws SignaturesMissingException if any signatures that should have been present are missing.
*/ */
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class, SignatureException::class) @JvmOverloads
fun toLedgerTransaction(services: ServiceHub) = verifySignatures().toLedgerTransaction(services) @Throws(SignatureException::class, AttachmentResolutionException::class, TransactionResolutionException::class)
fun toLedgerTransaction(services: ServiceHub, checkSufficientSignatures: Boolean = true): LedgerTransaction {
checkSignaturesAreValid()
if (checkSufficientSignatures) verifySignatures()
return tx.toLedgerTransaction(services)
}
/**
* Checks the transaction's signatures are valid, optionally calls [verifySignatures] to check
* all required signatures are present, calls [WireTransaction.toLedgerTransaction] with the
* passed in [ServiceHub] to resolve the dependencies and return an unverified
* LedgerTransaction, then verifies the LedgerTransaction.
*
* @throws AttachmentResolutionException if a required attachment was not found in storage.
* @throws TransactionResolutionException if an input points to a transaction not found in storage.
* @throws SignatureException if any signatures were invalid or unrecognised
* @throws SignaturesMissingException if any signatures that should have been present are missing.
*/
@JvmOverloads
@Throws(SignatureException::class, AttachmentResolutionException::class, TransactionResolutionException::class, TransactionVerificationException::class)
fun verify(services: ServiceHub, checkSufficientSignatures: Boolean = true) {
checkSignaturesAreValid()
if (checkSufficientSignatures) verifySignatures()
tx.toLedgerTransaction(services).verify()
}
override fun toString(): String = "${javaClass.simpleName}(id=$id)" override fun toString(): String = "${javaClass.simpleName}(id=$id)"
} }

View File

@ -1,13 +1,16 @@
package net.corda.core.transactions package net.corda.core.transactions
import co.paralleluniverse.strands.Strand import co.paralleluniverse.strands.Strand
import com.google.common.annotations.VisibleForTesting
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.FlowStateMachine
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.security.SignatureException
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -40,8 +43,6 @@ open class TransactionBuilder(
protected var timeWindow: TimeWindow? = null) { protected var timeWindow: TimeWindow? = null) {
constructor(type: TransactionType, notary: Party) : this(type, notary, (Strand.currentStrand() as? FlowStateMachine<*>)?.id?.uuid ?: UUID.randomUUID()) constructor(type: TransactionType, notary: Party) : this(type, notary, (Strand.currentStrand() as? FlowStateMachine<*>)?.id?.uuid ?: UUID.randomUUID())
val time: TimeWindow? get() = timeWindow // TODO: rename using a more descriptive name (i.e. timeWindowGetter) or remove if unused.
/** /**
* Creates a copy of the builder. * Creates a copy of the builder.
*/ */
@ -57,27 +58,6 @@ open class TransactionBuilder(
timeWindow = timeWindow timeWindow = timeWindow
) )
/**
* Places a [TimeWindow] in this transaction, removing any existing command if there is one.
* The command requires a signature from the Notary service, which acts as a Timestamp Authority.
* The signature can be obtained using [NotaryFlow].
*
* The window of time in which the final time-window may lie is defined as [time] +/- [timeTolerance].
* If you want a non-symmetrical time window you must add the command via [addCommand] yourself. The tolerance
* should be chosen such that your code can finish building the transaction and sending it to the TSA within that
* window of time, taking into account factors such as network latency. Transactions being built by a group of
* collaborating parties may therefore require a higher time tolerance than a transaction being built by a single
* node.
*/
fun addTimeWindow(time: Instant, timeTolerance: Duration) = addTimeWindow(TimeWindow.withTolerance(time, timeTolerance))
fun addTimeWindow(timeWindow: TimeWindow) {
check(notary != null) { "Only notarised transactions can have a time-window" }
signers.add(notary!!.owningKey)
check(currentSigs.isEmpty()) { "Cannot change time-window after signing" }
this.timeWindow = timeWindow
}
// DOCSTART 1 // DOCSTART 1
/** A more convenient way to add items to this transaction that calls the add* methods for you based on type */ /** A more convenient way to add items to this transaction that calls the add* methods for you based on type */
fun withItems(vararg items: Any): TransactionBuilder { fun withItems(vararg items: Any): TransactionBuilder {
@ -95,62 +75,18 @@ open class TransactionBuilder(
} }
// DOCEND 1 // DOCEND 1
/** The signatures that have been collected so far - might be incomplete! */
protected val currentSigs = arrayListOf<DigitalSignature.WithKey>()
@Deprecated("Use ServiceHub.signInitialTransaction() instead.")
fun signWith(key: KeyPair): TransactionBuilder {
check(currentSigs.none { it.by == key.public }) { "This partial transaction was already signed by ${key.public}" }
val data = toWireTransaction().id
addSignatureUnchecked(key.sign(data.bytes))
return this
}
/**
* Checks that the given signature matches one of the commands and that it is a correct signature over the tx, then
* adds it.
*
* @throws SignatureException if the signature didn't match the transaction contents.
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/
fun checkAndAddSignature(sig: DigitalSignature.WithKey) {
checkSignature(sig)
addSignatureUnchecked(sig)
}
/**
* Checks that the given signature matches one of the commands and that it is a correct signature over the tx.
*
* @throws SignatureException if the signature didn't match the transaction contents.
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/
fun checkSignature(sig: DigitalSignature.WithKey) {
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
sig.verify(toWireTransaction().id)
}
/** Adds the signature directly to the transaction, without checking it for validity. */
fun addSignatureUnchecked(sig: DigitalSignature.WithKey): TransactionBuilder {
currentSigs.add(sig)
return this
}
fun toWireTransaction() = WireTransaction(ArrayList(inputs), ArrayList(attachments), fun toWireTransaction() = WireTransaction(ArrayList(inputs), ArrayList(attachments),
ArrayList(outputs), ArrayList(commands), notary, signers.toList(), type, timeWindow) ArrayList(outputs), ArrayList(commands), notary, signers.toList(), type, timeWindow)
fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction { @Throws(AttachmentResolutionException::class, TransactionResolutionException::class)
if (checkSufficientSignatures) { fun toLedgerTransaction(services: ServiceHub) = toWireTransaction().toLedgerTransaction(services)
val gotKeys = currentSigs.map { it.by }.toSet()
val missing: Set<PublicKey> = signers.filter { !it.isFulfilledBy(gotKeys) }.toSet() @Throws(AttachmentResolutionException::class, TransactionResolutionException::class, TransactionVerificationException::class)
if (missing.isNotEmpty()) fun verify(services: ServiceHub) {
throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.joinToString()}") toLedgerTransaction(services).verify()
}
val wtx = toWireTransaction()
return SignedTransaction(wtx.serialize(), ArrayList(currentSigs))
} }
open fun addInputState(stateAndRef: StateAndRef<*>) { open fun addInputState(stateAndRef: StateAndRef<*>) {
check(currentSigs.isEmpty())
val notary = stateAndRef.state.notary val notary = stateAndRef.state.notary
require(notary == this.notary) { "Input state requires notary \"$notary\" which does not match the transaction notary \"${this.notary}\"." } require(notary == this.notary) { "Input state requires notary \"$notary\" which does not match the transaction notary \"${this.notary}\"." }
signers.add(notary.owningKey) signers.add(notary.owningKey)
@ -158,12 +94,10 @@ open class TransactionBuilder(
} }
fun addAttachment(attachmentId: SecureHash) { fun addAttachment(attachmentId: SecureHash) {
check(currentSigs.isEmpty())
attachments.add(attachmentId) attachments.add(attachmentId)
} }
fun addOutputState(state: TransactionState<*>): Int { fun addOutputState(state: TransactionState<*>): Int {
check(currentSigs.isEmpty())
outputs.add(state) outputs.add(state)
return outputs.size - 1 return outputs.size - 1
} }
@ -178,7 +112,6 @@ open class TransactionBuilder(
} }
fun addCommand(arg: Command) { fun addCommand(arg: Command) {
check(currentSigs.isEmpty())
// TODO: replace pubkeys in commands with 'pointers' to keys in signers // TODO: replace pubkeys in commands with 'pointers' to keys in signers
signers.addAll(arg.signers) signers.addAll(arg.signers)
commands.add(arg) commands.add(arg)
@ -187,10 +120,84 @@ open class TransactionBuilder(
fun addCommand(data: CommandData, vararg keys: PublicKey) = addCommand(Command(data, listOf(*keys))) fun addCommand(data: CommandData, vararg keys: PublicKey) = addCommand(Command(data, listOf(*keys)))
fun addCommand(data: CommandData, keys: List<PublicKey>) = addCommand(Command(data, keys)) fun addCommand(data: CommandData, keys: List<PublicKey>) = addCommand(Command(data, keys))
/**
* Places a [TimeWindow] in this transaction, removing any existing command if there is one.
* The command requires a signature from the Notary service, which acts as a Timestamp Authority.
* The signature can be obtained using [NotaryFlow].
*
* The window of time in which the final time-window may lie is defined as [time] +/- [timeTolerance].
* If you want a non-symmetrical time window you must add the command via [addCommand] yourself. The tolerance
* should be chosen such that your code can finish building the transaction and sending it to the TSA within that
* window of time, taking into account factors such as network latency. Transactions being built by a group of
* collaborating parties may therefore require a higher time tolerance than a transaction being built by a single
* node.
*/
fun addTimeWindow(time: Instant, timeTolerance: Duration) = addTimeWindow(TimeWindow.withTolerance(time, timeTolerance))
fun addTimeWindow(timeWindow: TimeWindow) {
check(notary != null) { "Only notarised transactions can have a time-window" }
signers.add(notary!!.owningKey)
this.timeWindow = timeWindow
}
// Accessors that yield immutable snapshots. // Accessors that yield immutable snapshots.
fun inputStates(): List<StateRef> = ArrayList(inputs) fun inputStates(): List<StateRef> = ArrayList(inputs)
fun attachments(): List<SecureHash> = ArrayList(attachments)
fun outputStates(): List<TransactionState<*>> = ArrayList(outputs) fun outputStates(): List<TransactionState<*>> = ArrayList(outputs)
fun commands(): List<Command> = ArrayList(commands) fun commands(): List<Command> = ArrayList(commands)
fun attachments(): List<SecureHash> = ArrayList(attachments)
/** The signatures that have been collected so far - might be incomplete! */
@Deprecated("Signatures should be gathered on a SignedTransaction instead.")
protected val currentSigs = arrayListOf<DigitalSignature.WithKey>()
@Deprecated("Use ServiceHub.signInitialTransaction() instead.")
fun signWith(key: KeyPair): TransactionBuilder {
val data = toWireTransaction().id
addSignatureUnchecked(key.sign(data.bytes))
return this
}
/** Adds the signature directly to the transaction, without checking it for validity. */
@Deprecated("Use ServiceHub.signInitialTransaction() instead.")
fun addSignatureUnchecked(sig: DigitalSignature.WithKey): TransactionBuilder {
currentSigs.add(sig)
return this
}
@Deprecated("Use ServiceHub.signInitialTransaction() instead.")
fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction {
if (checkSufficientSignatures) {
val gotKeys = currentSigs.map { it.by }.toSet()
val missing: Set<PublicKey> = signers.filter { !it.isFulfilledBy(gotKeys) }.toSet()
if (missing.isNotEmpty())
throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.joinToString()}")
}
val wtx = toWireTransaction()
return SignedTransaction(wtx.serialize(), ArrayList(currentSigs))
}
/**
* Checks that the given signature matches one of the commands and that it is a correct signature over the tx, then
* adds it.
*
* @throws SignatureException if the signature didn't match the transaction contents.
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/
@Deprecated("Use WireTransaction.checkSignature() instead.")
fun checkAndAddSignature(sig: DigitalSignature.WithKey) {
checkSignature(sig)
addSignatureUnchecked(sig)
}
/**
* Checks that the given signature matches one of the commands and that it is a correct signature over the tx.
*
* @throws SignatureException if the signature didn't match the transaction contents.
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/
@Deprecated("Use WireTransaction.checkSignature() instead.")
fun checkSignature(sig: DigitalSignature.WithKey) {
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
sig.verify(toWireTransaction().id)
}
} }

View File

@ -2,8 +2,10 @@ package net.corda.core.transactions
import com.esotericsoftware.kryo.pool.KryoPool import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.MerkleTree import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.keys
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.indexOfOrThrow import net.corda.core.indexOfOrThrow
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
@ -13,6 +15,7 @@ import net.corda.core.serialization.p2PKryo
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import java.security.PublicKey import java.security.PublicKey
import java.security.SignatureException
import java.util.function.Predicate import java.util.function.Predicate
/** /**
@ -135,6 +138,17 @@ class WireTransaction(
) )
} }
/**
* Checks that the given signature matches one of the commands and that it is a correct signature over the tx.
*
* @throws SignatureException if the signature didn't match the transaction contents.
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/
fun checkSignature(sig: DigitalSignature.WithKey) {
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
sig.verify(id)
}
override fun toString(): String { override fun toString(): String {
val buf = StringBuilder() val buf = StringBuilder()
buf.appendln("Transaction:") buf.appendln("Transaction:")

View File

@ -6,6 +6,8 @@ import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.MEGA_CORP_PUBKEY
import net.corda.testing.node.MockServices
import net.corda.testing.node.MockTransactionStorage import net.corda.testing.node.MockTransactionStorage
import org.junit.Test import org.junit.Test
import java.security.KeyPair import java.security.KeyPair
@ -28,24 +30,29 @@ class TransactionGraphSearchTests {
* @param command the command to add to the origin transaction. * @param command the command to add to the origin transaction.
* @param signer signer for the two transactions and their commands. * @param signer signer for the two transactions and their commands.
*/ */
fun buildTransactions(command: CommandData, signer: KeyPair): GraphTransactionStorage { fun buildTransactions(command: CommandData): GraphTransactionStorage {
val originTx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val megaCorpServices = MockServices(MEGA_CORP_KEY)
addOutputState(DummyState(random31BitValue())) val notaryServices = MockServices(DUMMY_NOTARY_KEY)
addCommand(command, signer.public)
signWith(signer) val originBuilder = TransactionType.General.Builder(DUMMY_NOTARY)
signWith(DUMMY_NOTARY_KEY) originBuilder.addOutputState(DummyState(random31BitValue()))
}.toSignedTransaction(false) originBuilder.addCommand(command, MEGA_CORP_PUBKEY)
val inputTx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
addInputState(originTx.tx.outRef<DummyState>(0)) val originPtx = megaCorpServices.signInitialTransaction(originBuilder)
signWith(signer) val originTx = notaryServices.addSignature(originPtx)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction(false) val inputBuilder = TransactionType.General.Builder(DUMMY_NOTARY)
inputBuilder.addInputState(originTx.tx.outRef<DummyState>(0))
val inputPtx = megaCorpServices.signInitialTransaction(inputBuilder)
val inputTx = megaCorpServices.addSignature(inputPtx)
return GraphTransactionStorage(originTx, inputTx) return GraphTransactionStorage(originTx, inputTx)
} }
@Test @Test
fun `return empty from empty`() { fun `return empty from empty`() {
val storage = buildTransactions(DummyContract.Commands.Create(), MEGA_CORP_KEY) val storage = buildTransactions(DummyContract.Commands.Create())
val search = TransactionGraphSearch(storage, emptyList()) val search = TransactionGraphSearch(storage, emptyList())
search.query = TransactionGraphSearch.Query() search.query = TransactionGraphSearch.Query()
val expected = emptyList<WireTransaction>() val expected = emptyList<WireTransaction>()
@ -56,7 +63,7 @@ class TransactionGraphSearchTests {
@Test @Test
fun `return empty from no match`() { fun `return empty from no match`() {
val storage = buildTransactions(DummyContract.Commands.Create(), MEGA_CORP_KEY) val storage = buildTransactions(DummyContract.Commands.Create())
val search = TransactionGraphSearch(storage, listOf(storage.inputTx.tx)) val search = TransactionGraphSearch(storage, listOf(storage.inputTx.tx))
search.query = TransactionGraphSearch.Query() search.query = TransactionGraphSearch.Query()
val expected = emptyList<WireTransaction>() val expected = emptyList<WireTransaction>()
@ -67,7 +74,7 @@ class TransactionGraphSearchTests {
@Test @Test
fun `return origin on match`() { fun `return origin on match`() {
val storage = buildTransactions(DummyContract.Commands.Create(), MEGA_CORP_KEY) val storage = buildTransactions(DummyContract.Commands.Create())
val search = TransactionGraphSearch(storage, listOf(storage.inputTx.tx)) val search = TransactionGraphSearch(storage, listOf(storage.inputTx.tx))
search.query = TransactionGraphSearch.Query(DummyContract.Commands.Create::class.java) search.query = TransactionGraphSearch.Query(DummyContract.Commands.Create::class.java)
val expected = listOf(storage.originTx.tx) val expected = listOf(storage.originTx.tx)

View File

@ -13,7 +13,9 @@ import net.corda.flows.CollectSignaturesFlow
import net.corda.flows.FinalityFlow import net.corda.flows.FinalityFlow
import net.corda.flows.SignTransactionFlow import net.corda.flows.SignTransactionFlow
import net.corda.testing.MINI_CORP_KEY import net.corda.testing.MINI_CORP_KEY
import net.corda.testing.MINI_CORP_PUBKEY
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockServices
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -26,6 +28,7 @@ class CollectSignaturesFlowTests {
lateinit var b: MockNetwork.MockNode lateinit var b: MockNetwork.MockNode
lateinit var c: MockNetwork.MockNode lateinit var c: MockNetwork.MockNode
lateinit var notary: Party lateinit var notary: Party
val services = MockServices()
@Before @Before
fun setup() { fun setup() {
@ -162,7 +165,8 @@ class CollectSignaturesFlowTests {
@Test @Test
fun `fails when not signed by initiator`() { fun `fails when not signed by initiator`() {
val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.legalIdentity.ref(1)) val onePartyDummyContract = DummyContract.generateInitial(1337, notary, a.info.legalIdentity.ref(1))
val ptx = onePartyDummyContract.signWith(MINI_CORP_KEY).toSignedTransaction(false) val miniCorpServices = MockServices(MINI_CORP_KEY)
val ptx = miniCorpServices.signInitialTransaction(onePartyDummyContract)
val flow = a.services.startFlow(CollectSignaturesFlow(ptx)) val flow = a.services.startFlow(CollectSignaturesFlow(ptx))
mockNet.runNetwork() mockNet.runNetwork()
assertFailsWith<IllegalArgumentException>("The Initiator of CollectSignaturesFlow must have signed the transaction.") { assertFailsWith<IllegalArgumentException>("The Initiator of CollectSignaturesFlow must have signed the transaction.") {

View File

@ -14,6 +14,7 @@ import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockServices
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -32,6 +33,8 @@ class ResolveTransactionsFlowTest {
lateinit var a: MockNetwork.MockNode lateinit var a: MockNetwork.MockNode
lateinit var b: MockNetwork.MockNode lateinit var b: MockNetwork.MockNode
lateinit var notary: Party lateinit var notary: Party
val megaCorpServices = MockServices(MEGA_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
@Before @Before
fun setup() { fun setup() {
@ -94,9 +97,8 @@ class ResolveTransactionsFlowTest {
val count = 50 val count = 50
var cursor = stx2 var cursor = stx2
repeat(count) { repeat(count) {
val stx = DummyContract.move(cursor.tx.outRef(0), MINI_CORP) val builder = DummyContract.move(cursor.tx.outRef(0), MINI_CORP)
.addSignatureUnchecked(NullSignature) val stx = megaCorpServices.signInitialTransaction(builder)
.toSignedTransaction(false)
a.database.transaction { a.database.transaction {
a.services.recordTransactions(stx) a.services.recordTransactions(stx)
} }
@ -114,15 +116,13 @@ class ResolveTransactionsFlowTest {
val stx1 = makeTransactions().first val stx1 = makeTransactions().first
val stx2 = DummyContract.move(stx1.tx.outRef(0), MINI_CORP).run { val stx2 = DummyContract.move(stx1.tx.outRef(0), MINI_CORP).run {
signWith(MEGA_CORP_KEY) val ptx = megaCorpServices.signInitialTransaction(this)
signWith(DUMMY_NOTARY_KEY) notaryServices.addSignature(ptx)
toSignedTransaction()
} }
val stx3 = DummyContract.move(listOf(stx1.tx.outRef(0), stx2.tx.outRef(0)), MINI_CORP).run { val stx3 = DummyContract.move(listOf(stx1.tx.outRef(0), stx2.tx.outRef(0)), MINI_CORP).run {
signWith(MEGA_CORP_KEY) val ptx = megaCorpServices.signInitialTransaction(this)
signWith(DUMMY_NOTARY_KEY) notaryServices.addSignature(ptx)
toSignedTransaction()
} }
a.database.transaction { a.database.transaction {
@ -168,15 +168,19 @@ class ResolveTransactionsFlowTest {
val dummy1: SignedTransaction = DummyContract.generateInitial(0, notary, MEGA_CORP.ref(1)).let { val dummy1: SignedTransaction = DummyContract.generateInitial(0, notary, MEGA_CORP.ref(1)).let {
if (withAttachment != null) if (withAttachment != null)
it.addAttachment(withAttachment) it.addAttachment(withAttachment)
if (signFirstTX) when (signFirstTX) {
it.signWith(MEGA_CORP_KEY) true -> {
it.signWith(DUMMY_NOTARY_KEY) val ptx = megaCorpServices.signInitialTransaction(it)
it.toSignedTransaction(false) notaryServices.addSignature(ptx)
}
false -> {
notaryServices.signInitialTransaction(it)
}
}
} }
val dummy2: SignedTransaction = DummyContract.move(dummy1.tx.outRef(0), MINI_CORP).let { val dummy2: SignedTransaction = DummyContract.move(dummy1.tx.outRef(0), MINI_CORP).let {
it.signWith(MEGA_CORP_KEY) val ptx = megaCorpServices.signInitialTransaction(it)
it.signWith(DUMMY_NOTARY_KEY) notaryServices.addSignature(ptx)
it.toSignedTransaction()
} }
a.database.transaction { a.database.transaction {
a.services.recordTransactions(dummy1, dummy2) a.services.recordTransactions(dummy1, dummy2)

View File

@ -5,14 +5,9 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.DUMMY_KEY_2 import net.corda.core.utilities.*
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.testing.*
import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.testing.node.MockServices
import net.corda.core.utilities.TEST_TX_TIME
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.MINI_CORP
import net.corda.testing.generateStateRef
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.SignatureException import java.security.SignatureException
@ -53,7 +48,8 @@ class TransactionSerializationTests {
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), DUMMY_NOTARY) val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), DUMMY_NOTARY)
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), DUMMY_NOTARY) val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), DUMMY_NOTARY)
val megaCorpServices = MockServices(MEGA_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
lateinit var tx: TransactionBuilder lateinit var tx: TransactionBuilder
@Before @Before
@ -65,53 +61,47 @@ class TransactionSerializationTests {
@Test @Test
fun signWireTX() { fun signWireTX() {
tx.signWith(DUMMY_NOTARY_KEY) val ptx = megaCorpServices.signInitialTransaction(tx)
tx.signWith(MEGA_CORP_KEY) val stx = notaryServices.addSignature(ptx)
val signedTX = tx.toSignedTransaction()
// Now check that the signature we just made verifies. // Now check that the signature we just made verifies.
signedTX.verifySignatures() stx.verifySignatures()
// Corrupt the data and ensure the signature catches the problem. // Corrupt the data and ensure the signature catches the problem.
signedTX.id.bytes[5] = signedTX.id.bytes[5].inc() stx.id.bytes[5] = stx.id.bytes[5].inc()
assertFailsWith(SignatureException::class) { assertFailsWith(SignatureException::class) {
signedTX.verifySignatures() stx.verifySignatures()
} }
} }
@Test @Test
fun wrongKeys() { fun wrongKeys() {
// Can't convert if we don't have signatures for all commands val ptx = megaCorpServices.signInitialTransaction(tx)
assertFailsWith(IllegalStateException::class) { val stx = notaryServices.addSignature(ptx)
tx.toSignedTransaction()
}
tx.signWith(MEGA_CORP_KEY)
tx.signWith(DUMMY_NOTARY_KEY)
val signedTX = tx.toSignedTransaction()
// Cannot construct with an empty sigs list. // Cannot construct with an empty sigs list.
assertFailsWith(IllegalArgumentException::class) { assertFailsWith(IllegalArgumentException::class) {
signedTX.copy(sigs = emptyList()) stx.copy(sigs = emptyList())
} }
// If the signature was replaced in transit, we don't like it. // If the signature was replaced in transit, we don't like it.
assertFailsWith(SignatureException::class) { assertFailsWith(SignatureException::class) {
val tx2 = TransactionType.General.Builder(DUMMY_NOTARY).withItems(inputState, outputState, changeState, val tx2 = TransactionType.General.Builder(DUMMY_NOTARY).withItems(inputState, outputState, changeState,
Command(TestCash.Commands.Move(), DUMMY_KEY_2.public)) Command(TestCash.Commands.Move(), DUMMY_KEY_2.public))
tx2.signWith(DUMMY_NOTARY_KEY)
tx2.signWith(DUMMY_KEY_2)
signedTX.copy(sigs = tx2.toSignedTransaction().sigs).verifySignatures() val ptx2 = notaryServices.signInitialTransaction(tx2)
val dummyServices = MockServices(DUMMY_KEY_2)
val stx2 = dummyServices.addSignature(ptx2)
stx.copy(sigs = stx2.sigs).verifySignatures()
} }
} }
@Test @Test
fun timeWindow() { fun timeWindow() {
tx.addTimeWindow(TEST_TX_TIME, 30.seconds) tx.addTimeWindow(TEST_TX_TIME, 30.seconds)
tx.signWith(MEGA_CORP_KEY) val ptx = megaCorpServices.signInitialTransaction(tx)
tx.signWith(DUMMY_NOTARY_KEY) val stx = notaryServices.addSignature(ptx)
val stx = tx.toSignedTransaction()
assertEquals(TEST_TX_TIME, stx.tx.timeWindow?.midpoint) assertEquals(TEST_TX_TIME, stx.tx.timeWindow?.midpoint)
} }
} }

View File

@ -205,6 +205,8 @@ class CommercialPaperTestsGeneric {
private lateinit var aliceVaultService: VaultService private lateinit var aliceVaultService: VaultService
private lateinit var alicesVault: Vault<ContractState> private lateinit var alicesVault: Vault<ContractState>
private val notaryServices = MockServices(DUMMY_NOTARY_KEY)
private lateinit var moveTX: SignedTransaction private lateinit var moveTX: SignedTransaction
@Test @Test
@ -215,7 +217,7 @@ class CommercialPaperTestsGeneric {
val databaseAlice = dataSourceAndDatabaseAlice.second val databaseAlice = dataSourceAndDatabaseAlice.second
databaseAlice.transaction { databaseAlice.transaction {
aliceServices = object : MockServices() { aliceServices = object : MockServices(ALICE_KEY) {
override val vaultService: VaultService = makeVaultService(dataSourcePropsAlice) override val vaultService: VaultService = makeVaultService(dataSourcePropsAlice)
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
@ -235,7 +237,7 @@ class CommercialPaperTestsGeneric {
val databaseBigCorp = dataSourceAndDatabaseBigCorp.second val databaseBigCorp = dataSourceAndDatabaseBigCorp.second
databaseBigCorp.transaction { databaseBigCorp.transaction {
bigCorpServices = object : MockServices() { bigCorpServices = object : MockServices(BIG_CORP_KEY) {
override val vaultService: VaultService = makeVaultService(dataSourcePropsBigCorp) override val vaultService: VaultService = makeVaultService(dataSourcePropsBigCorp)
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
@ -257,29 +259,27 @@ class CommercialPaperTestsGeneric {
// BigCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself. // BigCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself.
val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER
val issuance = bigCorpServices.myInfo.legalIdentity.ref(1) val issuance = bigCorpServices.myInfo.legalIdentity.ref(1)
val issueTX: SignedTransaction = val issueBuilder = CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, DUMMY_NOTARY)
CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, DUMMY_NOTARY).apply { issueBuilder.addTimeWindow(TEST_TX_TIME, 30.seconds)
addTimeWindow(TEST_TX_TIME, 30.seconds) val issuePtx = bigCorpServices.signInitialTransaction(issueBuilder)
signWith(bigCorpServices.key) val issueTx = notaryServices.addSignature(issuePtx)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
databaseAlice.transaction { databaseAlice.transaction {
// Alice pays $9000 to BigCorp to own some of their debt. // Alice pays $9000 to BigCorp to own some of their debt.
moveTX = run { moveTX = run {
val ptx = TransactionType.General.Builder(DUMMY_NOTARY) val builder = TransactionType.General.Builder(DUMMY_NOTARY)
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, AnonymousParty(bigCorpServices.key.public)) aliceVaultService.generateSpend(builder, 9000.DOLLARS, AnonymousParty(bigCorpServices.key.public))
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), AnonymousParty(aliceServices.key.public)) CommercialPaper().generateMove(builder, issueTx.tx.outRef(0), AnonymousParty(aliceServices.key.public))
ptx.signWith(bigCorpServices.key) val ptx = aliceServices.signInitialTransaction(builder)
ptx.signWith(aliceServices.key) val ptx2 = bigCorpServices.addSignature(ptx)
ptx.signWith(DUMMY_NOTARY_KEY) val stx = notaryServices.addSignature(ptx2)
ptx.toSignedTransaction() stx
} }
} }
databaseBigCorp.transaction { databaseBigCorp.transaction {
// Verify the txns are valid and insert into both sides. // Verify the txns are valid and insert into both sides.
listOf(issueTX, moveTX).forEach { listOf(issueTx, moveTX).forEach {
it.toLedgerTransaction(aliceServices).verify() it.toLedgerTransaction(aliceServices).verify()
aliceServices.recordTransactions(it) aliceServices.recordTransactions(it)
bigCorpServices.recordTransactions(it) bigCorpServices.recordTransactions(it)
@ -288,13 +288,13 @@ class CommercialPaperTestsGeneric {
databaseBigCorp.transaction { databaseBigCorp.transaction {
fun makeRedeemTX(time: Instant): Pair<SignedTransaction, UUID> { fun makeRedeemTX(time: Instant): Pair<SignedTransaction, UUID> {
val ptx = TransactionType.General.Builder(DUMMY_NOTARY) val builder = TransactionType.General.Builder(DUMMY_NOTARY)
ptx.addTimeWindow(time, 30.seconds) builder.addTimeWindow(time, 30.seconds)
CommercialPaper().generateRedeem(ptx, moveTX.tx.outRef(1), bigCorpVaultService) CommercialPaper().generateRedeem(builder, moveTX.tx.outRef(1), bigCorpVaultService)
ptx.signWith(aliceServices.key) val ptx = aliceServices.signInitialTransaction(builder)
ptx.signWith(bigCorpServices.key) val ptx2 = bigCorpServices.addSignature(ptx)
ptx.signWith(DUMMY_NOTARY_KEY) val stx = notaryServices.addSignature(ptx2)
return Pair(ptx.toSignedTransaction(), ptx.lockId) return Pair(stx, builder.lockId)
} }
val redeemTX = makeRedeemTX(TEST_TX_TIME + 10.days) val redeemTX = makeRedeemTX(TEST_TX_TIME + 10.days)

View File

@ -43,8 +43,8 @@ class CashTests {
amount = Amount(amount.quantity, token = amount.token.copy(amount.token.issuer.copy(reference = OpaqueBytes.of(ref)))) amount = Amount(amount.quantity, token = amount.token.copy(amount.token.issuer.copy(reference = OpaqueBytes.of(ref))))
) )
lateinit var services: MockServices lateinit var miniCorpServices: MockServices
val vault: VaultService get() = services.vaultService val vault: VaultService get() = miniCorpServices.vaultService
lateinit var dataSource: Closeable lateinit var dataSource: Closeable
lateinit var database: Database lateinit var database: Database
lateinit var vaultStatesUnconsumed: List<StateAndRef<Cash.State>> lateinit var vaultStatesUnconsumed: List<StateAndRef<Cash.State>>
@ -57,7 +57,7 @@ class CashTests {
dataSource = dataSourceAndDatabase.first dataSource = dataSourceAndDatabase.first
database = dataSourceAndDatabase.second database = dataSourceAndDatabase.second
database.transaction { database.transaction {
services = object : MockServices() { miniCorpServices = object : MockServices(MINI_CORP_KEY) {
override val keyManagementService: MockKeyManagementService = MockKeyManagementService(identityService, MINI_CORP_KEY, MEGA_CORP_KEY, OUR_KEY) override val keyManagementService: MockKeyManagementService = MockKeyManagementService(identityService, MINI_CORP_KEY, MEGA_CORP_KEY, OUR_KEY)
override val vaultService: VaultService = makeVaultService(dataSourceProps) override val vaultService: VaultService = makeVaultService(dataSourceProps)
@ -70,16 +70,16 @@ class CashTests {
} }
} }
services.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, miniCorpServices.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_IDENTITY_1) issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_IDENTITY_1)
services.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, miniCorpServices.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_IDENTITY_1) issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_IDENTITY_1)
services.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, miniCorpServices.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_IDENTITY_1) issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_IDENTITY_1)
services.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1, miniCorpServices.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_IDENTITY_1) issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_IDENTITY_1)
vaultStatesUnconsumed = services.vaultService.unconsumedStates<Cash.State>().toList() vaultStatesUnconsumed = miniCorpServices.vaultService.unconsumedStates<Cash.State>().toList()
} }
} }
@ -160,8 +160,7 @@ class CashTests {
// Test generation works. // Test generation works.
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply { val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) }.toWireTransaction()
}.toSignedTransaction().tx
assertTrue(tx.inputs.isEmpty()) assertTrue(tx.inputs.isEmpty())
val s = tx.outputs[0].data as Cash.State val s = tx.outputs[0].data as Cash.State
assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount) assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount)
@ -177,8 +176,7 @@ class CashTests {
val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34) val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34)
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply { val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
Cash().generateIssue(this, amount, owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY) Cash().generateIssue(this, amount, owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) }.toWireTransaction()
}.toSignedTransaction().tx
assertTrue(tx.inputs.isEmpty()) assertTrue(tx.inputs.isEmpty())
assertEquals(tx.outputs[0], tx.outputs[0]) assertEquals(tx.outputs[0], tx.outputs[0])
} }
@ -250,8 +248,7 @@ class CashTests {
var ptx = TransactionType.General.Builder(DUMMY_NOTARY) var ptx = TransactionType.General.Builder(DUMMY_NOTARY)
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY) Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY)
ptx.signWith(MINI_CORP_KEY) val tx = miniCorpServices.signInitialTransaction(ptx)
val tx = ptx.toSignedTransaction()
// Include the previously issued cash in a new issuance command // Include the previously issued cash in a new issuance command
ptx = TransactionType.General.Builder(DUMMY_NOTARY) ptx = TransactionType.General.Builder(DUMMY_NOTARY)

View File

@ -11,6 +11,7 @@ import net.corda.core.identity.AnonymousParty
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.utilities.* import net.corda.core.utilities.*
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MockServices
import org.junit.Test import org.junit.Test
import java.time.Duration import java.time.Duration
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
@ -39,6 +40,8 @@ class ObligationTests {
beneficiary = CHARLIE beneficiary = CHARLIE
) )
val outState = inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) val outState = inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2))
val miniCorpServices = MockServices(MINI_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
private fun cashObligationTestRoots( private fun cashObligationTestRoots(
group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
@ -125,8 +128,7 @@ class ObligationTests {
val tx = TransactionType.General.Builder(notary = null).apply { val tx = TransactionType.General.Builder(notary = null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = CHARLIE, notary = DUMMY_NOTARY) beneficiary = CHARLIE, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) }.toWireTransaction()
}.toSignedTransaction().tx
assertTrue(tx.inputs.isEmpty()) assertTrue(tx.inputs.isEmpty())
val expected = Obligation.State( val expected = Obligation.State(
obligor = MINI_CORP, obligor = MINI_CORP,
@ -203,12 +205,12 @@ class ObligationTests {
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = MINI_CORP, notary = DUMMY_NOTARY) beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) }.toWireTransaction()
}.toSignedTransaction()
// Include the previously issued obligation in a new issuance command // Include the previously issued obligation in a new issuance command
val ptx = TransactionType.General.Builder(DUMMY_NOTARY) val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
ptx.addInputState(tx.tx.outRef<Obligation.State<Currency>>(0)) ptx.addInputState(tx.outRef<Obligation.State<Currency>>(0))
Obligation<Currency>().generateIssue(ptx, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(ptx, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = MINI_CORP, notary = DUMMY_NOTARY) beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
} }
@ -220,9 +222,7 @@ class ObligationTests {
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE) val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY) }.toWireTransaction()
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction().tx
assertEquals(0, tx.outputs.size) assertEquals(0, tx.outputs.size)
} }
@ -233,9 +233,7 @@ class ObligationTests {
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE) val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY) }.toWireTransaction()
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction().tx
assertEquals(1, tx.outputs.size) assertEquals(1, tx.outputs.size)
val actual = tx.outputs[0].data val actual = tx.outputs[0].data
@ -249,10 +247,7 @@ class ObligationTests {
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE) val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY) }.toWireTransaction()
signWith(BOB_KEY)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction().tx
assertEquals(0, tx.outputs.size) assertEquals(0, tx.outputs.size)
} }
@ -263,9 +258,7 @@ class ObligationTests {
val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE) val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(null).apply { val tx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY) }.toWireTransaction()
signWith(BOB_KEY)
}.toSignedTransaction().tx
assertEquals(1, tx.outputs.size) assertEquals(1, tx.outputs.size)
val expected = obligationBobToAlice.copy(quantity = obligationBobToAlice.quantity - obligationAliceToBob.quantity) val expected = obligationBobToAlice.copy(quantity = obligationBobToAlice.quantity - obligationAliceToBob.quantity)
val actual = tx.outputs[0].data val actual = tx.outputs[0].data
@ -282,30 +275,31 @@ class ObligationTests {
var tx = TransactionType.General.Builder(null).apply { var tx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement.copy(dueBefore = dueBefore), 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement.copy(dueBefore = dueBefore), 100.DOLLARS.quantity,
beneficiary = MINI_CORP, notary = DUMMY_NOTARY) beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) }
}.toSignedTransaction() var stx = miniCorpServices.signInitialTransaction(tx)
var stateAndRef = tx.tx.outRef<Obligation.State<Currency>>(0) var stateAndRef = stx.tx.outRef<Obligation.State<Currency>>(0)
// Now generate a transaction marking the obligation as having defaulted // Now generate a transaction marking the obligation as having defaulted
tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateSetLifecycle(this, listOf(stateAndRef), Lifecycle.DEFAULTED, DUMMY_NOTARY) Obligation<Currency>().generateSetLifecycle(this, listOf(stateAndRef), Lifecycle.DEFAULTED, DUMMY_NOTARY)
signWith(MINI_CORP_KEY) }
signWith(DUMMY_NOTARY_KEY) var ptx = miniCorpServices.signInitialTransaction(tx, MINI_CORP_PUBKEY)
}.toSignedTransaction() stx = notaryServices.addSignature(ptx)
assertEquals(1, tx.tx.outputs.size)
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.DEFAULTED), tx.tx.outputs[0].data) assertEquals(1, stx.tx.outputs.size)
tx.verifySignatures() assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.DEFAULTED), stx.tx.outputs[0].data)
stx.verifySignatures()
// And set it back // And set it back
stateAndRef = tx.tx.outRef<Obligation.State<Currency>>(0) stateAndRef = stx.tx.outRef<Obligation.State<Currency>>(0)
tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateSetLifecycle(this, listOf(stateAndRef), Lifecycle.NORMAL, DUMMY_NOTARY) Obligation<Currency>().generateSetLifecycle(this, listOf(stateAndRef), Lifecycle.NORMAL, DUMMY_NOTARY)
signWith(MINI_CORP_KEY) }
signWith(DUMMY_NOTARY_KEY) ptx = miniCorpServices.signInitialTransaction(tx)
}.toSignedTransaction() stx = notaryServices.addSignature(ptx)
assertEquals(1, tx.tx.outputs.size) assertEquals(1, stx.tx.outputs.size)
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.NORMAL), tx.tx.outputs[0].data) assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.NORMAL), stx.tx.outputs[0].data)
tx.verifySignatures() stx.verifySignatures()
} }
/** Test generating a transaction to settle an obligation. */ /** Test generating a transaction to settle an obligation. */
@ -313,22 +307,18 @@ class ObligationTests {
fun `generate settlement transaction`() { fun `generate settlement transaction`() {
val cashTx = TransactionType.General.Builder(null).apply { val cashTx = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` defaultIssuer, MINI_CORP, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` defaultIssuer, MINI_CORP, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) }.toWireTransaction()
}.toSignedTransaction().tx
// Generate a transaction issuing the obligation // Generate a transaction issuing the obligation
val obligationTx = TransactionType.General.Builder(null).apply { val obligationTx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = MINI_CORP, notary = DUMMY_NOTARY) beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) }.toWireTransaction()
}.toSignedTransaction().tx
// Now generate a transaction settling the obligation // Now generate a transaction settling the obligation
val settleTx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val settleTx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateSettle(this, listOf(obligationTx.outRef(0)), listOf(cashTx.outRef(0)), Cash.Commands.Move(), DUMMY_NOTARY) Obligation<Currency>().generateSettle(this, listOf(obligationTx.outRef(0)), listOf(cashTx.outRef(0)), Cash.Commands.Move(), DUMMY_NOTARY)
signWith(DUMMY_NOTARY_KEY) }.toWireTransaction()
signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx
assertEquals(2, settleTx.inputs.size) assertEquals(2, settleTx.inputs.size)
assertEquals(1, settleTx.outputs.size) assertEquals(1, settleTx.outputs.size)
} }
@ -864,7 +854,7 @@ class ObligationTests {
@Test @Test
fun `summing balances due between parties`() { fun `summing balances due between parties`() {
val simple: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(Pair(Pair(ALICE, BOB), Amount(100000000, GBP))) val simple: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(Pair(Pair(ALICE, BOB), Amount(100000000, GBP)))
val expected: Map<AbstractParty, Long> = mapOf(Pair(ALICE, -100000000L), Pair(BOB, 100000000L)) val expected: Map<AbstractParty, Long> = mapOf(Pair(ALICE, -100000000L), Pair(BOB, 100000000L))
val actual = sumAmountsDue(simple) val actual = sumAmountsDue(simple)
assertEquals(expected, actual) assertEquals(expected, actual)
} }

View File

@ -394,15 +394,16 @@ class NodeVaultServiceTest {
@Test @Test
fun addNoteToTransaction() { fun addNoteToTransaction() {
database.transaction { val megaCorpServices = MockServices(MEGA_CORP_KEY)
database.transaction {
val freshKey = services.legalIdentityKey val freshKey = services.legalIdentityKey
// Issue a txn to Send us some Money // Issue a txn to Send us some Money
val usefulTX = TransactionType.General.Builder(null).apply { val usefulBuilder = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) }
}.toSignedTransaction() val usefulTX = megaCorpServices.signInitialTransaction(usefulBuilder)
services.recordTransactions(listOf(usefulTX)) services.recordTransactions(listOf(usefulTX))
@ -412,10 +413,10 @@ class NodeVaultServiceTest {
assertEquals(3, vaultSvc.getTransactionNotes(usefulTX.id).count()) assertEquals(3, vaultSvc.getTransactionNotes(usefulTX.id).count())
// Issue more Money (GBP) // Issue more Money (GBP)
val anotherTX = TransactionType.General.Builder(null).apply { val anotherBuilder = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY) Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) }
}.toSignedTransaction() val anotherTX = megaCorpServices.signInitialTransaction(anotherBuilder)
services.recordTransactions(listOf(anotherTX)) services.recordTransactions(listOf(anotherTX))

View File

@ -22,8 +22,7 @@ import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.*
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -42,6 +41,7 @@ class VaultWithCashTest {
val vault: VaultService get() = services.vaultService val vault: VaultService get() = services.vaultService
lateinit var dataSource: Closeable lateinit var dataSource: Closeable
lateinit var database: Database lateinit var database: Database
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
@Before @Before
fun setUp() { fun setUp() {
@ -91,32 +91,32 @@ class VaultWithCashTest {
@Test @Test
fun `issue and spend total correctly and irrelevant ignored`() { fun `issue and spend total correctly and irrelevant ignored`() {
val megaCorpServices = MockServices(MEGA_CORP_KEY)
database.transaction { database.transaction {
// A tx that sends us money. // A tx that sends us money.
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val usefulTX = TransactionType.General.Builder(null).apply { val usefulBuilder = TransactionType.General.Builder(null)
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY) Cash().generateIssue(usefulBuilder, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) val usefulTX = megaCorpServices.signInitialTransaction(usefulBuilder)
}.toSignedTransaction()
assertNull(vault.cashBalances[USD]) assertNull(vault.cashBalances[USD])
services.recordTransactions(usefulTX) services.recordTransactions(usefulTX)
// A tx that spends our money. // A tx that spends our money.
val spendTXBuilder = TransactionType.General.Builder(DUMMY_NOTARY).apply { val spendTXBuilder = TransactionType.General.Builder(DUMMY_NOTARY)
vault.generateSpend(this, 80.DOLLARS, BOB) vault.generateSpend(spendTXBuilder, 80.DOLLARS, BOB)
signWith(DUMMY_NOTARY_KEY) val spendPTX = services.signInitialTransaction(spendTXBuilder, freshKey)
} val spendTX = notaryServices.addSignature(spendPTX)
val spendTX = services.signInitialTransaction(spendTXBuilder, freshKey)
assertEquals(100.DOLLARS, vault.cashBalances[USD]) assertEquals(100.DOLLARS, vault.cashBalances[USD])
// A tx that doesn't send us anything. // A tx that doesn't send us anything.
val irrelevantTX = TransactionType.General.Builder(DUMMY_NOTARY).apply { val irrelevantBuilder = TransactionType.General.Builder(DUMMY_NOTARY)
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB, DUMMY_NOTARY) Cash().generateIssue(irrelevantBuilder, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY)
signWith(DUMMY_NOTARY_KEY) val irrelevantPTX = megaCorpServices.signInitialTransaction(irrelevantBuilder)
}.toSignedTransaction() val irrelevantTX = notaryServices.addSignature(irrelevantPTX)
services.recordTransactions(irrelevantTX) services.recordTransactions(irrelevantTX)
assertEquals(100.DOLLARS, vault.cashBalances[USD]) assertEquals(100.DOLLARS, vault.cashBalances[USD])
@ -150,12 +150,10 @@ class VaultWithCashTest {
backgroundExecutor.submit { backgroundExecutor.submit {
database.transaction { database.transaction {
try { try {
val txn1Builder = val txn1Builder = TransactionType.General.Builder(DUMMY_NOTARY)
TransactionType.General.Builder(DUMMY_NOTARY).apply { vault.generateSpend(txn1Builder, 60.DOLLARS, BOB)
vault.generateSpend(this, 60.DOLLARS, BOB) val ptxn1 = notaryServices.signInitialTransaction(txn1Builder)
signWith(DUMMY_NOTARY_KEY) val txn1 = services.addSignature(ptxn1, freshKey)
}
val txn1 = services.signInitialTransaction(txn1Builder, freshKey)
println("txn1: ${txn1.id} spent ${((txn1.tx.outputs[0].data) as Cash.State).amount}") println("txn1: ${txn1.id} spent ${((txn1.tx.outputs[0].data) as Cash.State).amount}")
println("""txn1 states: println("""txn1 states:
UNCONSUMED: ${vault.unconsumedStates<Cash.State>().count()} : ${vault.unconsumedStates<Cash.State>()}, UNCONSUMED: ${vault.unconsumedStates<Cash.State>().count()} : ${vault.unconsumedStates<Cash.State>()},
@ -182,12 +180,10 @@ class VaultWithCashTest {
backgroundExecutor.submit { backgroundExecutor.submit {
database.transaction { database.transaction {
try { try {
val txn2Builder = val txn2Builder = TransactionType.General.Builder(DUMMY_NOTARY)
TransactionType.General.Builder(DUMMY_NOTARY).apply { vault.generateSpend(txn2Builder, 80.DOLLARS, BOB)
vault.generateSpend(this, 80.DOLLARS, BOB) val ptxn2 = notaryServices.signInitialTransaction(txn2Builder)
signWith(DUMMY_NOTARY_KEY) val txn2 = services.addSignature(ptxn2, freshKey)
}
val txn2 = services.signInitialTransaction(txn2Builder, freshKey)
println("txn2: ${txn2.id} spent ${((txn2.tx.outputs[0].data) as Cash.State).amount}") println("txn2: ${txn2.id} spent ${((txn2.tx.outputs[0].data) as Cash.State).amount}")
println("""txn2 states: println("""txn2 states:
UNCONSUMED: ${vault.unconsumedStates<Cash.State>().count()} : ${vault.unconsumedStates<Cash.State>()}, UNCONSUMED: ${vault.unconsumedStates<Cash.State>().count()} : ${vault.unconsumedStates<Cash.State>()},
@ -229,9 +225,8 @@ class VaultWithCashTest {
val dummyIssueBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssueBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
signWith(DUMMY_NOTARY_KEY)
} }
val dummyIssue = services.signInitialTransaction(dummyIssueBuilder) val dummyIssue = notaryServices.signInitialTransaction(dummyIssueBuilder)
assertThatThrownBy { assertThatThrownBy {
dummyIssue.toLedgerTransaction(services).verify() dummyIssue.toLedgerTransaction(services).verify()
@ -248,11 +243,10 @@ class VaultWithCashTest {
val linearId = UniqueIdentifier() val linearId = UniqueIdentifier()
// Issue a linear state // Issue a linear state
val dummyIssueBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssueBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY)
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) dummyIssueBuilder.addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
signWith(DUMMY_NOTARY_KEY) val dummyIssuePtx = notaryServices.signInitialTransaction(dummyIssueBuilder)
} val dummyIssue = services.addSignature(dummyIssuePtx)
val dummyIssue = services.signInitialTransaction(dummyIssueBuilder, services.legalIdentityKey)
dummyIssue.toLedgerTransaction(services).verify() dummyIssue.toLedgerTransaction(services).verify()
@ -260,11 +254,12 @@ class VaultWithCashTest {
assertThat(vault.unconsumedStates<DummyLinearContract.State>()).hasSize(1) assertThat(vault.unconsumedStates<DummyLinearContract.State>()).hasSize(1)
// Move the same state // Move the same state
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyMoveBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
addInputState(dummyIssue.tx.outRef<LinearState>(0)) addInputState(dummyIssue.tx.outRef<LinearState>(0))
signWith(DUMMY_NOTARY_KEY) }
}.toSignedTransaction()
val dummyMove = notaryServices.signInitialTransaction(dummyMoveBuilder)
dummyIssue.toLedgerTransaction(services).verify() dummyIssue.toLedgerTransaction(services).verify()
@ -291,11 +286,10 @@ class VaultWithCashTest {
database.transaction { database.transaction {
// A tx that spends our money. // A tx that spends our money.
val spendTXBuilder = TransactionType.General.Builder(DUMMY_NOTARY).apply { val spendTXBuilder = TransactionType.General.Builder(DUMMY_NOTARY)
vault.generateSpend(this, 80.DOLLARS, BOB) vault.generateSpend(spendTXBuilder, 80.DOLLARS, BOB)
signWith(DUMMY_NOTARY_KEY) val spendPTX = notaryServices.signInitialTransaction(spendTXBuilder)
} val spendTX = services.addSignature(spendPTX, freshKey)
val spendTX = services.signInitialTransaction(spendTXBuilder, freshKey)
services.recordTransactions(spendTX) services.recordTransactions(spendTX)
val consumedStates = vault.consumedStates<ContractState>() val consumedStates = vault.consumedStates<ContractState>()
@ -322,13 +316,14 @@ class VaultWithCashTest {
linearStates.forEach { println(it.state.data.linearId) } linearStates.forEach { println(it.state.data.linearId) }
// Create a txn consuming different contract types // Create a txn consuming different contract types
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyMoveBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity))) addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity)))
addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity))) addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity)))
addInputState(linearStates.first()) addInputState(linearStates.first())
addInputState(deals.first()) addInputState(deals.first())
signWith(DUMMY_NOTARY_KEY) }
}.toSignedTransaction()
val dummyMove = notaryServices.signInitialTransaction(dummyMoveBuilder)
dummyMove.toLedgerTransaction(services).verify() dummyMove.toLedgerTransaction(services).verify()
services.recordTransactions(dummyMove) services.recordTransactions(dummyMove)

View File

@ -108,11 +108,9 @@ class AttachmentDemoFlow(val otherSide: Party, val hash: SecureHash.SHA256) : Fl
ptx.addAttachment(hash) ptx.addAttachment(hash)
progressTracker.currentStep = SIGNING progressTracker.currentStep = SIGNING
// Sign with the notary key
ptx.signWith(DUMMY_NOTARY_KEY)
// Send the transaction to the other recipient // Send the transaction to the other recipient
val stx = ptx.toSignedTransaction() val stx = serviceHub.signInitialTransaction(ptx)
return subFlow(FinalityFlow(stx, setOf(otherSide))).single() return subFlow(FinalityFlow(stx, setOf(otherSide))).single()
} }

View File

@ -117,7 +117,7 @@ open class RatesFixFlow(protected val tx: TransactionBuilder,
val resp = sendAndReceive<DigitalSignature.LegallyIdentifiable>(oracle, SignRequest(partialMerkleTx)) val resp = sendAndReceive<DigitalSignature.LegallyIdentifiable>(oracle, SignRequest(partialMerkleTx))
return resp.unwrap { sig -> return resp.unwrap { sig ->
check(sig.signer == oracle) check(sig.signer == oracle)
tx.checkSignature(sig) tx.toWireTransaction().checkSignature(sig)
sig sig
} }
} }

View File

@ -164,7 +164,7 @@ class NodeInterestRatesTest {
val wtx = tx.toWireTransaction() val wtx = tx.toWireTransaction()
val ftx = wtx.buildFilteredTransaction(Predicate { x -> fixCmdFilter(x) }) val ftx = wtx.buildFilteredTransaction(Predicate { x -> fixCmdFilter(x) })
val signature = oracle.sign(ftx) val signature = oracle.sign(ftx)
tx.checkAndAddSignature(signature) wtx.checkSignature(signature)
} }
} }
@ -228,8 +228,8 @@ class NodeInterestRatesTest {
val future = n1.services.startFlow(flow).resultFuture val future = n1.services.startFlow(flow).resultFuture
mockNet.runNetwork() mockNet.runNetwork()
future.getOrThrow() future.getOrThrow()
// We should now have a valid signature over our tx from the oracle. // We should now have a valid fix of our tx from the oracle.
val fix = tx.toSignedTransaction(true).tx.commands.map { it.value as Fix }.first() val fix = tx.toWireTransaction().commands.map { it.value as Fix }.first()
assertEquals(fixOf, fix.of) assertEquals(fixOf, fix.of)
assertEquals("0.678".bd, fix.value) assertEquals("0.678".bd, fix.value)
} }

View File

@ -201,6 +201,10 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
} }
class IRSTests { class IRSTests {
val megaCorpServices = MockServices(MEGA_CORP_KEY)
val miniCorpServices = MockServices(MINI_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
@Test @Test
fun ok() { fun ok() {
trade().verifies() trade().verifies()
@ -224,11 +228,10 @@ class IRSTests {
common = dummyIRS.common, common = dummyIRS.common,
notary = DUMMY_NOTARY).apply { notary = DUMMY_NOTARY).apply {
addTimeWindow(TEST_TX_TIME, 30.seconds) addTimeWindow(TEST_TX_TIME, 30.seconds)
signWith(MEGA_CORP_KEY)
signWith(MINI_CORP_KEY)
signWith(DUMMY_NOTARY_KEY)
} }
gtx.toSignedTransaction() val ptx1 = megaCorpServices.signInitialTransaction(gtx)
val ptx2 = miniCorpServices.addSignature(ptx1)
notaryServices.addSignature(ptx2)
} }
return genTX return genTX
} }
@ -308,13 +311,10 @@ class IRSTests {
val tx = TransactionType.General.Builder(DUMMY_NOTARY) val tx = TransactionType.General.Builder(DUMMY_NOTARY)
val fixing = Fix(nextFix, "0.052".percent.value) val fixing = Fix(nextFix, "0.052".percent.value)
InterestRateSwap().generateFix(tx, previousTXN.tx.outRef(0), fixing) InterestRateSwap().generateFix(tx, previousTXN.tx.outRef(0), fixing)
with(tx) { tx.addTimeWindow(TEST_TX_TIME, 30.seconds)
addTimeWindow(TEST_TX_TIME, 30.seconds) val ptx1 = megaCorpServices.signInitialTransaction(tx)
signWith(MEGA_CORP_KEY) val ptx2 = miniCorpServices.addSignature(ptx1)
signWith(MINI_CORP_KEY) notaryServices.addSignature(ptx2)
signWith(DUMMY_NOTARY_KEY)
}
tx.toSignedTransaction()
} }
fixTX.toLedgerTransaction(services).verify() fixTX.toLedgerTransaction(services).verify()
services.recordTransactions(fixTX) services.recordTransactions(fixTX)

View File

@ -16,6 +16,7 @@ import net.corda.core.node.NodeInfo
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.flows.FinalityFlow
import net.corda.flows.NotaryFlow import net.corda.flows.NotaryFlow
import net.corda.flows.TwoPartyTradeFlow import net.corda.flows.TwoPartyTradeFlow
import net.corda.testing.BOC import net.corda.testing.BOC
@ -69,8 +70,7 @@ class SellerFlow(val otherParty: Party,
@Suspendable @Suspendable
fun selfIssueSomeCommercialPaper(ownedBy: AbstractParty, notaryNode: NodeInfo): StateAndRef<CommercialPaper.State> { fun selfIssueSomeCommercialPaper(ownedBy: AbstractParty, notaryNode: NodeInfo): StateAndRef<CommercialPaper.State> {
// Make a fake company that's issued its own paper. // Make a fake company that's issued its own paper.
val keyPair = generateKeyPair() val party = Party(BOC.name, serviceHub.legalIdentityKey)
val party = Party(BOC.name, keyPair.public)
val issuance: SignedTransaction = run { val issuance: SignedTransaction = run {
val tx = CommercialPaper().generateIssue(party.ref(1, 2, 3), 1100.DOLLARS `issued by` DUMMY_CASH_ISSUER, val tx = CommercialPaper().generateIssue(party.ref(1, 2, 3), 1100.DOLLARS `issued by` DUMMY_CASH_ISSUER,
@ -85,29 +85,17 @@ class SellerFlow(val otherParty: Party,
tx.addTimeWindow(Instant.now(), 30.seconds) tx.addTimeWindow(Instant.now(), 30.seconds)
// Sign it as ourselves. // Sign it as ourselves.
tx.signWith(keyPair) val stx = serviceHub.signInitialTransaction(tx)
// Get the notary to sign the time-window. subFlow(FinalityFlow(stx)).single()
val notarySigs = subFlow(NotaryFlow.Client(tx.toSignedTransaction(false)))
notarySigs.forEach { tx.addSignatureUnchecked(it) }
// Commit it to local storage.
val stx = tx.toSignedTransaction(true)
serviceHub.recordTransactions(listOf(stx))
stx
} }
// Now make a dummy transaction that moves it to a new key, just to show that resolving dependencies works. // Now make a dummy transaction that moves it to a new key, just to show that resolving dependencies works.
val move: SignedTransaction = run { val move: SignedTransaction = run {
val builder = TransactionType.General.Builder(notaryNode.notaryIdentity) val builder = TransactionType.General.Builder(notaryNode.notaryIdentity)
CommercialPaper().generateMove(builder, issuance.tx.outRef(0), ownedBy) CommercialPaper().generateMove(builder, issuance.tx.outRef(0), ownedBy)
builder.signWith(keyPair) val stx = serviceHub.signInitialTransaction(builder)
val notarySignature = subFlow(NotaryFlow.Client(builder.toSignedTransaction(false))) subFlow(FinalityFlow(stx)).single()
notarySignature.forEach { builder.addSignatureUnchecked(it) }
val tx = builder.toSignedTransaction(true)
serviceHub.recordTransactions(listOf(tx))
tx
} }
return move.tx.outRef(0) return move.tx.outRef(0)

View File

@ -53,6 +53,8 @@ import java.util.concurrent.atomic.AtomicInteger
* - The Int.DOLLARS syntax doesn't work from Java. Use the DOLLARS(int) function instead. * - The Int.DOLLARS syntax doesn't work from Java. Use the DOLLARS(int) function instead.
*/ */
// TODO: Refactor these dummies to work with the new identities framework.
// A few dummy values for testing. // A few dummy values for testing.
val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() } val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() }
val MEGA_CORP_PUBKEY: PublicKey get() = MEGA_CORP_KEY.public val MEGA_CORP_PUBKEY: PublicKey get() = MEGA_CORP_KEY.public

View File

@ -139,7 +139,6 @@ data class TestTransactionDSLInterpreter private constructor(
// Verify on a copy of the transaction builder, so if it's then further modified it doesn't error due to // Verify on a copy of the transaction builder, so if it's then further modified it doesn't error due to
// the existing signature // the existing signature
transactionBuilder.copy().apply { transactionBuilder.copy().apply {
signWith(DUMMY_NOTARY_KEY)
toWireTransaction().toLedgerTransaction(services).verify() toWireTransaction().toLedgerTransaction(services).verify()
} }
return EnforceVerifyOrFail.Token return EnforceVerifyOrFail.Token

View File

@ -14,6 +14,7 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.flows.FinalityFlow import net.corda.flows.FinalityFlow
import net.corda.loadtest.LoadTest import net.corda.loadtest.LoadTest
import net.corda.loadtest.NodeConnection import net.corda.loadtest.NodeConnection
import net.corda.testing.node.MockServices
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
private val log = LoggerFactory.getLogger("NotaryTest") private val log = LoggerFactory.getLogger("NotaryTest")
@ -23,16 +24,15 @@ data class NotariseCommand(val issueTx: SignedTransaction, val moveTx: SignedTra
val dummyNotarisationTest = LoadTest<NotariseCommand, Unit>( val dummyNotarisationTest = LoadTest<NotariseCommand, Unit>(
"Notarising dummy transactions", "Notarising dummy transactions",
generate = { _, _ -> generate = { _, _ ->
val issuerServices = MockServices(DUMMY_CASH_ISSUER_KEY)
val generateTx = Generator.pickOne(simpleNodes).bind { node -> val generateTx = Generator.pickOne(simpleNodes).bind { node ->
Generator.int().map { Generator.int().map {
val issueTx = DummyContract.generateInitial(it, notary.info.notaryIdentity, DUMMY_CASH_ISSUER).apply { val issueBuilder = DummyContract.generateInitial(it, notary.info.notaryIdentity, DUMMY_CASH_ISSUER)
signWith(DUMMY_CASH_ISSUER_KEY) val issueTx = issuerServices.signInitialTransaction(issueBuilder)
} val asset = issueTx.tx.outRef<DummyContract.SingleOwnerState>(0)
val asset = issueTx.toWireTransaction().outRef<DummyContract.SingleOwnerState>(0) val moveBuilder = DummyContract.move(asset, DUMMY_CASH_ISSUER.party)
val moveTx = DummyContract.move(asset, DUMMY_CASH_ISSUER.party).apply { val moveTx = issuerServices.signInitialTransaction(moveBuilder)
signWith(DUMMY_CASH_ISSUER_KEY) NotariseCommand(issueTx, moveTx, node)
}
NotariseCommand(issueTx.toSignedTransaction(false), moveTx.toSignedTransaction(false), node)
} }
} }
Generator.replicate(10, generateTx) Generator.replicate(10, generateTx)