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)
}
/**
* Helper method to construct an initial partially signed transaction from a TransactionBuilder
* using the default identity key contained in the node.
@ -146,7 +145,6 @@ interface ServiceHub : ServicesForResolution {
*/
fun signInitialTransaction(builder: TransactionBuilder): SignedTransaction = signInitialTransaction(builder, legalIdentityKey)
/**
* Helper method to construct an initial partially signed transaction from a [TransactionBuilder]
* 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.NamedByHash
import net.corda.core.contracts.TransactionResolutionException
import net.corda.core.contracts.TransactionVerificationException
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.keys
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable
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)
/**
* Calls [verifySignatures] to check all required signatures are present, and then calls
* [WireTransaction.toLedgerTransaction] with the passed in [ServiceHub] to resolve the dependencies,
* returning an unverified LedgerTransaction.
* Checks the transaction's signatures are valid, optionally calls [verifySignatures] to check
* all required signatures are present, and then calls [WireTransaction.toLedgerTransaction]
* 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 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.
*/
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class, SignatureException::class)
fun toLedgerTransaction(services: ServiceHub) = verifySignatures().toLedgerTransaction(services)
@JvmOverloads
@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)"
}

View File

@ -1,13 +1,16 @@
package net.corda.core.transactions
import co.paralleluniverse.strands.Strand
import com.google.common.annotations.VisibleForTesting
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.internal.FlowStateMachine
import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.serialize
import java.security.KeyPair
import java.security.PublicKey
import java.security.SignatureException
import java.time.Duration
import java.time.Instant
import java.util.*
@ -40,8 +43,6 @@ open class TransactionBuilder(
protected var timeWindow: TimeWindow? = null) {
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.
*/
@ -57,27 +58,6 @@ open class TransactionBuilder(
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
/** 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 {
@ -95,62 +75,18 @@ open class TransactionBuilder(
}
// 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),
ArrayList(outputs), ArrayList(commands), notary, signers.toList(), type, timeWindow)
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))
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class)
fun toLedgerTransaction(services: ServiceHub) = toWireTransaction().toLedgerTransaction(services)
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class, TransactionVerificationException::class)
fun verify(services: ServiceHub) {
toLedgerTransaction(services).verify()
}
open fun addInputState(stateAndRef: StateAndRef<*>) {
check(currentSigs.isEmpty())
val notary = stateAndRef.state.notary
require(notary == this.notary) { "Input state requires notary \"$notary\" which does not match the transaction notary \"${this.notary}\"." }
signers.add(notary.owningKey)
@ -158,12 +94,10 @@ open class TransactionBuilder(
}
fun addAttachment(attachmentId: SecureHash) {
check(currentSigs.isEmpty())
attachments.add(attachmentId)
}
fun addOutputState(state: TransactionState<*>): Int {
check(currentSigs.isEmpty())
outputs.add(state)
return outputs.size - 1
}
@ -178,7 +112,6 @@ open class TransactionBuilder(
}
fun addCommand(arg: Command) {
check(currentSigs.isEmpty())
// TODO: replace pubkeys in commands with 'pointers' to keys in signers
signers.addAll(arg.signers)
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, 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.
fun inputStates(): List<StateRef> = ArrayList(inputs)
fun attachments(): List<SecureHash> = ArrayList(attachments)
fun outputStates(): List<TransactionState<*>> = ArrayList(outputs)
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 net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.keys
import net.corda.core.identity.Party
import net.corda.core.indexOfOrThrow
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.utilities.Emoji
import java.security.PublicKey
import java.security.SignatureException
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 {
val buf = StringBuilder()
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_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 org.junit.Test
import java.security.KeyPair
@ -28,24 +30,29 @@ class TransactionGraphSearchTests {
* @param command the command to add to the origin transaction.
* @param signer signer for the two transactions and their commands.
*/
fun buildTransactions(command: CommandData, signer: KeyPair): GraphTransactionStorage {
val originTx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
addOutputState(DummyState(random31BitValue()))
addCommand(command, signer.public)
signWith(signer)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction(false)
val inputTx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
addInputState(originTx.tx.outRef<DummyState>(0))
signWith(signer)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction(false)
fun buildTransactions(command: CommandData): GraphTransactionStorage {
val megaCorpServices = MockServices(MEGA_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
val originBuilder = TransactionType.General.Builder(DUMMY_NOTARY)
originBuilder.addOutputState(DummyState(random31BitValue()))
originBuilder.addCommand(command, MEGA_CORP_PUBKEY)
val originPtx = megaCorpServices.signInitialTransaction(originBuilder)
val originTx = notaryServices.addSignature(originPtx)
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)
}
@Test
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())
search.query = TransactionGraphSearch.Query()
val expected = emptyList<WireTransaction>()
@ -56,7 +63,7 @@ class TransactionGraphSearchTests {
@Test
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))
search.query = TransactionGraphSearch.Query()
val expected = emptyList<WireTransaction>()
@ -67,7 +74,7 @@ class TransactionGraphSearchTests {
@Test
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))
search.query = TransactionGraphSearch.Query(DummyContract.Commands.Create::class.java)
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.SignTransactionFlow
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.MockServices
import org.junit.After
import org.junit.Before
import org.junit.Test
@ -26,6 +28,7 @@ class CollectSignaturesFlowTests {
lateinit var b: MockNetwork.MockNode
lateinit var c: MockNetwork.MockNode
lateinit var notary: Party
val services = MockServices()
@Before
fun setup() {
@ -162,7 +165,8 @@ class CollectSignaturesFlowTests {
@Test
fun `fails when not signed by initiator`() {
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))
mockNet.runNetwork()
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.MINI_CORP
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockServices
import org.junit.After
import org.junit.Before
import org.junit.Test
@ -32,6 +33,8 @@ class ResolveTransactionsFlowTest {
lateinit var a: MockNetwork.MockNode
lateinit var b: MockNetwork.MockNode
lateinit var notary: Party
val megaCorpServices = MockServices(MEGA_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
@Before
fun setup() {
@ -94,9 +97,8 @@ class ResolveTransactionsFlowTest {
val count = 50
var cursor = stx2
repeat(count) {
val stx = DummyContract.move(cursor.tx.outRef(0), MINI_CORP)
.addSignatureUnchecked(NullSignature)
.toSignedTransaction(false)
val builder = DummyContract.move(cursor.tx.outRef(0), MINI_CORP)
val stx = megaCorpServices.signInitialTransaction(builder)
a.database.transaction {
a.services.recordTransactions(stx)
}
@ -114,15 +116,13 @@ class ResolveTransactionsFlowTest {
val stx1 = makeTransactions().first
val stx2 = DummyContract.move(stx1.tx.outRef(0), MINI_CORP).run {
signWith(MEGA_CORP_KEY)
signWith(DUMMY_NOTARY_KEY)
toSignedTransaction()
val ptx = megaCorpServices.signInitialTransaction(this)
notaryServices.addSignature(ptx)
}
val stx3 = DummyContract.move(listOf(stx1.tx.outRef(0), stx2.tx.outRef(0)), MINI_CORP).run {
signWith(MEGA_CORP_KEY)
signWith(DUMMY_NOTARY_KEY)
toSignedTransaction()
val ptx = megaCorpServices.signInitialTransaction(this)
notaryServices.addSignature(ptx)
}
a.database.transaction {
@ -168,15 +168,19 @@ class ResolveTransactionsFlowTest {
val dummy1: SignedTransaction = DummyContract.generateInitial(0, notary, MEGA_CORP.ref(1)).let {
if (withAttachment != null)
it.addAttachment(withAttachment)
if (signFirstTX)
it.signWith(MEGA_CORP_KEY)
it.signWith(DUMMY_NOTARY_KEY)
it.toSignedTransaction(false)
when (signFirstTX) {
true -> {
val ptx = megaCorpServices.signInitialTransaction(it)
notaryServices.addSignature(ptx)
}
false -> {
notaryServices.signInitialTransaction(it)
}
}
}
val dummy2: SignedTransaction = DummyContract.move(dummy1.tx.outRef(0), MINI_CORP).let {
it.signWith(MEGA_CORP_KEY)
it.signWith(DUMMY_NOTARY_KEY)
it.toSignedTransaction()
val ptx = megaCorpServices.signInitialTransaction(it)
notaryServices.addSignature(ptx)
}
a.database.transaction {
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.seconds
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.DUMMY_KEY_2
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY
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 net.corda.core.utilities.*
import net.corda.testing.*
import net.corda.testing.node.MockServices
import org.junit.Before
import org.junit.Test
import java.security.SignatureException
@ -53,7 +48,8 @@ class TransactionSerializationTests {
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 megaCorpServices = MockServices(MEGA_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
lateinit var tx: TransactionBuilder
@Before
@ -65,53 +61,47 @@ class TransactionSerializationTests {
@Test
fun signWireTX() {
tx.signWith(DUMMY_NOTARY_KEY)
tx.signWith(MEGA_CORP_KEY)
val signedTX = tx.toSignedTransaction()
val ptx = megaCorpServices.signInitialTransaction(tx)
val stx = notaryServices.addSignature(ptx)
// Now check that the signature we just made verifies.
signedTX.verifySignatures()
stx.verifySignatures()
// 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) {
signedTX.verifySignatures()
stx.verifySignatures()
}
}
@Test
fun wrongKeys() {
// Can't convert if we don't have signatures for all commands
assertFailsWith(IllegalStateException::class) {
tx.toSignedTransaction()
}
tx.signWith(MEGA_CORP_KEY)
tx.signWith(DUMMY_NOTARY_KEY)
val signedTX = tx.toSignedTransaction()
val ptx = megaCorpServices.signInitialTransaction(tx)
val stx = notaryServices.addSignature(ptx)
// Cannot construct with an empty sigs list.
assertFailsWith(IllegalArgumentException::class) {
signedTX.copy(sigs = emptyList())
stx.copy(sigs = emptyList())
}
// If the signature was replaced in transit, we don't like it.
assertFailsWith(SignatureException::class) {
val tx2 = TransactionType.General.Builder(DUMMY_NOTARY).withItems(inputState, outputState, changeState,
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
fun timeWindow() {
tx.addTimeWindow(TEST_TX_TIME, 30.seconds)
tx.signWith(MEGA_CORP_KEY)
tx.signWith(DUMMY_NOTARY_KEY)
val stx = tx.toSignedTransaction()
val ptx = megaCorpServices.signInitialTransaction(tx)
val stx = notaryServices.addSignature(ptx)
assertEquals(TEST_TX_TIME, stx.tx.timeWindow?.midpoint)
}
}

View File

@ -205,6 +205,8 @@ class CommercialPaperTestsGeneric {
private lateinit var aliceVaultService: VaultService
private lateinit var alicesVault: Vault<ContractState>
private val notaryServices = MockServices(DUMMY_NOTARY_KEY)
private lateinit var moveTX: SignedTransaction
@Test
@ -215,7 +217,7 @@ class CommercialPaperTestsGeneric {
val databaseAlice = dataSourceAndDatabaseAlice.second
databaseAlice.transaction {
aliceServices = object : MockServices() {
aliceServices = object : MockServices(ALICE_KEY) {
override val vaultService: VaultService = makeVaultService(dataSourcePropsAlice)
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
@ -235,7 +237,7 @@ class CommercialPaperTestsGeneric {
val databaseBigCorp = dataSourceAndDatabaseBigCorp.second
databaseBigCorp.transaction {
bigCorpServices = object : MockServices() {
bigCorpServices = object : MockServices(BIG_CORP_KEY) {
override val vaultService: VaultService = makeVaultService(dataSourcePropsBigCorp)
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.
val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER
val issuance = bigCorpServices.myInfo.legalIdentity.ref(1)
val issueTX: SignedTransaction =
CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, DUMMY_NOTARY).apply {
addTimeWindow(TEST_TX_TIME, 30.seconds)
signWith(bigCorpServices.key)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
val issueBuilder = CommercialPaper().generateIssue(issuance, faceValue, TEST_TX_TIME + 30.days, DUMMY_NOTARY)
issueBuilder.addTimeWindow(TEST_TX_TIME, 30.seconds)
val issuePtx = bigCorpServices.signInitialTransaction(issueBuilder)
val issueTx = notaryServices.addSignature(issuePtx)
databaseAlice.transaction {
// Alice pays $9000 to BigCorp to own some of their debt.
moveTX = run {
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, AnonymousParty(bigCorpServices.key.public))
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), AnonymousParty(aliceServices.key.public))
ptx.signWith(bigCorpServices.key)
ptx.signWith(aliceServices.key)
ptx.signWith(DUMMY_NOTARY_KEY)
ptx.toSignedTransaction()
val builder = TransactionType.General.Builder(DUMMY_NOTARY)
aliceVaultService.generateSpend(builder, 9000.DOLLARS, AnonymousParty(bigCorpServices.key.public))
CommercialPaper().generateMove(builder, issueTx.tx.outRef(0), AnonymousParty(aliceServices.key.public))
val ptx = aliceServices.signInitialTransaction(builder)
val ptx2 = bigCorpServices.addSignature(ptx)
val stx = notaryServices.addSignature(ptx2)
stx
}
}
databaseBigCorp.transaction {
// Verify the txns are valid and insert into both sides.
listOf(issueTX, moveTX).forEach {
listOf(issueTx, moveTX).forEach {
it.toLedgerTransaction(aliceServices).verify()
aliceServices.recordTransactions(it)
bigCorpServices.recordTransactions(it)
@ -288,13 +288,13 @@ class CommercialPaperTestsGeneric {
databaseBigCorp.transaction {
fun makeRedeemTX(time: Instant): Pair<SignedTransaction, UUID> {
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
ptx.addTimeWindow(time, 30.seconds)
CommercialPaper().generateRedeem(ptx, moveTX.tx.outRef(1), bigCorpVaultService)
ptx.signWith(aliceServices.key)
ptx.signWith(bigCorpServices.key)
ptx.signWith(DUMMY_NOTARY_KEY)
return Pair(ptx.toSignedTransaction(), ptx.lockId)
val builder = TransactionType.General.Builder(DUMMY_NOTARY)
builder.addTimeWindow(time, 30.seconds)
CommercialPaper().generateRedeem(builder, moveTX.tx.outRef(1), bigCorpVaultService)
val ptx = aliceServices.signInitialTransaction(builder)
val ptx2 = bigCorpServices.addSignature(ptx)
val stx = notaryServices.addSignature(ptx2)
return Pair(stx, builder.lockId)
}
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))))
)
lateinit var services: MockServices
val vault: VaultService get() = services.vaultService
lateinit var miniCorpServices: MockServices
val vault: VaultService get() = miniCorpServices.vaultService
lateinit var dataSource: Closeable
lateinit var database: Database
lateinit var vaultStatesUnconsumed: List<StateAndRef<Cash.State>>
@ -57,7 +57,7 @@ class CashTests {
dataSource = dataSourceAndDatabase.first
database = dataSourceAndDatabase.second
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 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)
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)
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)
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)
vaultStatesUnconsumed = services.vaultService.unconsumedStates<Cash.State>().toList()
vaultStatesUnconsumed = miniCorpServices.vaultService.unconsumedStates<Cash.State>().toList()
}
}
@ -160,8 +160,7 @@ class CashTests {
// Test generation works.
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)
signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
assertTrue(tx.inputs.isEmpty())
val s = tx.outputs[0].data as Cash.State
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 tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
Cash().generateIssue(this, amount, owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
assertTrue(tx.inputs.isEmpty())
assertEquals(tx.outputs[0], tx.outputs[0])
}
@ -250,8 +248,7 @@ class CashTests {
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)
ptx.signWith(MINI_CORP_KEY)
val tx = ptx.toSignedTransaction()
val tx = miniCorpServices.signInitialTransaction(ptx)
// Include the previously issued cash in a new issuance command
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.utilities.*
import net.corda.testing.*
import net.corda.testing.node.MockServices
import org.junit.Test
import java.time.Duration
import java.time.temporal.ChronoUnit
@ -39,6 +40,8 @@ class ObligationTests {
beneficiary = CHARLIE
)
val outState = inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2))
val miniCorpServices = MockServices(MINI_CORP_KEY)
val notaryServices = MockServices(DUMMY_NOTARY_KEY)
private fun cashObligationTestRoots(
group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
@ -125,8 +128,7 @@ class ObligationTests {
val tx = TransactionType.General.Builder(notary = null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = CHARLIE, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
assertTrue(tx.inputs.isEmpty())
val expected = Obligation.State(
obligor = MINI_CORP,
@ -203,12 +205,12 @@ class ObligationTests {
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY)
}.toSignedTransaction()
}.toWireTransaction()
// Include the previously issued obligation in a new issuance command
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,
beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
}
@ -220,9 +222,7 @@ class ObligationTests {
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
assertEquals(0, tx.outputs.size)
}
@ -233,9 +233,7 @@ class ObligationTests {
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
assertEquals(1, tx.outputs.size)
val actual = tx.outputs[0].data
@ -249,10 +247,7 @@ class ObligationTests {
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY)
signWith(BOB_KEY)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
assertEquals(0, tx.outputs.size)
}
@ -263,9 +258,7 @@ class ObligationTests {
val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY)
signWith(BOB_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
assertEquals(1, tx.outputs.size)
val expected = obligationBobToAlice.copy(quantity = obligationBobToAlice.quantity - obligationAliceToBob.quantity)
val actual = tx.outputs[0].data
@ -282,30 +275,31 @@ class ObligationTests {
var tx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement.copy(dueBefore = dueBefore), 100.DOLLARS.quantity,
beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY)
}.toSignedTransaction()
var stateAndRef = tx.tx.outRef<Obligation.State<Currency>>(0)
}
var stx = miniCorpServices.signInitialTransaction(tx)
var stateAndRef = stx.tx.outRef<Obligation.State<Currency>>(0)
// Now generate a transaction marking the obligation as having defaulted
tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateSetLifecycle(this, listOf(stateAndRef), Lifecycle.DEFAULTED, DUMMY_NOTARY)
signWith(MINI_CORP_KEY)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
assertEquals(1, tx.tx.outputs.size)
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.DEFAULTED), tx.tx.outputs[0].data)
tx.verifySignatures()
}
var ptx = miniCorpServices.signInitialTransaction(tx, MINI_CORP_PUBKEY)
stx = notaryServices.addSignature(ptx)
assertEquals(1, stx.tx.outputs.size)
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.DEFAULTED), stx.tx.outputs[0].data)
stx.verifySignatures()
// 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 {
Obligation<Currency>().generateSetLifecycle(this, listOf(stateAndRef), Lifecycle.NORMAL, DUMMY_NOTARY)
signWith(MINI_CORP_KEY)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
assertEquals(1, tx.tx.outputs.size)
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.NORMAL), tx.tx.outputs[0].data)
tx.verifySignatures()
}
ptx = miniCorpServices.signInitialTransaction(tx)
stx = notaryServices.addSignature(ptx)
assertEquals(1, stx.tx.outputs.size)
assertEquals(stateAndRef.state.data.copy(lifecycle = Lifecycle.NORMAL), stx.tx.outputs[0].data)
stx.verifySignatures()
}
/** Test generating a transaction to settle an obligation. */
@ -313,22 +307,18 @@ class ObligationTests {
fun `generate settlement transaction`() {
val cashTx = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` defaultIssuer, MINI_CORP, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
// Generate a transaction issuing the obligation
val obligationTx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
// Now generate a transaction settling the obligation
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)
signWith(DUMMY_NOTARY_KEY)
signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx
}.toWireTransaction()
assertEquals(2, settleTx.inputs.size)
assertEquals(1, settleTx.outputs.size)
}
@ -864,7 +854,7 @@ class ObligationTests {
@Test
fun `summing balances due between parties`() {
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)
assertEquals(expected, actual)
}

View File

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

View File

@ -108,11 +108,9 @@ class AttachmentDemoFlow(val otherSide: Party, val hash: SecureHash.SHA256) : Fl
ptx.addAttachment(hash)
progressTracker.currentStep = SIGNING
// Sign with the notary key
ptx.signWith(DUMMY_NOTARY_KEY)
// Send the transaction to the other recipient
val stx = ptx.toSignedTransaction()
val stx = serviceHub.signInitialTransaction(ptx)
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))
return resp.unwrap { sig ->
check(sig.signer == oracle)
tx.checkSignature(sig)
tx.toWireTransaction().checkSignature(sig)
sig
}
}

View File

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

View File

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

View File

@ -16,6 +16,7 @@ import net.corda.core.node.NodeInfo
import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
import net.corda.flows.FinalityFlow
import net.corda.flows.NotaryFlow
import net.corda.flows.TwoPartyTradeFlow
import net.corda.testing.BOC
@ -69,8 +70,7 @@ class SellerFlow(val otherParty: Party,
@Suspendable
fun selfIssueSomeCommercialPaper(ownedBy: AbstractParty, notaryNode: NodeInfo): StateAndRef<CommercialPaper.State> {
// Make a fake company that's issued its own paper.
val keyPair = generateKeyPair()
val party = Party(BOC.name, keyPair.public)
val party = Party(BOC.name, serviceHub.legalIdentityKey)
val issuance: SignedTransaction = run {
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)
// Sign it as ourselves.
tx.signWith(keyPair)
val stx = serviceHub.signInitialTransaction(tx)
// Get the notary to sign the time-window.
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
subFlow(FinalityFlow(stx)).single()
}
// Now make a dummy transaction that moves it to a new key, just to show that resolving dependencies works.
val move: SignedTransaction = run {
val builder = TransactionType.General.Builder(notaryNode.notaryIdentity)
CommercialPaper().generateMove(builder, issuance.tx.outRef(0), ownedBy)
builder.signWith(keyPair)
val notarySignature = subFlow(NotaryFlow.Client(builder.toSignedTransaction(false)))
notarySignature.forEach { builder.addSignatureUnchecked(it) }
val tx = builder.toSignedTransaction(true)
serviceHub.recordTransactions(listOf(tx))
tx
val stx = serviceHub.signInitialTransaction(builder)
subFlow(FinalityFlow(stx)).single()
}
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.
*/
// TODO: Refactor these dummies to work with the new identities framework.
// A few dummy values for testing.
val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() }
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
// the existing signature
transactionBuilder.copy().apply {
signWith(DUMMY_NOTARY_KEY)
toWireTransaction().toLedgerTransaction(services).verify()
}
return EnforceVerifyOrFail.Token

View File

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