Removed the StorageService and puts its components directly into the service hub

This commit is contained in:
Shams Asari 2017-06-29 11:13:40 +01:00
parent ecc96b29b0
commit a08f701dc5
45 changed files with 240 additions and 316 deletions

View File

@ -12,12 +12,12 @@ import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.messaging.StateMachineUpdate import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction

View File

@ -7,9 +7,9 @@ import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.StateMachineInfo import net.corda.core.messaging.StateMachineInfo
import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.messaging.StateMachineUpdate import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction

View File

@ -79,8 +79,7 @@ interface ContractState {
* so that they receive the updated state, and don't end up in a situation where they can no longer use a state * so that they receive the updated state, and don't end up in a situation where they can no longer use a state
* they possess, since someone consumed that state during the notary change process. * they possess, since someone consumed that state during the notary change process.
* *
* The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants * The participants list should normally be derived from the contents of the state.
* list should just contain the owner.
*/ */
val participants: List<AbstractParty> val participants: List<AbstractParty>
} }
@ -126,7 +125,7 @@ infix fun <T : ContractState> T.withNotary(newNotary: Party) = TransactionState(
* Definition for an issued product, which can be cash, a cash-like thing, assets, or generally anything else that's * Definition for an issued product, which can be cash, a cash-like thing, assets, or generally anything else that's
* quantifiable with integer quantities. * quantifiable with integer quantities.
* *
* @param P the type of product underlying the definition, for example [Currency]. * @param P the type of product underlying the definition, for example [java.util.Currency].
*/ */
@CordaSerializable @CordaSerializable
data class Issued<out P : Any>(val issuer: PartyAndReference, val product: P) { data class Issued<out P : Any>(val issuer: PartyAndReference, val product: P) {
@ -159,8 +158,8 @@ interface Scheduled {
} }
/** /**
* Represents a contract state (unconsumed output) of type [LinearState] and a point in time that a lifecycle event is expected to take place * Represents a contract state (unconsumed output) of type [LinearState] and a point in time that a lifecycle event is
* for that contract state. * expected to take place for that contract state.
* *
* This is effectively the input to a scheduler, which wakes up at that point in time and asks the contract state what * This is effectively the input to a scheduler, which wakes up at that point in time and asks the contract state what
* lifecycle processing needs to take place. e.g. a fixing or a late payment etc. * lifecycle processing needs to take place. e.g. a fixing or a late payment etc.
@ -168,10 +167,11 @@ interface Scheduled {
data class ScheduledStateRef(val ref: StateRef, override val scheduledAt: Instant) : Scheduled data class ScheduledStateRef(val ref: StateRef, override val scheduledAt: Instant) : Scheduled
/** /**
* This class represents the lifecycle activity that a contract state of type [LinearState] would like to perform at a given point in time. * This class represents the lifecycle activity that a contract state of type [LinearState] would like to perform at a
* e.g. run a fixing flow. * given point in time. e.g. run a fixing flow.
* *
* Note the use of [FlowLogicRef] to represent a safe way to transport a [FlowLogic] out of the contract sandbox. * Note the use of [FlowLogicRef] to represent a safe way to transport a [net.corda.core.flows.FlowLogic] out of the
* contract sandbox.
* *
* Currently we support only flow based activities as we expect there to be a transaction generated off the back of * Currently we support only flow based activities as we expect there to be a transaction generated off the back of
* the activity, otherwise we have to start tracking secondary state on the platform of which scheduled activities * the activity, otherwise we have to start tracking secondary state on the platform of which scheduled activities
@ -383,9 +383,9 @@ class TimeWindow private constructor(
// DOCSTART 5 // DOCSTART 5
/** /**
* Implemented by a program that implements business logic on the shared ledger. All participants run this code for * Implemented by a program that implements business logic on the shared ledger. All participants run this code for
* every [LedgerTransaction] they see on the network, for every input and output state. All contracts must accept the * every [net.corda.core.transactions.LedgerTransaction] they see on the network, for every input and output state. All
* transaction for it to be accepted: failure of any aborts the entire thing. The time is taken from a trusted * contracts must accept the transaction for it to be accepted: failure of any aborts the entire thing. The time is taken
* time-window attached to the transaction itself i.e. it is NOT necessarily the current time. * from a trusted time-window attached to the transaction itself i.e. it is NOT necessarily the current time.
* *
* TODO: Contract serialization is likely to change, so the annotation is likely temporary. * TODO: Contract serialization is likely to change, so the annotation is likely temporary.
*/ */
@ -461,9 +461,8 @@ interface Attachment : NamedByHash {
abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment { abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment {
companion object { companion object {
fun SerializeAsTokenContext.attachmentDataLoader(id: SecureHash): () -> ByteArray { fun SerializeAsTokenContext.attachmentDataLoader(id: SecureHash): () -> ByteArray {
val storage = serviceHub.storageService.attachments
return { return {
val a = storage.openAttachment(id) ?: throw MissingAttachmentsException(listOf(id)) val a = serviceHub.attachments.openAttachment(id) ?: throw MissingAttachmentsException(listOf(id))
(a as? AbstractAttachment)?.attachmentData ?: a.open().use { it.readBytes() } (a as? AbstractAttachment)?.attachmentData ?: a.open().use { it.readBytes() }
} }
} }

View File

@ -13,7 +13,6 @@ import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.node.services.vault.PageSpecification import net.corda.core.node.services.vault.PageSpecification
import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.node.services.vault.QueryCriteria
@ -48,6 +47,9 @@ sealed class StateMachineUpdate {
data class Removed(override val id: StateMachineRunId, val result: ErrorOr<*>) : StateMachineUpdate() data class Removed(override val id: StateMachineRunId, val result: ErrorOr<*>) : StateMachineUpdate()
} }
@CordaSerializable
data class StateMachineTransactionMapping(val stateMachineRunId: StateMachineRunId, val transactionId: SecureHash)
/** /**
* RPC operations that the node exposes to clients using the Java client library. These can be called from * RPC operations that the node exposes to clients using the Java client library. These can be called from
* client apps and are implemented by the node in the [net.corda.node.internal.CordaRPCOpsImpl] class. * client apps and are implemented by the node in the [net.corda.node.internal.CordaRPCOpsImpl] class.

View File

@ -17,8 +17,8 @@ import java.time.Clock
*/ */
interface ServicesForResolution { interface ServicesForResolution {
val identityService: IdentityService val identityService: IdentityService
val storageService: AttachmentsStorageService /** 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]. * Given a [StateRef] loads the referenced transaction and looks up the specified output [ContractState].
* *
@ -40,7 +40,13 @@ interface ServiceHub : ServicesForResolution {
val vaultService: VaultService val vaultService: VaultService
val vaultQueryService: VaultQueryService val vaultQueryService: VaultQueryService
val keyManagementService: KeyManagementService val keyManagementService: KeyManagementService
override val storageService: StorageService /**
* 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 networkMapCache: NetworkMapCache val networkMapCache: NetworkMapCache
val transactionVerifierService: TransactionVerifierService val transactionVerifierService: TransactionVerifierService
val clock: Clock val clock: Clock
@ -77,7 +83,7 @@ interface ServiceHub : ServicesForResolution {
*/ */
@Throws(TransactionResolutionException::class) @Throws(TransactionResolutionException::class)
override fun loadState(stateRef: StateRef): TransactionState<*> { override fun loadState(stateRef: StateRef): TransactionState<*> {
val definingTx = storageService.validatedTransactions.getTransaction(stateRef.txhash) ?: throw TransactionResolutionException(stateRef.txhash) val definingTx = validatedTransactions.getTransaction(stateRef.txhash) ?: throw TransactionResolutionException(stateRef.txhash)
return definingTx.tx.outputs[stateRef.index] return definingTx.tx.outputs[stateRef.index]
} }
@ -87,7 +93,7 @@ interface ServiceHub : ServicesForResolution {
* @throws IllegalProtocolLogicException or IllegalArgumentException if there are problems with the [logicType] or [args]. * @throws IllegalProtocolLogicException or IllegalArgumentException if there are problems with the [logicType] or [args].
*/ */
fun <T : ContractState> toStateAndRef(ref: StateRef): StateAndRef<T> { fun <T : ContractState> toStateAndRef(ref: StateRef): StateAndRef<T> {
val definingTx = storageService.validatedTransactions.getTransaction(ref.txhash) ?: throw TransactionResolutionException(ref.txhash) val definingTx = validatedTransactions.getTransaction(ref.txhash) ?: throw TransactionResolutionException(ref.txhash)
return definingTx.tx.outRef<T>(ref.index) return definingTx.tx.outRef<T>(ref.index)
} }

View File

@ -22,12 +22,10 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.flows.AnonymisedIdentity import net.corda.flows.AnonymisedIdentity
import org.bouncycastle.cert.X509CertificateHolder
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.io.InputStream import java.io.InputStream
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -528,44 +526,6 @@ interface FileUploader {
fun accepts(type: String): Boolean fun accepts(type: String): Boolean
} }
interface AttachmentsStorageService {
/** Provides access to storage of arbitrary JAR files (which may contain only data, no code). */
val attachments: AttachmentStorage
val attachmentsClassLoaderEnabled: Boolean
}
/**
* A sketch of an interface to a simple key/value storage system. Intended for persistence of simple blobs like
* transactions, serialised flow state machines and so on. Again, this isn't intended to imply lack of SQL or
* anything like that, this interface is only big enough to support the prototyping work.
*/
interface StorageService : AttachmentsStorageService {
/**
* 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
@Suppress("DEPRECATION")
@Deprecated("This service will be removed in a future milestone")
val uploaders: List<FileUploader>
val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage
}
/**
* Storage service, with extensions to allow validated transactions to be added to. For use only within [ServiceHub].
*/
interface TxWritableStorageService : StorageService {
/**
* 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.
*/
override val validatedTransactions: TransactionStorage
}
/** /**
* Provides verification service. The implementation may be a simple in-memory verify() call or perhaps an IPC/RPC. * Provides verification service. The implementation may be a simple in-memory verify() call or perhaps an IPC/RPC.
*/ */

View File

@ -1,19 +0,0 @@
package net.corda.core.node.services
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.DataFeed
import net.corda.core.serialization.CordaSerializable
import rx.Observable
@CordaSerializable
data class StateMachineTransactionMapping(val stateMachineRunId: StateMachineRunId, val transactionId: SecureHash)
/**
* 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.
*/
interface StateMachineRecordedTransactionMappingStorage {
fun addMapping(stateMachineRunId: StateMachineRunId, transactionId: SecureHash)
fun track(): DataFeed<List<StateMachineTransactionMapping>, StateMachineTransactionMapping>
}

View File

@ -317,6 +317,9 @@ class MissingAttachmentsException(val ids: List<SecureHash>) : Exception()
/** A serialisation engine that knows how to deserialise code inside a sandbox */ /** A serialisation engine that knows how to deserialise code inside a sandbox */
@ThreadSafe @ThreadSafe
object WireTransactionSerializer : Serializer<WireTransaction>() { object WireTransactionSerializer : Serializer<WireTransaction>() {
@VisibleForTesting
internal val attachmentsClassLoaderEnabled = "attachments.class.loader.enabled"
override fun write(kryo: Kryo, output: Output, obj: WireTransaction) { override fun write(kryo: Kryo, output: Output, obj: WireTransaction) {
kryo.writeClassAndObject(output, obj.inputs) kryo.writeClassAndObject(output, obj.inputs)
kryo.writeClassAndObject(output, obj.attachments) kryo.writeClassAndObject(output, obj.attachments)
@ -329,12 +332,12 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
} }
private fun attachmentsClassLoader(kryo: Kryo, attachmentHashes: List<SecureHash>): ClassLoader? { private fun attachmentsClassLoader(kryo: Kryo, attachmentHashes: List<SecureHash>): ClassLoader? {
kryo.context[attachmentsClassLoaderEnabled] as? Boolean ?: false || return null
val serializationContext = kryo.serializationContext() ?: return null // Some tests don't set one. val serializationContext = kryo.serializationContext() ?: return null // Some tests don't set one.
serializationContext.serviceHub.storageService.attachmentsClassLoaderEnabled || return null
val missing = ArrayList<SecureHash>() val missing = ArrayList<SecureHash>()
val attachments = ArrayList<Attachment>() val attachments = ArrayList<Attachment>()
attachmentHashes.forEach { id -> attachmentHashes.forEach { id ->
serializationContext.serviceHub.storageService.attachments.openAttachment(id)?.let { attachments += it } ?: run { missing += id } serializationContext.serviceHub.attachments.openAttachment(id)?.let { attachments += it } ?: run { missing += id }
} }
missing.isNotEmpty() && throw MissingAttachmentsException(missing) missing.isNotEmpty() && throw MissingAttachmentsException(missing)
return AttachmentsClassLoader(attachments) return AttachmentsClassLoader(attachments)
@ -635,7 +638,7 @@ object X500NameSerializer : Serializer<X500Name>() {
*/ */
@ThreadSafe @ThreadSafe
object CertPathSerializer : Serializer<CertPath>() { object CertPathSerializer : Serializer<CertPath>() {
val factory = CertificateFactory.getInstance("X.509") val factory: CertificateFactory = CertificateFactory.getInstance("X.509")
override fun read(kryo: Kryo, input: Input, type: Class<CertPath>): CertPath { override fun read(kryo: Kryo, input: Input, type: Class<CertPath>): CertPath {
return factory.generateCertPath(input) return factory.generateCertPath(input)
} }
@ -646,7 +649,7 @@ object CertPathSerializer : Serializer<CertPath>() {
} }
/** /**
* For serialising an [CX509CertificateHolder] in an X.500 standard format. * For serialising an [X509CertificateHolder] in an X.500 standard format.
*/ */
@ThreadSafe @ThreadSafe
object X509CertificateSerializer : Serializer<X509CertificateHolder>() { object X509CertificateSerializer : Serializer<X509CertificateHolder>() {

View File

@ -73,7 +73,7 @@ class WireTransaction(
fun toLedgerTransaction(services: ServicesForResolution): LedgerTransaction { fun toLedgerTransaction(services: ServicesForResolution): LedgerTransaction {
return toLedgerTransaction( return toLedgerTransaction(
resolveIdentity = { services.identityService.partyFromKey(it) }, resolveIdentity = { services.identityService.partyFromKey(it) },
resolveAttachment = { services.storageService.attachments.openAttachment(it) }, resolveAttachment = { services.attachments.openAttachment(it) },
resolveStateRef = { services.loadState(it) } resolveStateRef = { services.loadState(it) }
) )
} }

View File

@ -18,13 +18,13 @@ import net.corda.core.serialization.SerializeAsTokenContext
class FetchAttachmentsFlow(requests: Set<SecureHash>, class FetchAttachmentsFlow(requests: Set<SecureHash>,
otherSide: Party) : FetchDataFlow<Attachment, ByteArray>(requests, otherSide) { otherSide: Party) : FetchDataFlow<Attachment, ByteArray>(requests, otherSide) {
override fun load(txid: SecureHash): Attachment? = serviceHub.storageService.attachments.openAttachment(txid) override fun load(txid: SecureHash): Attachment? = serviceHub.attachments.openAttachment(txid)
override fun convert(wire: ByteArray): Attachment = FetchedAttachment({ wire }) override fun convert(wire: ByteArray): Attachment = FetchedAttachment({ wire })
override fun maybeWriteToDisk(downloaded: List<Attachment>) { override fun maybeWriteToDisk(downloaded: List<Attachment>) {
for (attachment in downloaded) { for (attachment in downloaded) {
serviceHub.storageService.attachments.importAttachment(attachment.open()) serviceHub.attachments.importAttachment(attachment.open())
} }
} }

View File

@ -17,7 +17,5 @@ import net.corda.core.transactions.SignedTransaction
class FetchTransactionsFlow(requests: Set<SecureHash>, otherSide: Party) : class FetchTransactionsFlow(requests: Set<SecureHash>, otherSide: Party) :
FetchDataFlow<SignedTransaction, SignedTransaction>(requests, otherSide) { FetchDataFlow<SignedTransaction, SignedTransaction>(requests, otherSide) {
override fun load(txid: SecureHash): SignedTransaction? { override fun load(txid: SecureHash): SignedTransaction? = serviceHub.validatedTransactions.getTransaction(txid)
return serviceHub.storageService.validatedTransactions.getTransaction(txid)
}
} }

View File

@ -4,10 +4,8 @@ import net.corda.core.contracts.*
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import java.security.PublicKey
/** /**
* A flow to be used for changing a state's Notary. This is required since all input states to a transaction * A flow to be used for changing a state's Notary. This is required since all input states to a transaction
@ -55,7 +53,7 @@ class NotaryChangeFlow<out T : ContractState>(
private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<AbstractParty> { private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<AbstractParty> {
val stateRef = originalState.ref val stateRef = originalState.ref
val txId = stateRef.txhash val txId = stateRef.txhash
val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId) val issuingTx = serviceHub.validatedTransactions.getTransaction(txId)
?: throw StateReplacementException("Transaction $txId not found") ?: throw StateReplacementException("Transaction $txId not found")
val outputs = issuingTx.tx.outputs val outputs = issuingTx.tx.outputs

View File

@ -193,7 +193,7 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
private fun fetchMissingAttachments(downloads: List<WireTransaction>) { private fun fetchMissingAttachments(downloads: List<WireTransaction>) {
// TODO: This could be done in parallel with other fetches for extra speed. // TODO: This could be done in parallel with other fetches for extra speed.
val missingAttachments = downloads.flatMap { wtx -> val missingAttachments = downloads.flatMap { wtx ->
wtx.attachments.filter { serviceHub.storageService.attachments.openAttachment(it) == null } wtx.attachments.filter { serviceHub.attachments.openAttachment(it) == null }
} }
if (missingAttachments.isNotEmpty()) if (missingAttachments.isNotEmpty())
subFlow(FetchAttachmentsFlow(missingAttachments.toSet(), otherSide)) subFlow(FetchAttachmentsFlow(missingAttachments.toSet(), otherSide))

View File

@ -64,8 +64,8 @@ class ContractUpgradeFlowTest {
a.services.startFlow(FinalityFlow(stx, setOf(a.info.legalIdentity, b.info.legalIdentity))) a.services.startFlow(FinalityFlow(stx, setOf(a.info.legalIdentity, b.info.legalIdentity)))
mockNet.runNetwork() mockNet.runNetwork()
val atx = a.database.transaction { a.services.storageService.validatedTransactions.getTransaction(stx.id) } val atx = a.database.transaction { a.services.validatedTransactions.getTransaction(stx.id) }
val btx = b.database.transaction { b.services.storageService.validatedTransactions.getTransaction(stx.id) } val btx = b.database.transaction { b.services.validatedTransactions.getTransaction(stx.id) }
requireNotNull(atx) requireNotNull(atx)
requireNotNull(btx) requireNotNull(btx)
@ -85,13 +85,13 @@ class ContractUpgradeFlowTest {
fun check(node: MockNetwork.MockNode) { fun check(node: MockNetwork.MockNode) {
val nodeStx = node.database.transaction { val nodeStx = node.database.transaction {
node.services.storageService.validatedTransactions.getTransaction(result.ref.txhash) node.services.validatedTransactions.getTransaction(result.ref.txhash)
} }
requireNotNull(nodeStx) requireNotNull(nodeStx)
// Verify inputs. // Verify inputs.
val input = node.database.transaction { val input = node.database.transaction {
node.services.storageService.validatedTransactions.getTransaction(nodeStx!!.tx.inputs.single().txhash) node.services.validatedTransactions.getTransaction(nodeStx!!.tx.inputs.single().txhash)
} }
requireNotNull(input) requireNotNull(input)
assertTrue(input!!.tx.outputs.single().data is DummyContract.State) assertTrue(input!!.tx.outputs.single().data is DummyContract.State)
@ -132,8 +132,8 @@ class ContractUpgradeFlowTest {
mockNet.runNetwork() mockNet.runNetwork()
handle.returnValue.getOrThrow() handle.returnValue.getOrThrow()
val atx = a.database.transaction { a.services.storageService.validatedTransactions.getTransaction(stx.id) } val atx = a.database.transaction { a.services.validatedTransactions.getTransaction(stx.id) }
val btx = b.database.transaction { b.services.storageService.validatedTransactions.getTransaction(stx.id) } val btx = b.database.transaction { b.services.validatedTransactions.getTransaction(stx.id) }
requireNotNull(atx) requireNotNull(atx)
requireNotNull(btx) requireNotNull(btx)
@ -156,11 +156,11 @@ class ContractUpgradeFlowTest {
val result = resultFuture.getOrThrow() val result = resultFuture.getOrThrow()
// Check results. // Check results.
listOf(a, b).forEach { listOf(a, b).forEach {
val signedTX = a.database.transaction { a.services.storageService.validatedTransactions.getTransaction(result.ref.txhash) } val signedTX = a.database.transaction { a.services.validatedTransactions.getTransaction(result.ref.txhash) }
requireNotNull(signedTX) requireNotNull(signedTX)
// Verify inputs. // Verify inputs.
val input = a.database.transaction { a.services.storageService.validatedTransactions.getTransaction(signedTX!!.tx.inputs.single().txhash) } val input = a.database.transaction { a.services.validatedTransactions.getTransaction(signedTX!!.tx.inputs.single().txhash) }
requireNotNull(input) requireNotNull(input)
assertTrue(input!!.tx.outputs.single().data is DummyContract.State) assertTrue(input!!.tx.outputs.single().data is DummyContract.State)

View File

@ -58,8 +58,8 @@ class ResolveTransactionsFlowTest {
val results = future.getOrThrow() val results = future.getOrThrow()
assertEquals(listOf(stx1.id, stx2.id), results.map { it.id }) assertEquals(listOf(stx1.id, stx2.id), results.map { it.id })
b.database.transaction { b.database.transaction {
assertEquals(stx1, b.storage.validatedTransactions.getTransaction(stx1.id)) assertEquals(stx1, b.services.validatedTransactions.getTransaction(stx1.id))
assertEquals(stx2, b.storage.validatedTransactions.getTransaction(stx2.id)) assertEquals(stx2, b.services.validatedTransactions.getTransaction(stx2.id))
} }
} }
// DOCEND 1 // DOCEND 1
@ -81,9 +81,9 @@ class ResolveTransactionsFlowTest {
mockNet.runNetwork() mockNet.runNetwork()
future.getOrThrow() future.getOrThrow()
b.database.transaction { b.database.transaction {
assertEquals(stx1, b.storage.validatedTransactions.getTransaction(stx1.id)) assertEquals(stx1, b.services.validatedTransactions.getTransaction(stx1.id))
// But stx2 wasn't inserted, just stx1. // But stx2 wasn't inserted, just stx1.
assertNull(b.storage.validatedTransactions.getTransaction(stx2.id)) assertNull(b.services.validatedTransactions.getTransaction(stx2.id))
} }
} }
@ -148,7 +148,7 @@ class ResolveTransactionsFlowTest {
} }
// TODO: this operation should not require an explicit transaction // TODO: this operation should not require an explicit transaction
val id = a.database.transaction { val id = a.database.transaction {
a.services.storageService.attachments.importAttachment(makeJar()) a.services.attachments.importAttachment(makeJar())
} }
val stx2 = makeTransactions(withAttachment = id).second val stx2 = makeTransactions(withAttachment = id).second
val p = ResolveTransactionsFlow(stx2, a.info.legalIdentity) val p = ResolveTransactionsFlow(stx2, a.info.legalIdentity)
@ -158,7 +158,7 @@ class ResolveTransactionsFlowTest {
// TODO: this operation should not require an explicit transaction // TODO: this operation should not require an explicit transaction
b.database.transaction { b.database.transaction {
assertNotNull(b.services.storageService.attachments.openAttachment(id)) assertNotNull(b.services.attachments.openAttachment(id))
} }
} }

View File

@ -8,7 +8,6 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.StorageService
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
@ -22,7 +21,6 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.net.URL import java.net.URL
import java.net.URLClassLoader import java.net.URLClassLoader
import java.security.PublicKey
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -42,11 +40,9 @@ class AttachmentClassLoaderTests {
val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentClassLoaderTests::class.java.getResource("isolated.jar") val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentClassLoaderTests::class.java.getResource("isolated.jar")
private fun <T> Kryo.withAttachmentStorage(attachmentStorage: AttachmentStorage, block: () -> T) = run { private fun <T> Kryo.withAttachmentStorage(attachmentStorage: AttachmentStorage, block: () -> T) = run {
context.put(WireTransactionSerializer.attachmentsClassLoaderEnabled, true)
val serviceHub = mock<ServiceHub>() val serviceHub = mock<ServiceHub>()
val storageService = mock<StorageService>() whenever(serviceHub.attachments).thenReturn(attachmentStorage)
whenever(serviceHub.storageService).thenReturn(storageService)
whenever(storageService.attachmentsClassLoaderEnabled).thenReturn(true)
whenever(storageService.attachments).thenReturn(attachmentStorage)
withSerializationContext(SerializeAsTokenContext(serviceHub) {}, block) withSerializationContext(SerializeAsTokenContext(serviceHub) {}, block)
} }
} }

View File

@ -42,9 +42,12 @@ private fun createAttachmentData(content: String) = ByteArrayOutputStream().appl
private fun Attachment.extractContent() = ByteArrayOutputStream().apply { extractFile("content", this) }.toString(UTF_8.name()) private fun Attachment.extractContent() = ByteArrayOutputStream().apply { extractFile("content", this) }.toString(UTF_8.name())
private fun MockNetwork.MockNode.attachments() = services.storageService.attachments as NodeAttachmentService private fun MockNetwork.MockNode.saveAttachment(content: String) = database.transaction {
private fun MockNetwork.MockNode.saveAttachment(content: String) = database.transaction { attachments().importAttachment(createAttachmentData(content).inputStream()) } attachments.importAttachment(createAttachmentData(content).inputStream())
private fun MockNetwork.MockNode.hackAttachment(attachmentId: SecureHash, content: String) = database.transaction { attachments().updateAttachment(attachmentId, createAttachmentData(content)) } }
private fun MockNetwork.MockNode.hackAttachment(attachmentId: SecureHash, content: String) = database.transaction {
attachments.updateAttachment(attachmentId, createAttachmentData(content))
}
/** /**
* @see NodeAttachmentService.importAttachment * @see NodeAttachmentService.importAttachment
@ -122,7 +125,7 @@ class AttachmentSerializationTest {
private class OpenAttachmentLogic(server: MockNetwork.MockNode, private val attachmentId: SecureHash) : ClientLogic(server) { private class OpenAttachmentLogic(server: MockNetwork.MockNode, private val attachmentId: SecureHash) : ClientLogic(server) {
@Suspendable @Suspendable
override fun getAttachmentContent(): String { override fun getAttachmentContent(): String {
val localAttachment = serviceHub.storageService.attachments.openAttachment(attachmentId)!! val localAttachment = serviceHub.attachments.openAttachment(attachmentId)!!
communicate() communicate()
return localAttachment.extractContent() return localAttachment.extractContent()
} }
@ -153,7 +156,7 @@ class AttachmentSerializationTest {
override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNetwork.MockNode { override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, advertisedServices: Set<ServiceInfo>, id: Int, overrideServices: Map<ServiceInfo, KeyPair>?, entropyRoot: BigInteger): MockNetwork.MockNode {
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {
override fun startMessagingService(rpcOps: RPCOps) { override fun startMessagingService(rpcOps: RPCOps) {
attachments().checkAttachmentsOnLoad = checkAttachmentsOnLoad attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad
super.startMessagingService(rpcOps) super.startMessagingService(rpcOps)
} }
} }
@ -180,7 +183,7 @@ class AttachmentSerializationTest {
@Test @Test
fun `only the hash of a regular attachment should be saved in checkpoint`() { fun `only the hash of a regular attachment should be saved in checkpoint`() {
val attachmentId = client.saveAttachment("genuine") val attachmentId = client.saveAttachment("genuine")
client.attachments().checkAttachmentsOnLoad = false // Cached by AttachmentImpl. client.attachments.checkAttachmentsOnLoad = false // Cached by AttachmentImpl.
launchFlow(OpenAttachmentLogic(server, attachmentId), 1) launchFlow(OpenAttachmentLogic(server, attachmentId), 1)
client.hackAttachment(attachmentId, "hacked") client.hackAttachment(attachmentId, "hacked")
assertEquals("hacked", rebootClientAndGetAttachmentContent(false)) // Pass in false to allow non-genuine data to be loaded. assertEquals("hacked", rebootClientAndGetAttachmentContent(false)) // Pass in false to allow non-genuine data to be loaded.

View File

@ -7,10 +7,12 @@ various services the node provides. The services offered by the ``ServiceHub`` a
* Provides information on other nodes on the network (e.g. notaries…) * Provides information on other nodes on the network (e.g. notaries…)
* ``ServiceHub.identityService`` * ``ServiceHub.identityService``
* Allows you to resolve anonymous identities to well-known identities if you have the required certificates * Allows you to resolve anonymous identities to well-known identities if you have the required certificates
* ``ServiceHub.attachments``
* Gives you access to the node's attachments
* ``ServiceHub.validatedTransactions``
* Gives you access to the transactions stored in the node
* ``ServiceHub.vaultService`` * ``ServiceHub.vaultService``
* Stores the nodes current and historic states * Stores the nodes current and historic states
* ``ServiceHub.storageService``
* Stores additional information such as transactions and attachments
* ``ServiceHub.keyManagementService`` * ``ServiceHub.keyManagementService``
* Manages signing transactions and generating fresh public keys * Manages signing transactions and generating fresh public keys
* ``ServiceHub.myInfo`` * ``ServiceHub.myInfo``

View File

@ -6,6 +6,7 @@ from the previous milestone release.
UNRELEASED UNRELEASED
---------- ----------
* Changes in ``NodeInfo``: * Changes in ``NodeInfo``:
* ``PhysicalLocation`` was renamed to ``WorldMapLocation`` to emphasise that it doesn't need to map to a truly physical * ``PhysicalLocation`` was renamed to ``WorldMapLocation`` to emphasise that it doesn't need to map to a truly physical
@ -13,8 +14,12 @@ UNRELEASED
* Slots for multiple IP addresses and ``legalIdentitiesAndCert``s were introduced. Addresses are no longer of type * Slots for multiple IP addresses and ``legalIdentitiesAndCert``s were introduced. Addresses are no longer of type
``SingleMessageRecipient``, but of ``HostAndPort``. ``SingleMessageRecipient``, but of ``HostAndPort``.
* ``ServiceHub.storageService`` has been removed. ``attachments`` and ``validatedTransactions`` are now direct members of
``ServiceHub``.
Milestone 13 Milestone 13
---------- ------------
Special thank you to `Frederic Dalibard <https://github.com/FredericDalibard>`_, for his contribution which adds Special thank you to `Frederic Dalibard <https://github.com/FredericDalibard>`_, for his contribution which adds
support for more currencies to the DemoBench and Explorer tools. support for more currencies to the DemoBench and Explorer tools.

View File

@ -220,7 +220,7 @@ class CommercialPaperTestsGeneric {
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) { for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx) validatedTransactions.addTransaction(stx)
} }
// Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions.
vaultService.notifyAll(txs.map { it.tx }) vaultService.notifyAll(txs.map { it.tx })
@ -240,7 +240,7 @@ class CommercialPaperTestsGeneric {
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) { for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx) validatedTransactions.addTransaction(stx)
} }
// Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions.
vaultService.notifyAll(txs.map { it.tx }) vaultService.notifyAll(txs.map { it.tx })
@ -251,8 +251,8 @@ class CommercialPaperTestsGeneric {
} }
// Propagate the cash transactions to each side. // Propagate the cash transactions to each side.
aliceServices.recordTransactions(bigCorpVault.states.map { bigCorpServices.storageService.validatedTransactions.getTransaction(it.ref.txhash)!! }) aliceServices.recordTransactions(bigCorpVault.states.map { bigCorpServices.validatedTransactions.getTransaction(it.ref.txhash)!! })
bigCorpServices.recordTransactions(alicesVault.states.map { aliceServices.storageService.validatedTransactions.getTransaction(it.ref.txhash)!! }) bigCorpServices.recordTransactions(alicesVault.states.map { aliceServices.validatedTransactions.getTransaction(it.ref.txhash)!! })
// BigCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself. // BigCorp™ issues $10,000 of commercial paper, to mature in 30 days, owned initially by itself.
val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER

View File

@ -63,7 +63,7 @@ class CashTests {
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) { for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx) validatedTransactions.addTransaction(stx)
} }
// Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions.
vaultService.notifyAll(txs.map { it.tx }) vaultService.notifyAll(txs.map { it.tx })

View File

@ -45,7 +45,10 @@ import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.network.NetworkMapService.RegistrationResponse import net.corda.node.services.network.NetworkMapService.RegistrationResponse
import net.corda.node.services.network.NodeRegistration import net.corda.node.services.network.NodeRegistration
import net.corda.node.services.network.PersistentNetworkMapService import net.corda.node.services.network.PersistentNetworkMapService
import net.corda.node.services.persistence.* import net.corda.node.services.persistence.DBCheckpointStorage
import net.corda.node.services.persistence.DBTransactionMappingStorage
import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.node.services.schema.HibernateObserver import net.corda.node.services.schema.HibernateObserver
import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.schema.NodeSchemaService
import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.statemachine.FlowStateMachineImpl
@ -70,7 +73,6 @@ import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Modifier.* import java.lang.reflect.Modifier.*
import java.net.JarURLConnection import java.net.JarURLConnection
import java.net.URI import java.net.URI
import java.nio.file.FileAlreadyExistsException
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.security.KeyPair import java.security.KeyPair
@ -120,10 +122,14 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
private val flowFactories = ConcurrentHashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>() private val flowFactories = ConcurrentHashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>()
protected val partyKeys = mutableSetOf<KeyPair>() protected val partyKeys = mutableSetOf<KeyPair>()
val services = object : ServiceHubInternal() { 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 networkService: MessagingService get() = network
override val networkMapCache: NetworkMapCacheInternal get() = netMapCache override val networkMapCache: NetworkMapCacheInternal get() = netMapCache
override val storageService: TxWritableStorageService get() = storage
override val vaultService: VaultService get() = vault override val vaultService: VaultService get() = vault
override val vaultQueryService: VaultQueryService get() = vaultQuery override val vaultQueryService: VaultQueryService get() = vaultQuery
override val keyManagementService: KeyManagementService get() = keyManagement override val keyManagementService: KeyManagementService get() = keyManagement
@ -157,7 +163,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
database.transaction { database.transaction {
recordTransactionsInternal(storage, txs) super.recordTransactions(txs)
} }
} }
} }
@ -167,9 +173,12 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
} }
lateinit var info: NodeInfo lateinit var info: NodeInfo
lateinit var storage: TxWritableStorageService
lateinit var checkpointStorage: CheckpointStorage lateinit var checkpointStorage: CheckpointStorage
lateinit var smm: StateMachineManager 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 vault: VaultService
lateinit var vaultQuery: VaultQueryService lateinit var vaultQuery: VaultQueryService
lateinit var keyManagement: KeyManagementService lateinit var keyManagement: KeyManagementService
@ -469,9 +478,10 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
*/ */
private fun makeServices(keyStoreWrapper: KeyStoreWrapper): MutableList<Any> { private fun makeServices(keyStoreWrapper: KeyStoreWrapper): MutableList<Any> {
val keyStore = keyStoreWrapper.keyStore val keyStore = keyStoreWrapper.keyStore
val storageServices = initialiseStorageService(configuration.baseDirectory) attachments = createAttachmentStorage()
storage = storageServices.first transactions = createTransactionStorage()
checkpointStorage = storageServices.second transactionMappings = DBTransactionMappingStorage()
checkpointStorage = DBCheckpointStorage()
netMapCache = InMemoryNetworkMapCache(services) netMapCache = InMemoryNetworkMapCache(services)
network = makeMessagingService() network = makeMessagingService()
schemas = makeSchemaService() schemas = makeSchemaService()
@ -490,11 +500,13 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
keyManagement = makeKeyManagementService(identity) keyManagement = makeKeyManagementService(identity)
scheduler = NodeSchedulerService(services, database, unfinishedSchedules = busyNodeLatch) scheduler = NodeSchedulerService(services, database, unfinishedSchedules = busyNodeLatch)
val tokenizableServices = mutableListOf(storage, network, vault, vaultQuery, keyManagement, identity, platformClock, scheduler) val tokenizableServices = mutableListOf(attachments, network, vault, vaultQuery, keyManagement, identity, platformClock, scheduler)
makeAdvertisedServices(tokenizableServices) makeAdvertisedServices(tokenizableServices)
return tokenizableServices return tokenizableServices
} }
protected open fun createTransactionStorage(): TransactionStorage = DBTransactionStorage()
private fun scanCordapps(): ScanResult? { private fun scanCordapps(): ScanResult? {
val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package") val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package")
val paths = if (scanPackage != null) { val paths = if (scanPackage != null) {
@ -548,9 +560,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
} }
private fun initUploaders() { private fun initUploaders() {
val uploaders: List<FileUploader> = listOf(storage.attachments as NodeAttachmentService) + uploaders = listOf(attachments) + cordappServices.values.filterIsInstance(AcceptsFileUpload::class.java)
cordappServices.values.filterIsInstance(AcceptsFileUpload::class.java)
(storage as StorageServiceImpl).initUploaders(uploaders)
} }
private fun makeVaultObservers() { private fun makeVaultObservers() {
@ -625,7 +635,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
* Run any tasks that are needed to ensure the node is in a correct state before running start(). * Run any tasks that are needed to ensure the node is in a correct state before running start().
*/ */
open fun setup(): AbstractNode { open fun setup(): AbstractNode {
createNodeDir() configuration.baseDirectory.createDirectories()
return this return this
} }
@ -761,22 +771,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected abstract fun startMessagingService(rpcOps: RPCOps) protected abstract fun startMessagingService(rpcOps: RPCOps)
protected open fun initialiseStorageService(dir: Path): Pair<TxWritableStorageService, CheckpointStorage> {
val attachments = makeAttachmentStorage(dir)
val checkpointStorage = DBCheckpointStorage()
val transactionStorage = DBTransactionStorage()
val stateMachineTransactionMappingStorage = DBTransactionMappingStorage()
return Pair(
constructStorageService(attachments, transactionStorage, stateMachineTransactionMappingStorage),
checkpointStorage
)
}
protected open fun constructStorageService(attachments: AttachmentStorage,
transactionStorage: TransactionStorage,
stateMachineRecordedTransactionMappingStorage: StateMachineRecordedTransactionMappingStorage) =
StorageServiceImpl(attachments, transactionStorage, stateMachineRecordedTransactionMappingStorage)
protected fun obtainLegalIdentity(): PartyAndCertificate = identityKeyPair.first protected fun obtainLegalIdentity(): PartyAndCertificate = identityKeyPair.first
protected fun obtainLegalIdentityKey(): KeyPair = identityKeyPair.second protected fun obtainLegalIdentityKey(): KeyPair = identityKeyPair.second
private val identityKeyPair by lazy { obtainKeyPair("identity", configuration.myLegalName) } private val identityKeyPair by lazy { obtainKeyPair("identity", configuration.myLegalName) }
@ -846,18 +840,10 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
protected open fun generateKeyPair() = cryptoGenerateKeyPair() protected open fun generateKeyPair() = cryptoGenerateKeyPair()
protected fun makeAttachmentStorage(dir: Path): AttachmentStorage { private fun createAttachmentStorage(): NodeAttachmentService {
val attachmentsDir = dir / "attachments" val attachmentsDir = (configuration.baseDirectory / "attachments").createDirectories()
try {
attachmentsDir.createDirectory()
} catch (e: FileAlreadyExistsException) {
}
return NodeAttachmentService(attachmentsDir, configuration.dataSourceProperties, services.monitoringService.metrics) return NodeAttachmentService(attachmentsDir, configuration.dataSourceProperties, services.monitoringService.metrics)
} }
protected fun createNodeDir() {
configuration.baseDirectory.createDirectories()
}
} }
private class KeyStoreWrapper(val keyStore: KeyStore, val storePath: Path, private val storePassword: String) { private class KeyStoreWrapper(val keyStore: KeyStore, val storePath: Path, private val storePassword: String) {

View File

@ -12,7 +12,6 @@ import net.corda.core.identity.Party
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.node.services.vault.PageSpecification import net.corda.core.node.services.vault.PageSpecification
import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.node.services.vault.QueryCriteria
@ -76,7 +75,7 @@ class CordaRPCOpsImpl(
override fun verifiedTransactionsFeed(): DataFeed<List<SignedTransaction>, SignedTransaction> { override fun verifiedTransactionsFeed(): DataFeed<List<SignedTransaction>, SignedTransaction> {
return database.transaction { return database.transaction {
services.storageService.validatedTransactions.track() services.validatedTransactions.track()
} }
} }
@ -92,7 +91,7 @@ class CordaRPCOpsImpl(
override fun stateMachineRecordedTransactionMappingFeed(): DataFeed<List<StateMachineTransactionMapping>, StateMachineTransactionMapping> { override fun stateMachineRecordedTransactionMappingFeed(): DataFeed<List<StateMachineTransactionMapping>, StateMachineTransactionMapping> {
return database.transaction { return database.transaction {
services.storageService.stateMachineRecordedTransactionMapping.track() services.stateMachineRecordedTransactionMapping.track()
} }
} }
@ -143,21 +142,21 @@ class CordaRPCOpsImpl(
override fun attachmentExists(id: SecureHash): Boolean { override fun attachmentExists(id: SecureHash): Boolean {
// TODO: this operation should not require an explicit transaction // TODO: this operation should not require an explicit transaction
return database.transaction { return database.transaction {
services.storageService.attachments.openAttachment(id) != null services.attachments.openAttachment(id) != null
} }
} }
override fun openAttachment(id: SecureHash): InputStream { override fun openAttachment(id: SecureHash): InputStream {
// TODO: this operation should not require an explicit transaction // TODO: this operation should not require an explicit transaction
return database.transaction { return database.transaction {
services.storageService.attachments.openAttachment(id)!!.open() services.attachments.openAttachment(id)!!.open()
} }
} }
override fun uploadAttachment(jar: InputStream): SecureHash { override fun uploadAttachment(jar: InputStream): SecureHash {
// TODO: this operation should not require an explicit transaction // TODO: this operation should not require an explicit transaction
return database.transaction { return database.transaction {
services.storageService.attachments.importAttachment(jar) services.attachments.importAttachment(jar)
} }
} }
@ -166,7 +165,7 @@ class CordaRPCOpsImpl(
override fun currentNodeTime(): Instant = Instant.now(services.clock) override fun currentNodeTime(): Instant = Instant.now(services.clock)
@Suppress("OverridingDeprecatedMember", "DEPRECATION") @Suppress("OverridingDeprecatedMember", "DEPRECATION")
override fun uploadFile(dataType: String, name: String?, file: InputStream): String { override fun uploadFile(dataType: String, name: String?, file: InputStream): String {
val acceptor = services.storageService.uploaders.firstOrNull { it.accepts(dataType) } val acceptor = services.uploaders.firstOrNull { it.accepts(dataType) }
return database.transaction { return database.transaction {
acceptor?.upload(file) ?: throw RuntimeException("Cannot find file upload acceptor for $dataType") acceptor?.upload(file) ?: throw RuntimeException("Cannot find file upload acceptor for $dataType")
} }

View File

@ -27,14 +27,14 @@ import net.corda.flows.*
*/ */
class FetchTransactionsHandler(otherParty: Party) : FetchDataHandler<SignedTransaction>(otherParty) { class FetchTransactionsHandler(otherParty: Party) : FetchDataHandler<SignedTransaction>(otherParty) {
override fun getData(id: SecureHash): SignedTransaction? { override fun getData(id: SecureHash): SignedTransaction? {
return serviceHub.storageService.validatedTransactions.getTransaction(id) return serviceHub.validatedTransactions.getTransaction(id)
} }
} }
// TODO: Use Artemis message streaming support here, called "large messages". This avoids the need to buffer. // TODO: Use Artemis message streaming support here, called "large messages". This avoids the need to buffer.
class FetchAttachmentsHandler(otherParty: Party) : FetchDataHandler<ByteArray>(otherParty) { class FetchAttachmentsHandler(otherParty: Party) : FetchDataHandler<ByteArray>(otherParty) {
override fun getData(id: SecureHash): ByteArray? { override fun getData(id: SecureHash): ByteArray? {
return serviceHub.storageService.attachments.openAttachment(id)?.open()?.readBytes() return serviceHub.attachments.openAttachment(id)?.open()?.readBytes()
} }
} }

View File

@ -1,16 +1,20 @@
package net.corda.node.services.api package net.corda.node.services.api
import com.google.common.annotations.VisibleForTesting import com.google.common.annotations.VisibleForTesting
import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StateMachineRunId
import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.FlowStateMachine
import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.node.services.FileUploader
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.TxWritableStorageService import net.corda.core.node.services.TransactionStorage
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
@ -58,34 +62,38 @@ sealed class NetworkCacheError : Exception() {
class DeregistrationFailed : NetworkCacheError() class DeregistrationFailed : NetworkCacheError()
} }
abstract class ServiceHubInternal : PluginServiceHub { interface ServiceHubInternal : PluginServiceHub {
companion object { companion object {
private val log = loggerFor<ServiceHubInternal>() private val log = loggerFor<ServiceHubInternal>()
} }
abstract val monitoringService: MonitoringService
abstract val schemaService: SchemaService
abstract override val networkMapCache: NetworkMapCacheInternal
abstract val schedulerService: SchedulerService
abstract val auditService: AuditService
abstract val rpcFlows: List<Class<out FlowLogic<*>>>
abstract val networkService: MessagingService
abstract val database: Database
abstract val configuration: NodeConfiguration
/** /**
* Given a list of [SignedTransaction]s, writes them to the given storage for validated transactions and then * A map of hash->tx where tx has been signature/contract validated and the states are known to be correct.
* sends them to the vault for further processing. This is intended for implementations to call from * The signatures aren't technically needed after that point, but we keep them around so that we can relay
* [recordTransactions]. * the transaction data to other nodes that need it.
*
* @param txs The transactions to record.
*/ */
internal fun recordTransactionsInternal(writableStorageService: TxWritableStorageService, txs: Iterable<SignedTransaction>) { override val validatedTransactions: TransactionStorage
val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage
val monitoringService: MonitoringService
val schemaService: SchemaService
override val networkMapCache: NetworkMapCacheInternal
val schedulerService: SchedulerService
val auditService: AuditService
val rpcFlows: List<Class<out FlowLogic<*>>>
val networkService: MessagingService
val database: Database
val configuration: NodeConfiguration
@Suppress("DEPRECATION")
@Deprecated("This service will be removed in a future milestone")
val uploaders: List<FileUploader>
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id val stateMachineRunId = FlowStateMachineImpl.currentStateMachine()?.id
val recordedTransactions = txs.filter { writableStorageService.validatedTransactions.addTransaction(it) } val recordedTransactions = txs.filter { validatedTransactions.addTransaction(it) }
if (stateMachineRunId != null) { if (stateMachineRunId != null) {
recordedTransactions.forEach { recordedTransactions.forEach {
storageService.stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id) stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id)
} }
} else { } else {
log.warn("Transactions recorded from outside of a state machine") log.warn("Transactions recorded from outside of a state machine")
@ -104,7 +112,7 @@ abstract class ServiceHubInternal : PluginServiceHub {
* Starts an already constructed flow. Note that you must be on the server thread to call this method. * Starts an already constructed flow. Note that you must be on the server thread to call this method.
* @param flowInitiator indicates who started the flow, see: [FlowInitiator]. * @param flowInitiator indicates who started the flow, see: [FlowInitiator].
*/ */
abstract fun <T> startFlow(logic: FlowLogic<T>, flowInitiator: FlowInitiator): FlowStateMachineImpl<T> fun <T> startFlow(logic: FlowLogic<T>, flowInitiator: FlowInitiator): FlowStateMachineImpl<T>
/** /**
* Will check [logicType] and [args] against a whitelist and if acceptable then construct and initiate the flow. * Will check [logicType] and [args] against a whitelist and if acceptable then construct and initiate the flow.
@ -124,5 +132,14 @@ abstract class ServiceHubInternal : PluginServiceHub {
return startFlow(logic, flowInitiator) return startFlow(logic, flowInitiator)
} }
abstract fun getFlowFactory(initiatingFlowClass: Class<out FlowLogic<*>>): InitiatedFlowFactory<*>? fun getFlowFactory(initiatingFlowClass: Class<out FlowLogic<*>>): InitiatedFlowFactory<*>?
} }
/**
* 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.
*/
interface StateMachineRecordedTransactionMappingStorage {
fun addMapping(stateMachineRunId: StateMachineRunId, transactionId: SecureHash)
fun track(): DataFeed<List<StateMachineTransactionMapping>, StateMachineTransactionMapping>
}

View File

@ -5,12 +5,11 @@ import net.corda.core.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.node.services.StateMachineRecordedTransactionMappingStorage import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.node.services.StateMachineTransactionMapping import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
import net.corda.node.utilities.* import net.corda.node.utilities.*
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.statements.InsertStatement import org.jetbrains.exposed.sql.statements.InsertStatement
import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe

View File

@ -5,6 +5,7 @@ import net.corda.core.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.node.services.TransactionStorage import net.corda.core.node.services.TransactionStorage
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.node.utilities.* import net.corda.node.utilities.*
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
@ -14,7 +15,7 @@ import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.util.Collections.synchronizedMap import java.util.Collections.synchronizedMap
class DBTransactionStorage : TransactionStorage { class DBTransactionStorage : TransactionStorage, SingletonSerializeAsToken() {
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}transactions") { private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}transactions") {
val txId = secureHash("tx_id") val txId = secureHash("tx_id")
val transaction = blob("transaction") val transaction = blob("transaction")
@ -59,7 +60,7 @@ class DBTransactionStorage : TransactionStorage {
} }
} }
val updatesPublisher = PublishSubject.create<SignedTransaction>().toSerialized() private val updatesPublisher = PublishSubject.create<SignedTransaction>().toSerialized()
override val updates: Observable<SignedTransaction> = updatesPublisher.wrapWithDatabaseTransaction() override val updates: Observable<SignedTransaction> = updatesPublisher.wrapWithDatabaseTransaction()
override fun track(): DataFeed<List<SignedTransaction>, SignedTransaction> { override fun track(): DataFeed<List<SignedTransaction>, SignedTransaction> {

View File

@ -5,9 +5,8 @@ import net.corda.core.bufferUntilSubscribed
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.node.services.StateMachineRecordedTransactionMappingStorage import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.node.services.StateMachineTransactionMapping import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.util.* import java.util.*
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe

View File

@ -14,10 +14,7 @@ import net.corda.core.div
import net.corda.core.extractZipFile import net.corda.core.extractZipFile
import net.corda.core.isDirectory import net.corda.core.isDirectory
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.*
import net.corda.core.serialization.SerializationToken
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.services.api.AcceptsFileUpload import net.corda.node.services.api.AcceptsFileUpload
import net.corda.node.services.database.RequeryConfiguration import net.corda.node.services.database.RequeryConfiguration
@ -38,8 +35,11 @@ import javax.annotation.concurrent.ThreadSafe
* Stores attachments in H2 database. * Stores attachments in H2 database.
*/ */
@ThreadSafe @ThreadSafe
class NodeAttachmentService(override var storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry) : AttachmentStorage, AcceptsFileUpload { class NodeAttachmentService(override var storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry)
private val log = loggerFor<NodeAttachmentService>() : AttachmentStorage, AcceptsFileUpload, SingletonSerializeAsToken() {
companion object {
private val log = loggerFor<NodeAttachmentService>()
}
val configuration = RequeryConfiguration(dataSourceProperties) val configuration = RequeryConfiguration(dataSourceProperties)
val session = configuration.sessionForModel(Models.PERSISTENCE) val session = configuration.sessionForModel(Models.PERSISTENCE)

View File

@ -1,18 +0,0 @@
package net.corda.node.services.persistence
import net.corda.core.node.services.*
import net.corda.core.serialization.SingletonSerializeAsToken
open class StorageServiceImpl(override val attachments: AttachmentStorage,
override val validatedTransactions: TransactionStorage,
override val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage)
: SingletonSerializeAsToken(), TxWritableStorageService {
override val attachmentsClassLoaderEnabled = false
lateinit override var uploaders: List<FileUploader>
fun initUploaders(uploadersList: List<FileUploader>) {
@Suppress("DEPRECATION")
uploaders = uploadersList
}
}

View File

@ -205,7 +205,7 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
override fun waitForLedgerCommit(hash: SecureHash, sessionFlow: FlowLogic<*>): SignedTransaction { override fun waitForLedgerCommit(hash: SecureHash, sessionFlow: FlowLogic<*>): SignedTransaction {
logger.debug { "waitForLedgerCommit($hash) ..." } logger.debug { "waitForLedgerCommit($hash) ..." }
suspend(WaitForLedgerCommit(hash, sessionFlow.stateMachine as FlowStateMachineImpl<*>)) suspend(WaitForLedgerCommit(hash, sessionFlow.stateMachine as FlowStateMachineImpl<*>))
val stx = serviceHub.storageService.validatedTransactions.getTransaction(hash) val stx = serviceHub.validatedTransactions.getTransaction(hash)
if (stx != null) { if (stx != null) {
logger.debug { "Transaction $hash committed to ledger" } logger.debug { "Transaction $hash committed to ledger" }
return stx return stx

View File

@ -187,7 +187,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
private fun listenToLedgerTransactions() { private fun listenToLedgerTransactions() {
// Observe the stream of committed, validated transactions and resume fibers that are waiting for them. // Observe the stream of committed, validated transactions and resume fibers that are waiting for them.
serviceHub.storageService.validatedTransactions.updates.subscribe { stx -> serviceHub.validatedTransactions.updates.subscribe { stx ->
val hash = stx.id val hash = stx.id
val fibers: Set<FlowStateMachineImpl<*>> = mutex.locked { fibersWaitingForLedgerCommit.removeAll(hash) } val fibers: Set<FlowStateMachineImpl<*>> = mutex.locked { fibersWaitingForLedgerCommit.removeAll(hash) }
if (fibers.isNotEmpty()) { if (fibers.isNotEmpty()) {
@ -268,7 +268,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
if (waitingForResponse != null) { if (waitingForResponse != null) {
if (waitingForResponse is WaitForLedgerCommit) { if (waitingForResponse is WaitForLedgerCommit) {
val stx = database.transaction { val stx = database.transaction {
serviceHub.storageService.validatedTransactions.getTransaction(waitingForResponse.hash) serviceHub.validatedTransactions.getTransaction(waitingForResponse.hash)
} }
if (stx != null) { if (stx != null) {
fiber.logger.info("Resuming fiber as tx ${waitingForResponse.hash} has committed") fiber.logger.info("Resuming fiber as tx ${waitingForResponse.hash} has committed")
@ -548,7 +548,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
private fun processWaitForCommitRequest(ioRequest: WaitForLedgerCommit) { private fun processWaitForCommitRequest(ioRequest: WaitForLedgerCommit) {
// Is it already committed? // Is it already committed?
val stx = database.transaction { val stx = database.transaction {
serviceHub.storageService.validatedTransactions.getTransaction(ioRequest.hash) serviceHub.validatedTransactions.getTransaction(ioRequest.hash)
} }
if (stx != null) { if (stx != null) {
resumeFiber(ioRequest.fiber) resumeFiber(ioRequest.fiber)

View File

@ -72,10 +72,10 @@ public class VaultQueryJavaTests {
@Override @Override
public void recordTransactions(@NotNull Iterable<SignedTransaction> txs) { public void recordTransactions(@NotNull Iterable<SignedTransaction> txs) {
for (SignedTransaction stx : txs) { for (SignedTransaction stx : txs) {
getStorageService().getValidatedTransactions().addTransaction(stx); getValidatedTransactions().addTransaction(stx);
} }
Stream<WireTransaction> wtxn = StreamSupport.stream(txs.spliterator(), false).map(txn -> txn.getTx()); Stream<WireTransaction> wtxn = StreamSupport.stream(txs.spliterator(), false).map(SignedTransaction::getTx);
getVaultService().notifyAll(wtxn.collect(Collectors.toList())); getVaultService().notifyAll(wtxn.collect(Collectors.toList()));
} }
}; };

View File

@ -11,11 +11,10 @@ import net.corda.flows.FetchDataFlow
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.database.RequeryConfiguration import net.corda.node.services.database.RequeryConfiguration
import net.corda.node.services.network.NetworkMapService import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.node.services.persistence.schemas.requery.AttachmentEntity import net.corda.node.services.persistence.schemas.requery.AttachmentEntity
import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.testing.node.MockNetwork
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.junit.Before import org.junit.Before
@ -61,7 +60,7 @@ class AttachmentTests {
// Insert an attachment into node zero's store directly. // Insert an attachment into node zero's store directly.
val id = n0.database.transaction { val id = n0.database.transaction {
n0.storage.attachments.importAttachment(ByteArrayInputStream(fakeAttachment())) n0.attachments.importAttachment(ByteArrayInputStream(fakeAttachment()))
} }
// Get node one to run a flow to fetch it and insert it. // Get node one to run a flow to fetch it and insert it.
@ -72,7 +71,7 @@ class AttachmentTests {
// Verify it was inserted into node one's store. // Verify it was inserted into node one's store.
val attachment = n1.database.transaction { val attachment = n1.database.transaction {
n1.storage.attachments.openAttachment(id)!! n1.attachments.openAttachment(id)!!
} }
assertEquals(id, attachment.open().readBytes().sha256()) assertEquals(id, attachment.open().readBytes().sha256())
@ -108,7 +107,7 @@ class AttachmentTests {
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {
override fun start(): MockNetwork.MockNode { override fun start(): MockNetwork.MockNode {
super.start() super.start()
(storage.attachments as NodeAttachmentService).checkAttachmentsOnLoad = false attachments.checkAttachmentsOnLoad = false
return this return this
} }
} }
@ -119,7 +118,7 @@ class AttachmentTests {
val attachment = fakeAttachment() val attachment = fakeAttachment()
// Insert an attachment into node zero's store directly. // Insert an attachment into node zero's store directly.
val id = n0.database.transaction { val id = n0.database.transaction {
n0.storage.attachments.importAttachment(ByteArrayInputStream(attachment)) n0.attachments.importAttachment(ByteArrayInputStream(attachment))
} }
// Corrupt its store. // Corrupt its store.
@ -130,7 +129,7 @@ class AttachmentTests {
corruptAttachment.attId = id corruptAttachment.attId = id
corruptAttachment.content = attachment corruptAttachment.content = attachment
n0.database.transaction { n0.database.transaction {
(n0.storage.attachments as NodeAttachmentService).session.update(corruptAttachment) n0.attachments.session.update(corruptAttachment)
} }

View File

@ -9,15 +9,21 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.flows.* import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.FlowStateMachine
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.* 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.serialization.serialize
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -28,7 +34,6 @@ import net.corda.flows.TwoPartyTradeFlow.Seller
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.persistence.DBTransactionStorage import net.corda.node.services.persistence.DBTransactionStorage
import net.corda.node.services.persistence.StorageServiceImpl
import net.corda.node.services.persistence.checkpoints import net.corda.node.services.persistence.checkpoints
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.* import net.corda.testing.*
@ -211,7 +216,7 @@ class TwoPartyTradeFlowTests {
assertThat(bobNode.checkpointStorage.checkpoints()).hasSize(1) assertThat(bobNode.checkpointStorage.checkpoints()).hasSize(1)
} }
val storage = bobNode.storage.validatedTransactions val storage = bobNode.services.validatedTransactions
val bobTransactionsBeforeCrash = bobNode.database.transaction { val bobTransactionsBeforeCrash = bobNode.database.transaction {
(storage as DBTransactionStorage).transactions (storage as DBTransactionStorage).transactions
} }
@ -252,7 +257,9 @@ class TwoPartyTradeFlowTests {
} }
bobNode.database.transaction { bobNode.database.transaction {
val restoredBobTransactions = bobTransactionsBeforeCrash.filter { bobNode.storage.validatedTransactions.getTransaction(it.id) != null } val restoredBobTransactions = bobTransactionsBeforeCrash.filter {
bobNode.services.validatedTransactions.getTransaction(it.id) != null
}
assertThat(restoredBobTransactions).containsAll(bobTransactionsBeforeCrash) assertThat(restoredBobTransactions).containsAll(bobTransactionsBeforeCrash)
} }
@ -276,13 +283,9 @@ class TwoPartyTradeFlowTests {
overrideServices: Map<ServiceInfo, KeyPair>?, overrideServices: Map<ServiceInfo, KeyPair>?,
entropyRoot: BigInteger): MockNetwork.MockNode { entropyRoot: BigInteger): MockNetwork.MockNode {
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) { return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {
// That constructs the storage service object in a customised way ... // That constructs a recording tx storage
override fun constructStorageService( override fun createTransactionStorage(): TransactionStorage {
attachments: AttachmentStorage, return RecordingTransactionStorage(database, super.createTransactionStorage())
transactionStorage: TransactionStorage,
stateMachineRecordedTransactionMappingStorage: StateMachineRecordedTransactionMappingStorage
): StorageServiceImpl {
return StorageServiceImpl(attachments, RecordingTransactionStorage(database, transactionStorage), stateMachineRecordedTransactionMappingStorage)
} }
} }
} }
@ -326,7 +329,7 @@ class TwoPartyTradeFlowTests {
mockNet.runNetwork() mockNet.runNetwork()
run { run {
val records = (bobNode.storage.validatedTransactions as RecordingTransactionStorage).records val records = (bobNode.services.validatedTransactions as RecordingTransactionStorage).records
// Check Bobs's database accesses as Bob's cash transactions are downloaded by Alice. // Check Bobs's database accesses as Bob's cash transactions are downloaded by Alice.
records.expectEvents(isStrict = false) { records.expectEvents(isStrict = false) {
sequence( sequence(
@ -344,7 +347,7 @@ class TwoPartyTradeFlowTests {
// Bob has downloaded the attachment. // Bob has downloaded the attachment.
bobNode.database.transaction { bobNode.database.transaction {
bobNode.storage.attachments.openAttachment(attachmentID)!!.openAsJAR().use { bobNode.services.attachments.openAttachment(attachmentID)!!.openAsJAR().use {
it.nextJarEntry it.nextJarEntry
val contents = it.reader().readText() val contents = it.reader().readText()
assertTrue(contents.contains("Our commercial paper is top notch stuff")) assertTrue(contents.contains("Our commercial paper is top notch stuff"))
@ -354,7 +357,7 @@ class TwoPartyTradeFlowTests {
// And from Alice's perspective ... // And from Alice's perspective ...
run { run {
val records = (aliceNode.storage.validatedTransactions as RecordingTransactionStorage).records val records = (aliceNode.services.validatedTransactions as RecordingTransactionStorage).records
records.expectEvents(isStrict = false) { records.expectEvents(isStrict = false) {
sequence( sequence(
// Seller Alice sends her seller info to Bob, who wants to check the asset for sale. // Seller Alice sends her seller info to Bob, who wants to check the asset for sale.
@ -422,8 +425,10 @@ class TwoPartyTradeFlowTests {
mockNet.runNetwork() // Clear network map registration messages mockNet.runNetwork() // Clear network map registration messages
val aliceTxStream = aliceNode.storage.validatedTransactions.track().second val aliceTxStream = aliceNode.services.validatedTransactions.track().updates
val aliceTxMappings = with(aliceNode) { database.transaction { storage.stateMachineRecordedTransactionMapping.track().second } } val aliceTxMappings = with(aliceNode) {
database.transaction { services.stateMachineRecordedTransactionMapping.track().updates }
}
val aliceSmId = runBuyerAndSeller(notaryNode, aliceNode, bobNode, val aliceSmId = runBuyerAndSeller(notaryNode, aliceNode, bobNode,
"alice's paper".outputStateAndRef()).sellerId "alice's paper".outputStateAndRef()).sellerId
@ -443,7 +448,7 @@ class TwoPartyTradeFlowTests {
) )
aliceTxStream.expectEvents { aliceTxExpectations } aliceTxStream.expectEvents { aliceTxExpectations }
val aliceMappingExpectations = sequence( val aliceMappingExpectations = sequence(
expect { (stateMachineRunId, transactionId) -> expect<StateMachineTransactionMapping> { (stateMachineRunId, transactionId) ->
require(stateMachineRunId == aliceSmId) require(stateMachineRunId == aliceSmId)
require(transactionId == bobsFakeCash[0].id) require(transactionId == bobsFakeCash[0].id)
}, },
@ -451,9 +456,9 @@ class TwoPartyTradeFlowTests {
require(stateMachineRunId == aliceSmId) require(stateMachineRunId == aliceSmId)
require(transactionId == bobsFakeCash[2].id) require(transactionId == bobsFakeCash[2].id)
}, },
expect { mapping: StateMachineTransactionMapping -> expect { (stateMachineRunId, transactionId) ->
require(mapping.stateMachineRunId == aliceSmId) require(stateMachineRunId == aliceSmId)
require(mapping.transactionId == bobsFakeCash[1].id) require(transactionId == bobsFakeCash[1].id)
} }
) )
aliceTxMappings.expectEvents { aliceMappingExpectations } aliceTxMappings.expectEvents { aliceMappingExpectations }
@ -589,7 +594,7 @@ class TwoPartyTradeFlowTests {
} }
return node.database.transaction { return node.database.transaction {
node.services.recordTransactions(signed) node.services.recordTransactions(signed)
val validatedTransactions = node.services.storageService.validatedTransactions val validatedTransactions = node.services.validatedTransactions
if (validatedTransactions is RecordingTransactionStorage) { if (validatedTransactions is RecordingTransactionStorage) {
validatedTransactions.records.clear() validatedTransactions.records.clear()
} }

View File

@ -6,7 +6,6 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.* import net.corda.core.node.services.*
import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SerializeAsToken
import net.corda.core.transactions.SignedTransaction
import net.corda.node.internal.InitiatedFlowFactory import net.corda.node.internal.InitiatedFlowFactory
import net.corda.node.serialization.NodeClock import net.corda.node.serialization.NodeClock
import net.corda.node.services.api.* import net.corda.node.services.api.*
@ -17,9 +16,11 @@ import net.corda.node.services.statemachine.FlowStateMachineImpl
import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.services.transactions.InMemoryTransactionVerifierService
import net.corda.testing.MOCK_IDENTITY_SERVICE import net.corda.testing.MOCK_IDENTITY_SERVICE
import net.corda.testing.node.MockAttachmentStorage
import net.corda.testing.node.MockNetworkMapCache import net.corda.testing.node.MockNetworkMapCache
import net.corda.testing.node.MockStorageService
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import net.corda.testing.node.MockStateMachineRecordedTransactionMappingStorage
import net.corda.testing.node.MockTransactionStorage
import java.time.Clock import java.time.Clock
open class MockServiceHubInternal( open class MockServiceHubInternal(
@ -28,13 +29,16 @@ open class MockServiceHubInternal(
val keyManagement: KeyManagementService? = null, val keyManagement: KeyManagementService? = null,
val network: MessagingService? = null, val network: MessagingService? = null,
val identity: IdentityService? = MOCK_IDENTITY_SERVICE, val identity: IdentityService? = MOCK_IDENTITY_SERVICE,
val storage: TxWritableStorageService? = MockStorageService(), override val attachments: AttachmentStorage = MockAttachmentStorage(),
override val validatedTransactions: TransactionStorage = MockTransactionStorage(),
override val uploaders: List<FileUploader> = listOf<FileUploader>(),
override val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage(),
val mapCache: NetworkMapCacheInternal? = null, val mapCache: NetworkMapCacheInternal? = null,
val scheduler: SchedulerService? = null, val scheduler: SchedulerService? = null,
val overrideClock: Clock? = NodeClock(), val overrideClock: Clock? = NodeClock(),
val schemas: SchemaService? = NodeSchemaService(), val schemas: SchemaService? = NodeSchemaService(),
val customTransactionVerifierService: TransactionVerifierService? = InMemoryTransactionVerifierService(2) val customTransactionVerifierService: TransactionVerifierService? = InMemoryTransactionVerifierService(2)
) : ServiceHubInternal() { ) : ServiceHubInternal {
override val vaultQueryService: VaultQueryService override val vaultQueryService: VaultQueryService
get() = customVaultQuery ?: throw UnsupportedOperationException() get() = customVaultQuery ?: throw UnsupportedOperationException()
override val transactionVerifierService: TransactionVerifierService override val transactionVerifierService: TransactionVerifierService
@ -49,8 +53,6 @@ open class MockServiceHubInternal(
get() = network ?: throw UnsupportedOperationException() get() = network ?: throw UnsupportedOperationException()
override val networkMapCache: NetworkMapCacheInternal override val networkMapCache: NetworkMapCacheInternal
get() = mapCache ?: MockNetworkMapCache(this) get() = mapCache ?: MockNetworkMapCache(this)
override val storageService: StorageService
get() = storage ?: throw UnsupportedOperationException()
override val schedulerService: SchedulerService override val schedulerService: SchedulerService
get() = scheduler ?: throw UnsupportedOperationException() get() = scheduler ?: throw UnsupportedOperationException()
override val clock: Clock override val clock: Clock
@ -67,14 +69,9 @@ open class MockServiceHubInternal(
override val schemaService: SchemaService override val schemaService: SchemaService
get() = schemas ?: throw UnsupportedOperationException() get() = schemas ?: throw UnsupportedOperationException()
override val auditService: AuditService = DummyAuditService() override val auditService: AuditService = DummyAuditService()
// We isolate the storage service with writable TXes so that it can't be accessed except via recordTransactions()
private val txStorageService: TxWritableStorageService
get() = storage ?: throw UnsupportedOperationException()
lateinit var smm: StateMachineManager lateinit var smm: StateMachineManager
override fun recordTransactions(txs: Iterable<SignedTransaction>) = recordTransactionsInternal(txStorageService, txs)
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T = throw UnsupportedOperationException() override fun <T : SerializeAsToken> cordaService(type: Class<T>): T = throw UnsupportedOperationException()
override fun <T> startFlow(logic: FlowLogic<T>, flowInitiator: FlowInitiator): FlowStateMachineImpl<T> { override fun <T> startFlow(logic: FlowLogic<T>, flowInitiator: FlowInitiator): FlowStateMachineImpl<T> {

View File

@ -99,7 +99,7 @@ class NotaryChangeTests {
val newState = future.resultFuture.getOrThrow() val newState = future.resultFuture.getOrThrow()
assertEquals(newState.state.notary, newNotary) assertEquals(newState.state.notary, newNotary)
val notaryChangeTx = clientNodeA.services.storageService.validatedTransactions.getTransaction(newState.ref.txhash)!!.tx val notaryChangeTx = clientNodeA.services.validatedTransactions.getTransaction(newState.ref.txhash)!!.tx
// Check that all encumbrances have been propagated to the outputs // Check that all encumbrances have been propagated to the outputs
val originalOutputs = issueTx.outputs.map { it.data } val originalOutputs = issueTx.outputs.map { it.data }

View File

@ -85,7 +85,7 @@ class HibernateConfigurationTest {
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) { for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx) validatedTransactions.addTransaction(stx)
} }
// Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions.
vaultService.notifyAll(txs.map { it.tx }) vaultService.notifyAll(txs.map { it.tx })

View File

@ -6,7 +6,6 @@ import net.corda.contracts.testing.fillWithSomeTestCash
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.StatesNotAvailableException import net.corda.core.node.services.StatesNotAvailableException
import net.corda.core.node.services.TxWritableStorageService
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.node.services.unconsumedStates import net.corda.core.node.services.unconsumedStates
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
@ -53,7 +52,7 @@ class NodeVaultServiceTest {
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) { for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx) validatedTransactions.addTransaction(stx)
} }
// Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions.
vaultService.notifyAll(txs.map { it.tx }) vaultService.notifyAll(txs.map { it.tx })
@ -77,17 +76,12 @@ class NodeVaultServiceTest {
val w1 = vaultSvc.unconsumedStates<Cash.State>() val w1 = vaultSvc.unconsumedStates<Cash.State>()
assertThat(w1).hasSize(3) assertThat(w1).hasSize(3)
val originalStorage = services.storageService
val originalVault = vaultSvc val originalVault = vaultSvc
val services2 = object : MockServices() { val services2 = object : MockServices() {
override val vaultService: VaultService get() = originalVault override val vaultService: VaultService get() = originalVault
// We need to be able to find the same transactions as before, too.
override val storageService: TxWritableStorageService get() = originalStorage
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) { for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx) validatedTransactions.addTransaction(stx)
vaultService.notify(stx.tx) vaultService.notify(stx.tx)
} }
} }

View File

@ -75,7 +75,7 @@ class VaultQueryTests {
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) { for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx) validatedTransactions.addTransaction(stx)
} }
// Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions.
vaultService.notifyAll(txs.map { it.tx }) vaultService.notifyAll(txs.map { it.tx })
@ -93,9 +93,9 @@ class VaultQueryTests {
/** /**
* Helper method for generating a Persistent H2 test database * Helper method for generating a Persistent H2 test database
*/ */
@Ignore //@Test @Ignore
@Test
fun createPersistentTestDb() { fun createPersistentTestDb() {
val dataSourceAndDatabase = configureDatabase(makePersistentDataSourceProperties()) val dataSourceAndDatabase = configureDatabase(makePersistentDataSourceProperties())
val dataSource = dataSourceAndDatabase.first val dataSource = dataSourceAndDatabase.first
val database = dataSourceAndDatabase.second val database = dataSourceAndDatabase.second

View File

@ -3,7 +3,9 @@ package net.corda.node.services.vault
import net.corda.contracts.DummyDealContract import net.corda.contracts.DummyDealContract
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.DUMMY_CASH_ISSUER import net.corda.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.contracts.testing.* import net.corda.contracts.testing.fillWithSomeTestCash
import net.corda.contracts.testing.fillWithSomeTestDeals
import net.corda.contracts.testing.fillWithSomeTestLinearStates
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
@ -54,7 +56,7 @@ class VaultWithCashTest {
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
for (stx in txs) { for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx) validatedTransactions.addTransaction(stx)
} }
// Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions. // Refactored to use notifyAll() as we have no other unit test for that method with multiple transactions.
vaultService.notifyAll(txs.map { it.tx }) vaultService.notifyAll(txs.map { it.tx })

View File

@ -55,14 +55,14 @@ class BuyerFlow(val otherParty: Party) : FlowLogic<Unit>() {
private fun logIssuanceAttachment(tradeTX: SignedTransaction) { private fun logIssuanceAttachment(tradeTX: SignedTransaction) {
// Find the original CP issuance. // Find the original CP issuance.
val search = TransactionGraphSearch(serviceHub.storageService.validatedTransactions, listOf(tradeTX.tx)) val search = TransactionGraphSearch(serviceHub.validatedTransactions, listOf(tradeTX.tx))
search.query = TransactionGraphSearch.Query(withCommandOfType = CommercialPaper.Commands.Issue::class.java, search.query = TransactionGraphSearch.Query(withCommandOfType = CommercialPaper.Commands.Issue::class.java,
followInputsOfType = CommercialPaper.State::class.java) followInputsOfType = CommercialPaper.State::class.java)
val cpIssuance = search.call().single() val cpIssuance = search.call().single()
// Buyer will fetch the attachment from the seller automatically when it resolves the transaction. // 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. // For demo purposes just extract attachment jars when saved to disk, so the user can explore them.
val attachmentsPath = (serviceHub.storageService.attachments).let { val attachmentsPath = (serviceHub.attachments).let {
it.automaticallyExtractAttachments = true it.automaticallyExtractAttachments = true
it.storePath it.storePath
} }

View File

@ -79,7 +79,7 @@ class SellerFlow(val otherParty: Party,
// TODO: Consider moving these two steps below into generateIssue. // TODO: Consider moving these two steps below into generateIssue.
// Attach the prospectus. // Attach the prospectus.
tx.addAttachment(serviceHub.storageService.attachments.openAttachment(PROSPECTUS_HASH)!!.id) tx.addAttachment(serviceHub.attachments.openAttachment(PROSPECTUS_HASH)!!.id)
// Requesting a time-window to be set, all CP must have a validation window. // Requesting a time-window to be set, all CP must have a validation window.
tx.addTimeWindow(Instant.now(), 30.seconds) tx.addTimeWindow(Instant.now(), 30.seconds)

View File

@ -211,8 +211,9 @@ data class TestLedgerDSLInterpreter private constructor(
} }
} }
internal fun resolveAttachment(attachmentId: SecureHash): Attachment = internal fun resolveAttachment(attachmentId: SecureHash): Attachment {
services.storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId) return services.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
}
private fun <R> interpretTransactionDsl( private fun <R> interpretTransactionDsl(
transactionBuilder: TransactionBuilder, transactionBuilder: TransactionBuilder,
@ -276,7 +277,7 @@ data class TestLedgerDSLInterpreter private constructor(
dsl(LedgerDSL(copy())) dsl(LedgerDSL(copy()))
override fun attachment(attachment: InputStream): SecureHash { override fun attachment(attachment: InputStream): SecureHash {
return services.storageService.attachments.importAttachment(attachment) return services.attachments.importAttachment(attachment)
} }
override fun verifies(): EnforceVerifyOrFail { override fun verifies(): EnforceVerifyOrFail {

View File

@ -6,7 +6,6 @@ import net.corda.core.crypto.*
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.* import net.corda.core.node.services.*
@ -16,6 +15,7 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_CA import net.corda.core.utilities.DUMMY_CA
import net.corda.core.utilities.getTestPartyAndCertificate import net.corda.core.utilities.getTestPartyAndCertificate
import net.corda.flows.AnonymisedIdentity import net.corda.flows.AnonymisedIdentity
import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
import net.corda.node.services.database.HibernateConfiguration import net.corda.node.services.database.HibernateConfiguration
import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.node.services.keys.freshCertificate import net.corda.node.services.keys.freshCertificate
@ -28,7 +28,6 @@ import net.corda.node.services.vault.NodeVaultService
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MOCK_IDENTITIES import net.corda.testing.MOCK_IDENTITIES
import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.MOCK_VERSION_INFO
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.operator.ContentSigner import org.bouncycastle.operator.ContentSigner
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
@ -41,11 +40,9 @@ import java.nio.file.Paths
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath
import java.time.Clock import java.time.Clock
import java.util.* import java.util.*
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
import javax.annotation.concurrent.ThreadSafe
// TODO: We need a single, rationalised unit testing environment that is usable for everything. Fix this! // TODO: We need a single, rationalised unit testing environment that is usable for everything. Fix this!
// That means it probably shouldn't be in the 'core' module, which lacks enough code to create a realistic test env. // That means it probably shouldn't be in the 'core' module, which lacks enough code to create a realistic test env.
@ -61,14 +58,16 @@ open class MockServices(vararg val keys: KeyPair) : ServiceHub {
override fun recordTransactions(txs: Iterable<SignedTransaction>) { override fun recordTransactions(txs: Iterable<SignedTransaction>) {
txs.forEach { txs.forEach {
storageService.stateMachineRecordedTransactionMapping.addMapping(StateMachineRunId.createRandom(), it.id) stateMachineRecordedTransactionMapping.addMapping(StateMachineRunId.createRandom(), it.id)
} }
for (stx in txs) { for (stx in txs) {
storageService.validatedTransactions.addTransaction(stx) validatedTransactions.addTransaction(stx)
} }
} }
override val storageService: TxWritableStorageService = MockStorageService() override val attachments: AttachmentStorage = MockAttachmentStorage()
override val validatedTransactions: TransactionStorage = MockTransactionStorage()
val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage()
override final val identityService: IdentityService = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DUMMY_CA.certificate) override final val identityService: IdentityService = InMemoryIdentityService(MOCK_IDENTITIES, trustRoot = DUMMY_CA.certificate)
override val keyManagementService: KeyManagementService = MockKeyManagementService(identityService, *keys) override val keyManagementService: KeyManagementService = MockKeyManagementService(identityService, *keys)
@ -185,15 +184,6 @@ open class MockTransactionStorage : TransactionStorage {
override fun getTransaction(id: SecureHash): SignedTransaction? = txns[id] override fun getTransaction(id: SecureHash): SignedTransaction? = txns[id]
} }
@ThreadSafe
class MockStorageService(override val attachments: AttachmentStorage = MockAttachmentStorage(),
override val validatedTransactions: TransactionStorage = MockTransactionStorage(),
override val uploaders: List<FileUploader> = listOf<FileUploader>(),
override val stateMachineRecordedTransactionMapping: StateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage())
: SingletonSerializeAsToken(), TxWritableStorageService {
override val attachmentsClassLoaderEnabled = false
}
/** /**
* Make properties appropriate for creating a DataSource for unit tests. * Make properties appropriate for creating a DataSource for unit tests.
* *