Testing cleanup:

- Move code out of ambiguously named TestUtils files (there were three). Sometimes it's simpler to just put these things into the contract source files directly.
- Remove JavaTestHelpers objects (there were three), in favour of just giving the top level kotlin file class better names.
- Misc other small tweaks and cleanups.
This commit is contained in:
Mike Hearn 2016-07-11 16:13:42 +02:00
parent 2cd5014941
commit cc22810b09
20 changed files with 243 additions and 347 deletions

View File

@ -17,7 +17,7 @@ import java.time.Instant;
import java.util.Currency;
import java.util.List;
import static com.r3corda.core.contracts.ContractsDSLKt.requireSingleCommand;
import static com.r3corda.core.contracts.ContractsDSL.requireSingleCommand;
import static kotlin.collections.CollectionsKt.single;

View File

@ -170,3 +170,8 @@ class CommercialPaper : Contract {
}
}
infix fun CommercialPaper.State.`owned by`(owner: PublicKey) = copy(owner = owner)
infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary)
infix fun ICommercialPaperState.`owned by`(newOwner: PublicKey) = withOwner(newOwner)

View File

@ -1,10 +1,7 @@
package com.r3corda.contracts.asset
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.newSecureRandom
import com.r3corda.core.crypto.toStringShort
import com.r3corda.core.crypto.*
import com.r3corda.core.node.services.Wallet
import com.r3corda.core.utilities.Emoji
import java.security.PublicKey
@ -17,7 +14,6 @@ import java.util.*
// Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode.
val CASH_PROGRAM_ID = Cash()
//SecureHash.sha256("cash")
/**
* A cash transaction may split and merge money represented by a set of (issuer, depositRef) pairs, across multiple
@ -241,4 +237,25 @@ val Wallet.cashBalances: Map<Currency, Amount<Currency>> get() = states.
// Turn into a Map<Currency, List<Amount>> like { GBP -> (£100, £500, etc), USD -> ($2000, $50) }
groupBy { it.token.product }.
// Collapse to Map<Currency, Amount> by summing all the amounts of the same currency together.
mapValues { it.value.map { Amount(it.quantity, it.token.product) }.sumOrThrow() }
mapValues { it.value.map { Amount(it.quantity, it.token.product) }.sumOrThrow() }
fun Cash.State.ownedBy(owner: PublicKey) = copy(owner = owner)
fun Cash.State.issuedBy(party: Party) = copy(amount = Amount(amount.quantity, issuanceDef.copy(issuer = deposit.copy(party = party))))
fun Cash.State.issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, issuanceDef.copy(issuer = deposit)))
fun Cash.State.withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit)))
infix fun Cash.State.`owned by`(owner: PublicKey) = ownedBy(owner)
infix fun Cash.State.`issued by`(party: Party) = issuedBy(party)
infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit)
// Unit testing helpers. These could go in a separate file but it's hardly worth it for just a few functions.
/** A randomly generated key. */
val DUMMY_CASH_ISSUER_KEY by lazy { generateKeyPair() }
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
val DUMMY_CASH_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_CASH_ISSUER_KEY.public).ref(1) }
/** An extension property that lets you write 100.DOLLARS.CASH */
val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKey)
/** An extension property that lets you get a cash state from an issued token, under the [NullPublicKey] */
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullPublicKey)

View File

@ -1,15 +1,18 @@
package com.r3corda.contracts.asset
import com.google.common.annotations.VisibleForTesting
import com.r3corda.contracts.asset.FungibleAssetState
import com.r3corda.contracts.asset.sumFungibleOrNull
import com.r3corda.contracts.asset.Obligation.Lifecycle.NORMAL
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.NullPublicKey
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.toStringShort
import com.r3corda.core.random63BitValue
import com.r3corda.core.testing.MINI_CORP
import com.r3corda.core.testing.TEST_TX_TIME
import com.r3corda.core.utilities.Emoji
import com.r3corda.core.utilities.NonEmptySet
import com.r3corda.core.utilities.nonEmptySetOf
import java.security.PublicKey
import java.time.Duration
import java.time.Instant
@ -812,3 +815,16 @@ fun <P> Iterable<ContractState>.sumObligationsOrNull(): Amount<P>?
fun <P> Iterable<ContractState>.sumObligationsOrZero(product: P): Amount<P>
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(product)
infix fun <T> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
infix fun <T> Obligation.IssuanceDefinition<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
infix fun <T> Obligation.State<T>.between(parties: Pair<Party, PublicKey>) = copy(obligor = parties.first, beneficiary = parties.second)
infix fun <T> Obligation.State<T>.`owned by`(owner: PublicKey) = copy(beneficiary = owner)
infix fun <T> Obligation.State<T>.`issued by`(party: Party) = copy(obligor = party)
// For Java users:
fun <T> Obligation.State<T>.ownedBy(owner: PublicKey) = copy(beneficiary = owner)
fun <T> Obligation.State<T>.issuedBy(party: Party) = copy(obligor = party)
val Issued<Currency>.OBLIGATION_DEF: Obligation.StateTemplate<Currency>
get() = Obligation.StateTemplate(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
get() = Obligation.State(Obligation.Lifecycle.NORMAL, MINI_CORP, token.OBLIGATION_DEF, quantity, NullPublicKey)

View File

@ -1,78 +0,0 @@
package com.r3corda.contracts.testing
import com.r3corda.contracts.*
import com.r3corda.contracts.asset.Cash
import com.r3corda.contracts.asset.Obligation
import com.r3corda.core.contracts.Amount
import com.r3corda.core.contracts.ContractState
import com.r3corda.core.contracts.PartyAndReference
import com.r3corda.core.contracts.Issued
import com.r3corda.core.contracts.TransactionState
import com.r3corda.core.crypto.NullPublicKey
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.testing.MINI_CORP
import com.r3corda.core.testing.TEST_TX_TIME
import com.r3corda.core.utilities.nonEmptySetOf
import java.security.PublicKey
import java.time.Instant
import java.util.*
object JavaTestHelpers {
@JvmStatic fun ownedBy(state: Cash.State, owner: PublicKey) = state.copy(owner = owner)
@JvmStatic fun issuedBy(state: Cash.State, party: Party) = state.copy(amount = Amount(state.amount.quantity, state.issuanceDef.copy(issuer = state.deposit.copy(party = party))))
@JvmStatic fun issuedBy(state: Cash.State, deposit: PartyAndReference) = state.copy(amount = Amount(state.amount.quantity, state.issuanceDef.copy(issuer = deposit)))
@JvmStatic fun withNotary(state: Cash.State, notary: Party) = TransactionState(state, notary)
@JvmStatic fun withDeposit(state: Cash.State, deposit: PartyAndReference) = state.copy(amount = state.amount.copy(token = state.amount.token.copy(issuer = deposit)))
@JvmStatic fun <T> at(state: Obligation.State<T>, dueBefore: Instant) = state.copy(template = state.template.copy(dueBefore = dueBefore))
@JvmStatic fun <T> at(issuanceDef: Obligation.IssuanceDefinition<T>, dueBefore: Instant) = issuanceDef.copy(template = issuanceDef.template.copy(dueBefore = dueBefore))
@JvmStatic fun <T> between(state: Obligation.State<T>, parties: Pair<Party, PublicKey>) = state.copy(obligor = parties.first, beneficiary = parties.second)
@JvmStatic fun <T> ownedBy(state: Obligation.State<T>, owner: PublicKey) = state.copy(beneficiary = owner)
@JvmStatic fun <T> issuedBy(state: Obligation.State<T>, party: Party) = state.copy(obligor = party)
@JvmStatic fun ownedBy(state: CommercialPaper.State, owner: PublicKey) = state.copy(owner = owner)
@JvmStatic fun withNotary(state: CommercialPaper.State, notary: Party) = TransactionState(state, notary)
@JvmStatic fun ownedBy(state: ICommercialPaperState, new_owner: PublicKey): ICommercialPaperState = state.withOwner(new_owner)
@JvmStatic fun withNotary(state: ContractState, notary: Party) = TransactionState(state, notary)
@JvmStatic fun CASH(amount: Amount<Currency>) = Cash.State(
Amount(amount.quantity, Issued(DUMMY_CASH_ISSUER, amount.token)),
NullPublicKey)
@JvmStatic fun STATE(amount: Amount<Issued<Currency>>) = Cash.State(amount, NullPublicKey)
// Allows you to write 100.DOLLARS.OBLIGATION
@JvmStatic fun OBLIGATION_DEF(issued: Issued<Currency>)
= Obligation.StateTemplate(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(issued), TEST_TX_TIME)
@JvmStatic fun OBLIGATION(amount: Amount<Issued<Currency>>) = Obligation.State(Obligation.Lifecycle.NORMAL, MINI_CORP,
OBLIGATION_DEF(amount.token), amount.quantity, NullPublicKey)
}
infix fun Cash.State.`owned by`(owner: PublicKey) = JavaTestHelpers.ownedBy(this, owner)
infix fun Cash.State.`issued by`(party: Party) = JavaTestHelpers.issuedBy(this, party)
infix fun Cash.State.`issued by`(deposit: PartyAndReference) = JavaTestHelpers.issuedBy(this, deposit)
infix fun Cash.State.`with notary`(notary: Party) = JavaTestHelpers.withNotary(this, notary)
infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = JavaTestHelpers.withDeposit(this, deposit)
infix fun <T> Obligation.State<T>.`at`(dueBefore: Instant) = JavaTestHelpers.at(this, dueBefore)
infix fun <T> Obligation.IssuanceDefinition<T>.`at`(dueBefore: Instant) = JavaTestHelpers.at(this, dueBefore)
infix fun <T> Obligation.State<T>.`between`(parties: Pair<Party, PublicKey>) = JavaTestHelpers.between(this, parties)
infix fun <T> Obligation.State<T>.`owned by`(owner: PublicKey) = JavaTestHelpers.ownedBy(this, owner)
infix fun <T> Obligation.State<T>.`issued by`(party: Party) = JavaTestHelpers.issuedBy(this, party)
infix fun CommercialPaper.State.`owned by`(owner: PublicKey) = JavaTestHelpers.ownedBy(this, owner)
infix fun CommercialPaper.State.`with notary`(notary: Party) = JavaTestHelpers.withNotary(this, notary)
infix fun ICommercialPaperState.`owned by`(new_owner: PublicKey) = JavaTestHelpers.ownedBy(this, new_owner)
infix fun ContractState.`with notary`(notary: Party) = JavaTestHelpers.withNotary(this, notary)
val DUMMY_CASH_ISSUER_KEY = generateKeyPair()
val DUMMY_CASH_ISSUER = Party("Snake Oil Issuer", DUMMY_CASH_ISSUER_KEY.public).ref(1)
/** Allows you to write 100.DOLLARS.CASH */
val Amount<Currency>.CASH: Cash.State get() = JavaTestHelpers.CASH(this)
val Amount<Issued<Currency>>.STATE: Cash.State get() = JavaTestHelpers.STATE(this)
/** Allows you to write 100.DOLLARS.CASH */
val Issued<Currency>.OBLIGATION_DEF: Obligation.StateTemplate<Currency> get() = JavaTestHelpers.OBLIGATION_DEF(this)
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency> get() = JavaTestHelpers.OBLIGATION(this)

View File

@ -5,15 +5,13 @@ import com.r3corda.core.serialization.OpaqueBytes;
import kotlin.Unit;
import org.junit.Test;
import static com.r3corda.core.testing.JavaTestHelpers.*;
import static com.r3corda.core.contracts.JavaTestHelpers.*;
import static com.r3corda.contracts.testing.JavaTestHelpers.*;
import static com.r3corda.core.testing.CoreTestUtils.*;
import static com.r3corda.core.contracts.ContractsDSL.*;
/**
* This is an incomplete Java replica of CashTests.kt to show how to use the Java test DSL
*/
public class CashTestsJava {
private OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{1});
private PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef);
private Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), getDUMMY_PUBKEY_1());
@ -43,7 +41,9 @@ public class CashTestsJava {
});
tx.tweak(tw -> {
tw.output(outState);
tw.output(issuedBy(outState, getMINI_CORP()));
// issuedBy() can't be directly imported because it conflicts with other identically named functions
// with different overloads (for some reason).
tw.output(com.r3corda.contracts.asset.CashKt.issuedBy(outState, getMINI_CORP()));
tw.command(getDUMMY_PUBKEY_1(), new Cash.Commands.Move());
return tw.failsWith("at least one asset input");
});

View File

@ -1,7 +1,6 @@
package com.r3corda.contracts
import com.r3corda.contracts.asset.Cash
import com.r3corda.contracts.testing.*
import com.r3corda.contracts.asset.*
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
@ -171,7 +170,7 @@ class CommercialPaperTestsGeneric {
}
}
fun <T : ContractState> cashOutputsToWallet(vararg outputs: TransactionState<T>): Pair<LedgerTransaction, List<StateAndRef<T>>> {
fun cashOutputsToWallet(vararg outputs: TransactionState<Cash.State>): Pair<LedgerTransaction, List<StateAndRef<Cash.State>>> {
val ltx = LedgerTransaction(emptyList(), listOf(*outputs), emptyList(), emptyList(), SecureHash.randomSHA256(), emptyList(), TransactionType.General())
return Pair(ltx, outputs.mapIndexed { index, state -> StateAndRef(state, StateRef(ltx.id, index)) })
}

View File

@ -1,9 +1,5 @@
package com.r3corda.contracts.asset
import com.r3corda.contracts.testing.`issued by`
import com.r3corda.contracts.testing.`owned by`
import com.r3corda.contracts.testing.`with deposit`
import com.r3corda.contracts.testing.`with notary`
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash

View File

@ -1,3 +1,4 @@
@file:JvmName("ContractsDSL")
package com.r3corda.core.contracts
import com.r3corda.core.crypto.Party
@ -17,34 +18,27 @@ import java.util.*
//// Currencies ///////////////////////////////////////////////////////////////////////////////////////////////////////
fun currency(code: String) = Currency.getInstance(code)
fun currency(code: String) = Currency.getInstance(code)!!
// Java interop
object JavaTestHelpers {
@JvmStatic val USD: Currency get() = currency("USD")
@JvmStatic val GBP: Currency get() = currency("GBP")
@JvmStatic val CHF: Currency get() = currency("CHF")
@JvmField val USD = currency("USD")
@JvmField val GBP = currency("GBP")
@JvmField val CHF = currency("CHF")
@JvmStatic fun DOLLARS(amount: Int) = Amount(amount.toLong() * 100, USD)
@JvmStatic fun DOLLARS(amount: Double) = Amount((amount * 100).toLong(), USD)
@JvmStatic fun POUNDS(amount: Int) = Amount(amount.toLong() * 100, GBP)
@JvmStatic fun SWISS_FRANCS(amount: Int) = Amount(amount.toLong() * 100, CHF)
fun DOLLARS(amount: Int): Amount<Currency> = Amount(amount.toLong() * 100, USD)
fun DOLLARS(amount: Double): Amount<Currency> = Amount((amount * 100).toLong(), USD)
fun POUNDS(amount: Int): Amount<Currency> = Amount(amount.toLong() * 100, GBP)
fun SWISS_FRANCS(amount: Int): Amount<Currency> = Amount(amount.toLong() * 100, CHF)
@JvmStatic fun issuedBy(currency: Currency, deposit: PartyAndReference) = Issued<Currency>(deposit, currency)
@JvmStatic fun issuedBy(amount: Amount<Currency>, deposit: PartyAndReference) = Amount(amount.quantity, issuedBy(amount.token, deposit))
}
val Int.DOLLARS: Amount<Currency> get() = DOLLARS(this)
val Double.DOLLARS: Amount<Currency> get() = DOLLARS(this)
val Int.POUNDS: Amount<Currency> get() = POUNDS(this)
val Int.SWISS_FRANCS: Amount<Currency> get() = SWISS_FRANCS(this)
val USD = JavaTestHelpers.USD
val GBP = JavaTestHelpers.GBP
val CHF = JavaTestHelpers.CHF
infix fun Currency.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
infix fun Amount<Currency>.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
infix fun Currency.issuedBy(deposit: PartyAndReference) = Issued<Currency>(deposit, this)
infix fun Amount<Currency>.issuedBy(deposit: PartyAndReference) = Amount(quantity, token.issuedBy(deposit))
val Int.DOLLARS: Amount<Currency> get() = JavaTestHelpers.DOLLARS(this)
val Double.DOLLARS: Amount<Currency> get() = JavaTestHelpers.DOLLARS(this)
val Int.POUNDS: Amount<Currency> get() = JavaTestHelpers.POUNDS(this)
val Int.SWISS_FRANCS: Amount<Currency> get() = JavaTestHelpers.SWISS_FRANCS(this)
infix fun Currency.`issued by`(deposit: PartyAndReference) = JavaTestHelpers.issuedBy(this, deposit)
infix fun Amount<Currency>.`issued by`(deposit: PartyAndReference) = JavaTestHelpers.issuedBy(this, deposit)
//// Requirements /////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -108,13 +108,18 @@ data class TransactionState<out T : ContractState>(
val data: T,
/** Identity of the notary that ensures the state is not used as an input to a transaction more than once */
val notary: Party) {
/**
* Copies the underlying state, replacing the notary field with the new value.
* To replace the notary, we need an approval (signature) from _all_ participants of the [ContractState]
*/
fun withNewNotary(newNotary: Party) = TransactionState(this.data, newNotary)
fun withNotary(newNotary: Party) = TransactionState(this.data, newNotary)
}
/** Wraps the [ContractState] in a [TransactionState] object */
infix fun <T : ContractState> T.`with notary`(newNotary: Party) = withNotary(newNotary)
infix fun <T : ContractState> T.withNotary(newNotary: Party) = TransactionState(this, newNotary)
/**
* Marker interface for data classes that represent the issuance state for a contract. These are intended as templates
* from which the state object is initialised.

View File

@ -0,0 +1,120 @@
@file:Suppress("UNUSED_PARAMETER", "UNCHECKED_CAST")
@file:JvmName("CoreTestUtils")
package com.r3corda.core.testing
import com.google.common.base.Throwables
import com.google.common.net.HostAndPort
import com.r3corda.core.contracts.Attachment
import com.r3corda.core.contracts.StateRef
import com.r3corda.core.contracts.TransactionBuilder
import com.r3corda.core.crypto.DummyPublicKey
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.node.services.IdentityService
import com.r3corda.core.node.services.StorageService
import com.r3corda.core.node.services.testing.MockIdentityService
import com.r3corda.core.node.services.testing.MockStorageService
import java.net.ServerSocket
import java.security.KeyPair
import java.security.PublicKey
import java.time.Instant
/**
* JAVA INTEROP
* ------------
*
* Please keep the following points in mind when extending the Kotlin DSL:
*
* - Annotate functions with Kotlin defaults with @JvmOverloads. This produces the relevant overloads for Java.
* - Void closures in arguments are inconvenient in Java, use overloading to define non-closure variants as well.
* - Top-level vals are trickier. *DO NOT USE @JvmField at the top level!* It's surprisingly easy to
* introduce a static init cycle because of the way Kotlin compiles top-level things, which can cause
* non-deterministic behaviour, including your field not being initialized at all! Instead opt for a proper Kotlin
* val either with a custom @JvmStatic get() or a lazy delegate if the initialiser has side-effects. See examples below.
* - Infix functions work as regular ones from Java, but symbols with spaces in them don't! Define a camelCase variant
* as well.
* - varargs are exposed as array types in Java. Define overloads for common cases.
* - The Int.DOLLARS syntax doesn't work from Java. Use the DOLLARS(int) function instead.
*/
// A dummy time at which we will be pretending test transactions are created.
val TEST_TX_TIME: Instant get() = Instant.parse("2015-04-17T12:00:00.00Z")
// A few dummy values for testing.
val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() }
val MEGA_CORP_PUBKEY: PublicKey get() = MEGA_CORP_KEY.public
val MINI_CORP_KEY: KeyPair by lazy { generateKeyPair() }
val MINI_CORP_PUBKEY: PublicKey get() = MINI_CORP_KEY.public
val ORACLE_KEY: KeyPair by lazy { generateKeyPair() }
val ORACLE_PUBKEY: PublicKey get() = ORACLE_KEY.public
val DUMMY_PUBKEY_1: PublicKey get() = DummyPublicKey("x1")
val DUMMY_PUBKEY_2: PublicKey get() = DummyPublicKey("x2")
val DUMMY_KEY_1: KeyPair by lazy { generateKeyPair() }
val DUMMY_KEY_2: KeyPair by lazy { generateKeyPair() }
val DUMMY_KEY_3: KeyPair by lazy { generateKeyPair() }
val ALICE_KEY: KeyPair by lazy { generateKeyPair() }
val ALICE_PUBKEY: PublicKey get() = ALICE_KEY.public
val ALICE: Party get() = Party("Alice", ALICE_PUBKEY)
val BOB_KEY: KeyPair by lazy { generateKeyPair() }
val BOB_PUBKEY: PublicKey get() = BOB_KEY.public
val BOB: Party get() = Party("Bob", BOB_PUBKEY)
val MEGA_CORP: Party get() = Party("MegaCorp", MEGA_CORP_PUBKEY)
val MINI_CORP: Party get() = Party("MiniCorp", MINI_CORP_PUBKEY)
val DUMMY_NOTARY_KEY: KeyPair by lazy { generateKeyPair() }
val DUMMY_NOTARY: Party get() = Party("Notary Service", DUMMY_NOTARY_KEY.public)
val ALL_TEST_KEYS: List<KeyPair> get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, ALICE_KEY, BOB_KEY, DUMMY_NOTARY_KEY)
val MOCK_IDENTITY_SERVICE: MockIdentityService get() = MockIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0)
/** If an exception is thrown by the body, rethrows the root cause exception. */
inline fun <R> rootCauseExceptions(body: () -> R): R {
try {
return body()
} catch(e: Exception) {
throw Throwables.getRootCause(e)
}
}
fun freeLocalHostAndPort(): HostAndPort {
val freePort = ServerSocket(0).use { it.localPort }
return HostAndPort.fromParts("localhost", freePort)
}
/**
* Creates and tests a ledger built by the passed in dsl.
* @param identityService: The [IdentityService] to be used while building the ledger.
* @param storageService: The [StorageService] to be used for storing e.g. [Attachment]s.
* @param dsl: The dsl building the ledger.
*/
@JvmOverloads fun ledger(
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
storageService: StorageService = MockStorageService(),
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
): LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(identityService, storageService))
dsl(ledgerDsl)
return ledgerDsl
}
/**
* Creates a ledger with a single transaction, built by the passed in dsl.
*
* @see LedgerDSLInterpreter._transaction
*/
@JvmOverloads fun transaction(
transactionLabel: String? = null,
transactionBuilder: TransactionBuilder = TransactionBuilder(),
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
) = ledger { this.transaction(transactionLabel, transactionBuilder, dsl) }

View File

@ -7,7 +7,6 @@ import com.r3corda.core.crypto.SecureHash
import com.r3corda.core.crypto.signWithECDSA
import com.r3corda.core.node.services.IdentityService
import com.r3corda.core.node.services.StorageService
import com.r3corda.core.node.services.testing.MockStorageService
import com.r3corda.core.serialization.serialize
import java.io.InputStream
import java.security.KeyPair
@ -41,24 +40,6 @@ import java.util.*
* tests. Top level primitives [ledger] and [transaction] that bind the interpreter types are also defined here.
*/
/**
* @see JavaTestHelpers.transaction
*/
fun transaction(
transactionLabel: String? = null,
transactionBuilder: TransactionBuilder = TransactionBuilder(),
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
) = JavaTestHelpers.transaction(transactionLabel, transactionBuilder, dsl)
/**
* @see JavaTestHelpers.ledger
*/
fun ledger(
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
storageService: StorageService = MockStorageService(),
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
) = JavaTestHelpers.ledger(identityService, storageService, dsl)
@Deprecated(
message = "ledger doesn't nest, use tweak",
replaceWith = ReplaceWith("tweak"),

View File

@ -1,145 +0,0 @@
@file:Suppress("UNUSED_PARAMETER", "UNCHECKED_CAST")
package com.r3corda.core.testing
import com.google.common.base.Throwables
import com.google.common.net.HostAndPort
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.*
import com.r3corda.core.node.services.IdentityService
import com.r3corda.core.node.services.StorageService
import com.r3corda.core.node.services.testing.MockIdentityService
import com.r3corda.core.node.services.testing.MockStorageService
import java.net.ServerSocket
import java.security.KeyPair
import java.security.PublicKey
import java.time.Instant
/**
* JAVA INTEROP. Please keep the following points in mind when extending the Kotlin DSL
*
* - Annotate functions with Kotlin defaults with @JvmOverloads. This produces the relevant overloads for Java.
* - Void closures in arguments are inconvenient in Java, use overloading to define non-closure variants as well.
* - Top-level funs should be defined in a [JavaTestHelpers] object and annotated with @JvmStatic first and should be
* referred to from the global fun. This allows static importing of [JavaTestHelpers] in Java tests, which mimicks
* top-level funs.
* - Top-level vals are trickier. *DO NOT USE @JvmField INSIDE [JavaTestHelpers]*. It's surprisingly easy to
* introduce a static init cycle because of the way Kotlin compiles top-level things, which can cause
* non-deterministic behaviour, including your field not being initialized at all! Instead opt for a proper Kotlin
* val either with a custom @JvmStatic get() or a lazy delegate if the initialiser has side-effects See examples below.
* - Infix functions work as regular ones from Java, but symbols with spaces in them don't! Define a camelCase variant
* as well.
* - varargs are exposed as array types in Java. Define overloads for common cases.
* - The Int.DOLLARS syntax doesn't work from Java. To remedy add a @JvmStatic DOLLARS(Int) function to
* [JavaTestHelpers]
*/
object JavaTestHelpers {
// A dummy time at which we will be pretending test transactions are created.
@JvmStatic val TEST_TX_TIME: Instant get() = Instant.parse("2015-04-17T12:00:00.00Z")
// A few dummy values for testing.
@JvmStatic val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() }
@JvmStatic val MEGA_CORP_PUBKEY: PublicKey get() = MEGA_CORP_KEY.public
@JvmStatic val MINI_CORP_KEY: KeyPair by lazy { generateKeyPair() }
@JvmStatic val MINI_CORP_PUBKEY: PublicKey get() = MINI_CORP_KEY.public
@JvmStatic val ORACLE_KEY: KeyPair by lazy { generateKeyPair() }
@JvmStatic val ORACLE_PUBKEY: PublicKey get() = ORACLE_KEY.public
@JvmStatic val DUMMY_PUBKEY_1: PublicKey get() = DummyPublicKey("x1")
@JvmStatic val DUMMY_PUBKEY_2: PublicKey get() = DummyPublicKey("x2")
@JvmStatic val DUMMY_KEY_1: KeyPair by lazy { generateKeyPair() }
@JvmStatic val DUMMY_KEY_2: KeyPair by lazy { generateKeyPair() }
@JvmStatic val DUMMY_KEY_3: KeyPair by lazy { generateKeyPair() }
@JvmStatic val ALICE_KEY: KeyPair by lazy { generateKeyPair() }
@JvmStatic val ALICE_PUBKEY: PublicKey get() = ALICE_KEY.public
@JvmStatic val ALICE: Party get() = Party("Alice", ALICE_PUBKEY)
@JvmStatic val BOB_KEY: KeyPair by lazy { generateKeyPair() }
@JvmStatic val BOB_PUBKEY: PublicKey get() = BOB_KEY.public
@JvmStatic val BOB: Party get() = Party("Bob", BOB_PUBKEY)
@JvmStatic val MEGA_CORP: Party get() = Party("MegaCorp", MEGA_CORP_PUBKEY)
@JvmStatic val MINI_CORP: Party get() = Party("MiniCorp", MINI_CORP_PUBKEY)
@JvmStatic val DUMMY_NOTARY_KEY: KeyPair by lazy { generateKeyPair() }
@JvmStatic val DUMMY_NOTARY: Party get() = Party("Notary Service", DUMMY_NOTARY_KEY.public)
@JvmStatic val ALL_TEST_KEYS: List<KeyPair> get() = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, ALICE_KEY, BOB_KEY, DUMMY_NOTARY_KEY)
@JvmStatic val MOCK_IDENTITY_SERVICE: MockIdentityService get() = MockIdentityService(listOf(MEGA_CORP, MINI_CORP, DUMMY_NOTARY))
@JvmStatic fun generateStateRef() = StateRef(SecureHash.randomSHA256(), 0)
/** If an exception is thrown by the body, rethrows the root cause exception. */
@JvmStatic inline fun <R> rootCauseExceptions(body: () -> R): R {
try {
return body()
} catch(e: Exception) {
throw Throwables.getRootCause(e)
}
}
@JvmStatic fun freeLocalHostAndPort(): HostAndPort {
val freePort = ServerSocket(0).use { it.localPort }
return HostAndPort.fromParts("localhost", freePort)
}
/**
* Creates and tests a ledger built by the passed in dsl.
* @param identityService The [IdentityService] to be used while building the ledger.
* @param storageService The [StorageService] to be used for storing e.g. [Attachment]s.
* @param dsl The dsl building the ledger.
*/
@JvmStatic @JvmOverloads fun ledger(
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
storageService: StorageService = MockStorageService(),
dsl: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
): LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(identityService, storageService))
dsl(ledgerDsl)
return ledgerDsl
}
/**
* Creates a ledger with a single transaction, built by the passed in dsl.
* @see LedgerDSLInterpreter._transaction
*/
@JvmStatic @JvmOverloads fun transaction(
transactionLabel: String? = null,
transactionBuilder: TransactionBuilder = TransactionBuilder(),
dsl: TransactionDSL<TransactionDSLInterpreter>.() -> EnforceVerifyOrFail
) = ledger { this.transaction(transactionLabel, transactionBuilder, dsl) }
}
val TEST_TX_TIME = JavaTestHelpers.TEST_TX_TIME
val MEGA_CORP_KEY = JavaTestHelpers.MEGA_CORP_KEY
val MEGA_CORP_PUBKEY = JavaTestHelpers.MEGA_CORP_PUBKEY
val MINI_CORP_KEY = JavaTestHelpers.MINI_CORP_KEY
val MINI_CORP_PUBKEY = JavaTestHelpers.MINI_CORP_PUBKEY
val ORACLE_KEY = JavaTestHelpers.ORACLE_KEY
val ORACLE_PUBKEY = JavaTestHelpers.ORACLE_PUBKEY
val DUMMY_PUBKEY_1 = JavaTestHelpers.DUMMY_PUBKEY_1
val DUMMY_PUBKEY_2 = JavaTestHelpers.DUMMY_PUBKEY_2
val DUMMY_KEY_1 = JavaTestHelpers.DUMMY_KEY_1
val DUMMY_KEY_2 = JavaTestHelpers.DUMMY_KEY_2
val DUMMY_KEY_3 = JavaTestHelpers.DUMMY_KEY_3
val ALICE_KEY = JavaTestHelpers.ALICE_KEY
val ALICE_PUBKEY = JavaTestHelpers.ALICE_PUBKEY
val ALICE = JavaTestHelpers.ALICE
val BOB_KEY = JavaTestHelpers.BOB_KEY
val BOB_PUBKEY = JavaTestHelpers.BOB_PUBKEY
val BOB = JavaTestHelpers.BOB
val MEGA_CORP = JavaTestHelpers.MEGA_CORP
val MINI_CORP = JavaTestHelpers.MINI_CORP
val DUMMY_NOTARY_KEY = JavaTestHelpers.DUMMY_NOTARY_KEY
val DUMMY_NOTARY = JavaTestHelpers.DUMMY_NOTARY
val ALL_TEST_KEYS = JavaTestHelpers.ALL_TEST_KEYS
val MOCK_IDENTITY_SERVICE = JavaTestHelpers.MOCK_IDENTITY_SERVICE
fun generateStateRef() = JavaTestHelpers.generateStateRef()
fun freeLocalHostAndPort() = JavaTestHelpers.freeLocalHostAndPort()
inline fun <R> rootCauseExceptions(body: () -> R) = JavaTestHelpers.rootCauseExceptions(body)

View File

@ -35,7 +35,7 @@ object NotaryChangeProtocol: AbstractStateReplacementProtocol<Party>() {
override fun assembleTx(): Pair<SignedTransaction, List<PublicKey>> {
val state = originalState.state
val newState = state.withNewNotary(modification)
val newState = state.withNotary(modification)
val participants = state.data.participants
val tx = TransactionType.NotaryChange.Builder().withItems(originalState, newState)
tx.signWith(serviceHub.storageService.myLegalIdentityKey)

View File

@ -1,46 +0,0 @@
package com.r3corda.node.internal.testing
import com.r3corda.core.contracts.StateAndRef
import com.r3corda.core.contracts.DummyContract
import com.r3corda.core.contracts.StateRef
import com.r3corda.core.contracts.TransactionState
import com.r3corda.core.contracts.TransactionType
import com.r3corda.core.crypto.Party
import com.r3corda.core.seconds
import com.r3corda.core.testing.DUMMY_NOTARY
import com.r3corda.core.testing.DUMMY_NOTARY_KEY
import com.r3corda.node.internal.AbstractNode
import java.time.Instant
import java.util.*
fun issueState(node: AbstractNode): StateAndRef<*> {
val tx = DummyContract().generateInitial(node.info.identity.ref(0), Random().nextInt(), DUMMY_NOTARY)
tx.signWith(node.storage.myLegalIdentityKey)
tx.signWith(DUMMY_NOTARY_KEY)
val stx = tx.toSignedTransaction()
node.services.recordTransactions(listOf(stx))
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}
fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode): StateAndRef<DummyContract.MultiOwnerState> {
val state = TransactionState(DummyContract.MultiOwnerState(0,
listOf(nodeA.info.identity.owningKey, nodeB.info.identity.owningKey)), DUMMY_NOTARY)
val tx = TransactionType.NotaryChange.Builder().withItems(state)
tx.signWith(nodeA.storage.myLegalIdentityKey)
tx.signWith(nodeB.storage.myLegalIdentityKey)
tx.signWith(DUMMY_NOTARY_KEY)
val stx = tx.toSignedTransaction()
nodeA.services.recordTransactions(listOf(stx))
nodeB.services.recordTransactions(listOf(stx))
val stateAndRef = StateAndRef(state, StateRef(stx.id, 0))
return stateAndRef
}
fun issueInvalidState(node: AbstractNode, notary: Party = DUMMY_NOTARY): StateAndRef<*> {
val tx = DummyContract().generateInitial(node.info.identity.ref(0), Random().nextInt(), notary)
tx.setTime(Instant.now(), notary, 30.seconds)
tx.signWith(node.storage.myLegalIdentityKey)
val stx = tx.toSignedTransaction(false)
node.services.recordTransactions(listOf(stx))
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}

View File

@ -2,10 +2,10 @@ package com.r3corda.node.messaging
import com.google.common.util.concurrent.ListenableFuture
import com.r3corda.contracts.CommercialPaper
import com.r3corda.contracts.asset.CASH
import com.r3corda.contracts.asset.Cash
import com.r3corda.contracts.testing.CASH
import com.r3corda.contracts.testing.`issued by`
import com.r3corda.contracts.testing.`owned by`
import com.r3corda.contracts.asset.`issued by`
import com.r3corda.contracts.asset.`owned by`
import com.r3corda.contracts.testing.fillWithSomeTestCash
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party

View File

@ -1,14 +1,14 @@
package com.r3corda.node.services
import com.r3corda.contracts.asset.CASH
import com.r3corda.contracts.asset.Cash
import com.r3corda.contracts.testing.CASH
import com.r3corda.contracts.testing.`issued by`
import com.r3corda.contracts.testing.`owned by`
import com.r3corda.contracts.testing.`with notary`
import com.r3corda.contracts.asset.`issued by`
import com.r3corda.contracts.asset.`owned by`
import com.r3corda.core.bd
import com.r3corda.core.contracts.DOLLARS
import com.r3corda.core.contracts.Fix
import com.r3corda.core.contracts.TransactionType
import com.r3corda.core.contracts.`with notary`
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.testing.ALICE_PUBKEY

View File

@ -1,12 +1,13 @@
package com.r3corda.node.services
import com.r3corda.core.contracts.*
import com.r3corda.core.crypto.Party
import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.seconds
import com.r3corda.core.testing.DUMMY_NOTARY
import com.r3corda.core.testing.DUMMY_NOTARY_KEY
import com.r3corda.node.internal.AbstractNode
import com.r3corda.node.internal.testing.MockNetwork
import com.r3corda.node.internal.testing.issueMultiPartyState
import com.r3corda.node.internal.testing.issueState
import com.r3corda.node.services.network.NetworkMapService
import com.r3corda.node.services.transactions.SimpleNotaryService
import org.junit.Before
@ -15,6 +16,8 @@ import protocols.NotaryChangeProtocol
import protocols.NotaryChangeProtocol.Instigator
import protocols.StateReplacementException
import protocols.StateReplacementRefused
import java.time.Instant
import java.util.*
import java.util.concurrent.ExecutionException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@ -90,4 +93,36 @@ class NotaryChangeTests {
// - The requesting party wants to change additional state fields
// - Multiple states in a single "notary change" transaction
// - Transaction contains additional states and commands with business logic
}
}
fun issueState(node: AbstractNode): StateAndRef<*> {
val tx = DummyContract().generateInitial(node.info.identity.ref(0), Random().nextInt(), DUMMY_NOTARY)
tx.signWith(node.storage.myLegalIdentityKey)
tx.signWith(DUMMY_NOTARY_KEY)
val stx = tx.toSignedTransaction()
node.services.recordTransactions(listOf(stx))
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}
fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode): StateAndRef<DummyContract.MultiOwnerState> {
val state = TransactionState(DummyContract.MultiOwnerState(0,
listOf(nodeA.info.identity.owningKey, nodeB.info.identity.owningKey)), DUMMY_NOTARY)
val tx = TransactionType.NotaryChange.Builder().withItems(state)
tx.signWith(nodeA.storage.myLegalIdentityKey)
tx.signWith(nodeB.storage.myLegalIdentityKey)
tx.signWith(DUMMY_NOTARY_KEY)
val stx = tx.toSignedTransaction()
nodeA.services.recordTransactions(listOf(stx))
nodeB.services.recordTransactions(listOf(stx))
val stateAndRef = StateAndRef(state, StateRef(stx.id, 0))
return stateAndRef
}
fun issueInvalidState(node: AbstractNode, notary: Party = DUMMY_NOTARY): StateAndRef<*> {
val tx = DummyContract().generateInitial(node.info.identity.ref(0), Random().nextInt(), notary)
tx.setTime(Instant.now(), notary, 30.seconds)
tx.signWith(node.storage.myLegalIdentityKey)
val stx = tx.toSignedTransaction(false)
node.services.recordTransactions(listOf(stx))
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}

View File

@ -7,7 +7,6 @@ import com.r3corda.core.testing.DUMMY_NOTARY
import com.r3corda.core.testing.DUMMY_NOTARY_KEY
import com.r3corda.core.testing.MINI_CORP_KEY
import com.r3corda.node.internal.testing.MockNetwork
import com.r3corda.node.internal.testing.issueState
import com.r3corda.node.services.network.NetworkMapService
import com.r3corda.node.services.transactions.SimpleNotaryService
import com.r3corda.protocols.NotaryError

View File

@ -8,8 +8,6 @@ import com.r3corda.core.testing.DUMMY_NOTARY_KEY
import com.r3corda.core.testing.MEGA_CORP_KEY
import com.r3corda.core.testing.MINI_CORP_KEY
import com.r3corda.node.internal.testing.MockNetwork
import com.r3corda.node.internal.testing.issueInvalidState
import com.r3corda.node.internal.testing.issueState
import com.r3corda.node.services.network.NetworkMapService
import com.r3corda.node.services.transactions.ValidatingNotaryService
import com.r3corda.protocols.NotaryError