mirror of
https://github.com/corda/corda.git
synced 2025-02-03 01:31:24 +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.getOrThrow
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
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.serialization.OpaqueBytes
|
||||
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.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.StateMachineInfo
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
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.seconds
|
||||
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
|
||||
* 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
|
||||
* list should just contain the owner.
|
||||
* The participants list should normally be derived from the contents of the state.
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
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
|
||||
* for that contract state.
|
||||
* Represents a contract state (unconsumed output) of type [LinearState] and a point in time that a lifecycle event is
|
||||
* 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
|
||||
* 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
|
||||
|
||||
/**
|
||||
* This class represents the lifecycle activity that a contract state of type [LinearState] would like to perform at a given point in time.
|
||||
* e.g. run a fixing flow.
|
||||
* This class represents the lifecycle activity that a contract state of type [LinearState] would like to perform at a
|
||||
* 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
|
||||
* 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
|
||||
/**
|
||||
* 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
|
||||
* transaction for it to be accepted: failure of any aborts the entire thing. The time is taken from a trusted
|
||||
* time-window attached to the transaction itself i.e. it is NOT necessarily the current time.
|
||||
* every [net.corda.core.transactions.LedgerTransaction] they see on the network, for every input and output state. All
|
||||
* contracts must accept the transaction for it to be accepted: failure of any aborts the entire thing. The time is taken
|
||||
* 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.
|
||||
*/
|
||||
@ -461,9 +461,8 @@ interface Attachment : NamedByHash {
|
||||
abstract class AbstractAttachment(dataLoader: () -> ByteArray) : Attachment {
|
||||
companion object {
|
||||
fun SerializeAsTokenContext.attachmentDataLoader(id: SecureHash): () -> ByteArray {
|
||||
val storage = serviceHub.storageService.attachments
|
||||
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() }
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.NodeInfo
|
||||
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.PageSpecification
|
||||
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()
|
||||
}
|
||||
|
||||
@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
|
||||
* 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 {
|
||||
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].
|
||||
*
|
||||
@ -40,7 +40,13 @@ interface ServiceHub : ServicesForResolution {
|
||||
val vaultService: VaultService
|
||||
val vaultQueryService: VaultQueryService
|
||||
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 transactionVerifierService: TransactionVerifierService
|
||||
val clock: Clock
|
||||
@ -77,7 +83,7 @@ interface ServiceHub : ServicesForResolution {
|
||||
*/
|
||||
@Throws(TransactionResolutionException::class)
|
||||
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]
|
||||
}
|
||||
|
||||
@ -87,7 +93,7 @@ interface ServiceHub : ServicesForResolution {
|
||||
* @throws IllegalProtocolLogicException or IllegalArgumentException if there are problems with the [logicType] or [args].
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -22,12 +22,10 @@ import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.transactions.WireTransaction
|
||||
import net.corda.flows.AnonymisedIdentity
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import rx.Observable
|
||||
import rx.subjects.PublishSubject
|
||||
import java.io.InputStream
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.CertPath
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
@ -528,44 +526,6 @@ interface FileUploader {
|
||||
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.
|
||||
*/
|
||||
|
@ -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 */
|
||||
@ThreadSafe
|
||||
object WireTransactionSerializer : Serializer<WireTransaction>() {
|
||||
@VisibleForTesting
|
||||
internal val attachmentsClassLoaderEnabled = "attachments.class.loader.enabled"
|
||||
|
||||
override fun write(kryo: Kryo, output: Output, obj: WireTransaction) {
|
||||
kryo.writeClassAndObject(output, obj.inputs)
|
||||
kryo.writeClassAndObject(output, obj.attachments)
|
||||
@ -329,12 +332,12 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
|
||||
}
|
||||
|
||||
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.
|
||||
serializationContext.serviceHub.storageService.attachmentsClassLoaderEnabled || return null
|
||||
val missing = ArrayList<SecureHash>()
|
||||
val attachments = ArrayList<Attachment>()
|
||||
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)
|
||||
return AttachmentsClassLoader(attachments)
|
||||
@ -635,7 +638,7 @@ object X500NameSerializer : Serializer<X500Name>() {
|
||||
*/
|
||||
@ThreadSafe
|
||||
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 {
|
||||
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
|
||||
object X509CertificateSerializer : Serializer<X509CertificateHolder>() {
|
||||
|
@ -73,7 +73,7 @@ class WireTransaction(
|
||||
fun toLedgerTransaction(services: ServicesForResolution): LedgerTransaction {
|
||||
return toLedgerTransaction(
|
||||
resolveIdentity = { services.identityService.partyFromKey(it) },
|
||||
resolveAttachment = { services.storageService.attachments.openAttachment(it) },
|
||||
resolveAttachment = { services.attachments.openAttachment(it) },
|
||||
resolveStateRef = { services.loadState(it) }
|
||||
)
|
||||
}
|
||||
|
@ -18,13 +18,13 @@ import net.corda.core.serialization.SerializeAsTokenContext
|
||||
class FetchAttachmentsFlow(requests: Set<SecureHash>,
|
||||
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 maybeWriteToDisk(downloaded: List<Attachment>) {
|
||||
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) :
|
||||
FetchDataFlow<SignedTransaction, SignedTransaction>(requests, otherSide) {
|
||||
|
||||
override fun load(txid: SecureHash): SignedTransaction? {
|
||||
return serviceHub.storageService.validatedTransactions.getTransaction(txid)
|
||||
}
|
||||
override fun load(txid: SecureHash): SignedTransaction? = serviceHub.validatedTransactions.getTransaction(txid)
|
||||
}
|
||||
|
@ -4,10 +4,8 @@ import net.corda.core.contracts.*
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
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
|
||||
@ -55,7 +53,7 @@ class NotaryChangeFlow<out T : ContractState>(
|
||||
private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<AbstractParty> {
|
||||
val stateRef = originalState.ref
|
||||
val txId = stateRef.txhash
|
||||
val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId)
|
||||
val issuingTx = serviceHub.validatedTransactions.getTransaction(txId)
|
||||
?: throw StateReplacementException("Transaction $txId not found")
|
||||
val outputs = issuingTx.tx.outputs
|
||||
|
||||
|
@ -193,7 +193,7 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
|
||||
private fun fetchMissingAttachments(downloads: List<WireTransaction>) {
|
||||
// TODO: This could be done in parallel with other fetches for extra speed.
|
||||
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())
|
||||
subFlow(FetchAttachmentsFlow(missingAttachments.toSet(), otherSide))
|
||||
|
@ -64,8 +64,8 @@ class ContractUpgradeFlowTest {
|
||||
a.services.startFlow(FinalityFlow(stx, setOf(a.info.legalIdentity, b.info.legalIdentity)))
|
||||
mockNet.runNetwork()
|
||||
|
||||
val atx = a.database.transaction { a.services.storageService.validatedTransactions.getTransaction(stx.id) }
|
||||
val btx = b.database.transaction { b.services.storageService.validatedTransactions.getTransaction(stx.id) }
|
||||
val atx = a.database.transaction { a.services.validatedTransactions.getTransaction(stx.id) }
|
||||
val btx = b.database.transaction { b.services.validatedTransactions.getTransaction(stx.id) }
|
||||
requireNotNull(atx)
|
||||
requireNotNull(btx)
|
||||
|
||||
@ -85,13 +85,13 @@ class ContractUpgradeFlowTest {
|
||||
|
||||
fun check(node: MockNetwork.MockNode) {
|
||||
val nodeStx = node.database.transaction {
|
||||
node.services.storageService.validatedTransactions.getTransaction(result.ref.txhash)
|
||||
node.services.validatedTransactions.getTransaction(result.ref.txhash)
|
||||
}
|
||||
requireNotNull(nodeStx)
|
||||
|
||||
// Verify inputs.
|
||||
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)
|
||||
assertTrue(input!!.tx.outputs.single().data is DummyContract.State)
|
||||
@ -132,8 +132,8 @@ class ContractUpgradeFlowTest {
|
||||
mockNet.runNetwork()
|
||||
handle.returnValue.getOrThrow()
|
||||
|
||||
val atx = a.database.transaction { a.services.storageService.validatedTransactions.getTransaction(stx.id) }
|
||||
val btx = b.database.transaction { b.services.storageService.validatedTransactions.getTransaction(stx.id) }
|
||||
val atx = a.database.transaction { a.services.validatedTransactions.getTransaction(stx.id) }
|
||||
val btx = b.database.transaction { b.services.validatedTransactions.getTransaction(stx.id) }
|
||||
requireNotNull(atx)
|
||||
requireNotNull(btx)
|
||||
|
||||
@ -156,11 +156,11 @@ class ContractUpgradeFlowTest {
|
||||
val result = resultFuture.getOrThrow()
|
||||
// Check results.
|
||||
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)
|
||||
|
||||
// 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)
|
||||
assertTrue(input!!.tx.outputs.single().data is DummyContract.State)
|
||||
|
||||
|
@ -58,8 +58,8 @@ class ResolveTransactionsFlowTest {
|
||||
val results = future.getOrThrow()
|
||||
assertEquals(listOf(stx1.id, stx2.id), results.map { it.id })
|
||||
b.database.transaction {
|
||||
assertEquals(stx1, b.storage.validatedTransactions.getTransaction(stx1.id))
|
||||
assertEquals(stx2, b.storage.validatedTransactions.getTransaction(stx2.id))
|
||||
assertEquals(stx1, b.services.validatedTransactions.getTransaction(stx1.id))
|
||||
assertEquals(stx2, b.services.validatedTransactions.getTransaction(stx2.id))
|
||||
}
|
||||
}
|
||||
// DOCEND 1
|
||||
@ -81,9 +81,9 @@ class ResolveTransactionsFlowTest {
|
||||
mockNet.runNetwork()
|
||||
future.getOrThrow()
|
||||
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.
|
||||
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
|
||||
val id = a.database.transaction {
|
||||
a.services.storageService.attachments.importAttachment(makeJar())
|
||||
a.services.attachments.importAttachment(makeJar())
|
||||
}
|
||||
val stx2 = makeTransactions(withAttachment = id).second
|
||||
val p = ResolveTransactionsFlow(stx2, a.info.legalIdentity)
|
||||
@ -158,7 +158,7 @@ class ResolveTransactionsFlowTest {
|
||||
|
||||
// TODO: this operation should not require an explicit 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.Party
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.node.services.StorageService
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.DUMMY_NOTARY
|
||||
@ -22,7 +21,6 @@ import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.security.PublicKey
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import kotlin.test.assertEquals
|
||||
@ -42,11 +40,9 @@ class AttachmentClassLoaderTests {
|
||||
val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentClassLoaderTests::class.java.getResource("isolated.jar")
|
||||
|
||||
private fun <T> Kryo.withAttachmentStorage(attachmentStorage: AttachmentStorage, block: () -> T) = run {
|
||||
context.put(WireTransactionSerializer.attachmentsClassLoaderEnabled, true)
|
||||
val serviceHub = mock<ServiceHub>()
|
||||
val storageService = mock<StorageService>()
|
||||
whenever(serviceHub.storageService).thenReturn(storageService)
|
||||
whenever(storageService.attachmentsClassLoaderEnabled).thenReturn(true)
|
||||
whenever(storageService.attachments).thenReturn(attachmentStorage)
|
||||
whenever(serviceHub.attachments).thenReturn(attachmentStorage)
|
||||
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 MockNetwork.MockNode.attachments() = services.storageService.attachments as NodeAttachmentService
|
||||
private fun MockNetwork.MockNode.saveAttachment(content: String) = database.transaction { 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.saveAttachment(content: String) = database.transaction {
|
||||
attachments.importAttachment(createAttachmentData(content).inputStream())
|
||||
}
|
||||
private fun MockNetwork.MockNode.hackAttachment(attachmentId: SecureHash, content: String) = database.transaction {
|
||||
attachments.updateAttachment(attachmentId, createAttachmentData(content))
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NodeAttachmentService.importAttachment
|
||||
@ -122,7 +125,7 @@ class AttachmentSerializationTest {
|
||||
private class OpenAttachmentLogic(server: MockNetwork.MockNode, private val attachmentId: SecureHash) : ClientLogic(server) {
|
||||
@Suspendable
|
||||
override fun getAttachmentContent(): String {
|
||||
val localAttachment = serviceHub.storageService.attachments.openAttachment(attachmentId)!!
|
||||
val localAttachment = serviceHub.attachments.openAttachment(attachmentId)!!
|
||||
communicate()
|
||||
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 {
|
||||
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {
|
||||
override fun startMessagingService(rpcOps: RPCOps) {
|
||||
attachments().checkAttachmentsOnLoad = checkAttachmentsOnLoad
|
||||
attachments.checkAttachmentsOnLoad = checkAttachmentsOnLoad
|
||||
super.startMessagingService(rpcOps)
|
||||
}
|
||||
}
|
||||
@ -180,7 +183,7 @@ class AttachmentSerializationTest {
|
||||
@Test
|
||||
fun `only the hash of a regular attachment should be saved in checkpoint`() {
|
||||
val attachmentId = client.saveAttachment("genuine")
|
||||
client.attachments().checkAttachmentsOnLoad = false // Cached by AttachmentImpl.
|
||||
client.attachments.checkAttachmentsOnLoad = false // Cached by AttachmentImpl.
|
||||
launchFlow(OpenAttachmentLogic(server, attachmentId), 1)
|
||||
client.hackAttachment(attachmentId, "hacked")
|
||||
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…)
|
||||
* ``ServiceHub.identityService``
|
||||
* 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``
|
||||
* Stores the node’s current and historic states
|
||||
* ``ServiceHub.storageService``
|
||||
* Stores additional information such as transactions and attachments
|
||||
* ``ServiceHub.keyManagementService``
|
||||
* Manages signing transactions and generating fresh public keys
|
||||
* ``ServiceHub.myInfo``
|
||||
|
@ -6,6 +6,7 @@ from the previous milestone release.
|
||||
|
||||
UNRELEASED
|
||||
----------
|
||||
|
||||
* Changes in ``NodeInfo``:
|
||||
|
||||
* ``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
|
||||
``SingleMessageRecipient``, but of ``HostAndPort``.
|
||||
|
||||
* ``ServiceHub.storageService`` has been removed. ``attachments`` and ``validatedTransactions`` are now direct members of
|
||||
``ServiceHub``.
|
||||
|
||||
Milestone 13
|
||||
----------
|
||||
------------
|
||||
|
||||
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.
|
||||
|
||||
|
@ -220,7 +220,7 @@ class CommercialPaperTestsGeneric {
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||
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.
|
||||
vaultService.notifyAll(txs.map { it.tx })
|
||||
@ -240,7 +240,7 @@ class CommercialPaperTestsGeneric {
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||
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.
|
||||
vaultService.notifyAll(txs.map { it.tx })
|
||||
@ -251,8 +251,8 @@ class CommercialPaperTestsGeneric {
|
||||
}
|
||||
|
||||
// Propagate the cash transactions to each side.
|
||||
aliceServices.recordTransactions(bigCorpVault.states.map { bigCorpServices.storageService.validatedTransactions.getTransaction(it.ref.txhash)!! })
|
||||
bigCorpServices.recordTransactions(alicesVault.states.map { aliceServices.storageService.validatedTransactions.getTransaction(it.ref.txhash)!! })
|
||||
aliceServices.recordTransactions(bigCorpVault.states.map { bigCorpServices.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.
|
||||
val faceValue = 10000.DOLLARS `issued by` DUMMY_CASH_ISSUER
|
||||
|
@ -63,7 +63,7 @@ class CashTests {
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||
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.
|
||||
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.NodeRegistration
|
||||
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.NodeSchemaService
|
||||
import net.corda.node.services.statemachine.FlowStateMachineImpl
|
||||
@ -70,7 +73,6 @@ import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Modifier.*
|
||||
import java.net.JarURLConnection
|
||||
import java.net.URI
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.KeyPair
|
||||
@ -120,10 +122,14 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
private val flowFactories = ConcurrentHashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>()
|
||||
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 networkMapCache: NetworkMapCacheInternal get() = netMapCache
|
||||
override val storageService: TxWritableStorageService get() = storage
|
||||
override val vaultService: VaultService get() = vault
|
||||
override val vaultQueryService: VaultQueryService get() = vaultQuery
|
||||
override val keyManagementService: KeyManagementService get() = keyManagement
|
||||
@ -157,7 +163,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||
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 storage: TxWritableStorageService
|
||||
lateinit var checkpointStorage: CheckpointStorage
|
||||
lateinit var smm: StateMachineManager
|
||||
lateinit var attachments: NodeAttachmentService
|
||||
lateinit var transactions: TransactionStorage
|
||||
lateinit var transactionMappings: StateMachineRecordedTransactionMappingStorage
|
||||
lateinit var uploaders: List<FileUploader>
|
||||
lateinit var vault: VaultService
|
||||
lateinit var vaultQuery: VaultQueryService
|
||||
lateinit var keyManagement: KeyManagementService
|
||||
@ -469,9 +478,10 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
*/
|
||||
private fun makeServices(keyStoreWrapper: KeyStoreWrapper): MutableList<Any> {
|
||||
val keyStore = keyStoreWrapper.keyStore
|
||||
val storageServices = initialiseStorageService(configuration.baseDirectory)
|
||||
storage = storageServices.first
|
||||
checkpointStorage = storageServices.second
|
||||
attachments = createAttachmentStorage()
|
||||
transactions = createTransactionStorage()
|
||||
transactionMappings = DBTransactionMappingStorage()
|
||||
checkpointStorage = DBCheckpointStorage()
|
||||
netMapCache = InMemoryNetworkMapCache(services)
|
||||
network = makeMessagingService()
|
||||
schemas = makeSchemaService()
|
||||
@ -490,11 +500,13 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
keyManagement = makeKeyManagementService(identity)
|
||||
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)
|
||||
return tokenizableServices
|
||||
}
|
||||
|
||||
protected open fun createTransactionStorage(): TransactionStorage = DBTransactionStorage()
|
||||
|
||||
private fun scanCordapps(): ScanResult? {
|
||||
val scanPackage = System.getProperty("net.corda.node.cordapp.scan.package")
|
||||
val paths = if (scanPackage != null) {
|
||||
@ -548,9 +560,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
}
|
||||
|
||||
private fun initUploaders() {
|
||||
val uploaders: List<FileUploader> = listOf(storage.attachments as NodeAttachmentService) +
|
||||
cordappServices.values.filterIsInstance(AcceptsFileUpload::class.java)
|
||||
(storage as StorageServiceImpl).initUploaders(uploaders)
|
||||
uploaders = listOf(attachments) + cordappServices.values.filterIsInstance(AcceptsFileUpload::class.java)
|
||||
}
|
||||
|
||||
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().
|
||||
*/
|
||||
open fun setup(): AbstractNode {
|
||||
createNodeDir()
|
||||
configuration.baseDirectory.createDirectories()
|
||||
return this
|
||||
}
|
||||
|
||||
@ -761,22 +771,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
|
||||
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 obtainLegalIdentityKey(): KeyPair = identityKeyPair.second
|
||||
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 fun makeAttachmentStorage(dir: Path): AttachmentStorage {
|
||||
val attachmentsDir = dir / "attachments"
|
||||
try {
|
||||
attachmentsDir.createDirectory()
|
||||
} catch (e: FileAlreadyExistsException) {
|
||||
}
|
||||
private fun createAttachmentStorage(): NodeAttachmentService {
|
||||
val attachmentsDir = (configuration.baseDirectory / "attachments").createDirectories()
|
||||
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) {
|
||||
|
@ -12,7 +12,6 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.messaging.*
|
||||
import net.corda.core.node.NodeInfo
|
||||
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.PageSpecification
|
||||
import net.corda.core.node.services.vault.QueryCriteria
|
||||
@ -76,7 +75,7 @@ class CordaRPCOpsImpl(
|
||||
|
||||
override fun verifiedTransactionsFeed(): DataFeed<List<SignedTransaction>, SignedTransaction> {
|
||||
return database.transaction {
|
||||
services.storageService.validatedTransactions.track()
|
||||
services.validatedTransactions.track()
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +91,7 @@ class CordaRPCOpsImpl(
|
||||
|
||||
override fun stateMachineRecordedTransactionMappingFeed(): DataFeed<List<StateMachineTransactionMapping>, StateMachineTransactionMapping> {
|
||||
return database.transaction {
|
||||
services.storageService.stateMachineRecordedTransactionMapping.track()
|
||||
services.stateMachineRecordedTransactionMapping.track()
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,21 +142,21 @@ class CordaRPCOpsImpl(
|
||||
override fun attachmentExists(id: SecureHash): Boolean {
|
||||
// TODO: this operation should not require an explicit transaction
|
||||
return database.transaction {
|
||||
services.storageService.attachments.openAttachment(id) != null
|
||||
services.attachments.openAttachment(id) != null
|
||||
}
|
||||
}
|
||||
|
||||
override fun openAttachment(id: SecureHash): InputStream {
|
||||
// TODO: this operation should not require an explicit transaction
|
||||
return database.transaction {
|
||||
services.storageService.attachments.openAttachment(id)!!.open()
|
||||
services.attachments.openAttachment(id)!!.open()
|
||||
}
|
||||
}
|
||||
|
||||
override fun uploadAttachment(jar: InputStream): SecureHash {
|
||||
// TODO: this operation should not require an explicit 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)
|
||||
@Suppress("OverridingDeprecatedMember", "DEPRECATION")
|
||||
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 {
|
||||
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) {
|
||||
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.
|
||||
class FetchAttachmentsHandler(otherParty: Party) : FetchDataHandler<ByteArray>(otherParty) {
|
||||
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
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting
|
||||
import com.google.common.net.HostAndPort
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.SingleMessageRecipient
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
import net.corda.core.node.NodeInfo
|
||||
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.TxWritableStorageService
|
||||
import net.corda.core.node.services.TransactionStorage
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.loggerFor
|
||||
@ -58,34 +62,38 @@ sealed class NetworkCacheError : Exception() {
|
||||
class DeregistrationFailed : NetworkCacheError()
|
||||
}
|
||||
|
||||
abstract class ServiceHubInternal : PluginServiceHub {
|
||||
interface ServiceHubInternal : PluginServiceHub {
|
||||
companion object {
|
||||
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
|
||||
* sends them to the vault for further processing. This is intended for implementations to call from
|
||||
* [recordTransactions].
|
||||
*
|
||||
* @param txs The transactions to record.
|
||||
* 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.
|
||||
*/
|
||||
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 recordedTransactions = txs.filter { writableStorageService.validatedTransactions.addTransaction(it) }
|
||||
val recordedTransactions = txs.filter { validatedTransactions.addTransaction(it) }
|
||||
if (stateMachineRunId != null) {
|
||||
recordedTransactions.forEach {
|
||||
storageService.stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id)
|
||||
stateMachineRecordedTransactionMapping.addMapping(stateMachineRunId, it.id)
|
||||
}
|
||||
} else {
|
||||
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.
|
||||
* @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.
|
||||
@ -124,5 +132,14 @@ abstract class ServiceHubInternal : PluginServiceHub {
|
||||
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.flows.StateMachineRunId
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.node.services.StateMachineRecordedTransactionMappingStorage
|
||||
import net.corda.core.node.services.StateMachineTransactionMapping
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
|
||||
import net.corda.node.utilities.*
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||
import rx.Observable
|
||||
import rx.subjects.PublishSubject
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
|
||||
|
@ -5,6 +5,7 @@ import net.corda.core.bufferUntilSubscribed
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.node.services.TransactionStorage
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.node.utilities.*
|
||||
import org.jetbrains.exposed.sql.ResultRow
|
||||
@ -14,7 +15,7 @@ import rx.Observable
|
||||
import rx.subjects.PublishSubject
|
||||
import java.util.Collections.synchronizedMap
|
||||
|
||||
class DBTransactionStorage : TransactionStorage {
|
||||
class DBTransactionStorage : TransactionStorage, SingletonSerializeAsToken() {
|
||||
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}transactions") {
|
||||
val txId = secureHash("tx_id")
|
||||
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 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.flows.StateMachineRunId
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.node.services.StateMachineRecordedTransactionMappingStorage
|
||||
import net.corda.core.node.services.StateMachineTransactionMapping
|
||||
import rx.Observable
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
|
||||
import rx.subjects.PublishSubject
|
||||
import java.util.*
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
|
@ -14,10 +14,7 @@ import net.corda.core.div
|
||||
import net.corda.core.extractZipFile
|
||||
import net.corda.core.isDirectory
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SerializationToken
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.serialization.SerializeAsTokenContext
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.services.api.AcceptsFileUpload
|
||||
import net.corda.node.services.database.RequeryConfiguration
|
||||
@ -38,8 +35,11 @@ import javax.annotation.concurrent.ThreadSafe
|
||||
* Stores attachments in H2 database.
|
||||
*/
|
||||
@ThreadSafe
|
||||
class NodeAttachmentService(override var storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry) : AttachmentStorage, AcceptsFileUpload {
|
||||
private val log = loggerFor<NodeAttachmentService>()
|
||||
class NodeAttachmentService(override var storePath: Path, dataSourceProperties: Properties, metrics: MetricRegistry)
|
||||
: AttachmentStorage, AcceptsFileUpload, SingletonSerializeAsToken() {
|
||||
companion object {
|
||||
private val log = loggerFor<NodeAttachmentService>()
|
||||
}
|
||||
|
||||
val configuration = RequeryConfiguration(dataSourceProperties)
|
||||
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 {
|
||||
logger.debug { "waitForLedgerCommit($hash) ..." }
|
||||
suspend(WaitForLedgerCommit(hash, sessionFlow.stateMachine as FlowStateMachineImpl<*>))
|
||||
val stx = serviceHub.storageService.validatedTransactions.getTransaction(hash)
|
||||
val stx = serviceHub.validatedTransactions.getTransaction(hash)
|
||||
if (stx != null) {
|
||||
logger.debug { "Transaction $hash committed to ledger" }
|
||||
return stx
|
||||
|
@ -187,7 +187,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
|
||||
|
||||
private fun listenToLedgerTransactions() {
|
||||
// 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 fibers: Set<FlowStateMachineImpl<*>> = mutex.locked { fibersWaitingForLedgerCommit.removeAll(hash) }
|
||||
if (fibers.isNotEmpty()) {
|
||||
@ -268,7 +268,7 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
|
||||
if (waitingForResponse != null) {
|
||||
if (waitingForResponse is WaitForLedgerCommit) {
|
||||
val stx = database.transaction {
|
||||
serviceHub.storageService.validatedTransactions.getTransaction(waitingForResponse.hash)
|
||||
serviceHub.validatedTransactions.getTransaction(waitingForResponse.hash)
|
||||
}
|
||||
if (stx != null) {
|
||||
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) {
|
||||
// Is it already committed?
|
||||
val stx = database.transaction {
|
||||
serviceHub.storageService.validatedTransactions.getTransaction(ioRequest.hash)
|
||||
serviceHub.validatedTransactions.getTransaction(ioRequest.hash)
|
||||
}
|
||||
if (stx != null) {
|
||||
resumeFiber(ioRequest.fiber)
|
||||
|
@ -72,10 +72,10 @@ public class VaultQueryJavaTests {
|
||||
@Override
|
||||
public void recordTransactions(@NotNull Iterable<SignedTransaction> 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()));
|
||||
}
|
||||
};
|
||||
|
@ -11,11 +11,10 @@ import net.corda.flows.FetchDataFlow
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.database.RequeryConfiguration
|
||||
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.transactions.SimpleNotaryService
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.node.utilities.transaction
|
||||
import net.corda.testing.node.MockNetwork
|
||||
import net.corda.testing.node.makeTestDataSourceProperties
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.junit.Before
|
||||
@ -61,7 +60,7 @@ class AttachmentTests {
|
||||
|
||||
// Insert an attachment into node zero's store directly.
|
||||
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.
|
||||
@ -72,7 +71,7 @@ class AttachmentTests {
|
||||
|
||||
// Verify it was inserted into node one's store.
|
||||
val attachment = n1.database.transaction {
|
||||
n1.storage.attachments.openAttachment(id)!!
|
||||
n1.attachments.openAttachment(id)!!
|
||||
}
|
||||
|
||||
assertEquals(id, attachment.open().readBytes().sha256())
|
||||
@ -108,7 +107,7 @@ class AttachmentTests {
|
||||
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {
|
||||
override fun start(): MockNetwork.MockNode {
|
||||
super.start()
|
||||
(storage.attachments as NodeAttachmentService).checkAttachmentsOnLoad = false
|
||||
attachments.checkAttachmentsOnLoad = false
|
||||
return this
|
||||
}
|
||||
}
|
||||
@ -119,7 +118,7 @@ class AttachmentTests {
|
||||
val attachment = fakeAttachment()
|
||||
// Insert an attachment into node zero's store directly.
|
||||
val id = n0.database.transaction {
|
||||
n0.storage.attachments.importAttachment(ByteArrayInputStream(attachment))
|
||||
n0.attachments.importAttachment(ByteArrayInputStream(attachment))
|
||||
}
|
||||
|
||||
// Corrupt its store.
|
||||
@ -130,7 +129,7 @@ class AttachmentTests {
|
||||
corruptAttachment.attId = id
|
||||
corruptAttachment.content = attachment
|
||||
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.SecureHash
|
||||
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.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.FlowStateMachine
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.SingleMessageRecipient
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
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.transactions.SignedTransaction
|
||||
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.services.config.NodeConfiguration
|
||||
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.utilities.transaction
|
||||
import net.corda.testing.*
|
||||
@ -211,7 +216,7 @@ class TwoPartyTradeFlowTests {
|
||||
assertThat(bobNode.checkpointStorage.checkpoints()).hasSize(1)
|
||||
}
|
||||
|
||||
val storage = bobNode.storage.validatedTransactions
|
||||
val storage = bobNode.services.validatedTransactions
|
||||
val bobTransactionsBeforeCrash = bobNode.database.transaction {
|
||||
(storage as DBTransactionStorage).transactions
|
||||
}
|
||||
@ -252,7 +257,9 @@ class TwoPartyTradeFlowTests {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@ -276,13 +283,9 @@ class TwoPartyTradeFlowTests {
|
||||
overrideServices: Map<ServiceInfo, KeyPair>?,
|
||||
entropyRoot: BigInteger): MockNetwork.MockNode {
|
||||
return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, overrideServices, entropyRoot) {
|
||||
// That constructs the storage service object in a customised way ...
|
||||
override fun constructStorageService(
|
||||
attachments: AttachmentStorage,
|
||||
transactionStorage: TransactionStorage,
|
||||
stateMachineRecordedTransactionMappingStorage: StateMachineRecordedTransactionMappingStorage
|
||||
): StorageServiceImpl {
|
||||
return StorageServiceImpl(attachments, RecordingTransactionStorage(database, transactionStorage), stateMachineRecordedTransactionMappingStorage)
|
||||
// That constructs a recording tx storage
|
||||
override fun createTransactionStorage(): TransactionStorage {
|
||||
return RecordingTransactionStorage(database, super.createTransactionStorage())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,7 +329,7 @@ class TwoPartyTradeFlowTests {
|
||||
mockNet.runNetwork()
|
||||
|
||||
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.
|
||||
records.expectEvents(isStrict = false) {
|
||||
sequence(
|
||||
@ -344,7 +347,7 @@ class TwoPartyTradeFlowTests {
|
||||
|
||||
// Bob has downloaded the attachment.
|
||||
bobNode.database.transaction {
|
||||
bobNode.storage.attachments.openAttachment(attachmentID)!!.openAsJAR().use {
|
||||
bobNode.services.attachments.openAttachment(attachmentID)!!.openAsJAR().use {
|
||||
it.nextJarEntry
|
||||
val contents = it.reader().readText()
|
||||
assertTrue(contents.contains("Our commercial paper is top notch stuff"))
|
||||
@ -354,7 +357,7 @@ class TwoPartyTradeFlowTests {
|
||||
|
||||
// And from Alice's perspective ...
|
||||
run {
|
||||
val records = (aliceNode.storage.validatedTransactions as RecordingTransactionStorage).records
|
||||
val records = (aliceNode.services.validatedTransactions as RecordingTransactionStorage).records
|
||||
records.expectEvents(isStrict = false) {
|
||||
sequence(
|
||||
// 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
|
||||
|
||||
val aliceTxStream = aliceNode.storage.validatedTransactions.track().second
|
||||
val aliceTxMappings = with(aliceNode) { database.transaction { storage.stateMachineRecordedTransactionMapping.track().second } }
|
||||
val aliceTxStream = aliceNode.services.validatedTransactions.track().updates
|
||||
val aliceTxMappings = with(aliceNode) {
|
||||
database.transaction { services.stateMachineRecordedTransactionMapping.track().updates }
|
||||
}
|
||||
val aliceSmId = runBuyerAndSeller(notaryNode, aliceNode, bobNode,
|
||||
"alice's paper".outputStateAndRef()).sellerId
|
||||
|
||||
@ -443,7 +448,7 @@ class TwoPartyTradeFlowTests {
|
||||
)
|
||||
aliceTxStream.expectEvents { aliceTxExpectations }
|
||||
val aliceMappingExpectations = sequence(
|
||||
expect { (stateMachineRunId, transactionId) ->
|
||||
expect<StateMachineTransactionMapping> { (stateMachineRunId, transactionId) ->
|
||||
require(stateMachineRunId == aliceSmId)
|
||||
require(transactionId == bobsFakeCash[0].id)
|
||||
},
|
||||
@ -451,9 +456,9 @@ class TwoPartyTradeFlowTests {
|
||||
require(stateMachineRunId == aliceSmId)
|
||||
require(transactionId == bobsFakeCash[2].id)
|
||||
},
|
||||
expect { mapping: StateMachineTransactionMapping ->
|
||||
require(mapping.stateMachineRunId == aliceSmId)
|
||||
require(mapping.transactionId == bobsFakeCash[1].id)
|
||||
expect { (stateMachineRunId, transactionId) ->
|
||||
require(stateMachineRunId == aliceSmId)
|
||||
require(transactionId == bobsFakeCash[1].id)
|
||||
}
|
||||
)
|
||||
aliceTxMappings.expectEvents { aliceMappingExpectations }
|
||||
@ -589,7 +594,7 @@ class TwoPartyTradeFlowTests {
|
||||
}
|
||||
return node.database.transaction {
|
||||
node.services.recordTransactions(signed)
|
||||
val validatedTransactions = node.services.storageService.validatedTransactions
|
||||
val validatedTransactions = node.services.validatedTransactions
|
||||
if (validatedTransactions is RecordingTransactionStorage) {
|
||||
validatedTransactions.records.clear()
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.*
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.node.internal.InitiatedFlowFactory
|
||||
import net.corda.node.serialization.NodeClock
|
||||
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.transactions.InMemoryTransactionVerifierService
|
||||
import net.corda.testing.MOCK_IDENTITY_SERVICE
|
||||
import net.corda.testing.node.MockAttachmentStorage
|
||||
import net.corda.testing.node.MockNetworkMapCache
|
||||
import net.corda.testing.node.MockStorageService
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import net.corda.testing.node.MockStateMachineRecordedTransactionMappingStorage
|
||||
import net.corda.testing.node.MockTransactionStorage
|
||||
import java.time.Clock
|
||||
|
||||
open class MockServiceHubInternal(
|
||||
@ -28,13 +29,16 @@ open class MockServiceHubInternal(
|
||||
val keyManagement: KeyManagementService? = null,
|
||||
val network: MessagingService? = null,
|
||||
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 scheduler: SchedulerService? = null,
|
||||
val overrideClock: Clock? = NodeClock(),
|
||||
val schemas: SchemaService? = NodeSchemaService(),
|
||||
val customTransactionVerifierService: TransactionVerifierService? = InMemoryTransactionVerifierService(2)
|
||||
) : ServiceHubInternal() {
|
||||
) : ServiceHubInternal {
|
||||
override val vaultQueryService: VaultQueryService
|
||||
get() = customVaultQuery ?: throw UnsupportedOperationException()
|
||||
override val transactionVerifierService: TransactionVerifierService
|
||||
@ -49,8 +53,6 @@ open class MockServiceHubInternal(
|
||||
get() = network ?: throw UnsupportedOperationException()
|
||||
override val networkMapCache: NetworkMapCacheInternal
|
||||
get() = mapCache ?: MockNetworkMapCache(this)
|
||||
override val storageService: StorageService
|
||||
get() = storage ?: throw UnsupportedOperationException()
|
||||
override val schedulerService: SchedulerService
|
||||
get() = scheduler ?: throw UnsupportedOperationException()
|
||||
override val clock: Clock
|
||||
@ -67,14 +69,9 @@ open class MockServiceHubInternal(
|
||||
override val schemaService: SchemaService
|
||||
get() = schemas ?: throw UnsupportedOperationException()
|
||||
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
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) = recordTransactionsInternal(txStorageService, txs)
|
||||
|
||||
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T = throw UnsupportedOperationException()
|
||||
|
||||
override fun <T> startFlow(logic: FlowLogic<T>, flowInitiator: FlowInitiator): FlowStateMachineImpl<T> {
|
||||
|
@ -99,7 +99,7 @@ class NotaryChangeTests {
|
||||
val newState = future.resultFuture.getOrThrow()
|
||||
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
|
||||
val originalOutputs = issueTx.outputs.map { it.data }
|
||||
|
@ -85,7 +85,7 @@ class HibernateConfigurationTest {
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||
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.
|
||||
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.identity.AnonymousParty
|
||||
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.unconsumedStates
|
||||
import net.corda.core.serialization.OpaqueBytes
|
||||
@ -53,7 +52,7 @@ class NodeVaultServiceTest {
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||
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.
|
||||
vaultService.notifyAll(txs.map { it.tx })
|
||||
@ -77,17 +76,12 @@ class NodeVaultServiceTest {
|
||||
val w1 = vaultSvc.unconsumedStates<Cash.State>()
|
||||
assertThat(w1).hasSize(3)
|
||||
|
||||
val originalStorage = services.storageService
|
||||
val originalVault = vaultSvc
|
||||
val services2 = object : MockServices() {
|
||||
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>) {
|
||||
for (stx in txs) {
|
||||
storageService.validatedTransactions.addTransaction(stx)
|
||||
validatedTransactions.addTransaction(stx)
|
||||
vaultService.notify(stx.tx)
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class VaultQueryTests {
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||
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.
|
||||
vaultService.notifyAll(txs.map { it.tx })
|
||||
@ -93,9 +93,9 @@ class VaultQueryTests {
|
||||
/**
|
||||
* Helper method for generating a Persistent H2 test database
|
||||
*/
|
||||
@Ignore //@Test
|
||||
@Ignore
|
||||
@Test
|
||||
fun createPersistentTestDb() {
|
||||
|
||||
val dataSourceAndDatabase = configureDatabase(makePersistentDataSourceProperties())
|
||||
val dataSource = dataSourceAndDatabase.first
|
||||
val database = dataSourceAndDatabase.second
|
||||
|
@ -3,7 +3,9 @@ package net.corda.node.services.vault
|
||||
import net.corda.contracts.DummyDealContract
|
||||
import net.corda.contracts.asset.Cash
|
||||
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.identity.AnonymousParty
|
||||
import net.corda.core.node.services.VaultService
|
||||
@ -54,7 +56,7 @@ class VaultWithCashTest {
|
||||
|
||||
override fun recordTransactions(txs: Iterable<SignedTransaction>) {
|
||||
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.
|
||||
vaultService.notifyAll(txs.map { it.tx })
|
||||
|
@ -55,14 +55,14 @@ class BuyerFlow(val otherParty: Party) : FlowLogic<Unit>() {
|
||||
|
||||
private fun logIssuanceAttachment(tradeTX: SignedTransaction) {
|
||||
// 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,
|
||||
followInputsOfType = CommercialPaper.State::class.java)
|
||||
val cpIssuance = search.call().single()
|
||||
|
||||
// Buyer will fetch the attachment from the seller automatically when it resolves the transaction.
|
||||
// For demo purposes just extract attachment jars when saved to disk, so the user can explore them.
|
||||
val attachmentsPath = (serviceHub.storageService.attachments).let {
|
||||
val attachmentsPath = (serviceHub.attachments).let {
|
||||
it.automaticallyExtractAttachments = true
|
||||
it.storePath
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ class SellerFlow(val otherParty: Party,
|
||||
// TODO: Consider moving these two steps below into generateIssue.
|
||||
|
||||
// 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.
|
||||
tx.addTimeWindow(Instant.now(), 30.seconds)
|
||||
|
@ -211,8 +211,9 @@ data class TestLedgerDSLInterpreter private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun resolveAttachment(attachmentId: SecureHash): Attachment =
|
||||
services.storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
|
||||
internal fun resolveAttachment(attachmentId: SecureHash): Attachment {
|
||||
return services.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
|
||||
}
|
||||
|
||||
private fun <R> interpretTransactionDsl(
|
||||
transactionBuilder: TransactionBuilder,
|
||||
@ -276,7 +277,7 @@ data class TestLedgerDSLInterpreter private constructor(
|
||||
dsl(LedgerDSL(copy()))
|
||||
|
||||
override fun attachment(attachment: InputStream): SecureHash {
|
||||
return services.storageService.attachments.importAttachment(attachment)
|
||||
return services.attachments.importAttachment(attachment)
|
||||
}
|
||||
|
||||
override fun verifies(): EnforceVerifyOrFail {
|
||||
|
@ -6,7 +6,6 @@ import net.corda.core.crypto.*
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.SingleMessageRecipient
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.ServiceHub
|
||||
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.getTestPartyAndCertificate
|
||||
import net.corda.flows.AnonymisedIdentity
|
||||
import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
|
||||
import net.corda.node.services.database.HibernateConfiguration
|
||||
import net.corda.node.services.identity.InMemoryIdentityService
|
||||
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.MOCK_IDENTITIES
|
||||
import net.corda.testing.MOCK_VERSION_INFO
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.bouncycastle.operator.ContentSigner
|
||||
import rx.Observable
|
||||
import rx.subjects.PublishSubject
|
||||
@ -41,11 +40,9 @@ import java.nio.file.Paths
|
||||
import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.CertPath
|
||||
import java.time.Clock
|
||||
import java.util.*
|
||||
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!
|
||||
// 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>) {
|
||||
txs.forEach {
|
||||
storageService.stateMachineRecordedTransactionMapping.addMapping(StateMachineRunId.createRandom(), it.id)
|
||||
stateMachineRecordedTransactionMapping.addMapping(StateMachineRunId.createRandom(), it.id)
|
||||
}
|
||||
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 val keyManagementService: KeyManagementService = MockKeyManagementService(identityService, *keys)
|
||||
|
||||
@ -185,15 +184,6 @@ open class MockTransactionStorage : TransactionStorage {
|
||||
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.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user