mirror of
https://github.com/corda/corda.git
synced 2025-02-20 09:26:41 +00:00
CORDA-2115: Notary whitelist verification changes (#4293)
* CORDA-2115: Notary whitelist verification changes - For regular and contract upgrade transactions: check that the notary is in the network parameter whitelist - For notary change transactions: check the the new notary is in the network parameter whitelist. This enabled support for network merging: the old notary doesn't have to be in the current network's notary whitelist for re-pointing old states to another notary. These checks are done during transaction construction/verification and also by the non-validating notary. * Address comments * Remove stale todo * Use notary whitelist of current network parameters for platform versoin 3 * Cleanup test * Move `getHistoricNotary` to `HistoricNetworkParameterStorage` in `core.internal` * Require `newNotary` to be notary on the network map during notary change
This commit is contained in:
parent
36c5b0cbae
commit
838c99c6e4
@ -2278,8 +2278,6 @@ public static class net.corda.core.flows.NotaryFlow$Client extends net.corda.cor
|
||||
@NotNull
|
||||
public java.util.List<net.corda.core.crypto.TransactionSignature> call()
|
||||
@NotNull
|
||||
protected final net.corda.core.identity.Party checkTransaction()
|
||||
@NotNull
|
||||
public net.corda.core.utilities.ProgressTracker getProgressTracker()
|
||||
@Suspendable
|
||||
@NotNull
|
||||
@ -6383,7 +6381,7 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem
|
||||
@NotNull
|
||||
public net.corda.core.node.services.NetworkMapCache getNetworkMapCache()
|
||||
@NotNull
|
||||
public final net.corda.core.node.NetworkParameters getNetworkParameters()
|
||||
public net.corda.core.node.NetworkParameters getNetworkParameters()
|
||||
@NotNull
|
||||
protected final net.corda.core.node.ServicesForResolution getServicesForResolution()
|
||||
@NotNull
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.client.rpc;
|
||||
|
||||
import net.corda.core.contracts.Amount;
|
||||
import net.corda.core.identity.Party;
|
||||
import net.corda.core.messaging.CordaRPCOps;
|
||||
import net.corda.core.messaging.FlowHandle;
|
||||
import net.corda.core.utilities.OpaqueBytes;
|
||||
@ -28,10 +29,11 @@ import static net.corda.finance.contracts.GetBalances.getCashBalance;
|
||||
import static net.corda.node.services.Permissions.invokeRpc;
|
||||
import static net.corda.node.services.Permissions.startFlow;
|
||||
import static net.corda.testing.core.TestConstants.ALICE_NAME;
|
||||
import static net.corda.testing.core.TestConstants.DUMMY_NOTARY_NAME;
|
||||
|
||||
public class CordaRPCJavaClientTest extends NodeBasedTest {
|
||||
public CordaRPCJavaClientTest() {
|
||||
super(Arrays.asList("net.corda.finance.contracts", CashSchemaV1.class.getPackage().getName()));
|
||||
super(Arrays.asList("net.corda.finance.contracts", CashSchemaV1.class.getPackage().getName()), Collections.singletonList(DUMMY_NOTARY_NAME));
|
||||
}
|
||||
|
||||
private List<String> perms = Arrays.asList(
|
||||
@ -73,9 +75,10 @@ public class CordaRPCJavaClientTest extends NodeBasedTest {
|
||||
public void testCashBalances() throws ExecutionException, InterruptedException {
|
||||
login(rpcUser.getUsername(), rpcUser.getPassword());
|
||||
|
||||
Party notaryIdentity = InternalTestUtilsKt.chooseIdentity(getNotaryNodes().get(0).getInfo());
|
||||
FlowHandle<AbstractCashFlow.Result> flowHandle = rpcProxy.startFlowDynamic(CashIssueFlow.class,
|
||||
DOLLARS(123), OpaqueBytes.of((byte)0),
|
||||
InternalTestUtilsKt.chooseIdentity(node.getInfo()));
|
||||
notaryIdentity);
|
||||
System.out.println("Started issuing cash, waiting on result");
|
||||
flowHandle.getReturnValue().get();
|
||||
|
||||
|
@ -9,6 +9,7 @@ import net.corda.core.internal.concurrent.flatMap
|
||||
import net.corda.core.internal.location
|
||||
import net.corda.core.internal.toPath
|
||||
import net.corda.core.messaging.*
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
@ -45,7 +46,7 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance")) {
|
||||
class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance"), notaries = listOf(DUMMY_NOTARY_NAME)) {
|
||||
companion object {
|
||||
val rpcUser = User("user1", "test", permissions = setOf(all()))
|
||||
}
|
||||
@ -65,7 +66,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance")) {
|
||||
client = CordaRPCClient(node.node.configuration.rpcOptions.address, CordaRPCClientConfiguration.DEFAULT.copy(
|
||||
maxReconnectAttempts = 5
|
||||
))
|
||||
identity = node.info.identityFromX500Name(ALICE_NAME)
|
||||
identity = notaryNodes.first().info.identityFromX500Name(DUMMY_NOTARY_NAME)
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -102,7 +102,8 @@ class FinalityFlow private constructor(val transaction: SignedTransaction,
|
||||
|
||||
transaction.pushToLoggingContext()
|
||||
logCommandData()
|
||||
val externalParticipants = extractExternalParticipants(verifyTx())
|
||||
val ledgerTransaction = verifyTx()
|
||||
val externalParticipants = extractExternalParticipants(ledgerTransaction)
|
||||
|
||||
if (sessions != null) {
|
||||
val missingRecipients = externalParticipants - sessions.map { it.counterparty }
|
||||
|
@ -2,7 +2,6 @@ package net.corda.core.flows
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.contracts.ComponentGroupEnum
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.crypto.SecureHash
|
||||
@ -10,6 +9,7 @@ import net.corda.core.crypto.TransactionSignature
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.BackpressureAwareTimedFlow
|
||||
import net.corda.core.internal.FetchDataFlow
|
||||
import net.corda.core.internal.notary.HistoricNetworkParameterStorage
|
||||
import net.corda.core.internal.notary.generateSignature
|
||||
import net.corda.core.internal.notary.validateSignatures
|
||||
import net.corda.core.internal.pushToLoggingContext
|
||||
@ -27,6 +27,8 @@ class NotaryFlow {
|
||||
* In case of a single-node or Raft notary, the flow will return a single signature. For the BFT notary multiple
|
||||
* signatures will be returned – one from each replica that accepted the input state commit.
|
||||
*
|
||||
* The transaction to be notarised, [stx], should be fully verified before calling this flow.
|
||||
*
|
||||
* @throws NotaryException in case the any of the inputs to the transaction have been consumed
|
||||
* by another transaction or the time-window is invalid or
|
||||
* the parameters used for this transaction are no longer in force in the network.
|
||||
@ -51,8 +53,8 @@ class NotaryFlow {
|
||||
override fun call(): List<TransactionSignature> {
|
||||
stx.pushToLoggingContext()
|
||||
val notaryParty = checkTransaction()
|
||||
progressTracker.currentStep = REQUESTING
|
||||
logger.info("Sending transaction to notary: ${notaryParty.name}.")
|
||||
progressTracker.currentStep = REQUESTING
|
||||
val response = notarise(notaryParty)
|
||||
logger.info("Notary responded.")
|
||||
progressTracker.currentStep = VALIDATING
|
||||
@ -63,6 +65,7 @@ class NotaryFlow {
|
||||
* Checks that the transaction specifies a valid notary, and verifies that it contains all required signatures
|
||||
* apart from the notary's.
|
||||
*/
|
||||
// TODO: [CORDA-2274] Perform full transaction verification once verification caching is enabled.
|
||||
protected fun checkTransaction(): Party {
|
||||
val notaryParty = stx.notary ?: throw IllegalStateException("Transaction does not specify a Notary")
|
||||
check(serviceHub.networkMapCache.isNotary(notaryParty)) { "$notaryParty is not a notary on the network" }
|
||||
@ -72,20 +75,37 @@ class NotaryFlow {
|
||||
stx.resolveTransactionWithSignatures(serviceHub).verifySignaturesExcept(notaryParty.owningKey)
|
||||
return notaryParty
|
||||
}
|
||||
|
||||
/** Notarises the transaction with the [notaryParty], obtains the notary's signature(s). */
|
||||
@Throws(NotaryException::class)
|
||||
@Suspendable
|
||||
protected fun notarise(notaryParty: Party): UntrustworthyData<NotarisationResponse> {
|
||||
val session = initiateFlow(notaryParty)
|
||||
val requestSignature = generateRequestSignature()
|
||||
return if (serviceHub.networkMapCache.isValidatingNotary(notaryParty)) {
|
||||
return if (isValidating(notaryParty)) {
|
||||
sendAndReceiveValidating(session, requestSignature)
|
||||
} else {
|
||||
sendAndReceiveNonValidating(notaryParty, session, requestSignature)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isValidating(notaryParty: Party): Boolean {
|
||||
val onTheCurrentWhitelist = serviceHub.networkMapCache.isNotary(notaryParty)
|
||||
return if (!onTheCurrentWhitelist) {
|
||||
/*
|
||||
Note that the only scenario where it's acceptable to use a notary not in the current network parameter whitelist is
|
||||
when performing a notary change transaction after a network merge – the old notary won't be on the whitelist of the new network,
|
||||
and can't be used for regular transactions.
|
||||
*/
|
||||
check(stx.coreTransaction is NotaryChangeWireTransaction) {
|
||||
"Notary $notaryParty is not on the network parameter whitelist. A non-whitelisted notary can only be used for notary change transactions"
|
||||
}
|
||||
val historicNotary = (serviceHub.networkParametersStorage as HistoricNetworkParameterStorage).getHistoricNotary(notaryParty)
|
||||
?: throw IllegalStateException("The notary party $notaryParty specified by transaction ${stx.id}, is not recognised as a current or historic notary.")
|
||||
historicNotary.validating
|
||||
|
||||
} else serviceHub.networkMapCache.isValidatingNotary(notaryParty)
|
||||
}
|
||||
|
||||
@Suspendable
|
||||
private fun sendAndReceiveValidating(session: FlowSession, signature: NotarisationRequestSignature): UntrustworthyData<NotarisationResponse> {
|
||||
val payload = NotarisationPayload(stx, signature)
|
||||
|
@ -0,0 +1,11 @@
|
||||
package net.corda.core.internal.notary
|
||||
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NotaryInfo
|
||||
|
||||
interface HistoricNetworkParameterStorage {
|
||||
/**
|
||||
* Returns the [NotaryInfo] for a notary [party] in the current or any historic network parameter whitelist, or null if not found.
|
||||
*/
|
||||
fun getHistoricNotary(party: Party): NotaryInfo?
|
||||
}
|
@ -4,17 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TimeWindow
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.NotarisationPayload
|
||||
import net.corda.core.flows.NotarisationRequest
|
||||
import net.corda.core.flows.NotarisationRequestSignature
|
||||
import net.corda.core.flows.NotarisationResponse
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.flows.NotaryException
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
import net.corda.core.flows.WaitTimeUpdate
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.MIN_PLATFORM_VERSION_FOR_BACKPRESSURE_MESSAGE
|
||||
import net.corda.core.internal.checkParameterHash
|
||||
@ -55,9 +45,6 @@ abstract class NotaryServiceFlow(val otherSideSession: FlowSession, val service:
|
||||
|
||||
@Suspendable
|
||||
override fun call(): Void? {
|
||||
check(serviceHub.myInfo.legalIdentities.any { serviceHub.networkMapCache.isNotary(it) }) {
|
||||
"We are not a notary on the network"
|
||||
}
|
||||
val requestPayload = otherSideSession.receive<NotarisationPayload>().unwrap { it }
|
||||
|
||||
try {
|
||||
@ -123,6 +110,21 @@ abstract class NotaryServiceFlow(val otherSideSession: FlowSession, val service:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that network parameters hash on this transaction is the current hash for the network.
|
||||
*/
|
||||
// TODO: ENT-2666 Implement network parameters fuzzy checking. By design in Corda network we have propagation time delay.
|
||||
// We will never end up in perfect synchronization with all the nodes. However, network parameters update process
|
||||
// lets us predict what is the reasonable time window for changing parameters on most of the nodes.
|
||||
@Suspendable
|
||||
protected fun checkParametersHash(networkParametersHash: SecureHash?) {
|
||||
if (networkParametersHash == null && serviceHub.networkParameters.minimumPlatformVersion < 4) return
|
||||
val notaryParametersHash = serviceHub.networkParametersStorage.currentHash
|
||||
require(notaryParametersHash == networkParametersHash) {
|
||||
"Transaction for notarisation was tagged with parameters with hash: $networkParametersHash, but current network parameters are: $notaryParametersHash"
|
||||
}
|
||||
}
|
||||
|
||||
/** Verifies that the correct notarisation request was signed by the counterparty. */
|
||||
private fun validateRequestSignature(request: NotarisationRequest, signature: NotarisationRequestSignature) {
|
||||
val requestingParty = otherSideSession.counterparty
|
||||
@ -133,8 +135,7 @@ abstract class NotaryServiceFlow(val otherSideSession: FlowSession, val service:
|
||||
* Override to implement custom logic to perform transaction verification based on validity and privacy requirements.
|
||||
*/
|
||||
@Suspendable
|
||||
protected open fun verifyTransaction(requestPayload: NotarisationPayload) {
|
||||
}
|
||||
abstract fun verifyTransaction(requestPayload: NotarisationPayload)
|
||||
|
||||
@Suspendable
|
||||
private fun signTransactionAndSendResponse(txId: SecureHash) {
|
||||
@ -158,7 +159,7 @@ abstract class NotaryServiceFlow(val otherSideSession: FlowSession, val service:
|
||||
private fun logError(error: NotaryError) {
|
||||
val errorCause = when (error) {
|
||||
is NotaryError.RequestSignatureInvalid -> error.cause
|
||||
is NotaryError.TransactionInvalid -> error.cause
|
||||
is NotaryError.TransactionInvalid -> error.cause
|
||||
is NotaryError.General -> error.cause
|
||||
else -> null
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package net.corda.core.node.services
|
||||
|
||||
import net.corda.core.CordaInternal
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.internal.SignedDataWithCert
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NotaryInfo
|
||||
|
||||
/**
|
||||
* Interface for handling network parameters storage used for resolving transactions according to parameters that were
|
||||
|
@ -29,13 +29,12 @@ abstract class CoreTransaction : BaseTransaction() {
|
||||
abstract class FullTransaction : BaseTransaction() {
|
||||
abstract override val inputs: List<StateAndRef<ContractState>>
|
||||
abstract override val references: List<StateAndRef<ContractState>>
|
||||
// TODO NetworkParameters field is nullable only because of the API stability and the fact that our ledger transactions exposed constructors
|
||||
// (they were data classes). This should be revisited.
|
||||
/**
|
||||
* Network parameters that were in force when this transaction was created. Resolved from the hash of network parameters on the corresponding
|
||||
* wire transaction.
|
||||
*/
|
||||
abstract val networkParameters: NetworkParameters?
|
||||
|
||||
override fun checkBaseInvariants() {
|
||||
super.checkBaseInvariants()
|
||||
checkInputsAndReferencesHaveSameNotary()
|
||||
@ -47,4 +46,17 @@ abstract class FullTransaction : BaseTransaction() {
|
||||
check(notaries.size == 1) { "All inputs and reference inputs must point to the same notary" }
|
||||
check(notaries.single() == notary) { "The specified notary must be the one specified by all inputs and input references" }
|
||||
}
|
||||
|
||||
/** Make sure the assigned notary is part of the network parameter whitelist. */
|
||||
protected fun checkNotaryWhitelisted() {
|
||||
notary?.let { notaryParty ->
|
||||
// Network parameters will never be null if the transaction is resolved from a CoreTransaction rather than constructed directly.
|
||||
networkParameters?.let { parameters ->
|
||||
val notaryWhitelist = parameters.notaries.map { it.identity }
|
||||
check(notaryParty in notaryWhitelist) {
|
||||
"Notary ($notaryParty) specified by the transaction is not on the network parameter whitelist: [${notaryWhitelist.joinToString()}]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -250,6 +250,7 @@ data class ContractUpgradeLedgerTransaction(
|
||||
private val upgradedContract: UpgradedContract<ContractState, *> = loadUpgradedContract()
|
||||
|
||||
init {
|
||||
checkNotaryWhitelisted()
|
||||
// TODO: relax this constraint once upgrading encumbered states is supported.
|
||||
check(inputs.all { it.state.contract == legacyContractClassName }) {
|
||||
"All input states must point to the legacy contract"
|
||||
|
@ -53,8 +53,6 @@ private constructor(
|
||||
val timeWindow: TimeWindow?,
|
||||
val privacySalt: PrivacySalt,
|
||||
/** Network parameters that were in force when the transaction was notarised. */
|
||||
// TODO Network parameters should never be null on LedgerTransaction, this is left only because of deprecated constructors. We should decide to
|
||||
// get rid of them.
|
||||
override val networkParameters: NetworkParameters?,
|
||||
override val references: List<StateAndRef<ContractState>>
|
||||
//DOCEND 1
|
||||
@ -67,6 +65,7 @@ private constructor(
|
||||
init {
|
||||
checkBaseInvariants()
|
||||
if (timeWindow != null) check(notary != null) { "Transactions with time-windows must be notarised" }
|
||||
checkNotaryWhitelisted()
|
||||
checkNoNotaryChange()
|
||||
checkEncumbrancesValid()
|
||||
}
|
||||
|
@ -123,8 +123,6 @@ private constructor(
|
||||
val newNotary: Party,
|
||||
override val id: SecureHash,
|
||||
override val sigs: List<TransactionSignature>,
|
||||
// TODO Network parameters should never be null on NotaryChangeLedgerTransaction, this is left only because of deprecated constructors. We should decide to
|
||||
// get rid of them.
|
||||
override val networkParameters: NetworkParameters?
|
||||
) : FullTransaction(), TransactionWithSignatures {
|
||||
companion object {
|
||||
@ -141,6 +139,22 @@ private constructor(
|
||||
|
||||
init {
|
||||
checkEncumbrances()
|
||||
checkNewNotaryWhitelisted()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the output notary is whitelisted.
|
||||
*
|
||||
* Note that for this transaction type we do not require the input notary to be whitelisted to support network merging.
|
||||
* For all other transaction types this is enforced.
|
||||
*/
|
||||
private fun checkNewNotaryWhitelisted() {
|
||||
networkParameters?.let { parameters ->
|
||||
val notaryWhitelist = parameters.notaries.map { it.identity }
|
||||
check(newNotary in notaryWhitelist) {
|
||||
"The output notary $newNotary is not whitelisted in the attached network parameters."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val references: List<StateAndRef<ContractState>> = emptyList()
|
||||
|
@ -6,6 +6,7 @@ import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.finance.POUNDS
|
||||
import net.corda.finance.`issued by`
|
||||
@ -48,10 +49,14 @@ class ConstraintsPropagationTests {
|
||||
doReturn(ALICE_PARTY).whenever(it).partyFromKey(ALICE_PUBKEY)
|
||||
doReturn(BOB_PARTY).whenever(it).partyFromKey(BOB_PUBKEY)
|
||||
},
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
.copy(whitelistedContractImplementations = mapOf(
|
||||
networkParameters = testNetworkParameters(
|
||||
minimumPlatformVersion = 4,
|
||||
whitelistedContractImplementations = mapOf(
|
||||
Cash.PROGRAM_ID to listOf(SecureHash.zeroHash, SecureHash.allOnesHash),
|
||||
noPropagationContractClassName to listOf(SecureHash.zeroHash)))
|
||||
noPropagationContractClassName to listOf(SecureHash.zeroHash)
|
||||
),
|
||||
notaries = listOf(NotaryInfo(DUMMY_NOTARY, true))
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
@ -43,8 +44,10 @@ class PackageOwnershipVerificationTests {
|
||||
doReturn(ALICE_PARTY).whenever(it).partyFromKey(ALICE_PUBKEY)
|
||||
doReturn(BOB_PARTY).whenever(it).partyFromKey(BOB_PUBKEY)
|
||||
},
|
||||
networkParameters = testNetworkParameters()
|
||||
.copy(packageOwnership = mapOf("net.corda.core.contracts" to OWNER_KEY_PAIR.public))
|
||||
networkParameters = testNetworkParameters(
|
||||
packageOwnership = mapOf("net.corda.core.contracts" to OWNER_KEY_PAIR.public),
|
||||
notaries = listOf(NotaryInfo(DUMMY_NOTARY, true))
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
@ -70,7 +73,6 @@ class PackageOwnershipVerificationTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@BelongsToContract(DummyContract::class)
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.SecureHash.Companion.zeroHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.ReferenceStateRef
|
||||
@ -49,6 +50,7 @@ class PartialMerkleTreeTest {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
|
||||
private val nodes = "abcdef"
|
||||
private lateinit var hashed: List<SecureHash.SHA256>
|
||||
private lateinit var expectedRoot: SecureHash
|
||||
@ -56,6 +58,7 @@ class PartialMerkleTreeTest {
|
||||
private lateinit var testLedger: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
|
||||
private lateinit var txs: List<WireTransaction>
|
||||
private lateinit var testTx: WireTransaction
|
||||
|
||||
@Before
|
||||
fun init() {
|
||||
hashed = nodes.map { it.serialize().sha256() }
|
||||
@ -66,8 +69,9 @@ class PartialMerkleTreeTest {
|
||||
cordappPackages = emptyList(),
|
||||
initialIdentity = TestIdentity(MEGA_CORP.name),
|
||||
identityService = rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY) },
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
|
||||
},
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4, notaries = listOf(NotaryInfo(DUMMY_NOTARY, true)))
|
||||
).ledger(DUMMY_NOTARY) {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
|
@ -120,7 +120,7 @@ class WithReferencedStatesFlowTests {
|
||||
private val mockNet = InternalMockNetwork(
|
||||
cordappsForAllNodes = cordappsForPackages("net.corda.core.flows", "net.corda.testing.contracts"),
|
||||
threadPerNode = true,
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
initialNetworkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,9 @@ import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
@ -29,10 +31,15 @@ class LedgerTransactionQueryTests {
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
private val keyPair = generateKeyPair()
|
||||
private val services = MockServices(listOf("net.corda.testing.contracts"), CordaX500Name("MegaCorp", "London", "GB"),
|
||||
private val services = MockServices(
|
||||
listOf("net.corda.testing.contracts"),
|
||||
TestIdentity(CordaX500Name("MegaCorp", "London", "GB"), keyPair),
|
||||
rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(null).whenever(it).partyFromKey(keyPair.public)
|
||||
}, keyPair)
|
||||
},
|
||||
testNetworkParameters(notaries = listOf(NotaryInfo(DUMMY_NOTARY, true))),
|
||||
keyPair
|
||||
)
|
||||
private val identity: Party = services.myInfo.singleIdentity()
|
||||
|
||||
@Before
|
||||
|
@ -8,6 +8,7 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
@ -49,8 +50,7 @@ class ReferenceStateTests {
|
||||
doReturn(ALICE_PARTY).whenever(it).partyFromKey(ALICE_PUBKEY)
|
||||
doReturn(BOB_PARTY).whenever(it).partyFromKey(BOB_PUBKEY)
|
||||
},
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4, notaries = listOf(NotaryInfo(DUMMY_NOTARY, true)))
|
||||
)
|
||||
|
||||
// This state has only been created to serve reference data so it cannot ever be used as an input or
|
||||
|
@ -5,10 +5,12 @@ import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.TestIdentity
|
||||
@ -51,11 +53,14 @@ class TransactionEncumbranceTests {
|
||||
val FIVE_PM: Instant = FOUR_PM.plus(1, ChronoUnit.HOURS)
|
||||
val timeLock = DummyTimeLock.State(FIVE_PM)
|
||||
|
||||
|
||||
val ledgerServices = MockServices(listOf("net.corda.core.transactions", "net.corda.finance.contracts.asset"), MEGA_CORP.name,
|
||||
val ledgerServices = MockServices(
|
||||
listOf("net.corda.core.transactions", "net.corda.finance.contracts.asset"),
|
||||
MEGA_CORP.name,
|
||||
rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
|
||||
})
|
||||
},
|
||||
testNetworkParameters(notaries = listOf(NotaryInfo(DUMMY_NOTARY, true)))
|
||||
)
|
||||
}
|
||||
|
||||
class DummyTimeLock : Contract {
|
||||
@ -255,7 +260,7 @@ class TransactionEncumbranceTests {
|
||||
unverifiedTransaction {
|
||||
attachments(Cash.PROGRAM_ID, TEST_TIMELOCK_ID)
|
||||
output(Cash.PROGRAM_ID, "state encumbered by 5pm time-lock", encumbrance = 1, contractState = state)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock",0, timeLock)
|
||||
output(TEST_TIMELOCK_ID, "5pm time-lock", 0, timeLock)
|
||||
}
|
||||
transaction {
|
||||
attachments(Cash.PROGRAM_ID)
|
||||
@ -330,27 +335,31 @@ class TransactionEncumbranceTests {
|
||||
// More complex encumbrance (full cycle of size 4) where one of the encumbered states is assigned to a different notary.
|
||||
// 0 -> 1, 1 -> 3, 3 -> 2, 2 -> 0
|
||||
// We expect that state at index 3 cannot be encumbered with the state at index 2, due to mismatched notaries.
|
||||
AssertionsForClassTypes.assertThatExceptionOfType(TransactionVerificationException.TransactionNotaryMismatchEncumbranceException::class.java).isThrownBy {
|
||||
TransactionBuilder()
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 1, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 3, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY2, 0, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 2, AutomaticHashConstraint)
|
||||
.addCommand(Cash.Commands.Issue(), MEGA_CORP.owningKey)
|
||||
.toLedgerTransaction(ledgerServices)
|
||||
}.withMessageContaining("index 3 is assigned to notary [O=Notary Service, L=Zurich, C=CH], while its encumbrance with index 2 is assigned to notary [O=Notary Service2, L=Zurich, C=CH]")
|
||||
AssertionsForClassTypes.assertThatExceptionOfType(TransactionVerificationException.TransactionNotaryMismatchEncumbranceException::class.java)
|
||||
.isThrownBy {
|
||||
TransactionBuilder()
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 1, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 3, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY2, 0, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 2, AutomaticHashConstraint)
|
||||
.addCommand(Cash.Commands.Issue(), MEGA_CORP.owningKey)
|
||||
.toLedgerTransaction(ledgerServices)
|
||||
}
|
||||
.withMessageContaining("index 3 is assigned to notary [O=Notary Service, L=Zurich, C=CH], while its encumbrance with index 2 is assigned to notary [O=Notary Service2, L=Zurich, C=CH]")
|
||||
|
||||
// Two different encumbrance chains, where only one fails due to mismatched notary.
|
||||
// 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2 where encumbered states with indices 2 and 3, respectively, are assigned
|
||||
// to different notaries.
|
||||
AssertionsForClassTypes.assertThatExceptionOfType(TransactionVerificationException.TransactionNotaryMismatchEncumbranceException::class.java).isThrownBy {
|
||||
TransactionBuilder()
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 1, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 0, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 3, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY2, 2, AutomaticHashConstraint)
|
||||
.addCommand(Cash.Commands.Issue(), MEGA_CORP.owningKey)
|
||||
.toLedgerTransaction(ledgerServices)
|
||||
}.withMessageContaining("index 2 is assigned to notary [O=Notary Service, L=Zurich, C=CH], while its encumbrance with index 3 is assigned to notary [O=Notary Service2, L=Zurich, C=CH]")
|
||||
AssertionsForClassTypes.assertThatExceptionOfType(TransactionVerificationException.TransactionNotaryMismatchEncumbranceException::class.java)
|
||||
.isThrownBy {
|
||||
TransactionBuilder()
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 1, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 0, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY, 3, AutomaticHashConstraint)
|
||||
.addOutputState(stateWithNewOwner, Cash.PROGRAM_ID, DUMMY_NOTARY2, 2, AutomaticHashConstraint)
|
||||
.addCommand(Cash.Commands.Issue(), MEGA_CORP.owningKey)
|
||||
.toLedgerTransaction(ledgerServices)
|
||||
}
|
||||
.withMessageContaining("index 2 is assigned to notary [O=Notary Service, L=Zurich, C=CH], while its encumbrance with index 3 is assigned to notary [O=Notary Service2, L=Zurich, C=CH]")
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.CompositeKey
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.core.*
|
||||
@ -176,7 +177,7 @@ class TransactionTests {
|
||||
notary,
|
||||
timeWindow,
|
||||
privacySalt,
|
||||
testNetworkParameters(),
|
||||
testNetworkParameters(notaries = listOf(NotaryInfo(DUMMY_NOTARY, true))),
|
||||
emptyList()
|
||||
)
|
||||
|
||||
|
@ -4,6 +4,7 @@ import net.corda.core.contracts.*
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
@ -14,6 +15,7 @@ import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.CASH
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.contracts.asset.STATE
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.dsl.EnforceVerifyOrFail
|
||||
import net.corda.testing.dsl.TransactionDSL
|
||||
@ -22,6 +24,7 @@ import net.corda.testing.internal.TEST_TX_TIME
|
||||
import net.corda.testing.internal.vault.VaultFiller
|
||||
import net.corda.testing.node.MockServices
|
||||
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
|
||||
import net.corda.testing.node.internal.MockNetworkParametersStorage
|
||||
import net.corda.testing.node.ledger
|
||||
import net.corda.testing.node.makeTestIdentityService
|
||||
import net.corda.testing.node.transaction
|
||||
@ -244,13 +247,23 @@ class CommercialPaperTestsGeneric {
|
||||
// MegaCorp will issue some commercial paper and Alice will buy it, using cash issued to her in the name
|
||||
// of the dummy cash issuer.
|
||||
|
||||
val networkParameters = testNetworkParameters(notaries = listOf(NotaryInfo(dummyNotary.party, true)))
|
||||
val allIdentities = arrayOf(megaCorp.identity, alice.identity, dummyCashIssuer.identity, dummyNotary.identity)
|
||||
val notaryServices = MockServices(listOf("net.corda.finance.contracts", "net.corda.finance.contracts.asset", "net.corda.finance.schemas"), dummyNotary)
|
||||
val issuerServices = MockServices(listOf("net.corda.finance.contracts", "net.corda.finance.contracts.asset", "net.corda.finance.schemas"), dummyCashIssuer, dummyNotary)
|
||||
val notaryServices = MockServices(
|
||||
listOf("net.corda.finance.contracts", "net.corda.finance.contracts.asset", "net.corda.finance.schemas"),
|
||||
dummyNotary
|
||||
)
|
||||
val issuerServices = MockServices(
|
||||
listOf("net.corda.finance.contracts", "net.corda.finance.contracts.asset", "net.corda.finance.schemas"),
|
||||
dummyCashIssuer,
|
||||
networkParameters,
|
||||
dummyNotary
|
||||
)
|
||||
val (aliceDatabase, aliceServices) = makeTestDatabaseAndMockServices(
|
||||
listOf("net.corda.finance.contracts", "net.corda.finance.schemas"),
|
||||
makeTestIdentityService(*allIdentities),
|
||||
alice
|
||||
alice,
|
||||
networkParameters
|
||||
)
|
||||
val aliceCash: Vault<Cash.State> = aliceDatabase.transaction {
|
||||
VaultFiller(aliceServices, dummyNotary).fillWithSomeTestCash(9000.DOLLARS, issuerServices, 1, dummyCashIssuer.ref(1))
|
||||
@ -259,10 +272,12 @@ class CommercialPaperTestsGeneric {
|
||||
val (megaCorpDatabase, megaCorpServices) = makeTestDatabaseAndMockServices(
|
||||
listOf("net.corda.finance.contracts", "net.corda.finance.schemas"),
|
||||
makeTestIdentityService(*allIdentities),
|
||||
megaCorp
|
||||
megaCorp,
|
||||
networkParameters
|
||||
)
|
||||
|
||||
val bigCorpCash: Vault<Cash.State> = megaCorpDatabase.transaction {
|
||||
VaultFiller(megaCorpServices, dummyNotary).fillWithSomeTestCash(13000.DOLLARS, issuerServices, 1, dummyCashIssuer.ref(1))
|
||||
VaultFiller(megaCorpServices, dummyNotary).fillWithSomeTestCash(13000.DOLLARS, issuerServices, 1, dummyCashIssuer.ref(1))
|
||||
}
|
||||
|
||||
// Propagate the cash transactions to each side.
|
||||
|
@ -23,6 +23,7 @@ import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.cordapp.CordappProviderImpl
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.common.internal.addNotary
|
||||
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
@ -67,7 +68,7 @@ class AttachmentLoadingTests {
|
||||
}
|
||||
|
||||
private val services = object : ServicesForResolution {
|
||||
private val testNetworkParameters = testNetworkParameters()
|
||||
private val testNetworkParameters = testNetworkParameters().addNotary(DUMMY_NOTARY)
|
||||
override fun loadState(stateRef: StateRef): TransactionState<*> = throw NotImplementedError()
|
||||
override fun loadStates(stateRefs: Set<StateRef>): Set<StateAndRef<ContractState>> = throw NotImplementedError()
|
||||
override val identityService = rigorousMock<IdentityService>().apply {
|
||||
|
@ -13,7 +13,10 @@ import net.corda.node.services.config.MB
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyState
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.core.dummyCommand
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.User
|
||||
@ -27,7 +30,6 @@ import kotlin.test.assertEquals
|
||||
class LargeTransactionsTest {
|
||||
private companion object {
|
||||
val BOB = TestIdentity(BOB_NAME, 80).party
|
||||
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
|
||||
}
|
||||
|
||||
@StartableByRPC
|
||||
@ -38,7 +40,8 @@ class LargeTransactionsTest {
|
||||
private val hash4: SecureHash) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
val tx = TransactionBuilder(notary = DUMMY_NOTARY)
|
||||
val notary = serviceHub.networkParameters.notaries.first().identity
|
||||
val tx = TransactionBuilder(notary)
|
||||
.addOutputState(DummyState(), DummyContract.PROGRAM_ID)
|
||||
.addCommand(dummyCommand(ourIdentity.owningKey))
|
||||
.addAttachment(hash1)
|
||||
|
@ -168,7 +168,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
val networkMapClient: NetworkMapClient? = configuration.networkServices?.let { NetworkMapClient(it.networkMapURL, versionInfo) }
|
||||
val attachments = NodeAttachmentService(metricRegistry, cacheFactory, database).tokenize()
|
||||
val cryptoService = configuration.makeCryptoService()
|
||||
val networkParametersStorage = DBNetworkParametersStorage(cacheFactory, database, networkMapClient).tokenize()
|
||||
@Suppress("LeakingThis")
|
||||
val networkParametersStorage = makeParametersStorage()
|
||||
val cordappProvider = CordappProviderImpl(cordappLoader, CordappConfigFileProvider(configuration.cordappDirectories), attachments).tokenize()
|
||||
@Suppress("LeakingThis")
|
||||
val keyManagementService = makeKeyManagementService(identityService).tokenize()
|
||||
@ -362,7 +363,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
|
||||
// Do all of this in a database transaction so anything that might need a connection has one.
|
||||
return database.transaction {
|
||||
networkParametersStorage.start(signedNetParams, trustRoot)
|
||||
networkParametersStorage.setCurrentParameters(signedNetParams, trustRoot)
|
||||
identityService.loadIdentities(nodeInfo.legalIdentitiesAndCerts)
|
||||
attachments.start()
|
||||
cordappProvider.start(netParams.whitelistedContractImplementations)
|
||||
@ -701,6 +702,10 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
return DBTransactionStorage(database, cacheFactory)
|
||||
}
|
||||
|
||||
protected open fun makeParametersStorage(): NetworkParametersStorageInternal {
|
||||
return DBNetworkParametersStorage(cacheFactory, database, networkMapClient).tokenize()
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected open fun acceptableLiveFiberCountOnStop(): Int = 0
|
||||
|
||||
@ -778,7 +783,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
||||
/** Some notary implementations only work with Java serialization. */
|
||||
maybeInstallSerializationFilter(serviceClass)
|
||||
|
||||
val constructor = serviceClass.getDeclaredConstructor(ServiceHubInternal::class.java, PublicKey::class.java).apply { isAccessible = true }
|
||||
val constructor = serviceClass.getDeclaredConstructor(ServiceHubInternal::class.java, PublicKey::class.java)
|
||||
.apply { isAccessible = true }
|
||||
val service = constructor.newInstance(services, notaryKey) as NotaryService
|
||||
|
||||
service.run {
|
||||
|
@ -1,10 +1,13 @@
|
||||
package net.corda.node.internal
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.DigitalSignatureWithCert
|
||||
import net.corda.core.internal.NamedCacheFactory
|
||||
import net.corda.core.internal.SignedDataWithCert
|
||||
import net.corda.core.internal.notary.HistoricNetworkParameterStorage
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.NetworkParametersStorage
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
@ -37,6 +40,8 @@ interface NetworkParametersStorageInternal : NetworkParametersStorage {
|
||||
* Hash should always be calculated over the serialized bytes.
|
||||
*/
|
||||
fun saveParameters(signedNetworkParameters: SignedDataWithCert<NetworkParameters>)
|
||||
|
||||
fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate)
|
||||
}
|
||||
|
||||
class DBNetworkParametersStorage(
|
||||
@ -45,7 +50,7 @@ class DBNetworkParametersStorage(
|
||||
// TODO It's very inefficient solution (at least at the beginning when node joins without historical data)
|
||||
// We could have historic parameters endpoint or always add parameters as an attachment to the transaction.
|
||||
private val networkMapClient: NetworkMapClient?
|
||||
) : NetworkParametersStorageInternal, SingletonSerializeAsToken() {
|
||||
) : NetworkParametersStorageInternal, HistoricNetworkParameterStorage, SingletonSerializeAsToken() {
|
||||
private lateinit var trustRoot: X509Certificate
|
||||
|
||||
companion object {
|
||||
@ -71,7 +76,7 @@ class DBNetworkParametersStorage(
|
||||
}
|
||||
}
|
||||
|
||||
fun start(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate) {
|
||||
override fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate) {
|
||||
this.trustRoot = trustRoot
|
||||
saveParameters(currentSignedParameters)
|
||||
_currentHash = currentSignedParameters.raw.hash
|
||||
@ -118,6 +123,22 @@ class DBNetworkParametersStorage(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to obtain notary info from the current network parameters. If not found, look through historical ones.
|
||||
*/
|
||||
override fun getHistoricNotary(party: Party): NotaryInfo? {
|
||||
val currentParameters = lookup(currentHash)
|
||||
?: throw IllegalStateException("Unable to obtain NotaryInfo – current network parameters not set.")
|
||||
val inCurrentParams = currentParameters.notaries.singleOrNull { it.identity == party }
|
||||
if (inCurrentParams == null) {
|
||||
val inOldParams = hashToParameters.allPersisted().flatMap { (_, signedParams) ->
|
||||
val parameters = signedParams.raw.deserialize()
|
||||
parameters.notaries.asSequence()
|
||||
}.firstOrNull { it.identity == party }
|
||||
return inOldParams
|
||||
} else return inCurrentParams
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "${NODE_DATABASE_PREFIX}network_parameters")
|
||||
class PersistentNetworkParameters(
|
||||
|
@ -29,13 +29,13 @@ class NotaryChangeHandler(otherSideSession: FlowSession) : AbstractStateReplacem
|
||||
override fun verifyProposal(stx: SignedTransaction, proposal: AbstractStateReplacementFlow.Proposal<Party>) {
|
||||
val state = proposal.stateRef
|
||||
val proposedTx = stx.resolveNotaryChangeTransaction(serviceHub)
|
||||
val newNotary = proposal.modification
|
||||
// TODO: Right now all nodes will automatically approve the notary change. We need to figure out if stricter controls are necessary.
|
||||
|
||||
if (state !in proposedTx.inputs.map { it.ref }) {
|
||||
throw StateReplacementException("The proposed state $state is not in the proposed transaction inputs")
|
||||
}
|
||||
|
||||
// TODO: load and compare against notary whitelist from config. Remove the check below
|
||||
val newNotary = proposal.modification
|
||||
val isNotary = serviceHub.networkMapCache.isNotary(newNotary)
|
||||
if (!isNotary) {
|
||||
throw StateReplacementException("The proposed node $newNotary does not run a Notary service")
|
||||
|
@ -1,11 +1,17 @@
|
||||
package net.corda.node.services.transactions
|
||||
|
||||
import net.corda.core.contracts.ComponentGroupEnum
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.NotarisationPayload
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.notary.NotaryInternalException
|
||||
import net.corda.core.internal.notary.NotaryServiceFlow
|
||||
import net.corda.core.internal.notary.SinglePartyNotaryService
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.transactions.ContractUpgradeFilteredTransaction
|
||||
import net.corda.core.transactions.CoreTransaction
|
||||
import net.corda.core.transactions.FilteredTransaction
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
import java.time.Duration
|
||||
@ -19,25 +25,77 @@ import java.time.Duration
|
||||
* undo the commit of the input states (the exact mechanism still needs to be worked out).
|
||||
*/
|
||||
class NonValidatingNotaryFlow(otherSideSession: FlowSession, service: SinglePartyNotaryService, etaThreshold: Duration) : NotaryServiceFlow(otherSideSession, service, etaThreshold) {
|
||||
private val minPlatformVersion get() = serviceHub.networkParameters.minimumPlatformVersion
|
||||
|
||||
override fun extractParts(requestPayload: NotarisationPayload): TransactionParts {
|
||||
val tx = requestPayload.coreTransaction
|
||||
return when (tx) {
|
||||
is FilteredTransaction -> {
|
||||
tx.apply {
|
||||
verify()
|
||||
checkAllComponentsVisible(ComponentGroupEnum.INPUTS_GROUP)
|
||||
checkAllComponentsVisible(ComponentGroupEnum.TIMEWINDOW_GROUP)
|
||||
checkAllComponentsVisible(ComponentGroupEnum.REFERENCES_GROUP)
|
||||
if(serviceHub.networkParameters.minimumPlatformVersion >= 4) checkAllComponentsVisible(ComponentGroupEnum.PARAMETERS_GROUP)
|
||||
}
|
||||
TransactionParts(tx.id, tx.inputs, tx.timeWindow, tx.notary, tx.references, networkParametersHash = tx.networkParametersHash)
|
||||
}
|
||||
is FilteredTransaction -> TransactionParts(tx.id, tx.inputs, tx.timeWindow, tx.notary, tx.references, networkParametersHash = tx.networkParametersHash)
|
||||
is ContractUpgradeFilteredTransaction,
|
||||
is NotaryChangeWireTransaction -> TransactionParts(tx.id, tx.inputs, null, tx.notary, networkParametersHash = tx.networkParametersHash)
|
||||
else -> {
|
||||
throw IllegalArgumentException("Received unexpected transaction type: ${tx::class.java.simpleName}," +
|
||||
"expected either ${FilteredTransaction::class.java.simpleName} or ${NotaryChangeWireTransaction::class.java.simpleName}")
|
||||
}
|
||||
else -> throw unexpectedTransactionType(tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun verifyTransaction(requestPayload: NotarisationPayload) {
|
||||
val tx = requestPayload.coreTransaction
|
||||
try {
|
||||
when (tx) {
|
||||
is FilteredTransaction -> {
|
||||
tx.apply {
|
||||
verify()
|
||||
checkAllComponentsVisible(ComponentGroupEnum.INPUTS_GROUP)
|
||||
checkAllComponentsVisible(ComponentGroupEnum.TIMEWINDOW_GROUP)
|
||||
checkAllComponentsVisible(ComponentGroupEnum.REFERENCES_GROUP)
|
||||
if (minPlatformVersion >= 4) checkAllComponentsVisible(ComponentGroupEnum.PARAMETERS_GROUP)
|
||||
}
|
||||
val notary = tx.notary ?: throw IllegalArgumentException("Transaction does not specify a notary.")
|
||||
checkNotaryWhitelisted(notary, tx.networkParametersHash)
|
||||
}
|
||||
is ContractUpgradeFilteredTransaction -> {
|
||||
checkNotaryWhitelisted(tx.notary, tx.networkParametersHash)
|
||||
}
|
||||
is NotaryChangeWireTransaction -> {
|
||||
checkNotaryWhitelisted(tx.newNotary, tx.networkParametersHash)
|
||||
}
|
||||
else -> throw unexpectedTransactionType(tx)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw NotaryInternalException(NotaryError.TransactionInvalid(e))
|
||||
}
|
||||
}
|
||||
|
||||
/** Make sure the transaction notary is part of the network parameter whitelist. */
|
||||
private fun checkNotaryWhitelisted(notary: Party, attachedParameterHash: SecureHash?) {
|
||||
if (minPlatformVersion >= 4) {
|
||||
// Expecting network parameters to be attached for platform version 4 or later.
|
||||
if (attachedParameterHash == null) {
|
||||
throw IllegalArgumentException("Transaction must contain network parameters.")
|
||||
}
|
||||
val attachedParameters = serviceHub.networkParametersStorage.lookup(attachedParameterHash)
|
||||
?: throw IllegalStateException("Unable to resolve network parameters from hash: $attachedParameterHash")
|
||||
|
||||
checkInWhitelist(attachedParameters, notary)
|
||||
} else {
|
||||
// Using current network parameters for platform versions 3 or earlier.
|
||||
val defaultParams = with(serviceHub.networkParametersStorage) {
|
||||
lookup(currentHash)
|
||||
?: throw IllegalStateException("Unable to verify whether the notary $notary is whitelisted: current network parameters not set.")
|
||||
}
|
||||
checkInWhitelist(defaultParams, notary)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkInWhitelist(networkParameters: NetworkParameters, notary: Party) {
|
||||
val notaryWhitelist = networkParameters.notaries.map { it.identity }
|
||||
|
||||
check(notary in notaryWhitelist) {
|
||||
"Notary specified by the transaction ($notary) is not on the network parameter whitelist: ${notaryWhitelist.joinToString()}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun unexpectedTransactionType(tx: CoreTransaction): IllegalArgumentException {
|
||||
return IllegalArgumentException("Received unexpected transaction type: ${tx::class.java.simpleName}," +
|
||||
"expected either ${FilteredTransaction::class.java.simpleName} or ${NotaryChangeWireTransaction::class.java.simpleName}")
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ open class ValidatingNotaryFlow(otherSideSession: FlowSession, service: SinglePa
|
||||
resolveAndContractVerify(stx)
|
||||
verifySignatures(stx)
|
||||
} catch (e: Exception) {
|
||||
throw NotaryInternalException(NotaryError.TransactionInvalid(e))
|
||||
throw NotaryInternalException(NotaryError.TransactionInvalid(e))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,7 +497,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
@Test
|
||||
fun `dependency with error on buyer side`() {
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
mockNet.defaultNotaryNode.services.ledger(DUMMY_NOTARY) {
|
||||
mockNet.defaultNotaryNode.services.ledger(mockNet.defaultNotaryIdentity) {
|
||||
runWithError(true, false, "at least one cash input")
|
||||
}
|
||||
}
|
||||
@ -505,7 +505,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) {
|
||||
@Test
|
||||
fun `dependency with error on seller side`() {
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
mockNet.defaultNotaryNode.services.ledger(DUMMY_NOTARY) {
|
||||
mockNet.defaultNotaryNode.services.ledger(mockNet.defaultNotaryIdentity) {
|
||||
runWithError(false, true, "Issuances have a time-window")
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.flows.NotaryChangeFlow
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
import net.corda.core.flows.StateReplacementException
|
||||
@ -13,11 +15,18 @@ import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.node.*
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.dummyCommand
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.MockNetworkNotarySpec
|
||||
import net.corda.testing.node.MockNodeParameters
|
||||
import net.corda.testing.node.StartedMockNode
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
@ -25,6 +34,9 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class NotaryChangeTests {
|
||||
private val oldNotaryName = CordaX500Name("Old Notary", "Zurich", "CH")
|
||||
private val newNotaryName = CordaX500Name("New Notary", "Zurich", "CH")
|
||||
|
||||
private lateinit var mockNet: MockNetwork
|
||||
private lateinit var oldNotaryNode: StartedMockNode
|
||||
private lateinit var clientNodeA: StartedMockNode
|
||||
@ -35,17 +47,16 @@ class NotaryChangeTests {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
val oldNotaryName = CordaX500Name("Regulator A", "Paris", "FR")
|
||||
mockNet = MockNetwork(
|
||||
notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME), MockNetworkNotarySpec(oldNotaryName)),
|
||||
notarySpecs = listOf(MockNetworkNotarySpec(oldNotaryName), MockNetworkNotarySpec(newNotaryName)),
|
||||
cordappPackages = listOf("net.corda.testing.contracts")
|
||||
)
|
||||
clientNodeA = mockNet.createNode(MockNodeParameters(legalName = ALICE_NAME))
|
||||
clientNodeB = mockNet.createNode(MockNodeParameters(legalName = BOB_NAME))
|
||||
clientA = clientNodeA.info.singleIdentity()
|
||||
oldNotaryNode = mockNet.notaryNodes[1]
|
||||
newNotaryParty = clientNodeA.services.networkMapCache.getNotary(DUMMY_NOTARY_NAME)!!
|
||||
oldNotaryNode = mockNet.notaryNodes[0]
|
||||
oldNotaryParty = clientNodeA.services.networkMapCache.getNotary(oldNotaryName)!!
|
||||
newNotaryParty = clientNodeA.services.networkMapCache.getNotary(newNotaryName)!!
|
||||
}
|
||||
|
||||
@After
|
||||
@ -77,11 +88,13 @@ class NotaryChangeTests {
|
||||
assertEquals(loadedStateA, loadedStateB)
|
||||
}
|
||||
|
||||
// TODO: Re-enable the test when parameter currentness checks are in place, ENT-2666.
|
||||
@Test
|
||||
@Ignore
|
||||
fun `should throw when a participant refuses to change Notary`() {
|
||||
val state = issueMultiPartyState(clientNodeA, clientNodeB, oldNotaryNode, oldNotaryParty)
|
||||
val newEvilNotary = getTestPartyAndCertificate(CordaX500Name(organisation = "Evil R3", locality = "London", country = "GB"), generateKeyPair().public)
|
||||
val flow = NotaryChangeFlow(state, newEvilNotary.party)
|
||||
|
||||
val flow = NotaryChangeFlow(state, newNotaryParty)
|
||||
val future = clientNodeA.startFlow(flow)
|
||||
|
||||
mockNet.runNetwork()
|
||||
@ -112,8 +125,10 @@ class NotaryChangeTests {
|
||||
assertTrue(originalOutputs.size == newOutputs.size && originalOutputs.containsAll(newOutputs))
|
||||
|
||||
// Check if encumbrance linking between states has not changed.
|
||||
val originalLinkedStates = issueTx.outputs.asSequence().filter { it.encumbrance != null }.map { Pair(it.data, issueTx.outputs[it.encumbrance!!].data) }.toSet()
|
||||
val notaryChangeLinkedStates = notaryChangeTx.outputs.asSequence().filter { it.encumbrance != null }.map { Pair(it.data, notaryChangeTx.outputs[it.encumbrance!!].data) }.toSet()
|
||||
val originalLinkedStates = issueTx.outputs.asSequence().filter { it.encumbrance != null }
|
||||
.map { Pair(it.data, issueTx.outputs[it.encumbrance!!].data) }.toSet()
|
||||
val notaryChangeLinkedStates = notaryChangeTx.outputs.asSequence().filter { it.encumbrance != null }
|
||||
.map { Pair(it.data, notaryChangeTx.outputs[it.encumbrance!!].data) }.toSet()
|
||||
|
||||
assertTrue { originalLinkedStates.size == notaryChangeLinkedStates.size && originalLinkedStates.containsAll(notaryChangeLinkedStates) }
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ class DBNetworkParametersStorageTest {
|
||||
networkMapClient = createMockNetworkMapClient()
|
||||
nodeParametersStorage = DBNetworkParametersStorage(TestingNamedCacheFactory(), database, networkMapClient).apply {
|
||||
database.transaction {
|
||||
start(netParams1, DEV_ROOT_CA.certificate)
|
||||
setCurrentParameters(netParams1, DEV_ROOT_CA.certificate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,21 @@
|
||||
package net.corda.node.services.persistence
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.`issued by`
|
||||
import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.flows.AbstractCashFlow
|
||||
import net.corda.finance.flows.CashIssueFlow
|
||||
import net.corda.finance.issuedBy
|
||||
import net.corda.node.internal.NetworkParametersStorageInternal
|
||||
import net.corda.node.services.identity.PersistentIdentityService
|
||||
import net.corda.node.services.keys.E2ETestKeyManagementService
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
@ -17,6 +26,7 @@ import net.corda.testing.node.StartedMockNode
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class HibernateColumnConverterTests {
|
||||
@ -25,6 +35,20 @@ class HibernateColumnConverterTests {
|
||||
private lateinit var bankOfCorda: Party
|
||||
private lateinit var notary: Party
|
||||
|
||||
class TestCashIssueFlow(private val amount: Amount<Currency>,
|
||||
private val issuerBankPartyRef: OpaqueBytes,
|
||||
private val notary: Party) : AbstractCashFlow<AbstractCashFlow.Result>(tracker()) {
|
||||
@Suspendable
|
||||
override fun call(): AbstractCashFlow.Result {
|
||||
val builder = TransactionBuilder(notary)
|
||||
val issuer = ourIdentity.ref(issuerBankPartyRef)
|
||||
val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), ourIdentity, notary)
|
||||
val tx = serviceHub.signInitialTransaction(builder, signers)
|
||||
serviceHub.recordTransactions(tx)
|
||||
return Result(tx, ourIdentity)
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
fun start() {
|
||||
mockNet = MockNetwork(
|
||||
@ -59,10 +83,10 @@ class HibernateColumnConverterTests {
|
||||
val newKeyAndCert = keyService.freshKeyAndCert(bankOfCordaNode.info.legalIdentitiesAndCerts[0], false)
|
||||
val randomNotary = Party(BOC_NAME, newKeyAndCert.owningKey)
|
||||
|
||||
val future = bankOfCordaNode.startFlow(CashIssueFlow(expected, ref, randomNotary))
|
||||
val future = bankOfCordaNode.startFlow(TestCashIssueFlow(expected, ref, randomNotary))
|
||||
mockNet.runNetwork()
|
||||
val issueTx = future.getOrThrow().stx
|
||||
val output = issueTx.tx.outputsOfType<Cash.State>().single()
|
||||
assertEquals(expected.`issued by`(bankOfCorda.ref(ref)), output.amount)
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ class NotaryServiceTests {
|
||||
mockNet = InternalMockNetwork(
|
||||
cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"),
|
||||
notarySpecs = listOf(MockNetworkNotarySpec(DUMMY_NOTARY_NAME, validating = false)),
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
initialNetworkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
)
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
notaryServices = mockNet.defaultNotaryNode.services //TODO get rid of that
|
||||
|
@ -0,0 +1,257 @@
|
||||
package net.corda.node.services.transactions
|
||||
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.flows.NotaryChangeFlow
|
||||
import net.corda.core.flows.NotaryError
|
||||
import net.corda.core.flows.NotaryException
|
||||
import net.corda.core.flows.NotaryFlow
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.NotaryChangeTransactionBuilder
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.transactions.NotaryChangeLedgerTransaction
|
||||
import net.corda.core.transactions.NotaryChangeWireTransaction
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.dummyCommand
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.MockNetworkNotarySpec
|
||||
import net.corda.testing.node.internal.*
|
||||
import org.junit.After
|
||||
import org.junit.Assume
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.security.KeyPair
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class NotaryWhitelistTests(
|
||||
/** Specified whether validating notaries should be used. */
|
||||
private val isValidating: Boolean
|
||||
) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "Validating = {0}")
|
||||
fun data(): Collection<Boolean> = listOf(true, false)
|
||||
}
|
||||
|
||||
private val oldNotaryName = CordaX500Name("Old Notary", "Zurich", "CH")
|
||||
private val newNotaryName = CordaX500Name("New Notary", "Zurich", "CH")
|
||||
|
||||
private lateinit var mockNet: InternalMockNetwork
|
||||
private lateinit var aliceNode: TestStartedNode
|
||||
private lateinit var oldNotary: Party
|
||||
private lateinit var newNotary: Party
|
||||
private lateinit var alice: Party
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = InternalMockNetwork(
|
||||
cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"),
|
||||
notarySpecs = listOf(MockNetworkNotarySpec(oldNotaryName, validating = isValidating), MockNetworkNotarySpec(newNotaryName, validating = isValidating)),
|
||||
initialNetworkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
)
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
oldNotary = mockNet.notaryNodes[0].info.legalIdentities.last()
|
||||
newNotary = mockNet.notaryNodes[1].info.legalIdentities.last()
|
||||
|
||||
alice = aliceNode.services.myInfo.singleIdentity()
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
mockNet.stopNodes()
|
||||
}
|
||||
|
||||
/**
|
||||
* This test verifies network merging support: when a sub-zone merges into another zone, and _its states are deemed to be low-risk_ by
|
||||
* the network operator, the old notary service can temporarily operate on the new zone to facilitate notary change requests (even though
|
||||
* it's not whitelisted for regular use).
|
||||
*/
|
||||
@Test
|
||||
fun `can perform notary change on a de-listed notary`() {
|
||||
// Issue a state using the old notary. It is currently whitelisted.
|
||||
val stateFakeNotary = issueStateOnOldNotary(oldNotary)
|
||||
|
||||
// Remove old notary from the whitelist
|
||||
val parameters = aliceNode.services.networkParameters
|
||||
val newParameters = removeOldNotary(parameters)
|
||||
mockNet.nodes.forEach {
|
||||
(it.networkParametersStorage as MockNetworkParametersStorage).setCurrentParametersUnverified(newParameters)
|
||||
}
|
||||
|
||||
// Re-point the state to the remaining whitelisted notary. The transaction itself should be considered valid, even though the old notary is not whitelisted.
|
||||
val futureChange = aliceNode.services.startFlow(NotaryChangeFlow(stateFakeNotary, newNotary)).resultFuture
|
||||
mockNet.runNetwork()
|
||||
val newSTate = futureChange.getOrThrow()
|
||||
|
||||
// Create a valid transaction consuming the re-pointed state.
|
||||
val validTxBuilder = TransactionBuilder(newNotary)
|
||||
.addInputState(newSTate)
|
||||
.addCommand(dummyCommand(alice.owningKey))
|
||||
val validStx = aliceNode.services.signInitialTransaction(validTxBuilder)
|
||||
|
||||
// The transaction verifies.
|
||||
validStx.verify(aliceNode.services, false)
|
||||
|
||||
// Notarisation should succeed.
|
||||
val future = runNotaryClient(validStx)
|
||||
future.getOrThrow()
|
||||
}
|
||||
|
||||
/**
|
||||
* Following on from the previous one, this test verifies that a non-whitelisted notary cannot be used for regular transactions.
|
||||
*/
|
||||
@Test
|
||||
fun `can't perform a regular transaction on a de-listed notary`() {
|
||||
// Issue a state using the old notary. It is currently whitelisted.
|
||||
val state = issueStateOnOldNotary(oldNotary)
|
||||
|
||||
// Remove old notary from the whitelist
|
||||
val parameters = aliceNode.services.networkParameters
|
||||
val newParameters = removeOldNotary(parameters)
|
||||
mockNet.nodes.forEach {
|
||||
(it.networkParametersStorage as MockNetworkParametersStorage).setCurrentParametersUnverified(newParameters)
|
||||
}
|
||||
|
||||
// Create a valid transaction consuming the state.
|
||||
val validTxBuilder = TransactionBuilder(oldNotary)
|
||||
.addInputState(state)
|
||||
.addOutputState(state.state.copy())
|
||||
.addCommand(dummyCommand(alice.owningKey))
|
||||
val validStx = aliceNode.services.signInitialTransaction(validTxBuilder)
|
||||
|
||||
// The transaction does not verify as the notary is no longer whitelisted.
|
||||
assertFailsWith<IllegalStateException> {
|
||||
validStx.verify(aliceNode.services, false)
|
||||
}
|
||||
|
||||
// Notarisation should fail (assuming the unlisted notary is not malicious).
|
||||
val future = runNotaryClient(validStx)
|
||||
val ex = assertFailsWith(NotaryException::class) {
|
||||
future.getOrThrow()
|
||||
}
|
||||
assert(ex.error is NotaryError.TransactionInvalid)
|
||||
assertEquals(validStx.id, ex.txId)
|
||||
}
|
||||
|
||||
private fun removeOldNotary(parameters: NetworkParameters): NetworkParameters {
|
||||
val newParameters = parameters.copy(notaries = parameters.notaries.drop(1))
|
||||
assert(newParameters.notaries.none { it.identity == oldNotary })
|
||||
assert(newParameters.notaries.any { it.identity == newNotary })
|
||||
return newParameters
|
||||
}
|
||||
|
||||
private fun issueStateOnOldNotary(oldNotaryParty: Party): StateAndRef<DummyContract.State> {
|
||||
val fakeTxBuilder = DummyContract
|
||||
.generateInitial(Random().nextInt(), oldNotaryParty, alice.ref(0))
|
||||
.setTimeWindow(Instant.now(), 30.seconds)
|
||||
val fakeStx = aliceNode.services.signInitialTransaction(fakeTxBuilder)
|
||||
|
||||
val sigs = runNotaryClient(fakeStx).getOrThrow()
|
||||
aliceNode.services.validatedTransactions.addTransaction(fakeStx + sigs)
|
||||
return fakeStx.tx.outRef(0)
|
||||
}
|
||||
|
||||
private fun runNotaryClient(stx: SignedTransaction): CordaFuture<List<TransactionSignature>> {
|
||||
val flow = NotaryFlow.Client(stx)
|
||||
val future = aliceNode.services.startFlow(flow).resultFuture
|
||||
mockNet.runNetwork()
|
||||
return future
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should reject transaction when a dependency does not contain notary in whitelist`() {
|
||||
Assume.assumeTrue(isValidating) // Skip the test for non-validating notaries
|
||||
|
||||
val fakeNotaryKeyPair = generateKeyPair()
|
||||
val fakeNotaryParty = Party(DUMMY_NOTARY_NAME.copy(organisation = "Fake notary"), fakeNotaryKeyPair.public)
|
||||
|
||||
// Issue a state using an unlisted notary. This transaction should not verify when checked by counterparties.
|
||||
val stateFakeNotary = issueStateWithFakeNotary(fakeNotaryParty, fakeNotaryKeyPair)
|
||||
|
||||
// Re-point the state to the whitelisted notary. The transaction itself should be considered valid, even though the old notary is not whitelisted.
|
||||
val notaryChangeLtx = changeNotary(stateFakeNotary, fakeNotaryParty, fakeNotaryKeyPair)
|
||||
|
||||
// Create a valid transaction consuming the re-pointed state.
|
||||
val inputStateValidNotary = notaryChangeLtx.outRef<DummyContract.State>(0)
|
||||
val validTxBuilder = TransactionBuilder(oldNotary)
|
||||
.addInputState(inputStateValidNotary)
|
||||
.addCommand(dummyCommand(alice.owningKey))
|
||||
val validStx = aliceNode.services.signInitialTransaction(validTxBuilder)
|
||||
|
||||
// The transaction itself verifies, as no resolution is done here.
|
||||
validStx.verify(aliceNode.services, false)
|
||||
|
||||
val future = runNotaryClient(validStx)
|
||||
|
||||
// The notary should reject this transaction – the issue transaction in the dependencies should not verify.
|
||||
val ex = assertFailsWith(NotaryException::class) {
|
||||
future.getOrThrow()
|
||||
}
|
||||
assert(ex.error is NotaryError.TransactionInvalid)
|
||||
assertEquals(validStx.id, ex.txId)
|
||||
}
|
||||
|
||||
private fun issueStateWithFakeNotary(fakeNotaryParty: Party, fakeNotaryKeyPair: KeyPair): StateAndRef<DummyContract.State> {
|
||||
val fakeTxBuilder = DummyContract
|
||||
.generateInitial(Random().nextInt(), fakeNotaryParty, alice.ref(0))
|
||||
.setTimeWindow(Instant.now(), 30.seconds)
|
||||
val fakeStx = aliceNode.services.signInitialTransaction(fakeTxBuilder)
|
||||
val notarySig = getNotarySig(fakeStx, fakeNotaryKeyPair)
|
||||
aliceNode.services.validatedTransactions.addTransaction(fakeStx + notarySig)
|
||||
return fakeStx.tx.outRef(0)
|
||||
}
|
||||
|
||||
/** Changes the notary service to [notary]. Does not actually communicate with a notary. */
|
||||
private fun changeNotary(inputState: StateAndRef<DummyContract.State>, fakeNotaryParty: Party, fakeNotaryKeyPair: KeyPair): NotaryChangeLedgerTransaction {
|
||||
val notaryChangeTx = NotaryChangeTransactionBuilder(
|
||||
listOf(inputState.ref),
|
||||
fakeNotaryParty,
|
||||
oldNotary,
|
||||
aliceNode.services.networkParametersStorage.currentHash
|
||||
).build()
|
||||
|
||||
val notaryChangeAliceSig = getAliceSig(notaryChangeTx)
|
||||
|
||||
val notaryChangeNotarySig = run {
|
||||
val metadata = SignatureMetadata(4, Crypto.findSignatureScheme(fakeNotaryParty.owningKey).schemeNumberID)
|
||||
val data = SignableData(notaryChangeTx.id, metadata)
|
||||
fakeNotaryKeyPair.sign(data)
|
||||
}
|
||||
val notaryChangeStx = SignedTransaction(notaryChangeTx, listOf(notaryChangeAliceSig, notaryChangeNotarySig))
|
||||
|
||||
aliceNode.services.validatedTransactions.addTransaction(notaryChangeStx)
|
||||
|
||||
// Resolving the ledger transaction verifies the whitelist checking logic – for notary change transactions the old notary
|
||||
// does not need to be whitelisted.
|
||||
val notaryChangeLtx = notaryChangeStx.resolveNotaryChangeTransaction(aliceNode.services)
|
||||
notaryChangeLtx.verifyRequiredSignatures()
|
||||
return notaryChangeLtx
|
||||
}
|
||||
|
||||
private fun getAliceSig(notaryChangeTx: NotaryChangeWireTransaction): TransactionSignature {
|
||||
val metadata = SignatureMetadata(4, Crypto.findSignatureScheme(alice.owningKey).schemeNumberID)
|
||||
val data = SignableData(notaryChangeTx.id, metadata)
|
||||
return aliceNode.services.keyManagementService.sign(data, alice.owningKey)
|
||||
}
|
||||
|
||||
private fun getNotarySig(fakeStx: SignedTransaction, fakeNotaryKeyPair: KeyPair): TransactionSignature {
|
||||
val metadata = SignatureMetadata(4, Crypto.findSignatureScheme(fakeNotaryKeyPair.public).schemeNumberID)
|
||||
val data = SignableData(fakeStx.id, metadata)
|
||||
return fakeNotaryKeyPair.sign(data)
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package net.corda.node.services.transactions
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
@ -58,7 +59,7 @@ class ResolveStatePointersTest {
|
||||
cordappPackages = cordapps,
|
||||
identityService = makeTestIdentityService(notary.identity, myself.identity),
|
||||
initialIdentity = myself,
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4, notaries = listOf(NotaryInfo(notary.party, true)))
|
||||
)
|
||||
services = databaseAndServices.second
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package net.corda.node.services.transactions
|
||||
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.contracts.Command
|
||||
import net.corda.core.contracts.PrivacySalt
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.crypto.*
|
||||
@ -15,7 +14,6 @@ import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.seconds
|
||||
@ -51,7 +49,7 @@ class ValidatingNotaryServiceTests {
|
||||
@Before
|
||||
fun setup() {
|
||||
mockNet = InternalMockNetwork(cordappsForAllNodes = cordappsForPackages("net.corda.testing.contracts"),
|
||||
networkParameters = testNetworkParameters(minimumPlatformVersion = 4))
|
||||
initialNetworkParameters = testNetworkParameters(minimumPlatformVersion = 4))
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
notaryNode = mockNet.defaultNotaryNode
|
||||
notary = mockNet.defaultNotaryIdentity
|
||||
@ -116,6 +114,7 @@ class ValidatingNotaryServiceTests {
|
||||
)
|
||||
val stx = SignedTransaction(wtx, listOf(sig))
|
||||
assertThat(stx.networkParametersHash).isNull()
|
||||
|
||||
val future = runNotaryClient(stx)
|
||||
val ex = assertFailsWith(NotaryException::class) { future.getOrThrow() }
|
||||
val notaryError = ex.error as NotaryError.TransactionInvalid
|
||||
|
@ -11,6 +11,7 @@ import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.*
|
||||
import net.corda.core.internal.NotaryChangeTransactionBuilder
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.StatesToRecord
|
||||
import net.corda.core.node.services.*
|
||||
import net.corda.core.node.services.vault.PageSpecification
|
||||
@ -91,7 +92,7 @@ class NodeVaultServiceTest {
|
||||
@Before
|
||||
fun setUp() {
|
||||
LogHelper.setLevel(NodeVaultService::class)
|
||||
val parameters = testNetworkParameters()
|
||||
val parameters = testNetworkParameters(notaries = listOf(NotaryInfo(DUMMY_NOTARY, true)))
|
||||
val databaseAndServices = MockServices.makeTestDatabaseAndMockServices(
|
||||
cordappPackages,
|
||||
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, DUMMY_CASH_ISSUER_IDENTITY, DUMMY_NOTARY_IDENTITY),
|
||||
|
@ -102,7 +102,8 @@ class VaultSoftLockManagerTest {
|
||||
|
||||
class ClientLogic(nodePair: NodePair, val state: ContractState) : NodePair.AbstractClientLogic<List<ContractState>>(nodePair) {
|
||||
override fun callImpl(): List<ContractState> {
|
||||
val stx = serviceHub.signInitialTransaction(TransactionBuilder(notary = ourIdentity).apply {
|
||||
val notary = serviceHub.networkParameters.notaries.first().identity
|
||||
val stx = serviceHub.signInitialTransaction(TransactionBuilder(notary).apply {
|
||||
addOutputState(state, ContractImpl::class.jvmName)
|
||||
addCommand(CommandDataImpl, ourIdentity.owningKey)
|
||||
})
|
||||
|
@ -11,7 +11,6 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.packageName
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.node.services.queryBy
|
||||
@ -24,6 +23,8 @@ import net.corda.finance.contracts.asset.Cash
|
||||
import net.corda.finance.contracts.getCashBalance
|
||||
import net.corda.finance.schemas.CashSchemaV1
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.common.internal.addNotary
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.internal.LogHelper
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
@ -74,16 +75,21 @@ class VaultWithCashTest {
|
||||
@Before
|
||||
fun setUp() {
|
||||
LogHelper.setLevel(VaultWithCashTest::class)
|
||||
|
||||
val networkParameters = testNetworkParameters().addNotary(DUMMY_NOTARY)
|
||||
val databaseAndServices = makeTestDatabaseAndMockServices(
|
||||
cordappPackages,
|
||||
makeTestIdentityService(MEGA_CORP_IDENTITY, MINI_CORP_IDENTITY, dummyCashIssuer.identity, dummyNotary.identity),
|
||||
TestIdentity(MEGA_CORP.name, servicesKey),
|
||||
networkParameters,
|
||||
moreKeys = *arrayOf(dummyNotary.keyPair))
|
||||
database = databaseAndServices.first
|
||||
services = databaseAndServices.second
|
||||
vaultFiller = VaultFiller(services, dummyNotary)
|
||||
issuerServices = MockServices(cordappPackages, dummyCashIssuer, rigorousMock(), MEGA_CORP_KEY)
|
||||
notaryServices = MockServices(cordappPackages, dummyNotary, rigorousMock<IdentityService>())
|
||||
|
||||
|
||||
issuerServices = MockServices(cordappPackages, dummyCashIssuer, rigorousMock(), networkParameters, MEGA_CORP_KEY)
|
||||
notaryServices = MockServices(cordappPackages, dummyNotary, rigorousMock(), networkParameters)
|
||||
notary = notaryServices.myInfo.legalIdentitiesAndCerts.single().party
|
||||
}
|
||||
|
||||
|
@ -12,18 +12,10 @@ import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.EUR
|
||||
import net.corda.finance.contracts.AccrualAdjustment
|
||||
import net.corda.finance.contracts.BusinessCalendar
|
||||
import net.corda.finance.contracts.DateRollConvention
|
||||
import net.corda.finance.contracts.DayCountBasisDay
|
||||
import net.corda.finance.contracts.DayCountBasisYear
|
||||
import net.corda.finance.contracts.Expression
|
||||
import net.corda.finance.contracts.Fix
|
||||
import net.corda.finance.contracts.FixOf
|
||||
import net.corda.finance.contracts.Frequency
|
||||
import net.corda.finance.contracts.PaymentRule
|
||||
import net.corda.finance.contracts.Tenor
|
||||
import net.corda.finance.contracts.*
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.common.internal.addNotary
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.TestIdentity
|
||||
@ -47,12 +39,9 @@ private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB"))
|
||||
private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB"))
|
||||
private val ORACLE_PUBKEY = TestIdentity(CordaX500Name("Oracle", "London", "GB")).publicKey
|
||||
private val DUMMY_NOTARY get() = dummyNotary.party
|
||||
private val DUMMY_NOTARY_KEY get() = dummyNotary.keyPair
|
||||
private val MEGA_CORP get() = megaCorp.party
|
||||
private val MEGA_CORP_KEY get() = megaCorp.keyPair
|
||||
private val MEGA_CORP_PUBKEY get() = megaCorp.publicKey
|
||||
private val MINI_CORP get() = miniCorp.party
|
||||
private val MINI_CORP_KEY get() = miniCorp.keyPair
|
||||
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
||||
return when (irsSelect) {
|
||||
1 -> {
|
||||
@ -228,7 +217,6 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
||||
)
|
||||
|
||||
return InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common, oracle = DUMMY_PARTY)
|
||||
|
||||
}
|
||||
else -> TODO("IRS number $irsSelect not defined")
|
||||
}
|
||||
@ -238,14 +226,20 @@ class IRSTests {
|
||||
@Rule
|
||||
@JvmField
|
||||
val testSerialization = SerializationEnvironmentRule()
|
||||
private val megaCorpServices = MockServices(listOf("net.corda.irs.contract"), MEGA_CORP.name, rigorousMock(), MEGA_CORP_KEY)
|
||||
private val miniCorpServices = MockServices(listOf("net.corda.irs.contract"), MINI_CORP.name, rigorousMock(), MINI_CORP_KEY)
|
||||
private val notaryServices = MockServices(listOf("net.corda.irs.contract"), DUMMY_NOTARY.name, rigorousMock(), DUMMY_NOTARY_KEY)
|
||||
private val ledgerServices
|
||||
get() = MockServices(emptyList(), MEGA_CORP.name, rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
|
||||
doReturn(null).whenever(it).partyFromKey(ORACLE_PUBKEY)
|
||||
})
|
||||
private val cordappPackages = listOf("net.corda.irs.contract")
|
||||
private val networkParameters = testNetworkParameters().addNotary(dummyNotary.party)
|
||||
private val megaCorpServices = MockServices(cordappPackages, megaCorp, rigorousMock(), networkParameters, megaCorp.keyPair)
|
||||
private val miniCorpServices = MockServices(cordappPackages, miniCorp, rigorousMock(), networkParameters, miniCorp.keyPair)
|
||||
private val notaryServices = MockServices(cordappPackages, dummyNotary, rigorousMock(), networkParameters, dummyNotary.keyPair)
|
||||
private val ledgerServices = MockServices(
|
||||
emptyList(),
|
||||
megaCorp,
|
||||
rigorousMock<IdentityServiceInternal>().also {
|
||||
doReturn(megaCorp.party).whenever(it).partyFromKey(megaCorp.publicKey)
|
||||
doReturn(null).whenever(it).partyFromKey(ORACLE_PUBKEY)
|
||||
},
|
||||
networkParameters
|
||||
)
|
||||
|
||||
@Test
|
||||
fun ok() {
|
||||
@ -346,7 +340,8 @@ class IRSTests {
|
||||
listOf(MEGA_CORP, MINI_CORP).forEach { party ->
|
||||
doReturn(party).whenever(it).partyFromKey(party.owningKey)
|
||||
}
|
||||
})
|
||||
},
|
||||
networkParameters = ledgerServices.networkParameters)
|
||||
var previousTXN = generateIRSTxn(1)
|
||||
previousTXN.toLedgerTransaction(services).verify()
|
||||
services.recordTransactions(previousTXN)
|
||||
@ -408,7 +403,6 @@ class IRSTests {
|
||||
// This does not throw an exception in the test itself; it evaluates the above and they will throw if they do not pass.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a typical transactional history for an IRS.
|
||||
*/
|
||||
@ -583,7 +577,6 @@ class IRSTests {
|
||||
this `fails with` "The termination dates are aligned"
|
||||
}
|
||||
|
||||
|
||||
val modifiedIRS4 = irs.copy(floatingLeg = irs.floatingLeg.copy(effectiveDate = irs.fixedLeg.effectiveDate.minusDays(1)))
|
||||
transaction {
|
||||
attachments(IRS_PROGRAM_ID)
|
||||
@ -594,7 +587,6 @@ class IRSTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `various fixing tests`() {
|
||||
val ld = LocalDate.of(2016, 3, 8)
|
||||
@ -672,7 +664,6 @@ class IRSTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This returns an example of transactions that are grouped by TradeId and then a fixing applied.
|
||||
* It's important to make the tradeID different for two reasons, the hashes will be the same and all sorts of confusion will
|
||||
@ -737,4 +728,4 @@ class IRSTests {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ open class MockNetwork(
|
||||
networkParameters: NetworkParameters = defaultParameters.networkParameters
|
||||
) : this(emptyList(), defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters, cordappsForAllNodes = cordappsForPackages(cordappPackages))
|
||||
|
||||
private val internalMockNetwork: InternalMockNetwork = InternalMockNetwork(defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, networkParameters = networkParameters, cordappsForAllNodes = cordappsForAllNodes)
|
||||
private val internalMockNetwork: InternalMockNetwork = InternalMockNetwork(defaultParameters, networkSendManuallyPumped, threadPerNode, servicePeerAllocationStrategy, notarySpecs, initialNetworkParameters = networkParameters, cordappsForAllNodes = cordappsForAllNodes)
|
||||
|
||||
/** In a mock network, nodes have an incrementing integer ID. Real networks do not have this. Returns the next ID that will be used. */
|
||||
val nextNodeId get(): Int = internalMockNetwork.nextNodeId
|
||||
|
@ -8,8 +8,8 @@ import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.SignedDataWithCert
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.FlowHandle
|
||||
import net.corda.core.messaging.FlowProgressHandle
|
||||
@ -17,12 +17,10 @@ import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
import net.corda.core.node.*
|
||||
import net.corda.core.node.services.*
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.cordapp.CordappLoader
|
||||
import net.corda.node.internal.NetworkParametersStorageInternal
|
||||
import net.corda.node.internal.ServicesForResolutionImpl
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.services.api.*
|
||||
@ -34,6 +32,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.contextTransaction
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.common.internal.addNotary
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.internal.DEV_ROOT_CA
|
||||
import net.corda.testing.internal.MockCordappProvider
|
||||
@ -47,7 +46,6 @@ import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import javax.persistence.EntityManager
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
/**
|
||||
* Returns a simple [InMemoryIdentityService] containing the supplied [identities].
|
||||
@ -67,7 +65,7 @@ open class MockServices private constructor(
|
||||
cordappLoader: CordappLoader,
|
||||
override val validatedTransactions: TransactionStorage,
|
||||
override val identityService: IdentityService,
|
||||
final override val networkParameters: NetworkParameters,
|
||||
private val initialNetworkParameters: NetworkParameters,
|
||||
private val initialIdentity: TestIdentity,
|
||||
private val moreKeys: Array<out KeyPair>
|
||||
) : ServiceHub {
|
||||
@ -118,8 +116,8 @@ open class MockServices private constructor(
|
||||
val database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService::wellKnownPartyFromX500Name, identityService::wellKnownPartyFromAnonymous, schemaService, schemaService.internalSchemas())
|
||||
val mockService = database.transaction {
|
||||
object : MockServices(cordappLoader, identityService, networkParameters, initialIdentity, moreKeys) {
|
||||
override val networkParametersStorage: NetworkParametersStorage = MockNetworkParametersStorage(networkParameters)
|
||||
override val vaultService: VaultService = makeVaultService(schemaService, database)
|
||||
override val networkParametersStorage: NetworkParametersStorage get() = MockNetworkParametersStorage(networkParameters)
|
||||
override fun recordTransactions(statesToRecord: StatesToRecord, txs: Iterable<SignedTransaction>) {
|
||||
ServiceHubInternal.recordTransactions(statesToRecord, txs,
|
||||
validatedTransactions as WritableTransactionStorage,
|
||||
@ -185,6 +183,7 @@ open class MockServices private constructor(
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: Iterable<String>, initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService(), key: KeyPair, vararg moreKeys: KeyPair) :
|
||||
this(cordappPackages, TestIdentity(initialIdentityName, key), identityService, *moreKeys)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] that can't load CorDapp code, which uses the provided identity service
|
||||
* (you can get one from [makeTestIdentityService]) and which represents the given identity.
|
||||
@ -214,6 +213,10 @@ open class MockServices private constructor(
|
||||
constructor(initialIdentityName: CordaX500Name, identityService: IdentityService = makeTestIdentityService())
|
||||
: this(listOf(getCallerPackage(MockServices::class)!!), TestIdentity(initialIdentityName), identityService)
|
||||
|
||||
@JvmOverloads
|
||||
constructor(cordappPackages: List<String>, initialIdentityName: CordaX500Name, identityService: IdentityService, networkParameters: NetworkParameters)
|
||||
: this(cordappPackages, TestIdentity(initialIdentityName), identityService, networkParameters)
|
||||
|
||||
/**
|
||||
* A helper constructor that requires at least one test identity to be registered, and which takes the package of
|
||||
* the caller as the package in which to find app code. This is the most convenient constructor and the one that
|
||||
@ -226,6 +229,13 @@ open class MockServices private constructor(
|
||||
*moreIdentities
|
||||
)
|
||||
|
||||
constructor(firstIdentity: TestIdentity, networkParameters: NetworkParameters, vararg moreIdentities: TestIdentity) : this(
|
||||
listOf(getCallerPackage(MockServices::class)!!),
|
||||
firstIdentity,
|
||||
networkParameters,
|
||||
*moreIdentities
|
||||
)
|
||||
|
||||
constructor(cordappPackages: List<String>, firstIdentity: TestIdentity, vararg moreIdentities: TestIdentity) : this(
|
||||
cordappPackages,
|
||||
firstIdentity,
|
||||
@ -233,6 +243,14 @@ open class MockServices private constructor(
|
||||
firstIdentity.keyPair
|
||||
)
|
||||
|
||||
constructor(cordappPackages: List<String>, firstIdentity: TestIdentity, networkParameters: NetworkParameters, vararg moreIdentities: TestIdentity) : this(
|
||||
cordappPackages,
|
||||
firstIdentity,
|
||||
makeTestIdentityService(*listOf(firstIdentity, *moreIdentities).map { it.identity }.toTypedArray()),
|
||||
networkParameters,
|
||||
firstIdentity.keyPair
|
||||
)
|
||||
|
||||
/**
|
||||
* Create a mock [ServiceHub] which uses the package of the caller to find CorDapp code. It uses a default service
|
||||
* identity.
|
||||
@ -245,6 +263,9 @@ open class MockServices private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override val networkParameters: NetworkParameters
|
||||
get() = networkParametersStorage.run { lookup(currentHash)!! }
|
||||
|
||||
final override val attachments = MockAttachmentStorage()
|
||||
override val keyManagementService: KeyManagementService by lazy { MockKeyManagementService(identityService, *arrayOf(initialIdentity.keyPair) + moreKeys) }
|
||||
override val vaultService: VaultService get() = throw UnsupportedOperationException()
|
||||
@ -257,10 +278,10 @@ open class MockServices private constructor(
|
||||
}
|
||||
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
|
||||
private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments).also {
|
||||
it.start(networkParameters.whitelistedContractImplementations)
|
||||
it.start(initialNetworkParameters.whitelistedContractImplementations)
|
||||
}
|
||||
override val cordappProvider: CordappProvider get() = mockCordappProvider
|
||||
override val networkParametersStorage: NetworkParametersStorage get() = MockNetworkParametersStorage(networkParameters)
|
||||
override val networkParametersStorage: NetworkParametersStorage = MockNetworkParametersStorage(initialNetworkParameters)
|
||||
|
||||
protected val servicesForResolution: ServicesForResolution
|
||||
get() {
|
||||
@ -322,4 +343,4 @@ fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serv
|
||||
}
|
||||
}
|
||||
return MockAppServiceHubImpl(serviceHub, serviceConstructor).serviceInstance
|
||||
}
|
||||
}
|
@ -8,17 +8,12 @@ import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.common.internal.addNotary
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.dsl.EnforceVerifyOrFail
|
||||
import net.corda.testing.dsl.LedgerDSL
|
||||
import net.corda.testing.dsl.LedgerDSLInterpreter
|
||||
import net.corda.testing.dsl.TestLedgerDSLInterpreter
|
||||
import net.corda.testing.dsl.TestTransactionDSLInterpreter
|
||||
import net.corda.testing.dsl.TransactionDSL
|
||||
import net.corda.testing.dsl.TransactionDSLInterpreter
|
||||
import net.corda.testing.dsl.*
|
||||
import net.corda.testing.internal.withTestSerializationEnvIfNotSet
|
||||
import net.corda.testing.node.internal.MockNetworkParametersStorage
|
||||
|
||||
/**
|
||||
* Creates and tests a ledger built by the passed in dsl.
|
||||
@ -28,17 +23,20 @@ fun ServiceHub.ledger(
|
||||
notary: Party = TestIdentity.fresh("ledger notary").party,
|
||||
script: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
|
||||
): LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
|
||||
val serializationExists = try {
|
||||
effectiveSerializationEnv
|
||||
true
|
||||
} catch (e: IllegalStateException) {
|
||||
false
|
||||
val currentParameters = networkParametersStorage.run {
|
||||
lookup(currentHash) ?: throw IllegalStateException("Current network parameters not found, $currentHash")
|
||||
|
||||
}
|
||||
return LedgerDSL(TestLedgerDSLInterpreter(this), notary).apply {
|
||||
if (serializationExists) {
|
||||
if (currentParameters.notaries.none { it.identity == notary }) {
|
||||
// Add the notary to the whitelist. Otherwise no constructed transactions will verify.
|
||||
val newParameters = currentParameters.addNotary(notary)
|
||||
(networkParametersStorage as MockNetworkParametersStorage).setCurrentParametersUnverified(newParameters)
|
||||
}
|
||||
|
||||
return withTestSerializationEnvIfNotSet("ledgerDSL") {
|
||||
val interpreter = TestLedgerDSLInterpreter(this)
|
||||
LedgerDSL(interpreter, notary).apply {
|
||||
script()
|
||||
} else {
|
||||
SerializationEnvironmentRule.run("ledger") { script() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import net.corda.core.utilities.seconds
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.AbstractNode
|
||||
import net.corda.node.internal.InitiatedFlowFactory
|
||||
import net.corda.node.internal.NetworkParametersStorageInternal
|
||||
import net.corda.node.internal.NodeFlowManager
|
||||
import net.corda.node.services.api.FlowStarter
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
@ -138,7 +139,6 @@ interface TestStartedNode {
|
||||
fun <T : FlowLogic<*>> registerInitiatedFlow(initiatedFlowClass: Class<T>, track: Boolean = false): Observable<T>
|
||||
|
||||
fun <T : FlowLogic<*>> registerInitiatedFlow(initiatingFlowClass: Class<out FlowLogic<*>>, initiatedFlowClass: Class<T>, track: Boolean = false): Observable<T>
|
||||
|
||||
}
|
||||
|
||||
open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNetworkParameters(),
|
||||
@ -147,16 +147,20 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
servicePeerAllocationStrategy: InMemoryMessagingNetwork.ServicePeerAllocationStrategy = defaultParameters.servicePeerAllocationStrategy,
|
||||
val notarySpecs: List<MockNetworkNotarySpec> = defaultParameters.notarySpecs,
|
||||
val testDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||
val networkParameters: NetworkParameters = testNetworkParameters(),
|
||||
initialNetworkParameters: NetworkParameters = testNetworkParameters(),
|
||||
val defaultFactory: (MockNodeArgs) -> MockNode = { args -> MockNode(args) },
|
||||
val cordappsForAllNodes: Collection<TestCordapp> = emptySet(),
|
||||
val autoVisibleNodes: Boolean = true) : AutoCloseable {
|
||||
|
||||
var networkParameters: NetworkParameters = initialNetworkParameters
|
||||
private set
|
||||
|
||||
init {
|
||||
// Apache SSHD for whatever reason registers a SFTP FileSystemProvider - which gets loaded by JimFS.
|
||||
// This SFTP support loads BouncyCastle, which we want to avoid.
|
||||
// Please see https://issues.apache.org/jira/browse/SSHD-736 - it's easier then to create our own fork of SSHD
|
||||
SecurityUtils.setAPrioriDisabledProvider("BC", true) // XXX: Why isn't this static?
|
||||
require(networkParameters.notaries.isEmpty()) { "Define notaries using notarySpecs" }
|
||||
require(initialNetworkParameters.notaries.isEmpty()) { "Define notaries using notarySpecs" }
|
||||
}
|
||||
|
||||
var nextNodeId = 0
|
||||
@ -229,8 +233,9 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
try {
|
||||
filesystem.getPath("/nodes").createDirectory()
|
||||
val notaryInfos = generateNotaryIdentities()
|
||||
networkParameters = initialNetworkParameters.copy(notaries = notaryInfos)
|
||||
// The network parameters must be serialised before starting any of the nodes
|
||||
networkParametersCopier = NetworkParametersCopier(networkParameters.copy(notaries = notaryInfos))
|
||||
networkParametersCopier = NetworkParametersCopier(networkParameters)
|
||||
@Suppress("LeakingThis")
|
||||
// Notary nodes need a platform version >= network min platform version.
|
||||
notaryNodes = createNotaries()
|
||||
@ -302,8 +307,6 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
internals.flowManager.registerInitiatedFlow(initiatingFlowClass, initiatedFlowClass)
|
||||
return smm.changes.filter { it is StateMachineManager.Change.Add }.map { it.logic }.ofType(initiatedFlowClass)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
val mockNet = args.network
|
||||
@ -430,8 +433,11 @@ open class InternalMockNetwork(defaultParameters: MockNetworkParameters = MockNe
|
||||
Observable.empty<T>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun makeParametersStorage(): NetworkParametersStorageInternal {
|
||||
return MockNetworkParametersStorage()
|
||||
}
|
||||
}
|
||||
|
||||
fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters()): MockNode {
|
||||
return createUnstartedNode(parameters, defaultFactory)
|
||||
|
@ -1,21 +1,41 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.SignedDataWithCert
|
||||
import net.corda.core.internal.notary.HistoricNetworkParameterStorage
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.node.internal.NetworkParametersStorageInternal
|
||||
import net.corda.nodeapi.internal.network.verifiedNetworkMapCert
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.internal.withTestSerializationEnvIfNotSet
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Instant
|
||||
|
||||
class MockNetworkParametersStorage(val currentParameters: NetworkParameters = testNetworkParameters(modifiedTime = Instant.MIN)) : NetworkParametersStorageInternal {
|
||||
class MockNetworkParametersStorage(private var currentParameters: NetworkParameters = testNetworkParameters(modifiedTime = Instant.MIN)) : NetworkParametersStorageInternal, HistoricNetworkParameterStorage {
|
||||
private val hashToParametersMap: HashMap<SecureHash, NetworkParameters> = HashMap()
|
||||
|
||||
init {
|
||||
hashToParametersMap[currentHash] = currentParameters
|
||||
storeCurrentParameters()
|
||||
}
|
||||
|
||||
override val currentHash: SecureHash get() = currentParameters.serialize().hash
|
||||
fun setCurrentParametersUnverified(networkParameters: NetworkParameters) {
|
||||
currentParameters = networkParameters
|
||||
storeCurrentParameters()
|
||||
}
|
||||
|
||||
override fun setCurrentParameters(currentSignedParameters: SignedDataWithCert<NetworkParameters>, trustRoot: X509Certificate) {
|
||||
setCurrentParametersUnverified(currentSignedParameters.verifiedNetworkMapCert(trustRoot))
|
||||
}
|
||||
|
||||
override val currentHash: SecureHash
|
||||
get() {
|
||||
return withTestSerializationEnvIfNotSet("networkParameters") {
|
||||
currentParameters.serialize().hash
|
||||
}
|
||||
}
|
||||
override val defaultHash: SecureHash get() = currentHash
|
||||
override fun getEpochFromHash(hash: SecureHash): Int? = lookup(hash)?.epoch
|
||||
override fun lookup(hash: SecureHash): NetworkParameters? = hashToParametersMap[hash]
|
||||
@ -24,4 +44,18 @@ class MockNetworkParametersStorage(val currentParameters: NetworkParameters = te
|
||||
val hash = signedNetworkParameters.raw.hash
|
||||
hashToParametersMap[hash] = networkParameters
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHistoricNotary(party: Party): NotaryInfo? {
|
||||
val inCurrentParams = currentParameters.notaries.singleOrNull { it.identity == party }
|
||||
if (inCurrentParams == null) {
|
||||
val inOldParams = hashToParametersMap.flatMap { (_, parameters) ->
|
||||
parameters.notaries
|
||||
}.firstOrNull { it.identity == party }
|
||||
return inOldParams
|
||||
} else return inCurrentParams
|
||||
}
|
||||
|
||||
private fun storeCurrentParameters() {
|
||||
hashToParametersMap[currentHash] = currentParameters
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,14 @@ package net.corda.testing.node.internal
|
||||
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.FlowManager
|
||||
@ -15,6 +17,7 @@ import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.NodeFlowManager
|
||||
import net.corda.node.internal.NodeWithInfo
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
@ -37,7 +40,9 @@ import kotlin.test.assertFalse
|
||||
// TODO Some of the logic here duplicates what's in the driver - the reason why it's not straightforward to replace it by
|
||||
// using DriverDSLImpl in `init()` and `stopAllNodes()` is because of the platform version passed to nodes (driver doesn't
|
||||
// support this, and it's a property of the Corda JAR)
|
||||
abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyList()) {
|
||||
abstract class NodeBasedTest
|
||||
@JvmOverloads
|
||||
constructor(private val cordappPackages: List<String> = emptyList(), private val notaries: List<CordaX500Name> = emptyList()) {
|
||||
companion object {
|
||||
private val WHITESPACE = "\\s++".toRegex()
|
||||
}
|
||||
@ -50,8 +55,8 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
private lateinit var defaultNetworkParameters: NetworkParametersCopier
|
||||
protected val notaryNodes = mutableListOf<NodeWithInfo>()
|
||||
private val nodes = mutableListOf<NodeWithInfo>()
|
||||
private val nodeInfos = mutableListOf<NodeInfo>()
|
||||
private val portAllocation = incrementalPortAllocation(10000)
|
||||
|
||||
init {
|
||||
@ -60,7 +65,14 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
|
||||
@Before
|
||||
fun init() {
|
||||
defaultNetworkParameters = NetworkParametersCopier(testNetworkParameters())
|
||||
val notaryInfos = notaries.map { NotaryInfo(installNotary(it), true) } // todo only validating ones
|
||||
defaultNetworkParameters = NetworkParametersCopier(testNetworkParameters(notaries = notaryInfos))
|
||||
notaries.mapTo(notaryNodes) { startNode(it) }
|
||||
}
|
||||
|
||||
private fun installNotary(legalName: CordaX500Name): Party {
|
||||
val baseDirectory = baseDirectory(legalName).createDirectories()
|
||||
return DevIdentityGenerator.installKeyStoreWithNodeIdentity(baseDirectory, legalName)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,17 +81,19 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
*/
|
||||
@After
|
||||
fun stopAllNodes() {
|
||||
val shutdownExecutor = Executors.newScheduledThreadPool(nodes.size)
|
||||
val nodesToShut = nodes + notaryNodes
|
||||
val shutdownExecutor = Executors.newScheduledThreadPool(nodesToShut.size)
|
||||
try {
|
||||
nodes.map { shutdownExecutor.fork(it::dispose) }.transpose().getOrThrow()
|
||||
nodesToShut.map { shutdownExecutor.fork(it::dispose) }.transpose().getOrThrow()
|
||||
// Wait until ports are released
|
||||
val portNotBoundChecks = nodes.flatMap {
|
||||
val portNotBoundChecks = nodesToShut.flatMap {
|
||||
listOf(
|
||||
addressMustNotBeBoundFuture(shutdownExecutor, it.node.configuration.p2pAddress),
|
||||
addressMustNotBeBoundFuture(shutdownExecutor, it.node.configuration.rpcOptions.address)
|
||||
)
|
||||
}
|
||||
nodes.clear()
|
||||
notaryNodes.clear()
|
||||
portNotBoundChecks.transpose().getOrThrow()
|
||||
} finally {
|
||||
shutdownExecutor.shutdown()
|
||||
@ -146,8 +160,8 @@ abstract class NodeBasedTest(private val cordappPackages: List<String> = emptyLi
|
||||
}
|
||||
|
||||
class InProcessNode(configuration: NodeConfiguration, versionInfo: VersionInfo, flowManager: FlowManager = NodeFlowManager(configuration.flowOverrides)) : Node(configuration, versionInfo, false, flowManager = flowManager) {
|
||||
override fun start() : NodeInfo {
|
||||
assertFalse(isInvalidJavaVersion(), "You are using a version of Java that is not supported (${SystemUtils.JAVA_VERSION}). Please upgrade to the latest version of Java 8." )
|
||||
override fun start(): NodeInfo {
|
||||
assertFalse(isInvalidJavaVersion(), "You are using a version of Java that is not supported (${SystemUtils.JAVA_VERSION}). Please upgrade to the latest version of Java 8.")
|
||||
return super.start()
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
package net.corda.testing.common.internal
|
||||
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.utilities.days
|
||||
import java.security.PublicKey
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
@ -16,16 +18,31 @@ fun testNetworkParameters(
|
||||
maxTransactionSize: Int = maxMessageSize * 50,
|
||||
whitelistedContractImplementations: Map<String, List<AttachmentId>> = emptyMap(),
|
||||
epoch: Int = 1,
|
||||
eventHorizon: Duration = 30.days
|
||||
eventHorizon: Duration = 30.days,
|
||||
packageOwnership: Map<String, PublicKey> = emptyMap()
|
||||
): NetworkParameters {
|
||||
return NetworkParameters(
|
||||
minimumPlatformVersion = minimumPlatformVersion,
|
||||
notaries = notaries,
|
||||
maxMessageSize = maxMessageSize,
|
||||
maxTransactionSize = maxTransactionSize,
|
||||
whitelistedContractImplementations = whitelistedContractImplementations,
|
||||
modifiedTime = modifiedTime,
|
||||
epoch = epoch,
|
||||
eventHorizon = eventHorizon
|
||||
whitelistedContractImplementations = whitelistedContractImplementations,
|
||||
eventHorizon = eventHorizon,
|
||||
packageOwnership = packageOwnership
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the specified notary [party] in the network parameter whitelist to ensure transactions verify.
|
||||
* If used when actual notarisation flows are involved, the right notary type (validating/non-validating) should be specified.
|
||||
*
|
||||
* Note that it returns new network parameters with a different hash.
|
||||
*/
|
||||
fun NetworkParameters.addNotary(party: Party, validating: Boolean = true): NetworkParameters {
|
||||
val notaryInfo = NotaryInfo(party, validating)
|
||||
val notaryList = notaries + notaryInfo
|
||||
return copy(notaries = notaryList)
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import net.corda.core.internal.NamedCacheFactory
|
||||
import net.corda.core.internal.createComponentGroups
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.internal.effectiveSerializationEnv
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.internal.cordapp.set
|
||||
@ -34,6 +35,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.registerDevP2pCertificates
|
||||
import net.corda.serialization.internal.amqp.AMQP_ENABLED
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.internal.stubs.CertificateStoreStubs
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.file.Files
|
||||
@ -41,9 +43,9 @@ import java.nio.file.Path
|
||||
import java.security.KeyPair
|
||||
import java.util.*
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.jar.Manifest
|
||||
import java.util.zip.ZipEntry
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import java.util.jar.Manifest
|
||||
|
||||
@Suppress("unused")
|
||||
inline fun <reified T : Any> T.kryoSpecific(reason: String, function: () -> Unit) = if (!AMQP_ENABLED) {
|
||||
@ -183,14 +185,29 @@ fun configureDatabase(hikariProperties: Properties,
|
||||
/**
|
||||
* Convenience method for creating a fake attachment containing a file with some content.
|
||||
*/
|
||||
fun fakeAttachment(filePath: String, content: String, manifestAttributes: Map<String,String> = emptyMap()): ByteArray {
|
||||
fun fakeAttachment(filePath: String, content: String, manifestAttributes: Map<String, String> = emptyMap()): ByteArray {
|
||||
val bs = ByteArrayOutputStream()
|
||||
val manifest = Manifest()
|
||||
manifestAttributes.forEach{ manifest[it.key] = it.value} //adding manually instead of putAll, as it requires typed keys, not strings
|
||||
manifestAttributes.forEach { manifest[it.key] = it.value } //adding manually instead of putAll, as it requires typed keys, not strings
|
||||
JarOutputStream(bs, manifest).use { js ->
|
||||
js.putNextEntry(ZipEntry(filePath))
|
||||
js.writer().apply { append(content); flush() }
|
||||
js.closeEntry()
|
||||
}
|
||||
return bs.toByteArray()
|
||||
}
|
||||
|
||||
/** If [effectiveSerializationEnv] is not set, runs the block with a new [SerializationEnvironmentRule]. */
|
||||
fun <R> withTestSerializationEnvIfNotSet(taskName: String, block: () -> R): R {
|
||||
val serializationExists = try {
|
||||
effectiveSerializationEnv
|
||||
true
|
||||
} catch (e: IllegalStateException) {
|
||||
false
|
||||
}
|
||||
return if (serializationExists) {
|
||||
block()
|
||||
} else SerializationEnvironmentRule.run(taskName) {
|
||||
block()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user