Clean up of ServiceHubInternal, including how it's created in AbstractNode

This commit is contained in:
Shams Asari 2017-07-03 14:29:18 +01:00
parent 56402c744a
commit 46e23b7716
25 changed files with 221 additions and 276 deletions

View File

@ -1,7 +1,7 @@
package net.corda.core.contracts
import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.ReadOnlyTransactionStorage
import net.corda.core.node.services.TransactionStorage
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import java.util.*
@ -18,7 +18,7 @@ import java.util.concurrent.Callable
* @param transactions map of transaction id to [SignedTransaction].
* @param startPoints transactions to use as starting points for the search.
*/
class TransactionGraphSearch(val transactions: ReadOnlyTransactionStorage,
class TransactionGraphSearch(val transactions: TransactionStorage,
val startPoints: List<WireTransaction>) : Callable<List<WireTransaction>> {
class Query(
val withCommandOfType: Class<out CommandData>? = null,

View File

@ -1,5 +1,6 @@
package net.corda.core.node
import com.google.common.collect.Lists
import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.node.services.*
@ -17,8 +18,10 @@ import java.time.Clock
*/
interface ServicesForResolution {
val identityService: IdentityService
/** Provides access to storage of arbitrary JAR files (which may contain only data, no code). */
val attachments: AttachmentStorage
/**
* Given a [StateRef] loads the referenced transaction and looks up the specified output [ContractState].
*
@ -40,12 +43,13 @@ interface ServiceHub : ServicesForResolution {
val vaultService: VaultService
val vaultQueryService: VaultQueryService
val keyManagementService: KeyManagementService
/**
* A map of hash->tx where tx has been signature/contract validated and the states are known to be correct.
* The signatures aren't technically needed after that point, but we keep them around so that we can relay
* the transaction data to other nodes that need it.
*/
val validatedTransactions: ReadOnlyTransactionStorage
val validatedTransactions: TransactionStorage
val networkMapCache: NetworkMapCache
val transactionVerifierService: TransactionVerifierService
@ -60,41 +64,41 @@ interface ServiceHub : ServicesForResolution {
fun <T : SerializeAsToken> cordaService(type: Class<T>): T
/**
* Given a [SignedTransaction], writes it to the local storage for validated transactions and then
* sends them to the vault for further processing. Expects to be run within a database transaction.
* Stores the given [SignedTransaction]s in the local transaction storage and then sends them to the vault for
* further processing. This is expected to be run within a database transaction.
*
* @param txs The transactions to record.
*/
// TODO: Make this take a single tx.
fun recordTransactions(txs: Iterable<SignedTransaction>)
/**
* Given some [SignedTransaction]s, writes them to the local storage for validated transactions and then
* sends them to the vault for further processing.
*
* @param txs The transactions to record.
* Stores the given [SignedTransaction]s in the local transaction storage and then sends them to the vault for
* further processing. This is expected to be run within a database transaction.
*/
fun recordTransactions(vararg txs: SignedTransaction) = recordTransactions(txs.toList())
fun recordTransactions(first: SignedTransaction, vararg remaining: SignedTransaction) {
recordTransactions(Lists.asList(first, remaining))
}
/**
* Given a [StateRef] loads the referenced transaction and looks up the specified output [ContractState].
*
* @throws TransactionResolutionException if the [StateRef] points to a non-existent transaction.
* @throws TransactionResolutionException if [stateRef] points to a non-existent transaction.
*/
@Throws(TransactionResolutionException::class)
override fun loadState(stateRef: StateRef): TransactionState<*> {
val definingTx = validatedTransactions.getTransaction(stateRef.txhash) ?: throw TransactionResolutionException(stateRef.txhash)
return definingTx.tx.outputs[stateRef.index]
val stx = validatedTransactions.getTransaction(stateRef.txhash) ?: throw TransactionResolutionException(stateRef.txhash)
return stx.tx.outputs[stateRef.index]
}
/**
* Will check [logicType] and [args] against a whitelist and if acceptable then construct and initiate the protocol.
* Converts the given [StateRef] into a [StateAndRef] object.
*
* @throws IllegalProtocolLogicException or IllegalArgumentException if there are problems with the [logicType] or [args].
* @throws TransactionResolutionException if [stateRef] points to a non-existent transaction.
*/
fun <T : ContractState> toStateAndRef(ref: StateRef): StateAndRef<T> {
val definingTx = validatedTransactions.getTransaction(ref.txhash) ?: throw TransactionResolutionException(ref.txhash)
return definingTx.tx.outRef<T>(ref.index)
@Throws(TransactionResolutionException::class)
fun <T : ContractState> toStateAndRef(stateRef: StateRef): StateAndRef<T> {
val stx = validatedTransactions.getTransaction(stateRef.txhash) ?: throw TransactionResolutionException(stateRef.txhash)
return stx.tx.outRef<T>(stateRef.index)
}
/**
@ -102,7 +106,7 @@ interface ServiceHub : ServicesForResolution {
* Node's primary signing identity.
* Typical use is during signing in flows and for unit test signing.
* When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService
* the matching [PrivateKey] will be looked up internally and used to sign.
* the matching [java.security.PrivateKey] will be looked up internally and used to sign.
* If the key is actually a CompositeKey, the first leaf key hosted on this node
* will be used to create the signature.
*/
@ -114,8 +118,8 @@ interface ServiceHub : ServicesForResolution {
* otherwise an IllegalArgumentException will be thrown.
* Typical use is during signing in flows and for unit test signing.
* When this [PublicKey] is passed into the signing methods below, or on the KeyManagementService
* the matching [PrivateKey] will be looked up internally and used to sign.
* If the key is actually a [CompositeKey], the first leaf key hosted on this node
* the matching [java.security.PrivateKey] will be looked up internally and used to sign.
* If the key is actually a [net.corda.core.crypto.CompositeKey], the first leaf key hosted on this node
* will be used to create the signature.
*/
val notaryIdentityKey: PublicKey get() = this.myInfo.notaryIdentity.owningKey
@ -125,7 +129,7 @@ interface ServiceHub : ServicesForResolution {
* using keys stored inside the node.
* @param builder The [TransactionBuilder] to seal with the node's signature.
* Any existing signatures on the builder will be preserved.
* @param publicKey The [PublicKey] matched to the internal [PrivateKey] to use in signing this transaction.
* @param publicKey The [PublicKey] matched to the internal [java.security.PrivateKey] to use in signing this transaction.
* If the passed in key is actually a CompositeKey the code searches for the first child key hosted within this node
* to sign with.
* @return Returns a SignedTransaction with the new node signature attached.
@ -150,30 +154,30 @@ interface ServiceHub : ServicesForResolution {
* using a set of keys all held in this node.
* @param builder The [TransactionBuilder] to seal with the node's signature.
* Any existing signatures on the builder will be preserved.
* @param signingPubKeys A list of [PublicKeys] used to lookup the matching [PrivateKey] and sign.
* @param signingPubKeys A list of [PublicKey]s used to lookup the matching [java.security.PrivateKey] and sign.
* @throws IllegalArgumentException is thrown if any keys are unavailable locally.
* @return Returns a [SignedTransaction] with the new node signature attached.
*/
fun signInitialTransaction(builder: TransactionBuilder, signingPubKeys: Iterable<PublicKey>): SignedTransaction {
var stx: SignedTransaction? = null
for (pubKey in signingPubKeys) {
stx = if (stx == null) {
signInitialTransaction(builder, pubKey)
} else {
addSignature(stx, pubKey)
}
val it = signingPubKeys.iterator()
var stx = signInitialTransaction(builder, it.next())
while (it.hasNext()) {
stx = addSignature(stx, it.next())
}
return stx!!
return stx
}
/**
* Helper method to create an additional signature for an existing (partially) [SignedTransaction].
* @param signedTransaction The [SignedTransaction] to which the signature will apply.
* @param publicKey The [PublicKey] matching to a signing [PrivateKey] hosted in the node.
* If the [PublicKey] is actually a [CompositeKey] the first leaf key found locally will be used for signing.
* @return The [DigitalSignature.WithKey] generated by signing with the internally held [PrivateKey].
* @param publicKey The [PublicKey] matching to a signing [java.security.PrivateKey] hosted in the node.
* If the [PublicKey] is actually a [net.corda.core.crypto.CompositeKey] the first leaf key found locally will be used
* for signing.
* @return The [DigitalSignature.WithKey] generated by signing with the internally held [java.security.PrivateKey].
*/
fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey): DigitalSignature.WithKey = keyManagementService.sign(signedTransaction.id.bytes, publicKey)
fun createSignature(signedTransaction: SignedTransaction, publicKey: PublicKey): DigitalSignature.WithKey {
return keyManagementService.sign(signedTransaction.id.bytes, publicKey)
}
/**
* Helper method to create an additional signature for an existing (partially) SignedTransaction
@ -181,16 +185,21 @@ interface ServiceHub : ServicesForResolution {
* @param signedTransaction The SignedTransaction to which the signature will apply.
* @return The DigitalSignature.WithKey generated by signing with the internally held identity PrivateKey.
*/
fun createSignature(signedTransaction: SignedTransaction): DigitalSignature.WithKey = createSignature(signedTransaction, legalIdentityKey)
fun createSignature(signedTransaction: SignedTransaction): DigitalSignature.WithKey {
return createSignature(signedTransaction, legalIdentityKey)
}
/**
* Helper method to append an additional signature to an existing (partially) [SignedTransaction].
* @param signedTransaction The [SignedTransaction] to which the signature will be added.
* @param publicKey The [PublicKey] matching to a signing [PrivateKey] hosted in the node.
* If the [PublicKey] is actually a [CompositeKey] the first leaf key found locally will be used for signing.
* @param publicKey The [PublicKey] matching to a signing [java.security.PrivateKey] hosted in the node.
* If the [PublicKey] is actually a [net.corda.core.crypto.CompositeKey] the first leaf key found locally will be used
* for signing.
* @return A new [SignedTransaction] with the addition of the new signature.
*/
fun addSignature(signedTransaction: SignedTransaction, publicKey: PublicKey): SignedTransaction = signedTransaction + createSignature(signedTransaction, publicKey)
fun addSignature(signedTransaction: SignedTransaction, publicKey: PublicKey): SignedTransaction {
return signedTransaction + createSignature(signedTransaction, publicKey)
}
/**
* Helper method to ap-pend an additional signature for an existing (partially) [SignedTransaction]

View File

@ -2,21 +2,14 @@ package net.corda.core.node.services
import net.corda.core.contracts.Attachment
import net.corda.core.crypto.SecureHash
import java.io.IOException
import java.io.InputStream
import java.nio.file.Path
import java.nio.file.FileAlreadyExistsException
/**
* An attachment store records potentially large binary objects, identified by their hash.
*/
interface AttachmentStorage {
/**
* If true, newly inserted attachments will be unzipped to a subdirectory of the [storePath]. This is intended for
* human browsing convenience: the attachment itself will still be the file (that is, edits to the extracted directory
* will not have any effect).
*/
var automaticallyExtractAttachments: Boolean
var storePath: Path
/**
* Returns a handle to a locally stored attachment, or null if it's not known. The handle can be used to open
* a stream for the data, which will be a zip/jar file.
@ -27,13 +20,14 @@ interface AttachmentStorage {
* Inserts the given attachment into the store, does *not* close the input stream. This can be an intensive
* operation due to the need to copy the bytes to disk and hash them along the way.
*
* Note that you should not pass a [JarInputStream] into this method and it will throw if you do, because access
* to the raw byte stream is required.
* Note that you should not pass a [java.util.jar.JarInputStream] into this method and it will throw if you do, because
* access to the raw byte stream is required.
*
* @throws FileAlreadyExistsException if the given byte stream has already been inserted.
* @throws IllegalArgumentException if the given byte stream is empty or a [JarInputStream].
* @throws IllegalArgumentException if the given byte stream is empty or a [java.util.jar.JarInputStream].
* @throws IOException if something went wrong.
*/
@Throws(FileAlreadyExistsException::class, IOException::class)
fun importAttachment(jar: InputStream): SecureHash
}

View File

@ -8,7 +8,7 @@ import rx.Observable
/**
* Thread-safe storage of transactions.
*/
interface ReadOnlyTransactionStorage {
interface TransactionStorage {
/**
* Return the transaction with the given [id], or null if no such transaction exists.
*/
@ -24,18 +24,4 @@ interface ReadOnlyTransactionStorage {
* Returns all currently stored transactions and further fresh ones.
*/
fun track(): DataFeed<List<SignedTransaction>, SignedTransaction>
}
/**
* Thread-safe storage of transactions.
*/
interface TransactionStorage : ReadOnlyTransactionStorage {
/**
* Add a new transaction to the store. If the store already has a transaction with the same id it will be
* overwritten.
* @param transaction The transaction to be recorded.
* @return true if the transaction was recorded successfully, false if it was already recorded.
*/
// TODO: Throw an exception if trying to add a transaction with fewer signatures than an existing entry.
fun addTransaction(transaction: SignedTransaction): Boolean
}
}

View File

@ -74,16 +74,15 @@ class FinalityFlow(val transactions: Iterable<SignedTransaction>,
@Suspendable
private fun notariseAndRecord(stxnsAndParties: List<Pair<SignedTransaction, Set<Party>>>): List<Pair<SignedTransaction, Set<Party>>> {
return stxnsAndParties.map { pair ->
val stx = pair.first
return stxnsAndParties.map { (stx, parties) ->
val notarised = if (needsNotarySignature(stx)) {
val notarySignatures = subFlow(NotaryFlow.Client(stx))
stx + notarySignatures
} else {
stx
}
serviceHub.recordTransactions(listOf(notarised))
Pair(notarised, pair.second)
serviceHub.recordTransactions(notarised)
Pair(notarised, parties)
}
}
@ -101,8 +100,7 @@ class FinalityFlow(val transactions: Iterable<SignedTransaction>,
}
private fun lookupParties(ltxns: List<Pair<SignedTransaction, LedgerTransaction>>): List<Pair<SignedTransaction, Set<Party>>> {
return ltxns.map { pair ->
val (stx, ltx) = pair
return ltxns.map { (stx, ltx) ->
// Calculate who is meant to see the results based on the participants involved.
val keys = ltx.outputs.flatMap { it.data.participants } + ltx.inputs.flatMap { it.state.data.participants }
// TODO: Is it safe to drop participants we don't know how to contact? Does not knowing how to contact them count as a reason to fail?

View File

@ -178,14 +178,14 @@ class ContractUpgradeFlowTest {
mockNet.runNetwork()
val stx = result.getOrThrow().stx
val stateAndRef = stx.tx.outRef<Cash.State>(0)
val baseState = a.database.transaction { a.vault.unconsumedStates<ContractState>().single() }
val baseState = a.database.transaction { a.services.vaultService.unconsumedStates<ContractState>().single() }
assertTrue(baseState.state.data is Cash.State, "Contract state is old version.")
// Starts contract upgrade flow.
val upgradeResult = a.services.startFlow(ContractUpgradeFlow(stateAndRef, CashV2::class.java)).resultFuture
mockNet.runNetwork()
upgradeResult.getOrThrow()
// Get contract state from the vault.
val firstState = a.database.transaction { a.vault.unconsumedStates<ContractState>().single() }
val firstState = a.database.transaction { a.services.vaultService.unconsumedStates<ContractState>().single() }
assertTrue(firstState.state.data is CashV2.State, "Contract state is upgraded to the new version.")
assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (firstState.state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.")
assertEquals<Collection<AbstractParty>>(listOf(a.info.legalIdentity), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.")

View File

@ -33,8 +33,8 @@ class CashPaymentFlowTests {
bankOfCorda = bankOfCordaNode.info.legalIdentity
notaryNode.registerInitiatedFlow(TxKeyFlow.Provider::class.java)
notaryNode.identity.registerIdentity(bankOfCordaNode.info.legalIdentityAndCert)
bankOfCordaNode.identity.registerIdentity(notaryNode.info.legalIdentityAndCert)
notaryNode.services.identityService.registerIdentity(bankOfCordaNode.info.legalIdentityAndCert)
bankOfCordaNode.services.identityService.registerIdentity(notaryNode.info.legalIdentityAndCert)
val future = bankOfCordaNode.services.startFlow(CashIssueFlow(initialBalance, ref,
bankOfCorda,
notary)).resultFuture

View File

@ -5,8 +5,8 @@ import net.corda.core.contracts.DummyContract
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionType
import net.corda.core.identity.Party
import net.corda.core.getOrThrow
import net.corda.core.identity.Party
import net.corda.core.map
import net.corda.core.utilities.DUMMY_BANK_A
import net.corda.flows.NotaryError
@ -26,28 +26,28 @@ class RaftNotaryServiceTests : NodeBasedTest() {
@Test
fun `detect double spend`() {
val (masterNode, alice) = Futures.allAsList(
startNotaryCluster(notaryName, 3).map { it.first() },
startNode(DUMMY_BANK_A.name)
val (bankA) = Futures.allAsList(
startNode(DUMMY_BANK_A.name),
startNotaryCluster(notaryName, 3).map { it.first() }
).getOrThrow()
val notaryParty = alice.netMapCache.getNotary(notaryName)!!
val notaryParty = bankA.services.networkMapCache.getNotary(notaryName)!!
val inputState = issueState(alice, notaryParty)
val inputState = issueState(bankA, notaryParty)
val firstTxBuilder = TransactionType.General.Builder(notaryParty).withItems(inputState)
val firstSpendTx = alice.services.signInitialTransaction(firstTxBuilder)
val firstSpendTx = bankA.services.signInitialTransaction(firstTxBuilder)
val firstSpend = alice.services.startFlow(NotaryFlow.Client(firstSpendTx))
val firstSpend = bankA.services.startFlow(NotaryFlow.Client(firstSpendTx))
firstSpend.resultFuture.getOrThrow()
val secondSpendBuilder = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity)
val dummyState = DummyContract.SingleOwnerState(0, bankA.info.legalIdentity)
addOutputState(dummyState)
this
}
val secondSpendTx = alice.services.signInitialTransaction(secondSpendBuilder)
val secondSpend = alice.services.startFlow(NotaryFlow.Client(secondSpendTx))
val secondSpendTx = bankA.services.signInitialTransaction(secondSpendBuilder)
val secondSpend = bankA.services.startFlow(NotaryFlow.Client(secondSpendTx))
val ex = assertFailsWith(NotaryException::class) { secondSpend.resultFuture.getOrThrow() }
val error = ex.error as NotaryError.Conflict
@ -58,7 +58,7 @@ class RaftNotaryServiceTests : NodeBasedTest() {
return node.database.transaction {
val builder = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0))
val stx = node.services.signInitialTransaction(builder)
node.services.recordTransactions(listOf(stx))
node.services.recordTransactions(stx)
StateAndRef(builder.outputStates().first(), StateRef(stx.id, 0))
}
}

View File

@ -20,7 +20,6 @@ import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.*
import net.corda.core.node.services.*
import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.node.services.NotaryService
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize
@ -42,6 +41,7 @@ import net.corda.node.services.messaging.MessagingService
import net.corda.node.services.messaging.sendRequest
import net.corda.node.services.network.InMemoryNetworkMapCache
import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.network.NetworkMapService.RegistrationRequest
import net.corda.node.services.network.NetworkMapService.RegistrationResponse
import net.corda.node.services.network.NodeRegistration
import net.corda.node.services.network.PersistentNetworkMapService
@ -122,78 +122,18 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
private val flowFactories = ConcurrentHashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>()
protected val partyKeys = mutableSetOf<KeyPair>()
val services = object : ServiceHubInternal {
override val attachments: AttachmentStorage get() = this@AbstractNode.attachments
override val uploaders: List<FileUploader> get() = this@AbstractNode.uploaders
override val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage
get() = this@AbstractNode.transactionMappings
override val validatedTransactions: TransactionStorage get() = this@AbstractNode.transactions
override val networkService: MessagingService get() = network
override val networkMapCache: NetworkMapCacheInternal get() = netMapCache
override val vaultService: VaultService get() = vault
override val vaultQueryService: VaultQueryService get() = vaultQuery
override val keyManagementService: KeyManagementService get() = keyManagement
override val identityService: IdentityService get() = identity
override val schedulerService: SchedulerService get() = scheduler
override val clock: Clock get() = platformClock
override val myInfo: NodeInfo get() = info
override val schemaService: SchemaService get() = schemas
override val transactionVerifierService: TransactionVerifierService get() = txVerifierService
override val auditService: AuditService get() = this@AbstractNode.auditService
override val database: Database get() = this@AbstractNode.database
override val configuration: NodeConfiguration get() = this@AbstractNode.configuration
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T {
require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" }
return cordappServices.getInstance(type) ?: throw IllegalArgumentException("Corda service ${type.name} does not exist")
}
override val rpcFlows: List<Class<out FlowLogic<*>>> get() = this@AbstractNode.rpcFlows
// Internal only
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())
override fun <T> startFlow(logic: FlowLogic<T>, flowInitiator: FlowInitiator): FlowStateMachineImpl<T> {
return serverThread.fetchFrom { smm.add(logic, flowInitiator) }
}
override fun getFlowFactory(initiatingFlowClass: Class<out FlowLogic<*>>): InitiatedFlowFactory<*>? {
return flowFactories[initiatingFlowClass]
}
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
database.transaction {
super.recordTransactions(txs)
}
}
}
open fun findMyLocation(): WorldMapLocation? {
return configuration.myLegalName.locationOrNull?.let { CityDatabase[it] }
}
val services: ServiceHubInternal get() = _services
private lateinit var _services: ServiceHubInternalImpl
lateinit var info: NodeInfo
lateinit var checkpointStorage: CheckpointStorage
lateinit var smm: StateMachineManager
lateinit var attachments: NodeAttachmentService
lateinit var transactions: TransactionStorage
lateinit var transactionMappings: StateMachineRecordedTransactionMappingStorage
lateinit var uploaders: List<FileUploader>
lateinit var vault: VaultService
lateinit var vaultQuery: VaultQueryService
lateinit var keyManagement: KeyManagementService
var inNodeNetworkMapService: NetworkMapService? = null
lateinit var txVerifierService: TransactionVerifierService
lateinit var identity: IdentityService
lateinit var network: MessagingService
lateinit var netMapCache: NetworkMapCacheInternal
lateinit var scheduler: NodeSchedulerService
lateinit var schemas: SchemaService
lateinit var auditService: AuditService
protected val runOnStop = ArrayList<() -> Any?>()
lateinit var database: Database
protected var dbCloser: (() -> Any?)? = null
private lateinit var rpcFlows: List<Class<out FlowLogic<*>>>
var isPreviousCheckpointsPresent = false
private set
@ -215,6 +155,10 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
/** The implementation of the [CordaRPCOps] interface used by this node. */
open val rpcOps: CordaRPCOps by lazy { CordaRPCOpsImpl(services, smm, database) } // Lazy to avoid init ordering issue with the SMM.
open fun findMyLocation(): WorldMapLocation? {
return configuration.myLegalName.locationOrNull?.let { CityDatabase[it] }
}
open fun start(): AbstractNode {
require(!started) { "Node has already been started" }
@ -233,8 +177,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
// Do all of this in a database transaction so anything that might need a connection has one.
initialiseDatabasePersistence {
val keyStoreWrapper = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
val tokenizableServices = makeServices(keyStoreWrapper)
val tokenizableServices = makeServices()
smm = StateMachineManager(services,
checkpointStorage,
@ -266,9 +209,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
if (scanResult != null) {
installCordaServices(scanResult)
registerInitiatedFlows(scanResult)
rpcFlows = findRPCFlows(scanResult)
} else {
rpcFlows = emptyList()
findRPCFlows(scanResult)
}
// TODO: Investigate having class path scanning find this flow
@ -283,7 +224,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
smm.start()
// Shut down the SMM so no Fibers are scheduled.
runOnStop += { smm.stop(acceptableLiveFiberCountOnStop()) }
scheduler.start()
_services.schedulerService.start()
}
started = true
return this
@ -301,7 +242,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
}
}
return scanResult.getClassesWithAnnotation(SerializeAsToken::class, CordaService::class)
scanResult.getClassesWithAnnotation(SerializeAsToken::class, CordaService::class)
.filter {
val serviceType = getServiceType(it)
if (serviceType != null && info.serviceIdentities(serviceType).isEmpty()) {
@ -434,19 +375,21 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
return observable
}
private fun findRPCFlows(scanResult: ScanResult): List<Class<out FlowLogic<*>>> {
private fun findRPCFlows(scanResult: ScanResult) {
fun Class<out FlowLogic<*>>.isUserInvokable(): Boolean {
return isPublic(modifiers) && !isLocalClass && !isAnonymousClass && (!isMemberClass || isStatic(modifiers))
}
return scanResult.getClassesWithAnnotation(FlowLogic::class, StartableByRPC::class).filter { it.isUserInvokable() } +
// Add any core flows here
listOf(
ContractUpgradeFlow::class.java,
// TODO Remove all Cash flows from default list once they are split into separate CorDapp.
CashIssueFlow::class.java,
CashExitFlow::class.java,
CashPaymentFlow::class.java)
_services.rpcFlows += scanResult
.getClassesWithAnnotation(FlowLogic::class, StartableByRPC::class)
.filter { it.isUserInvokable() } +
// Add any core flows here
listOf(
ContractUpgradeFlow::class.java,
// TODO Remove all Cash flows from default list once they are split into separate CorDapp.
CashIssueFlow::class.java,
CashExitFlow::class.java,
CashPaymentFlow::class.java)
}
/**
@ -476,36 +419,20 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
* Builds node internal, advertised, and plugin services.
* Returns a list of tokenizable services to be added to the serialisation context.
*/
private fun makeServices(keyStoreWrapper: KeyStoreWrapper): MutableList<Any> {
val keyStore = keyStoreWrapper.keyStore
attachments = createAttachmentStorage()
transactions = createTransactionStorage()
transactionMappings = DBTransactionMappingStorage()
private fun makeServices(): MutableList<Any> {
checkpointStorage = DBCheckpointStorage()
netMapCache = InMemoryNetworkMapCache(services)
_services = ServiceHubInternalImpl()
attachments = createAttachmentStorage()
network = makeMessagingService()
schemas = makeSchemaService()
vault = makeVaultService(configuration.dataSourceProperties)
vaultQuery = makeVaultQueryService(schemas)
txVerifierService = makeTransactionVerifierService()
auditService = DummyAuditService()
info = makeInfo()
identity = makeIdentityService(keyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)!! as X509Certificate,
keyStoreWrapper.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA),
info.legalIdentityAndCert)
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
// the identity key. But the infrastructure to make that easy isn't here yet.
keyManagement = makeKeyManagementService(identity)
scheduler = NodeSchedulerService(services, database, unfinishedSchedules = busyNodeLatch)
val tokenizableServices = mutableListOf(attachments, network, vault, vaultQuery, keyManagement, identity, platformClock, scheduler)
val tokenizableServices = mutableListOf(attachments, network, services.vaultService, services.vaultQueryService,
services.keyManagementService, services.identityService, platformClock, services.schedulerService)
makeAdvertisedServices(tokenizableServices)
return tokenizableServices
}
protected open fun createTransactionStorage(): TransactionStorage = DBTransactionStorage()
protected open fun makeTransactionStorage(): WritableTransactionStorage = DBTransactionStorage()
private fun scanCordapps(): ScanResult? {
val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package")
@ -560,14 +487,15 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
}
private fun initUploaders() {
uploaders = listOf(attachments) + cordappServices.values.filterIsInstance(AcceptsFileUpload::class.java)
_services.uploaders += attachments
cordappServices.values.filterIsInstanceTo(_services.uploaders, AcceptsFileUpload::class.java)
}
private fun makeVaultObservers() {
VaultSoftLockManager(vault, smm)
VaultSoftLockManager(services.vaultService, smm)
CashBalanceAsMetricsObserver(services, database)
ScheduledActivityObserver(services)
HibernateObserver(vault.rawUpdates, HibernateConfiguration(schemas))
HibernateObserver(services.vaultService.rawUpdates, HibernateConfiguration(services.schemaService))
}
private fun makeInfo(): NodeInfo {
@ -684,7 +612,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
val expires = instant + NetworkMapService.DEFAULT_EXPIRATION_PERIOD
val reg = NodeRegistration(info, instant.toEpochMilli(), ADD, expires)
val legalIdentityKey = obtainLegalIdentityKey()
val request = NetworkMapService.RegistrationRequest(reg.toWire(keyManagement, legalIdentityKey.public), network.myAddress)
val request = RegistrationRequest(reg.toWire(services.keyManagementService, legalIdentityKey.public), network.myAddress)
return network.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapAddress)
}
@ -735,7 +663,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
.toTypedArray()
val service = InMemoryIdentityService(setOf(info.legalIdentityAndCert), trustRoot = trustRoot, caCertificates = *caCertificates)
services.networkMapCache.partyNodes.forEach { service.registerIdentity(it.legalIdentityAndCert) }
netMapCache.changed.subscribe { mapChange ->
services.networkMapCache.changed.subscribe { mapChange ->
// TODO how should we handle network map removal
if (mapChange is MapChange.Added) {
service.registerIdentity(mapChange.node.legalIdentityAndCert)
@ -744,13 +672,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
return service
}
// TODO: sort out ordering of open & protected modifiers of functions in this class.
protected open fun makeVaultService(dataSourceProperties: Properties): VaultService = NodeVaultService(services, dataSourceProperties)
protected open fun makeVaultQueryService(schemas: SchemaService): VaultQueryService = HibernateVaultQueryImpl(HibernateConfiguration(schemas), vault.updatesPublisher)
protected open fun makeSchemaService(): SchemaService = NodeSchemaService(pluginRegistries.flatMap { it.requiredSchemas }.toSet())
protected abstract fun makeTransactionVerifierService(): TransactionVerifierService
open fun stop() {
@ -844,6 +765,60 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
val attachmentsDir = (configuration.baseDirectory / "attachments").createDirectories()
return NodeAttachmentService(attachmentsDir, configuration.dataSourceProperties, services.monitoringService.metrics)
}
private inner class ServiceHubInternalImpl : ServiceHubInternal, SingletonSerializeAsToken() {
override val rpcFlows = ArrayList<Class<out FlowLogic<*>>>()
override val uploaders = ArrayList<FileUploader>()
override val stateMachineRecordedTransactionMapping = DBTransactionMappingStorage()
override val auditService = DummyAuditService()
override val monitoringService = MonitoringService(MetricRegistry())
override val validatedTransactions = makeTransactionStorage()
override val transactionVerifierService by lazy { makeTransactionVerifierService() }
override val networkMapCache by lazy { InMemoryNetworkMapCache(this) }
override val vaultService by lazy { NodeVaultService(this, configuration.dataSourceProperties) }
override val vaultQueryService by lazy {
HibernateVaultQueryImpl(HibernateConfiguration(schemaService), vaultService.updatesPublisher)
}
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
// the KMS is meant for derived temporary keys used in transactions, and we're not supposed to sign things with
// the identity key. But the infrastructure to make that easy isn't here yet.
override val keyManagementService by lazy { makeKeyManagementService(identityService) }
override val schedulerService by lazy { NodeSchedulerService(this, unfinishedSchedules = busyNodeLatch) }
override val identityService by lazy {
val keyStoreWrapper = KeyStoreWrapper(configuration.trustStoreFile, configuration.trustStorePassword)
makeIdentityService(
keyStoreWrapper.keyStore.getCertificate(X509Utilities.CORDA_ROOT_CA)!! as X509Certificate,
keyStoreWrapper.certificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA),
info.legalIdentityAndCert)
}
override val attachments: AttachmentStorage get() = this@AbstractNode.attachments
override val networkService: MessagingService get() = network
override val clock: Clock get() = platformClock
override val myInfo: NodeInfo get() = info
override val schemaService by lazy { NodeSchemaService(pluginRegistries.flatMap { it.requiredSchemas }.toSet()) }
override val database: Database get() = this@AbstractNode.database
override val configuration: NodeConfiguration get() = this@AbstractNode.configuration
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T {
require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" }
return cordappServices.getInstance(type) ?: throw IllegalArgumentException("Corda service ${type.name} does not exist")
}
override fun <T> startFlow(logic: FlowLogic<T>, flowInitiator: FlowInitiator): FlowStateMachineImpl<T> {
return serverThread.fetchFrom { smm.add(logic, flowInitiator) }
}
override fun getFlowFactory(initiatingFlowClass: Class<out FlowLogic<*>>): InitiatedFlowFactory<*>? {
return flowFactories[initiatingFlowClass]
}
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
database.transaction {
super.recordTransactions(txs)
}
}
}
}
private class KeyStoreWrapper(val keyStore: KeyStore, val storePath: Path, private val storePassword: String) {

View File

@ -72,7 +72,7 @@ interface ServiceHubInternal : PluginServiceHub {
* The signatures aren't technically needed after that point, but we keep them around so that we can relay
* the transaction data to other nodes that need it.
*/
override val validatedTransactions: TransactionStorage
override val validatedTransactions: WritableTransactionStorage
val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage
val monitoringService: MonitoringService
val schemaService: SchemaService
@ -89,8 +89,9 @@ interface ServiceHubInternal : PluginServiceHub {
val uploaders: List<FileUploader>
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id
val recordedTransactions = txs.filter { validatedTransactions.addTransaction(it) }
require(recordedTransactions.isNotEmpty()) { "No transactions passed in for recording" }
val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id
if (stateMachineRunId != null) {
recordedTransactions.forEach {
stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id)
@ -135,6 +136,20 @@ interface ServiceHubInternal : PluginServiceHub {
fun getFlowFactory(initiatingFlowClass: Class<out FlowLogic<*>>): InitiatedFlowFactory<*>?
}
/**
* Thread-safe storage of transactions.
*/
interface WritableTransactionStorage : TransactionStorage {
/**
* Add a new transaction to the store. If the store already has a transaction with the same id it will be
* overwritten.
* @param transaction The transaction to be recorded.
* @return true if the transaction was recorded successfully, false if it was already recorded.
*/
// TODO: Throw an exception if trying to add a transaction with fewer signatures than an existing entry.
fun addTransaction(transaction: SignedTransaction): Boolean
}
/**
* This is the interface to storage storing state machine -> recorded tx mappings. Any time a transaction is recorded
* during a flow run [addMapping] should be called.

View File

@ -17,7 +17,6 @@ import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
import net.corda.node.utilities.*
import org.apache.activemq.artemis.utils.ReusableLatch
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.statements.InsertStatement
import java.time.Instant
@ -43,7 +42,6 @@ import javax.annotation.concurrent.ThreadSafe
*/
@ThreadSafe
class NodeSchedulerService(private val services: ServiceHubInternal,
private val database: Database,
private val schedulerTimerExecutor: Executor = Executors.newSingleThreadExecutor(),
private val unfinishedSchedules: ReusableLatch = ReusableLatch())
: SchedulerService, SingletonSerializeAsToken() {
@ -159,7 +157,7 @@ class NodeSchedulerService(private val services: ServiceHubInternal,
}
private fun onTimeReached(scheduledState: ScheduledStateRef) {
database.transaction {
services.database.transaction {
val scheduledFlow = getScheduledFlow(scheduledState)
if (scheduledFlow != null) {
// TODO Because the flow is executed asynchronously, there is a small window between this tx we're in

View File

@ -4,9 +4,9 @@ import com.google.common.annotations.VisibleForTesting
import net.corda.core.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash
import net.corda.core.messaging.DataFeed
import net.corda.core.node.services.TransactionStorage
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.SignedTransaction
import net.corda.node.services.api.WritableTransactionStorage
import net.corda.node.utilities.*
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.exposedLogger
@ -15,7 +15,7 @@ import rx.Observable
import rx.subjects.PublishSubject
import java.util.Collections.synchronizedMap
class DBTransactionStorage : TransactionStorage, SingletonSerializeAsToken() {
class DBTransactionStorage : WritableTransactionStorage, SingletonSerializeAsToken() {
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}transactions") {
val txId = secureHash("tx_id")
val transaction = blob("transaction")

View File

@ -8,10 +8,7 @@ import com.google.common.hash.HashingInputStream
import com.google.common.io.CountingInputStream
import net.corda.core.contracts.AbstractAttachment
import net.corda.core.contracts.Attachment
import net.corda.core.createDirectory
import net.corda.core.crypto.SecureHash
import net.corda.core.div
import net.corda.core.extractZipFile
import net.corda.core.isDirectory
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.*
@ -35,7 +32,7 @@ import javax.annotation.concurrent.ThreadSafe
* Stores attachments in H2 database.
*/
@ThreadSafe
class NodeAttachmentService(override var storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry)
class NodeAttachmentService(val storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry)
: AttachmentStorage, AcceptsFileUpload, SingletonSerializeAsToken() {
companion object {
private val log = loggerFor<NodeAttachmentService>()
@ -48,7 +45,6 @@ class NodeAttachmentService(override var storePath: Path, dataSourceProperties:
var checkAttachmentsOnLoad = true
private val attachmentCount = metrics.counter("Attachments")
@Volatile override var automaticallyExtractAttachments = false
init {
require(storePath.isDirectory()) { "$storePath must be a directory" }
@ -183,19 +179,6 @@ class NodeAttachmentService(override var storePath: Path, dataSourceProperties:
log.info("Stored new attachment $id")
if (automaticallyExtractAttachments) {
val extractTo = storePath / "$id.jar"
try {
extractTo.createDirectory()
extractZipFile(ByteArrayInputStream(bytes), extractTo)
} catch(e: FileAlreadyExistsException) {
log.trace("Did not extract attachment jar to directory because it already exists")
} catch(e: Exception) {
log.error("Failed to extract attachment jar $id, ", e)
// TODO: Delete the extractTo directory here.
}
}
return id
}

View File

@ -22,7 +22,6 @@ import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.node.NodeInfo
import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.TransactionStorage
import net.corda.core.node.services.Vault
import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction
@ -32,6 +31,7 @@ import net.corda.core.utilities.*
import net.corda.flows.TwoPartyTradeFlow.Buyer
import net.corda.flows.TwoPartyTradeFlow.Seller
import net.corda.node.internal.AbstractNode
import net.corda.node.services.api.WritableTransactionStorage
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.persistence.checkpoints
@ -153,7 +153,7 @@ class TwoPartyTradeFlowTests {
val cashLockId = UUID.randomUUID()
bobNode.database.transaction {
// lock the cash states with an arbitrary lockId (to prevent the Buyer flow from claiming the states)
bobNode.vault.softLockReserve(cashLockId, cashStates.states.map { it.ref }.toSet())
bobNode.services.vaultService.softLockReserve(cashLockId, cashStates.states.map { it.ref }.toSet())
}
val (bobStateMachine, aliceResult) = runBuyerAndSeller(notaryNode, aliceNode, bobNode,
@ -284,8 +284,8 @@ class TwoPartyTradeFlowTests {
entropyRoot: BigInteger): MockNetwork.MockNode {
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {
// That constructs a recording tx storage
override fun createTransactionStorage(): TransactionStorage {
return RecordingTransactionStorage(database, super.createTransactionStorage())
override fun makeTransactionStorage(): WritableTransactionStorage {
return RecordingTransactionStorage(database, super.makeTransactionStorage())
}
}
}
@ -311,7 +311,7 @@ class TwoPartyTradeFlowTests {
attachment(ByteArrayInputStream(stream.toByteArray()))
}
val extraKey = bobNode.keyManagement.keys.single()
val extraKey = bobNode.services.keyManagementService.keys.single()
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(extraKey),
DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second
@ -410,7 +410,7 @@ class TwoPartyTradeFlowTests {
attachment(ByteArrayInputStream(stream.toByteArray()))
}
val bobsKey = bobNode.keyManagement.keys.single()
val bobsKey = bobNode.services.keyManagementService.keys.single()
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(bobsKey),
DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second
@ -675,7 +675,7 @@ class TwoPartyTradeFlowTests {
}
class RecordingTransactionStorage(val database: Database, val delegate: TransactionStorage) : TransactionStorage {
class RecordingTransactionStorage(val database: Database, val delegate: WritableTransactionStorage) : WritableTransactionStorage {
override fun track(): DataFeed<List<SignedTransaction>, SignedTransaction> {
return database.transaction {
delegate.track()

View File

@ -18,19 +18,20 @@ import net.corda.node.services.transactions.InMemoryTransactionVerifierService
import net.corda.testing.MOCK_IDENTITY_SERVICE
import net.corda.testing.node.MockAttachmentStorage
import net.corda.testing.node.MockNetworkMapCache
import org.jetbrains.exposed.sql.Database
import net.corda.testing.node.MockStateMachineRecordedTransactionMappingStorage
import net.corda.testing.node.MockTransactionStorage
import org.jetbrains.exposed.sql.Database
import java.time.Clock
open class MockServiceHubInternal(
override val database: Database,
val customVault: VaultService? = null,
val customVaultQuery: VaultQueryService? = null,
val keyManagement: KeyManagementService? = null,
val network: MessagingService? = null,
val identity: IdentityService? = MOCK_IDENTITY_SERVICE,
override val attachments: AttachmentStorage = MockAttachmentStorage(),
override val validatedTransactions: TransactionStorage = MockTransactionStorage(),
override val validatedTransactions: WritableTransactionStorage = MockTransactionStorage(),
override val uploaders: List<FileUploader> = listOf<FileUploader>(),
override val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage(),
val mapCache: NetworkMapCacheInternal? = null,
@ -59,8 +60,6 @@ open class MockServiceHubInternal(
get() = overrideClock ?: throw UnsupportedOperationException()
override val myInfo: NodeInfo
get() = throw UnsupportedOperationException()
override val database: Database
get() = throw UnsupportedOperationException()
override val configuration: NodeConfiguration
get() = throw UnsupportedOperationException()
override val monitoringService: MonitoringService = MonitoringService(MetricRegistry())

View File

@ -135,7 +135,7 @@ class NotaryChangeTests {
addOutputState(stateB, notary, encumbrance = 1) // Encumbered by stateC
}
val stx = node.services.signInitialTransaction(tx)
node.services.recordTransactions(listOf(stx))
node.services.recordTransactions(stx)
return tx.toWireTransaction()
}
@ -152,7 +152,7 @@ fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> {
val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0))
val signedByNode = node.services.signInitialTransaction(tx)
val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
node.services.recordTransactions(listOf(stx))
node.services.recordTransactions(stx)
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}
@ -163,8 +163,8 @@ fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: A
val signedByA = nodeA.services.signInitialTransaction(tx)
val signedByAB = nodeB.services.addSignature(signedByA)
val stx = notaryNode.services.addSignature(signedByAB, notaryNode.services.notaryIdentityKey)
nodeA.services.recordTransactions(listOf(stx))
nodeB.services.recordTransactions(listOf(stx))
nodeA.services.recordTransactions(stx)
nodeB.services.recordTransactions(stx)
val stateAndRef = StateAndRef(state, StateRef(stx.id, 0))
return stateAndRef
}
@ -173,6 +173,6 @@ fun issueInvalidState(node: AbstractNode, notary: Party): StateAndRef<*> {
val tx = DummyContract.generateInitial(Random().nextInt(), notary, node.info.legalIdentity.ref(0))
tx.addTimeWindow(Instant.now(), 30.seconds)
val stx = node.services.signInitialTransaction(tx)
node.services.recordTransactions(listOf(stx))
node.services.recordTransactions(stx)
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}

View File

@ -87,11 +87,11 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
InMemoryMessagingNetwork.PeerHandle(0, nullIdentity),
AffinityExecutor.ServiceAffinityExecutor("test", 1),
database)
services = object : MockServiceHubInternal(overrideClock = testClock, keyManagement = kms, network = mockMessagingService), TestReference {
services = object : MockServiceHubInternal(database, overrideClock = testClock, keyManagement = kms, network = mockMessagingService), TestReference {
override val vaultService: VaultService = NodeVaultService(this, dataSourceProps)
override val testReference = this@NodeSchedulerServiceTest
}
scheduler = NodeSchedulerService(services, database, schedulerGatedExecutor)
scheduler = NodeSchedulerService(services, schedulerGatedExecutor)
smmExecutor = AffinityExecutor.ServiceAffinityExecutor("test", 1)
val mockSMM = StateMachineManager(services, DBCheckpointStorage(), smmExecutor, database)
mockSMM.changes.subscribe { change ->

View File

@ -38,13 +38,13 @@ class InMemoryNetworkMapCacheTest {
mockNet.runNetwork()
// Node A currently knows only about itself, so this returns node A
assertEquals(nodeA.netMapCache.getNodeByLegalIdentityKey(nodeA.info.legalIdentity.owningKey), nodeA.info)
assertEquals(nodeA.services.networkMapCache.getNodeByLegalIdentityKey(nodeA.info.legalIdentity.owningKey), nodeA.info)
nodeA.database.transaction {
nodeA.netMapCache.addNode(nodeB.info)
nodeA.services.networkMapCache.addNode(nodeB.info)
}
// The details of node B write over those for node A
assertEquals(nodeA.netMapCache.getNodeByLegalIdentityKey(nodeA.info.legalIdentity.owningKey), nodeB.info)
assertEquals(nodeA.services.networkMapCache.getNodeByLegalIdentityKey(nodeA.info.legalIdentity.owningKey), nodeB.info)
}
@Test

View File

@ -131,7 +131,7 @@ class NotaryServiceTests {
val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0))
val signedByNode = node.services.signInitialTransaction(tx)
val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
node.services.recordTransactions(listOf(stx))
node.services.recordTransactions(stx)
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}
}

View File

@ -82,7 +82,7 @@ class ValidatingNotaryServiceTests {
val tx = DummyContract.generateInitial(Random().nextInt(), notaryNode.info.notaryIdentity, node.info.legalIdentity.ref(0))
val signedByNode = node.services.signInitialTransaction(tx)
val stx = notaryNode.services.addSignature(signedByNode, notaryNode.services.notaryIdentityKey)
node.services.recordTransactions(listOf(stx))
node.services.recordTransactions(stx)
return StateAndRef(tx.outputStates().first(), StateRef(stx.id, 0))
}
}

View File

@ -405,7 +405,7 @@ class NodeVaultServiceTest {
}
val usefulTX = megaCorpServices.signInitialTransaction(usefulBuilder)
services.recordTransactions(listOf(usefulTX))
services.recordTransactions(usefulTX)
vaultSvc.addNoteToTransaction(usefulTX.id, "USD Sample Note 1")
vaultSvc.addNoteToTransaction(usefulTX.id, "USD Sample Note 2")
@ -418,7 +418,7 @@ class NodeVaultServiceTest {
}
val anotherTX = megaCorpServices.signInitialTransaction(anotherBuilder)
services.recordTransactions(listOf(anotherTX))
services.recordTransactions(anotherTX)
vaultSvc.addNoteToTransaction(anotherTX.id, "GPB Sample Note 1")
assertEquals(1, vaultSvc.getTransactionNotes(anotherTX.id).count())

View File

@ -167,7 +167,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
val serviceProviders: List<SimulatedNode> = listOf(notary, ratesOracle, networkMap)
val banks: List<SimulatedNode> = bankFactory.createAll()
val clocks = (serviceProviders + regulators + banks).map { it.services.clock as TestClock }
val clocks = (serviceProviders + regulators + banks).map { it.platformClock as TestClock }
// These are used from the network visualiser tool.
private val _allFlowSteps = PublishSubject.create<Pair<SimulatedNode, ProgressTracker.Change>>()

View File

@ -4,7 +4,6 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.CommercialPaper
import net.corda.core.contracts.Amount
import net.corda.core.contracts.TransactionGraphSearch
import net.corda.core.div
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.identity.Party
@ -39,7 +38,7 @@ class BuyerFlow(val otherParty: Party) : FlowLogic<Unit>() {
// This invokes the trading flow and out pops our finished transaction.
val tradeTX: SignedTransaction = subFlow(buyer)
// TODO: This should be moved into the flow itself.
serviceHub.recordTransactions(listOf(tradeTX))
serviceHub.recordTransactions(tradeTX)
println("Purchase complete - we are a happy customer! Final transaction is: " +
"\n\n${Emoji.renderIfSupported(tradeTX.tx)}")
@ -61,18 +60,11 @@ class BuyerFlow(val otherParty: Party) : FlowLogic<Unit>() {
val cpIssuance = search.call().single()
// Buyer will fetch the attachment from the seller automatically when it resolves the transaction.
// For demo purposes just extract attachment jars when saved to disk, so the user can explore them.
val attachmentsPath = (serviceHub.attachments).let {
it.automaticallyExtractAttachments = true
it.storePath
}
cpIssuance.attachments.first().let {
val p = attachmentsPath / "$it.jar"
println("""
The issuance of the commercial paper came with an attachment. You can find it expanded in this directory:
$p
The issuance of the commercial paper came with an attachment. You can find it in the attachments directory: $it.jar
${Emoji.renderIfSupported(cpIssuance)}""")
}

View File

@ -15,9 +15,11 @@ import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.RPCOps
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.WorldMapLocation
import net.corda.core.node.ServiceEntry
import net.corda.core.node.services.*
import net.corda.core.node.WorldMapLocation
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService
import net.corda.core.node.services.ServiceInfo
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.getTestPartyAndCertificate
import net.corda.core.utilities.loggerFor
@ -32,7 +34,6 @@ import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
import net.corda.testing.MOCK_VERSION_INFO
@ -181,8 +182,6 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
trustRoot = trustRoot, caCertificates = *caCertificates)
}
override fun makeVaultService(dataSourceProperties: Properties): VaultService = NodeVaultService(services, dataSourceProperties)
override fun makeKeyManagementService(identityService: IdentityService): KeyManagementService {
return E2ETestKeyManagementService(identityService, partyKeys + (overrideServices?.values ?: emptySet()))
}

View File

@ -16,6 +16,7 @@ import net.corda.core.utilities.DUMMY_CA
import net.corda.core.utilities.getTestPartyAndCertificate
import net.corda.flows.AnonymisedIdentity
import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
import net.corda.node.services.api.WritableTransactionStorage
import net.corda.node.services.database.HibernateConfiguration
import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.node.services.keys.freshCertificate
@ -35,8 +36,6 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.InputStream
import java.nio.file.Path
import java.nio.file.Paths
import java.security.KeyPair
import java.security.PrivateKey
import java.security.PublicKey
@ -66,7 +65,7 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub {
}
override val attachments: AttachmentStorage = MockAttachmentStorage()
override val validatedTransactions: TransactionStorage = MockTransactionStorage()
override val validatedTransactions: WritableTransactionStorage = MockTransactionStorage()
val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage()
override final val identityService: IdentityService = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DUMMY_CA.certificate)
override val keyManagementService: KeyManagementService = MockKeyManagementService(identityService, *keys)
@ -124,10 +123,8 @@ class MockKeyManagementService(val identityService: IdentityService,
}
}
class MockAttachmentStorage : AttachmentStorage {
class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
val files = HashMap<SecureHash, ByteArray>()
override var automaticallyExtractAttachments = false
override var storePath: Path = Paths.get("")
override fun openAttachment(id: SecureHash): Attachment? {
val f = files[id] ?: return null
@ -159,7 +156,7 @@ class MockStateMachineRecordedTransactionMappingStorage(
val storage: StateMachineRecordedTransactionMappingStorage = InMemoryStateMachineRecordedTransactionMappingStorage()
) : StateMachineRecordedTransactionMappingStorage by storage
open class MockTransactionStorage : TransactionStorage {
open class MockTransactionStorage : WritableTransactionStorage, SingletonSerializeAsToken() {
override fun track(): DataFeed<List<SignedTransaction>, SignedTransaction> {
return DataFeed(txns.values.toList(), _updatesPublisher)
}