mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +00:00
Testing: more utilities on Transaction and TransactionGroupForTest to help with testing signed transactions.
This commit is contained in:
parent
48192d8d9d
commit
fc000ec03c
@ -65,6 +65,11 @@ data class WireTransaction(val inputs: List<StateRef>,
|
||||
return LedgerTransaction(inputs, outputs, authenticatedArgs, originalHash)
|
||||
}
|
||||
|
||||
/** Serialises and returns this transaction as a [SignedWireTransaction] with no signatures attached. */
|
||||
fun toSignedTransaction(withSigs: List<DigitalSignature.WithKey> = emptyList()): SignedWireTransaction {
|
||||
return SignedWireTransaction(serialize(), withSigs)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
val buf = StringBuilder()
|
||||
buf.appendln("Transaction:")
|
||||
@ -155,7 +160,7 @@ class TransactionBuilder(private val inputs: MutableList<StateRef> = arrayListOf
|
||||
}
|
||||
|
||||
/** A more convenient way to add items to this transaction that calls the add* methods for you based on type */
|
||||
public fun withItems(vararg items: Any): TransactionBuilder {
|
||||
fun withItems(vararg items: Any): TransactionBuilder {
|
||||
for (t in items) {
|
||||
when (t) {
|
||||
is StateRef -> inputs.add(t)
|
||||
@ -263,13 +268,13 @@ class TransactionBuilder(private val inputs: MutableList<StateRef> = arrayListOf
|
||||
*/
|
||||
data class LedgerTransaction(
|
||||
/** The input states which will be consumed/invalidated by the execution of this transaction. */
|
||||
val inputs: List<StateRef>,
|
||||
val inputs: List<StateRef>,
|
||||
/** The states that will be generated by the execution of this transaction. */
|
||||
val outputs: List<ContractState>,
|
||||
val outputs: List<ContractState>,
|
||||
/** Arbitrary data passed to the program of each input state. */
|
||||
val commands: List<AuthenticatedObject<CommandData>>,
|
||||
/** The hash of the original serialised SignedTransaction */
|
||||
val hash: SecureHash
|
||||
val commands: List<AuthenticatedObject<CommandData>>,
|
||||
/** The hash of the original serialised WireTransaction */
|
||||
val hash: SecureHash
|
||||
) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T : ContractState> outRef(index: Int) = StateAndRef(outputs[index] as T, StateRef(hash, index))
|
||||
@ -280,4 +285,30 @@ data class LedgerTransaction(
|
||||
throw IllegalArgumentException("State not found in this transaction")
|
||||
return outRef(i)
|
||||
}
|
||||
|
||||
fun toWireTransaction(): WireTransaction {
|
||||
val wtx = WireTransaction(inputs, outputs, commands.map { Command(it.value, it.signers) })
|
||||
check(wtx.serialize().hash == hash)
|
||||
return wtx
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this transaction to [SignedWireTransaction] form, optionally using the provided keys to sign. There is
|
||||
* no requirement that [andSignWithKeys] include all required keys.
|
||||
*
|
||||
* @throws IllegalArgumentException if a key is provided that isn't listed in any command and [allowUnusedKeys]
|
||||
* is false.
|
||||
*/
|
||||
fun toSignedTransaction(andSignWithKeys: List<KeyPair> = emptyList(), allowUnusedKeys: Boolean = false): SignedWireTransaction {
|
||||
val allPubKeys = commands.flatMap { it.signers }.toSet()
|
||||
val wtx = toWireTransaction()
|
||||
val bits = wtx.serialize()
|
||||
val sigs = ArrayList<DigitalSignature.WithKey>()
|
||||
for (key in andSignWithKeys) {
|
||||
if (!allPubKeys.contains(key.public) && !allowUnusedKeys)
|
||||
throw IllegalArgumentException("Key provided that is not listed by any command")
|
||||
sigs += key.signWithECDSA(bits)
|
||||
}
|
||||
return wtx.toSignedTransaction(sigs)
|
||||
}
|
||||
}
|
@ -127,4 +127,30 @@ class TransactionGroupTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun signGroup() {
|
||||
val signedTxns: List<SignedWireTransaction> = transactionGroup {
|
||||
transaction {
|
||||
output("£1000") { A_THOUSAND_POUNDS }
|
||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||
}
|
||||
|
||||
transaction {
|
||||
input("£1000")
|
||||
output("alice's £1000") { A_THOUSAND_POUNDS `owned by` ALICE }
|
||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||
}
|
||||
|
||||
transaction {
|
||||
input("alice's £1000")
|
||||
arg(ALICE) { Cash.Commands.Move() }
|
||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Exit(1000.POUNDS) }
|
||||
}
|
||||
}.signAll()
|
||||
|
||||
// Now go through the conversion -> verification path with them.
|
||||
val ltxns = signedTxns.map { it.verifyToLedgerTransaction(MockIdentityService) }.toSet()
|
||||
TransactionGroup(ltxns, emptySet()).verify(MockContractFactory)
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ import core.crypto.DummyPublicKey
|
||||
import core.crypto.NullPublicKey
|
||||
import core.crypto.SecureHash
|
||||
import core.crypto.generateKeyPair
|
||||
import core.serialization.serialize
|
||||
import core.visualiser.GraphVisualiser
|
||||
import java.security.PublicKey
|
||||
import java.time.Instant
|
||||
@ -43,6 +44,8 @@ val BOB = BOB_KEY.public
|
||||
val MEGA_CORP = Party("MegaCorp", MEGA_CORP_PUBKEY)
|
||||
val MINI_CORP = Party("MiniCorp", MINI_CORP_PUBKEY)
|
||||
|
||||
val ALL_KEYS = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, ALICE_KEY, BOB_KEY)
|
||||
|
||||
val TEST_KEYS_TO_CORP_MAP: Map<PublicKey, Party> = mapOf(
|
||||
MEGA_CORP_PUBKEY to MEGA_CORP,
|
||||
MINI_CORP_PUBKEY to MINI_CORP
|
||||
@ -212,14 +215,13 @@ class TransactionGroupDSL<T : ContractState>(private val stateType: Class<T>) {
|
||||
inStates.add(label.outputRef)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts to a [LedgerTransaction] with the test institution map, and just assigns a random hash
|
||||
* (i.e. pretend it was signed)
|
||||
*/
|
||||
fun toLedgerTransaction(): LedgerTransaction {
|
||||
val wtx = WireTransaction(inStates, outStates.map { it.state }, commands)
|
||||
return wtx.toLedgerTransaction(MockIdentityService, SecureHash.randomSHA256())
|
||||
return wtx.toLedgerTransaction(MockIdentityService, wtx.serialize().hash)
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,6 +321,10 @@ class TransactionGroupDSL<T : ContractState>(private val stateType: Class<T>) {
|
||||
@Suppress("CAST_NEVER_SUCCEEDS")
|
||||
GraphVisualiser(this as TransactionGroupDSL<ContractState>).display()
|
||||
}
|
||||
|
||||
fun signAll(): List<SignedWireTransaction> {
|
||||
return txns.map { it.toSignedTransaction(andSignWithKeys = ALL_KEYS, allowUnusedKeys = true) }
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : ContractState> transactionGroupFor(body: TransactionGroupDSL<T>.() -> Unit) = TransactionGroupDSL<T>(T::class.java).apply { this.body() }
|
||||
|
Loading…
Reference in New Issue
Block a user