mirror of
https://github.com/corda/corda.git
synced 2025-06-01 15:10:54 +00:00
Removed the StorageService and puts its components directly into the service hub
This commit is contained in:
parent
ecc96b29b0
commit
a08f701dc5
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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>
|
|
||||||
}
|
|
@ -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>() {
|
||||||
|
@ -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) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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 node’s current and historic states
|
* Stores the node’s 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``
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 })
|
||||||
|
@ -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) {
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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> {
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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> {
|
||||||
|
@ -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 }
|
||||||
|
@ -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 })
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 })
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user