Merge branch 'master' into shams-master-merge-291117

# Conflicts:
#	node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
#	node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt
#	node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt
#	samples/notary-demo/src/main/kotlin/net/corda/notarydemo/BFTNotaryCordform.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/internal/demorun/DemoRunner.kt
#	testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt
This commit is contained in:
Shams Asari
2017-11-29 18:00:16 +00:00
171 changed files with 2334 additions and 1950 deletions

View File

@ -3,7 +3,6 @@
package net.corda.testing
import com.nhaarman.mockito_kotlin.mock
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
@ -23,11 +22,12 @@ import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.serialization.amqp.AMQP_ENABLED
import org.mockito.Mockito.mock
import org.mockito.internal.stubbing.answers.ThrowsException
import org.mockito.stubbing.Answer
import java.nio.file.Files
import java.security.KeyPair
import java.security.PublicKey
import java.util.*
import java.util.concurrent.atomic.AtomicInteger
/**
@ -178,12 +178,13 @@ fun NodeInfo.singleIdentity(): Party = singleIdentityAndCert().party
*/
class UndefinedMockBehaviorException(message: String) : RuntimeException(message)
inline fun <reified T : Any> rigorousMock() = rigorousMock(T::class.java)
/**
* Create a Mockito mock that has [UndefinedMockBehaviorException] as the default behaviour of all methods.
* @param T the type to mock. Note if you want to use [com.nhaarman.mockito_kotlin.doCallRealMethod] on a Kotlin interface,
* it won't work unless you mock a (trivial) abstract implementation of that interface instead.
*/
inline fun <reified T : Any> rigorousMock() = mock<T>(Answer {
fun <T> rigorousMock(clazz: Class<T>): T = mock(clazz) {
// Use ThrowsException to hack the stack trace, and lazily so we can customise the message:
ThrowsException(UndefinedMockBehaviorException("Please specify what should happen when '${it.method}' is called, or don't call it.")).answer(it)
})
ThrowsException(UndefinedMockBehaviorException("Please specify what should happen when '${it.method}' is called, or don't call it. Args: ${Arrays.toString(it.arguments)}")).answer(it)
}

View File

@ -1,16 +0,0 @@
package net.corda.testing
import net.corda.core.internal.div
import net.corda.core.internal.isDirectory
import java.nio.file.Path
import java.nio.file.Paths
object ProjectStructure {
val projectRootDir: Path = run {
var dir = Paths.get(javaClass.getResource("/").toURI())
while (!(dir / ".git").isDirectory()) {
dir = dir.parent
}
dir
}
}

View File

@ -1,24 +1,51 @@
package net.corda.testing
import com.nhaarman.mockito_kotlin.doNothing
import com.nhaarman.mockito_kotlin.whenever
import com.nhaarman.mockito_kotlin.*
import net.corda.client.rpc.internal.KryoClientSerializationScheme
import net.corda.core.internal.staticField
import net.corda.core.serialization.internal.*
import net.corda.node.serialization.KryoServerSerializationScheme
import net.corda.nodeapi.internal.serialization.*
import net.corda.nodeapi.internal.serialization.amqp.AMQPClientSerializationScheme
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
import net.corda.testing.common.internal.asContextEnv
import net.corda.testing.internal.testThreadFactory
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnector
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
private val inVMExecutors = ConcurrentHashMap<SerializationEnvironment, ExecutorService>()
/** @param inheritable whether new threads inherit the environment, use sparingly. */
class SerializationEnvironmentRule(private val inheritable: Boolean = false) : TestRule {
val env: SerializationEnvironment = createTestSerializationEnv()
override fun apply(base: Statement, description: Description?) = object : Statement() {
override fun evaluate() = env.asContextEnv(inheritable) {
base.evaluate()
companion object {
init {
// Can't turn it off, and it creates threads that do serialization, so hack it:
InVMConnector::class.staticField<ExecutorService>("threadPoolExecutor").value = rigorousMock<ExecutorService>().also {
doAnswer {
inVMExecutors.computeIfAbsent(effectiveSerializationEnv) {
Executors.newCachedThreadPool(testThreadFactory(true)) // Close enough to what InVMConnector makes normally.
}.execute(it.arguments[0] as Runnable)
}.whenever(it).execute(any())
}
}
}
lateinit var env: SerializationEnvironment
override fun apply(base: Statement, description: Description): Statement {
env = createTestSerializationEnv(description.toString())
return object : Statement() {
override fun evaluate() {
try {
env.asContextEnv(inheritable) { base.evaluate() }
} finally {
inVMExecutors.remove(env)
}
}
}
}
}
@ -30,7 +57,7 @@ interface GlobalSerializationEnvironment : SerializationEnvironment {
/** @param inheritable whether new threads inherit the environment, use sparingly. */
fun <T> withTestSerialization(inheritable: Boolean = false, callable: (SerializationEnvironment) -> T): T {
return createTestSerializationEnv().asContextEnv(inheritable, callable)
return createTestSerializationEnv("<context>").asContextEnv(inheritable, callable)
}
/**
@ -53,9 +80,10 @@ fun <T> withoutTestSerialization(callable: () -> T): T {
*/
fun setGlobalSerialization(armed: Boolean): GlobalSerializationEnvironment {
return if (armed) {
object : GlobalSerializationEnvironment, SerializationEnvironment by createTestSerializationEnv() {
object : GlobalSerializationEnvironment, SerializationEnvironment by createTestSerializationEnv("<global>") {
override fun unset() {
_globalSerializationEnv.set(null)
inVMExecutors.remove(this)
}
}.also {
_globalSerializationEnv.set(it)
@ -67,7 +95,7 @@ fun setGlobalSerialization(armed: Boolean): GlobalSerializationEnvironment {
}
}
private fun createTestSerializationEnv() = SerializationEnvironmentImpl(
private fun createTestSerializationEnv(label: String) = object : SerializationEnvironmentImpl(
SerializationFactoryImpl().apply {
registerScheme(KryoClientSerializationScheme())
registerScheme(KryoServerSerializationScheme())
@ -78,7 +106,9 @@ private fun createTestSerializationEnv() = SerializationEnvironmentImpl(
KRYO_RPC_SERVER_CONTEXT,
KRYO_RPC_CLIENT_CONTEXT,
if (isAmqpEnabled()) AMQP_STORAGE_CONTEXT else KRYO_STORAGE_CONTEXT,
KRYO_CHECKPOINT_CONTEXT)
KRYO_CHECKPOINT_CONTEXT) {
override fun toString() = "testSerializationEnv($label)"
}
private const val AMQP_ENABLE_PROP_NAME = "net.corda.testing.amqp.enable"

View File

@ -8,276 +8,245 @@ import net.corda.core.crypto.SignatureMetadata
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.Vault
import net.corda.core.toFuture
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.finance.contracts.Commodity
import net.corda.finance.contracts.DealState
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.contracts.asset.CommodityContract
import net.corda.finance.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.finance.contracts.asset.DUMMY_OBLIGATION_ISSUER
import net.corda.testing.*
import net.corda.testing.chooseIdentity
import net.corda.testing.chooseIdentityAndCert
import net.corda.testing.dummyCommand
import net.corda.testing.singleIdentity
import java.security.KeyPair
import java.security.PublicKey
import java.time.Duration
import java.time.Instant
import java.time.Instant.now
import java.util.*
@JvmOverloads
fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
issuerServices: ServiceHub = this,
/**
* The service hub should provide at least a key management service and a storage service.
* @param defaultNotary used in [fillWithSomeTestDeals] and [fillWithSomeTestLinearStates].
* @param altNotary used in [fillWithSomeTestCash], [fillWithSomeTestCommodity] and consume/evolve methods. If not specified, same as [defaultNotary].
* @param rngFactory used by [fillWithSomeTestCash] if no custom [Random] provided.
*/
class VaultFiller @JvmOverloads constructor(
private val services: ServiceHub,
private val defaultNotary: Party,
private val defaultNotaryKeyPair: KeyPair,
private val altNotary: Party = defaultNotary,
private val rngFactory: () -> Random = { Random(0L) }) {
companion object {
fun calculateRandomlySizedAmounts(howMuch: Amount<Currency>, min: Int, max: Int, rng: Random): LongArray {
val numSlots = min + Math.floor(rng.nextDouble() * (max - min)).toInt()
val baseSize = howMuch.quantity / numSlots
check(baseSize > 0) { baseSize }
val amounts = LongArray(numSlots) { baseSize }
var distanceFromGoal = 0L
// If we want 10 slots then max adjust is 0.1, so even if all random numbers come out to the largest downward
// adjustment possible, the last slot ends at zero. With 20 slots, max adjust is 0.05 etc.
val maxAdjust = 1.0 / numSlots
for (i in amounts.indices) {
if (i != amounts.lastIndex) {
val adjustBy = rng.nextDouble() * maxAdjust - (maxAdjust / 2)
val adjustment = (1 + adjustBy)
val adjustTo = (amounts[i] * adjustment).toLong()
amounts[i] = adjustTo
distanceFromGoal += baseSize - adjustTo
} else {
amounts[i] += distanceFromGoal
}
}
// The desired amount may not have divided equally to start with, so adjust the first value to make up.
amounts[0] += howMuch.quantity - amounts.sum()
return amounts
}
}
init {
require(defaultNotary.owningKey == defaultNotaryKeyPair.public) { "Default notary public keys must match." }
}
@JvmOverloads
fun fillWithSomeTestDeals(dealIds: List<String>,
issuerServices: ServiceHub = services,
participants: List<AbstractParty> = emptyList()): Vault<DealState> {
val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey
val me = AnonymousParty(myKey)
val transactions: List<SignedTransaction> = dealIds.map {
// Issue a deal state
val dummyIssue = TransactionBuilder(notary = defaultNotary).apply {
addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(me)), DUMMY_DEAL_PROGRAM_ID)
addCommand(dummyCommand())
}
val stx = issuerServices.signInitialTransaction(dummyIssue)
return@map services.addSignature(stx, defaultNotaryKeyPair.public)
}
services.recordTransactions(transactions)
// Get all the StateAndRefs of all the generated transactions.
val states = transactions.flatMap { stx ->
stx.tx.outputs.indices.map { i -> stx.tx.outRef<DealState>(i) }
}
return Vault(states)
}
@JvmOverloads
fun fillWithSomeTestLinearStates(numberToCreate: Int,
externalId: String? = null,
participants: List<AbstractParty> = emptyList(),
notary: Party = DUMMY_NOTARY): Vault<DealState> {
val myKey: PublicKey = myInfo.chooseIdentity().owningKey
val me = AnonymousParty(myKey)
val transactions: List<SignedTransaction> = dealIds.map {
// Issue a deal state
val dummyIssue = TransactionBuilder(notary = notary).apply {
addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(me)), DUMMY_DEAL_PROGRAM_ID)
addCommand(dummyCommand())
linearString: String = "",
linearNumber: Long = 0L,
linearBoolean: Boolean = false,
linearTimestamp: Instant = now()): Vault<LinearState> {
val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey
val me = AnonymousParty(myKey)
val issuerKey = defaultNotaryKeyPair
val signatureMetadata = SignatureMetadata(services.myInfo.platformVersion, Crypto.findSignatureScheme(issuerKey.public).schemeNumberID)
val transactions: List<SignedTransaction> = (1..numberToCreate).map {
// Issue a Linear state
val dummyIssue = TransactionBuilder(notary = defaultNotary).apply {
addOutputState(DummyLinearContract.State(
linearId = UniqueIdentifier(externalId),
participants = participants.plus(me),
linearString = linearString,
linearNumber = linearNumber,
linearBoolean = linearBoolean,
linearTimestamp = linearTimestamp), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
addCommand(dummyCommand())
}
return@map services.signInitialTransaction(dummyIssue).withAdditionalSignature(issuerKey, signatureMetadata)
}
val stx = issuerServices.signInitialTransaction(dummyIssue)
return@map addSignature(stx, notary.owningKey)
}
recordTransactions(transactions)
// Get all the StateAndRefs of all the generated transactions.
val states = transactions.flatMap { stx ->
stx.tx.outputs.indices.map { i -> stx.tx.outRef<DealState>(i) }
}
return Vault(states)
}
@JvmOverloads
fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int,
externalId: String? = null,
participants: List<AbstractParty> = emptyList(),
linearString: String = "",
linearNumber: Long = 0L,
linearBoolean: Boolean = false,
linearTimestamp: Instant = now()): Vault<LinearState> {
val myKey: PublicKey = myInfo.chooseIdentity().owningKey
val me = AnonymousParty(myKey)
val issuerKey = DUMMY_NOTARY_KEY
val signatureMetadata = SignatureMetadata(myInfo.platformVersion, Crypto.findSignatureScheme(issuerKey.public).schemeNumberID)
val transactions: List<SignedTransaction> = (1..numberToCreate).map {
// Issue a Linear state
val dummyIssue = TransactionBuilder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(
linearId = UniqueIdentifier(externalId),
participants = participants.plus(me),
linearString = linearString,
linearNumber = linearNumber,
linearBoolean = linearBoolean,
linearTimestamp = linearTimestamp), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
addCommand(dummyCommand())
services.recordTransactions(transactions)
// Get all the StateAndRefs of all the generated transactions.
val states = transactions.flatMap { stx ->
stx.tx.outputs.indices.map { i -> stx.tx.outRef<LinearState>(i) }
}
return@map signInitialTransaction(dummyIssue).withAdditionalSignature(issuerKey, signatureMetadata)
return Vault(states)
}
recordTransactions(transactions)
@JvmOverloads
fun fillWithSomeTestCash(howMuch: Amount<Currency>,
issuerServices: ServiceHub,
thisManyStates: Int,
issuedBy: PartyAndReference,
owner: AbstractParty? = null,
rng: Random? = null) = fillWithSomeTestCash(howMuch, issuerServices, thisManyStates, thisManyStates, issuedBy, owner, rng)
// Get all the StateAndRefs of all the generated transactions.
val states = transactions.flatMap { stx ->
stx.tx.outputs.indices.map { i -> stx.tx.outRef<LinearState>(i) }
/**
* Creates a random set of between (by default) 3 and 10 cash states that add up to the given amount and adds them
* to the vault. This is intended for unit tests. By default the cash is owned by the legal
* identity key from the storage service.
*
* @param issuerServices service hub of the issuer node, which will be used to sign the transaction.
* @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!).
*/
fun fillWithSomeTestCash(howMuch: Amount<Currency>,
issuerServices: ServiceHub,
atLeastThisManyStates: Int,
atMostThisManyStates: Int,
issuedBy: PartyAndReference,
owner: AbstractParty? = null,
rng: Random? = null): Vault<Cash.State> {
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng ?: rngFactory())
// We will allocate one state to one transaction, for simplicities sake.
val cash = Cash()
val transactions: List<SignedTransaction> = amounts.map { pennies ->
val issuance = TransactionBuilder(null as Party?)
cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy, howMuch.token)), owner ?: services.myInfo.singleIdentity(), altNotary)
return@map issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey)
}
services.recordTransactions(transactions)
// Get all the StateRefs of all the generated transactions.
val states = transactions.flatMap { stx ->
stx.tx.outputs.indices.map { i -> stx.tx.outRef<Cash.State>(i) }
}
return Vault(states)
}
return Vault(states)
}
/**
*
* @param issuerServices service hub of the issuer node, which will be used to sign the transaction.
* @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!).
*/
// TODO: need to make all FungibleAsset commands (issue, move, exit) generic
fun fillWithSomeTestCommodity(amount: Amount<Commodity>, issuerServices: ServiceHub, issuedBy: PartyAndReference): Vault<CommodityContract.State> {
val myKey: PublicKey = services.myInfo.chooseIdentity().owningKey
val me = AnonymousParty(myKey)
/**
* Creates a random set of cash states that add up to the given amount and adds them to the vault. This is intended for
* unit tests. The cash is owned by the legal identity key from the storage service.
*
* The service hub needs to provide at least a key management service and a storage service.
*
* @param issuerServices service hub of the issuer node, which will be used to sign the transaction.
* @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary.
* @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!).
*/
fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
issuerServices: ServiceHub,
outputNotary: Party,
states: Int,
issuedBy: PartyAndReference): Vault<Cash.State>
= fillWithSomeTestCash(howMuch, issuerServices, outputNotary, states, states, issuedBy = issuedBy)
/**
* Creates a random set of between (by default) 3 and 10 cash states that add up to the given amount and adds them
* to the vault. This is intended for unit tests. By default the cash is issued by [DUMMY_CASH_ISSUER] and owned by the legal
* identity key from the storage service.
*
* The service hub needs to provide at least a key management service and a storage service.
*
* @param issuerServices service hub of the issuer node, which will be used to sign the transaction.
* @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary.
* @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!).
*/
fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
issuerServices: ServiceHub = this,
outputNotary: Party = DUMMY_NOTARY,
atLeastThisManyStates: Int = 3,
atMostThisManyStates: Int = 10,
rng: Random = Random(),
owner: AbstractParty? = null,
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER): Vault<Cash.State> {
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
// We will allocate one state to one transaction, for simplicities sake.
val cash = Cash()
val transactions: List<SignedTransaction> = amounts.map { pennies ->
val commodity = CommodityContract()
val issuance = TransactionBuilder(null as Party?)
cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy, howMuch.token)),owner ?: myInfo.singleIdentity(), outputNotary)
return@map issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey)
commodity.generateIssue(issuance, Amount(amount.quantity, Issued(issuedBy, amount.token)), me, altNotary)
val transaction = issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey)
services.recordTransactions(transaction)
return Vault(setOf(transaction.tx.outRef(0)))
}
recordTransactions(transactions)
// Get all the StateRefs of all the generated transactions.
val states = transactions.flatMap { stx ->
stx.tx.outputs.indices.map { i -> stx.tx.outRef<Cash.State>(i) }
}
return Vault(states)
}
/**
*
* @param issuerServices service hub of the issuer node, which will be used to sign the transaction.
* @param outputNotary the notary to use for output states. The transaction is NOT signed by this notary.
* @return a vault object that represents the generated states (it will NOT be the full vault from the service hub!).
*/
// TODO: need to make all FungibleAsset commands (issue, move, exit) generic
fun ServiceHub.fillWithSomeTestCommodity(amount: Amount<Commodity>,
issuerServices: ServiceHub = this,
outputNotary: Party = DUMMY_NOTARY,
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })),
ownedBy: AbstractParty? = null,
issuedBy: PartyAndReference = DUMMY_OBLIGATION_ISSUER.ref(1)): Vault<CommodityContract.State> {
val myKey: PublicKey = ownedBy?.owningKey ?: myInfo.chooseIdentity().owningKey
val me = AnonymousParty(myKey)
val commodity = CommodityContract()
val issuance = TransactionBuilder(null as Party?)
commodity.generateIssue(issuance, Amount(amount.quantity, Issued(issuedBy.copy(reference = ref), amount.token)), me, outputNotary)
val transaction = issuerServices.signInitialTransaction(issuance, issuedBy.party.owningKey)
recordTransactions(transaction)
return Vault(setOf(transaction.tx.outRef<CommodityContract.State>(0)))
}
fun calculateRandomlySizedAmounts(howMuch: Amount<Currency>, min: Int, max: Int, rng: Random): LongArray {
val numSlots = min + Math.floor(rng.nextDouble() * (max - min)).toInt()
val baseSize = howMuch.quantity / numSlots
check(baseSize > 0) { baseSize }
val amounts = LongArray(numSlots) { baseSize }
var distanceFromGoal = 0L
// If we want 10 slots then max adjust is 0.1, so even if all random numbers come out to the largest downward
// adjustment possible, the last slot ends at zero. With 20 slots, max adjust is 0.05 etc.
val maxAdjust = 1.0 / numSlots
for (i in amounts.indices) {
if (i != amounts.lastIndex) {
val adjustBy = rng.nextDouble() * maxAdjust - (maxAdjust / 2)
val adjustment = (1 + adjustBy)
val adjustTo = (amounts[i] * adjustment).toLong()
amounts[i] = adjustTo
distanceFromGoal += baseSize - adjustTo
} else {
amounts[i] += distanceFromGoal
private fun <T : LinearState> consume(states: List<StateAndRef<T>>) {
// Create a txn consuming different contract types
states.forEach {
val builder = TransactionBuilder(notary = altNotary).apply {
addInputState(it)
addCommand(dummyCommand(altNotary.owningKey))
}
val consumedTx = services.signInitialTransaction(builder, altNotary.owningKey)
services.recordTransactions(consumedTx)
}
}
// The desired amount may not have divided equally to start with, so adjust the first value to make up.
amounts[0] += howMuch.quantity - amounts.sum()
return amounts
}
fun <T : LinearState> ServiceHub.consume(states: List<StateAndRef<T>>, notary: Party) {
// Create a txn consuming different contract types
states.forEach {
val builder = TransactionBuilder(notary = notary).apply {
addInputState(it)
addCommand(dummyCommand(notary.owningKey))
private fun <T : LinearState> consumeAndProduce(stateAndRef: StateAndRef<T>): StateAndRef<T> {
// Create a txn consuming different contract types
var builder = TransactionBuilder(notary = altNotary).apply {
addInputState(stateAndRef)
addCommand(dummyCommand(altNotary.owningKey))
}
val consumedTx = signInitialTransaction(builder, notary.owningKey)
val consumedTx = services.signInitialTransaction(builder, altNotary.owningKey)
services.recordTransactions(consumedTx)
// Create a txn consuming different contract types
builder = TransactionBuilder(notary = altNotary).apply {
addOutputState(DummyLinearContract.State(linearId = stateAndRef.state.data.linearId,
participants = stateAndRef.state.data.participants), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
addCommand(dummyCommand(altNotary.owningKey))
}
val producedTx = services.signInitialTransaction(builder, altNotary.owningKey)
services.recordTransactions(producedTx)
return producedTx.tx.outRef(0)
}
recordTransactions(consumedTx)
private fun <T : LinearState> consumeAndProduce(states: List<StateAndRef<T>>) {
states.forEach {
consumeAndProduce(it)
}
}
fun consumeDeals(dealStates: List<StateAndRef<DealState>>) = consume(dealStates)
fun consumeLinearStates(linearStates: List<StateAndRef<LinearState>>) = consume(linearStates)
fun evolveLinearStates(linearStates: List<StateAndRef<LinearState>>) = consumeAndProduce(linearStates)
fun evolveLinearState(linearState: StateAndRef<LinearState>): StateAndRef<LinearState> = consumeAndProduce(linearState)
/**
* Consume cash, sending any change to the default identity for this node. Only suitable for use in test scenarios,
* where nodes have a default identity.
*/
fun consumeCash(amount: Amount<Currency>, to: AbstractParty): Vault.Update<ContractState> {
val ourIdentity = services.myInfo.chooseIdentityAndCert()
val update = services.vaultService.rawUpdates.toFuture()
// A tx that spends our money.
val builder = TransactionBuilder(altNotary).apply {
Cash.generateSpend(services, this, amount, ourIdentity, to)
}
val spendTx = services.signInitialTransaction(builder, altNotary.owningKey)
services.recordTransactions(spendTx)
return update.getOrThrow(Duration.ofSeconds(3))
}
}
fun <T : LinearState> ServiceHub.consumeAndProduce(stateAndRef: StateAndRef<T>, notary: Party): StateAndRef<T> {
// Create a txn consuming different contract types
var builder = TransactionBuilder(notary = notary).apply {
addInputState(stateAndRef)
addCommand(dummyCommand(notary.owningKey))
}
val consumedTx = signInitialTransaction(builder, notary.owningKey)
recordTransactions(consumedTx)
// Create a txn consuming different contract types
builder = TransactionBuilder(notary = notary).apply {
addOutputState(DummyLinearContract.State(linearId = stateAndRef.state.data.linearId,
participants = stateAndRef.state.data.participants), DUMMY_LINEAR_CONTRACT_PROGRAM_ID)
addCommand(dummyCommand(notary.owningKey))
}
val producedTx = signInitialTransaction(builder, notary.owningKey)
recordTransactions(producedTx)
return producedTx.tx.outRef<T>(0)
}
fun <T : LinearState> ServiceHub.consumeAndProduce(states: List<StateAndRef<T>>, notary: Party) {
states.forEach {
consumeAndProduce(it, notary)
}
}
fun ServiceHub.consumeDeals(dealStates: List<StateAndRef<DealState>>, notary: Party) = consume(dealStates, notary)
fun ServiceHub.consumeLinearStates(linearStates: List<StateAndRef<LinearState>>, notary: Party) = consume(linearStates, notary)
fun ServiceHub.evolveLinearStates(linearStates: List<StateAndRef<LinearState>>, notary: Party) = consumeAndProduce(linearStates, notary)
fun ServiceHub.evolveLinearState(linearState: StateAndRef<LinearState>, notary: Party): StateAndRef<LinearState> = consumeAndProduce(linearState, notary)
/**
* Consume cash, sending any change to the default identity for this node. Only suitable for use in test scenarios,
* where nodes have a default identity.
*/
@JvmOverloads
fun ServiceHub.consumeCash(amount: Amount<Currency>, to: Party = CHARLIE, notary: Party): Vault.Update<ContractState> {
return consumeCash(amount, myInfo.chooseIdentityAndCert(), to, notary)
}
/**
* Consume cash, sending any change to the specified identity.
*/
@JvmOverloads
fun ServiceHub.consumeCash(amount: Amount<Currency>, ourIdentity: PartyAndCertificate, to: Party = CHARLIE, notary: Party): Vault.Update<ContractState> {
val update = vaultService.rawUpdates.toFuture()
val services = this
// A tx that spends our money.
val builder = TransactionBuilder(notary).apply {
Cash.generateSpend(services, this, amount, ourIdentity, to)
}
val spendTx = signInitialTransaction(builder, notary.owningKey)
recordTransactions(spendTx)
return update.getOrThrow(Duration.ofSeconds(3))
}

View File

@ -6,6 +6,7 @@ import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import org.slf4j.LoggerFactory
import java.io.IOException
import java.net.URL
import java.util.concurrent.TimeUnit
@ -14,11 +15,13 @@ import java.util.concurrent.TimeUnit
*/
object HttpUtils {
private val logger = LoggerFactory.getLogger(javaClass)
private val client by lazy {
OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS).build()
}
val defaultMapper: ObjectMapper by lazy {
net.corda.client.jackson.JacksonSupport.createNonRpcMapper()
}
@ -28,9 +31,9 @@ object HttpUtils {
return makeRequest(Request.Builder().url(url).header("Content-Type", "application/json").put(body).build())
}
fun postJson(url: URL, data: String): Boolean {
fun postJson(url: URL, data: String) {
val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), data)
return makeRequest(Request.Builder().url(url).header("Content-Type", "application/json").post(body).build())
makeExceptionalRequest(Request.Builder().url(url).header("Content-Type", "application/json").post(body).build())
}
fun postPlain(url: URL, data: String): Boolean {
@ -44,6 +47,14 @@ object HttpUtils {
return mapper.readValue(parameterisedUrl, T::class.java)
}
// TODO Move everything to use this instead of makeRequest
private fun makeExceptionalRequest(request: Request) {
val response = client.newCall(request).execute()
if (!response.isSuccessful) {
throw IOException("${request.method()} to ${request.url()} returned a ${response.code()}: ${response.body().string()}")
}
}
private fun makeRequest(request: Request): Boolean {
val response = client.newCall(request).execute()

View File

@ -0,0 +1,15 @@
package net.corda.testing.internal
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ThreadFactory
import java.util.concurrent.atomic.AtomicInteger
private val familyToNextPoolNumber = ConcurrentHashMap<String, AtomicInteger>()
fun Any.testThreadFactory(useEnclosingClassName: Boolean = false): ThreadFactory {
val poolFamily = javaClass.let { (if (useEnclosingClassName) it.enclosingClass else it).simpleName }
val poolNumber = familyToNextPoolNumber.computeIfAbsent(poolFamily) { AtomicInteger(1) }.getAndIncrement()
val nextThreadNumber = AtomicInteger(1)
return ThreadFactory { task ->
Thread(task, "$poolFamily-$poolNumber-${nextThreadNumber.getAndIncrement()}")
}
}