[CORDA-941]: Add NetworkParameters contract implementation whitelist. (#2580)

This commit is contained in:
Michele Sollecito 2018-02-23 14:29:02 +00:00 committed by GitHub
parent 977836f4eb
commit 5be0e4b39e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 641 additions and 287 deletions

View File

@ -14,8 +14,6 @@
public void setMessage(String) public void setMessage(String)
public void setOriginalExceptionClassName(String) public void setOriginalExceptionClassName(String)
## ##
public @interface net.corda.core.CordaInternal
##
public final class net.corda.core.CordaOID extends java.lang.Object public final class net.corda.core.CordaOID extends java.lang.Object
@org.jetbrains.annotations.NotNull public static final String CORDA_PLATFORM = "1.3.6.1.4.1.50530.1" @org.jetbrains.annotations.NotNull public static final String CORDA_PLATFORM = "1.3.6.1.4.1.50530.1"
public static final net.corda.core.CordaOID INSTANCE public static final net.corda.core.CordaOID INSTANCE
@ -209,7 +207,7 @@ public static final class net.corda.core.context.Trace$InvocationId$Companion ex
public static final class net.corda.core.context.Trace$SessionId$Companion extends java.lang.Object public static final class net.corda.core.context.Trace$SessionId$Companion extends java.lang.Object
@kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace$SessionId newInstance(String, java.time.Instant) @kotlin.jvm.JvmStatic @org.jetbrains.annotations.NotNull public final net.corda.core.context.Trace$SessionId newInstance(String, java.time.Instant)
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.AlwaysAcceptAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint @net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.contracts.AlwaysAcceptAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint
public boolean isSatisfiedBy(net.corda.core.contracts.Attachment) public boolean isSatisfiedBy(net.corda.core.contracts.Attachment)
public static final net.corda.core.contracts.AlwaysAcceptAttachmentConstraint INSTANCE public static final net.corda.core.contracts.AlwaysAcceptAttachmentConstraint INSTANCE
## ##
@ -284,14 +282,14 @@ public static final class net.corda.core.contracts.AmountTransfer$Companion exte
@org.jetbrains.annotations.NotNull public abstract java.io.InputStream open() @org.jetbrains.annotations.NotNull public abstract java.io.InputStream open()
@org.jetbrains.annotations.NotNull public abstract jar.JarInputStream openAsJAR() @org.jetbrains.annotations.NotNull public abstract jar.JarInputStream openAsJAR()
## ##
@net.corda.core.serialization.CordaSerializable public interface net.corda.core.contracts.AttachmentConstraint @net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public interface net.corda.core.contracts.AttachmentConstraint
public abstract boolean isSatisfiedBy(net.corda.core.contracts.Attachment) public abstract boolean isSatisfiedBy(net.corda.core.contracts.Attachment)
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.AttachmentResolutionException extends net.corda.core.flows.FlowException @net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.AttachmentResolutionException extends net.corda.core.flows.FlowException
public <init>(net.corda.core.crypto.SecureHash) public <init>(net.corda.core.crypto.SecureHash)
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getHash() @org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getHash()
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.AutomaticHashConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint @net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.contracts.AutomaticHashConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint
public boolean isSatisfiedBy(net.corda.core.contracts.Attachment) public boolean isSatisfiedBy(net.corda.core.contracts.Attachment)
public static final net.corda.core.contracts.AutomaticHashConstraint INSTANCE public static final net.corda.core.contracts.AutomaticHashConstraint INSTANCE
## ##
@ -343,14 +341,20 @@ public final class net.corda.core.contracts.ComponentGroupEnum extends java.lang
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.ContractAttachment extends java.lang.Object implements net.corda.core.contracts.Attachment @net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.ContractAttachment extends java.lang.Object implements net.corda.core.contracts.Attachment
public <init>(net.corda.core.contracts.Attachment, String) public <init>(net.corda.core.contracts.Attachment, String)
public <init>(net.corda.core.contracts.Attachment, String, Set)
public <init>(net.corda.core.contracts.Attachment, String, Set, String)
public void extractFile(String, java.io.OutputStream) public void extractFile(String, java.io.OutputStream)
@org.jetbrains.annotations.NotNull public final Set getAdditionalContracts()
@org.jetbrains.annotations.NotNull public final Set getAllContracts()
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.Attachment getAttachment() @org.jetbrains.annotations.NotNull public final net.corda.core.contracts.Attachment getAttachment()
@org.jetbrains.annotations.NotNull public final String getContract() @org.jetbrains.annotations.NotNull public final String getContract()
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId() @org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash getId()
@org.jetbrains.annotations.NotNull public List getSigners() @org.jetbrains.annotations.NotNull public List getSigners()
public int getSize() public int getSize()
@org.jetbrains.annotations.Nullable public final String getUploader()
@org.jetbrains.annotations.NotNull public java.io.InputStream open() @org.jetbrains.annotations.NotNull public java.io.InputStream open()
@org.jetbrains.annotations.NotNull public jar.JarInputStream openAsJAR() @org.jetbrains.annotations.NotNull public jar.JarInputStream openAsJAR()
@org.jetbrains.annotations.NotNull public String toString()
## ##
@net.corda.core.serialization.CordaSerializable public interface net.corda.core.contracts.ContractState @net.corda.core.serialization.CordaSerializable public interface net.corda.core.contracts.ContractState
@org.jetbrains.annotations.NotNull public abstract List getParticipants() @org.jetbrains.annotations.NotNull public abstract List getParticipants()
@ -366,7 +370,7 @@ public final class net.corda.core.contracts.ContractsDSL extends java.lang.Objec
@org.jetbrains.annotations.NotNull public abstract Collection getExitKeys() @org.jetbrains.annotations.NotNull public abstract Collection getExitKeys()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.contracts.FungibleAsset withNewOwnerAndAmount(net.corda.core.contracts.Amount, net.corda.core.identity.AbstractParty) @org.jetbrains.annotations.NotNull public abstract net.corda.core.contracts.FungibleAsset withNewOwnerAndAmount(net.corda.core.contracts.Amount, net.corda.core.identity.AbstractParty)
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.contracts.HashAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint @net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.contracts.HashAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint
public <init>(net.corda.core.crypto.SecureHash) public <init>(net.corda.core.crypto.SecureHash)
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash component1() @org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash component1()
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.HashAttachmentConstraint copy(net.corda.core.crypto.SecureHash) @org.jetbrains.annotations.NotNull public final net.corda.core.contracts.HashAttachmentConstraint copy(net.corda.core.crypto.SecureHash)
@ -557,6 +561,9 @@ public final class net.corda.core.contracts.TransactionStateKt extends java.lang
@net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.contracts.TransactionVerificationException extends net.corda.core.flows.FlowException @net.corda.core.serialization.CordaSerializable public abstract class net.corda.core.contracts.TransactionVerificationException extends net.corda.core.flows.FlowException
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getTxId() @org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getTxId()
## ##
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ConflictingAttachmentsRejection extends net.corda.core.contracts.TransactionVerificationException
public <init>(net.corda.core.crypto.SecureHash, String)
##
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ContractConstraintRejection extends net.corda.core.contracts.TransactionVerificationException @net.corda.core.serialization.CordaSerializable public static final class net.corda.core.contracts.TransactionVerificationException$ContractConstraintRejection extends net.corda.core.contracts.TransactionVerificationException
public <init>(net.corda.core.crypto.SecureHash, String) public <init>(net.corda.core.crypto.SecureHash, String)
## ##
@ -620,6 +627,10 @@ public static final class net.corda.core.contracts.UniqueIdentifier$Companion ex
@org.jetbrains.annotations.NotNull public abstract String getLegacyContract() @org.jetbrains.annotations.NotNull public abstract String getLegacyContract()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.contracts.ContractState upgrade(net.corda.core.contracts.ContractState) @org.jetbrains.annotations.NotNull public abstract net.corda.core.contracts.ContractState upgrade(net.corda.core.contracts.ContractState)
## ##
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint extends java.lang.Object implements net.corda.core.contracts.AttachmentConstraint
public boolean isSatisfiedBy(net.corda.core.contracts.Attachment)
public static final net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint INSTANCE
##
@net.corda.core.DoNotImplement public interface net.corda.core.cordapp.Cordapp @net.corda.core.DoNotImplement public interface net.corda.core.cordapp.Cordapp
@org.jetbrains.annotations.NotNull public abstract List getContractClassNames() @org.jetbrains.annotations.NotNull public abstract List getContractClassNames()
@org.jetbrains.annotations.NotNull public abstract List getCordappClasses() @org.jetbrains.annotations.NotNull public abstract List getCordappClasses()
@ -634,10 +645,25 @@ public static final class net.corda.core.contracts.UniqueIdentifier$Companion ex
@org.jetbrains.annotations.NotNull public abstract List getServiceFlows() @org.jetbrains.annotations.NotNull public abstract List getServiceFlows()
@org.jetbrains.annotations.NotNull public abstract List getServices() @org.jetbrains.annotations.NotNull public abstract List getServices()
## ##
@net.corda.core.DoNotImplement public interface net.corda.core.cordapp.CordappConfig
public abstract boolean exists(String)
@org.jetbrains.annotations.NotNull public abstract Object get(String)
public abstract boolean getBoolean(String)
public abstract double getDouble(String)
public abstract float getFloat(String)
public abstract int getInt(String)
public abstract long getLong(String)
@org.jetbrains.annotations.NotNull public abstract Number getNumber(String)
@org.jetbrains.annotations.NotNull public abstract String getString(String)
##
public final class net.corda.core.cordapp.CordappConfigException extends java.lang.Exception
public <init>(String, Throwable)
##
public final class net.corda.core.cordapp.CordappContext extends java.lang.Object public final class net.corda.core.cordapp.CordappContext extends java.lang.Object
public <init>(net.corda.core.cordapp.Cordapp, net.corda.core.crypto.SecureHash, ClassLoader, net.corda.core.cordapp.CordappConfig) public <init>(net.corda.core.cordapp.Cordapp, net.corda.core.crypto.SecureHash, ClassLoader, net.corda.core.cordapp.CordappConfig)
@org.jetbrains.annotations.Nullable public final net.corda.core.crypto.SecureHash getAttachmentId() @org.jetbrains.annotations.Nullable public final net.corda.core.crypto.SecureHash getAttachmentId()
@org.jetbrains.annotations.NotNull public final ClassLoader getClassLoader() @org.jetbrains.annotations.NotNull public final ClassLoader getClassLoader()
@org.jetbrains.annotations.NotNull public final net.corda.core.cordapp.CordappConfig getConfig()
@org.jetbrains.annotations.NotNull public final net.corda.core.cordapp.Cordapp getCordapp() @org.jetbrains.annotations.NotNull public final net.corda.core.cordapp.Cordapp getCordapp()
## ##
@net.corda.core.DoNotImplement public interface net.corda.core.cordapp.CordappProvider @net.corda.core.DoNotImplement public interface net.corda.core.cordapp.CordappProvider
@ -1305,6 +1331,39 @@ public @interface net.corda.core.flows.InitiatedBy
public @interface net.corda.core.flows.InitiatingFlow public @interface net.corda.core.flows.InitiatingFlow
public abstract int version() public abstract int version()
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.NotarisationPayload extends java.lang.Object
public <init>(Object, net.corda.core.flows.NotarisationRequestSignature)
@org.jetbrains.annotations.NotNull public final Object component1()
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotarisationRequestSignature component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotarisationPayload copy(Object, net.corda.core.flows.NotarisationRequestSignature)
public boolean equals(Object)
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.CoreTransaction getCoreTransaction()
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotarisationRequestSignature getRequestSignature()
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.SignedTransaction getSignedTransaction()
@org.jetbrains.annotations.NotNull public final Object getTransaction()
public int hashCode()
public String toString()
##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.NotarisationRequest extends java.lang.Object
public <init>(List, net.corda.core.crypto.SecureHash)
@org.jetbrains.annotations.NotNull public final List getStatesToConsume()
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash getTransactionId()
public final void verifySignature(net.corda.core.flows.NotarisationRequestSignature, net.corda.core.identity.Party)
public static final net.corda.core.flows.NotarisationRequest$Companion Companion
##
public static final class net.corda.core.flows.NotarisationRequest$Companion extends java.lang.Object
##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.flows.NotarisationRequestSignature extends java.lang.Object
public <init>(net.corda.core.crypto.DigitalSignature$WithKey, int)
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.DigitalSignature$WithKey component1()
public final int component2()
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotarisationRequestSignature copy(net.corda.core.crypto.DigitalSignature$WithKey, int)
public boolean equals(Object)
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.DigitalSignature$WithKey getDigitalSignature()
public final int getPlatformVersion()
public int hashCode()
public String toString()
##
@net.corda.core.flows.InitiatingFlow public final class net.corda.core.flows.NotaryChangeFlow extends net.corda.core.flows.AbstractStateReplacementFlow$Instigator @net.corda.core.flows.InitiatingFlow public final class net.corda.core.flows.NotaryChangeFlow extends net.corda.core.flows.AbstractStateReplacementFlow$Instigator
public <init>(net.corda.core.contracts.StateAndRef, net.corda.core.identity.Party, net.corda.core.utilities.ProgressTracker) public <init>(net.corda.core.contracts.StateAndRef, net.corda.core.identity.Party, net.corda.core.utilities.ProgressTracker)
@org.jetbrains.annotations.NotNull protected net.corda.core.flows.AbstractStateReplacementFlow$UpgradeTx assembleTx() @org.jetbrains.annotations.NotNull protected net.corda.core.flows.AbstractStateReplacementFlow$UpgradeTx assembleTx()
@ -1331,6 +1390,15 @@ public @interface net.corda.core.flows.InitiatingFlow
public int hashCode() public int hashCode()
@org.jetbrains.annotations.NotNull public String toString() @org.jetbrains.annotations.NotNull public String toString()
## ##
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.NotaryError$RequestSignatureInvalid extends net.corda.core.flows.NotaryError
public <init>(Throwable)
@org.jetbrains.annotations.NotNull public final Throwable component1()
@org.jetbrains.annotations.NotNull public final net.corda.core.flows.NotaryError$RequestSignatureInvalid copy(Throwable)
public boolean equals(Object)
@org.jetbrains.annotations.NotNull public final Throwable getCause()
public int hashCode()
@org.jetbrains.annotations.NotNull public String toString()
##
@net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.NotaryError$TimeWindowInvalid extends net.corda.core.flows.NotaryError @net.corda.core.serialization.CordaSerializable public static final class net.corda.core.flows.NotaryError$TimeWindowInvalid extends net.corda.core.flows.NotaryError
public <init>(java.time.Instant, net.corda.core.contracts.TimeWindow) public <init>(java.time.Instant, net.corda.core.contracts.TimeWindow)
@org.jetbrains.annotations.NotNull public final java.time.Instant component1() @org.jetbrains.annotations.NotNull public final java.time.Instant component1()
@ -1369,6 +1437,7 @@ public final class net.corda.core.flows.NotaryFlow extends java.lang.Object
public <init>(net.corda.core.transactions.SignedTransaction) public <init>(net.corda.core.transactions.SignedTransaction)
public <init>(net.corda.core.transactions.SignedTransaction, net.corda.core.utilities.ProgressTracker) public <init>(net.corda.core.transactions.SignedTransaction, net.corda.core.utilities.ProgressTracker)
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public List call() @co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull public List call()
@org.jetbrains.annotations.NotNull protected final net.corda.core.identity.Party checkTransaction()
@org.jetbrains.annotations.NotNull public net.corda.core.utilities.ProgressTracker getProgressTracker() @org.jetbrains.annotations.NotNull public net.corda.core.utilities.ProgressTracker getProgressTracker()
@co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull protected final net.corda.core.utilities.UntrustworthyData notarise(net.corda.core.identity.Party) @co.paralleluniverse.fibers.Suspendable @org.jetbrains.annotations.NotNull protected final net.corda.core.utilities.UntrustworthyData notarise(net.corda.core.identity.Party)
@org.jetbrains.annotations.NotNull protected final List validateResponse(net.corda.core.utilities.UntrustworthyData, net.corda.core.identity.Party) @org.jetbrains.annotations.NotNull protected final List validateResponse(net.corda.core.utilities.UntrustworthyData, net.corda.core.identity.Party)
@ -1748,14 +1817,15 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
@org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowProgressHandle startTrackedFlow(net.corda.core.flows.FlowLogic) @org.jetbrains.annotations.NotNull public abstract net.corda.core.messaging.FlowProgressHandle startTrackedFlow(net.corda.core.flows.FlowLogic)
## ##
@net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NetworkParameters extends java.lang.Object @net.corda.core.serialization.CordaSerializable public final class net.corda.core.node.NetworkParameters extends java.lang.Object
public <init>(int, List, int, int, java.time.Instant, int) public <init>(int, List, int, int, java.time.Instant, int, Map)
public final int component1() public final int component1()
@org.jetbrains.annotations.NotNull public final List component2() @org.jetbrains.annotations.NotNull public final List component2()
public final int component3() public final int component3()
public final int component4() public final int component4()
@org.jetbrains.annotations.NotNull public final java.time.Instant component5() @org.jetbrains.annotations.NotNull public final java.time.Instant component5()
public final int component6() public final int component6()
@org.jetbrains.annotations.NotNull public final net.corda.core.node.NetworkParameters copy(int, List, int, int, java.time.Instant, int) @org.jetbrains.annotations.NotNull public final Map component7()
@org.jetbrains.annotations.NotNull public final net.corda.core.node.NetworkParameters copy(int, List, int, int, java.time.Instant, int, Map)
public boolean equals(Object) public boolean equals(Object)
public final int getEpoch() public final int getEpoch()
public final int getMaxMessageSize() public final int getMaxMessageSize()
@ -1763,6 +1833,7 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
public final int getMinimumPlatformVersion() public final int getMinimumPlatformVersion()
@org.jetbrains.annotations.NotNull public final java.time.Instant getModifiedTime() @org.jetbrains.annotations.NotNull public final java.time.Instant getModifiedTime()
@org.jetbrains.annotations.NotNull public final List getNotaries() @org.jetbrains.annotations.NotNull public final List getNotaries()
@org.jetbrains.annotations.NotNull public final Map getWhitelistedContractImplementations()
public int hashCode() public int hashCode()
public String toString() public String toString()
## ##
@ -1804,6 +1875,7 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey) @org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction) @org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction)
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey) @org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
@org.jetbrains.annotations.NotNull public abstract net.corda.core.cordapp.CordappContext getAppContext()
@org.jetbrains.annotations.NotNull public abstract java.time.Clock getClock() @org.jetbrains.annotations.NotNull public abstract java.time.Clock getClock()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.ContractUpgradeService getContractUpgradeService() @org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.KeyManagementService getKeyManagementService() @org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.KeyManagementService getKeyManagementService()
@ -1826,6 +1898,7 @@ public @interface net.corda.core.messaging.RPCReturnsObservables
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.AttachmentStorage getAttachments() @org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.AttachmentStorage getAttachments()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.cordapp.CordappProvider getCordappProvider() @org.jetbrains.annotations.NotNull public abstract net.corda.core.cordapp.CordappProvider getCordappProvider()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.IdentityService getIdentityService() @org.jetbrains.annotations.NotNull public abstract net.corda.core.node.services.IdentityService getIdentityService()
@org.jetbrains.annotations.NotNull public abstract net.corda.core.node.NetworkParameters getNetworkParameters()
## ##
@net.corda.core.DoNotImplement public interface net.corda.core.node.StateLoader @net.corda.core.DoNotImplement public interface net.corda.core.node.StateLoader
@org.jetbrains.annotations.NotNull public abstract net.corda.core.contracts.TransactionState loadState(net.corda.core.contracts.StateRef) @org.jetbrains.annotations.NotNull public abstract net.corda.core.contracts.TransactionState loadState(net.corda.core.contracts.StateRef)
@ -1838,9 +1911,9 @@ public final class net.corda.core.node.StatesToRecord extends java.lang.Enum
## ##
@net.corda.core.DoNotImplement public interface net.corda.core.node.services.AttachmentStorage @net.corda.core.DoNotImplement public interface net.corda.core.node.services.AttachmentStorage
public abstract boolean hasAttachment(net.corda.core.crypto.SecureHash) public abstract boolean hasAttachment(net.corda.core.crypto.SecureHash)
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream) @kotlin.Deprecated @org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream)
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream, String, String) @org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream, String, String)
@org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash importOrGetAttachment(java.io.InputStream) @kotlin.Deprecated @org.jetbrains.annotations.NotNull public abstract net.corda.core.crypto.SecureHash importOrGetAttachment(java.io.InputStream)
@org.jetbrains.annotations.Nullable public abstract net.corda.core.contracts.Attachment openAttachment(net.corda.core.crypto.SecureHash) @org.jetbrains.annotations.Nullable public abstract net.corda.core.contracts.Attachment openAttachment(net.corda.core.crypto.SecureHash)
@org.jetbrains.annotations.NotNull public abstract List queryAttachments(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentSort) @org.jetbrains.annotations.NotNull public abstract List queryAttachments(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentSort)
## ##
@ -2968,6 +3041,7 @@ public static final class net.corda.core.transactions.FilteredTransaction$Compan
## ##
@net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.transactions.LedgerTransaction extends net.corda.core.transactions.FullTransaction @net.corda.core.serialization.CordaSerializable @net.corda.core.DoNotImplement public final class net.corda.core.transactions.LedgerTransaction extends net.corda.core.transactions.FullTransaction
public <init>(List, List, List, List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt) public <init>(List, List, List, List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt)
public <init>(List, List, List, List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt, net.corda.core.node.NetworkParameters)
@org.jetbrains.annotations.NotNull public final List commandsOfType(Class) @org.jetbrains.annotations.NotNull public final List commandsOfType(Class)
@org.jetbrains.annotations.NotNull public final List component1() @org.jetbrains.annotations.NotNull public final List component1()
@org.jetbrains.annotations.NotNull public final List component2() @org.jetbrains.annotations.NotNull public final List component2()
@ -2978,6 +3052,7 @@ public static final class net.corda.core.transactions.FilteredTransaction$Compan
@org.jetbrains.annotations.Nullable public final net.corda.core.contracts.TimeWindow component7() @org.jetbrains.annotations.Nullable public final net.corda.core.contracts.TimeWindow component7()
@org.jetbrains.annotations.NotNull public final net.corda.core.contracts.PrivacySalt component8() @org.jetbrains.annotations.NotNull public final net.corda.core.contracts.PrivacySalt component8()
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.LedgerTransaction copy(List, List, List, List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt) @org.jetbrains.annotations.NotNull public final net.corda.core.transactions.LedgerTransaction copy(List, List, List, List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt)
@org.jetbrains.annotations.NotNull public final net.corda.core.transactions.LedgerTransaction copy(List, List, List, List, net.corda.core.crypto.SecureHash, net.corda.core.identity.Party, net.corda.core.contracts.TimeWindow, net.corda.core.contracts.PrivacySalt, net.corda.core.node.NetworkParameters)
public boolean equals(Object) public boolean equals(Object)
@org.jetbrains.annotations.NotNull public final List filterCommands(Class, function.Predicate) @org.jetbrains.annotations.NotNull public final List filterCommands(Class, function.Predicate)
@org.jetbrains.annotations.NotNull public final List filterInRefs(Class, function.Predicate) @org.jetbrains.annotations.NotNull public final List filterInRefs(Class, function.Predicate)
@ -3690,6 +3765,7 @@ public final class net.corda.testing.driver.DriverParameters extends java.lang.O
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withExtraCordappPackagesToScan(List) @org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withExtraCordappPackagesToScan(List)
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withIsDebug(boolean) @org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withIsDebug(boolean)
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withJmxPolicy(net.corda.testing.driver.JmxPolicy) @org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withJmxPolicy(net.corda.testing.driver.JmxPolicy)
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withNetworkParameters(net.corda.core.node.NetworkParameters)
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withNotarySpecs(List) @org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withNotarySpecs(List)
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withPortAllocation(net.corda.testing.driver.PortAllocation) @org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withPortAllocation(net.corda.testing.driver.PortAllocation)
@org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withStartNodesInProcess(boolean) @org.jetbrains.annotations.NotNull public final net.corda.testing.driver.DriverParameters withStartNodesInProcess(boolean)
@ -4014,6 +4090,7 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey) @org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction) @org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey) @org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
@org.jetbrains.annotations.NotNull public net.corda.core.cordapp.CordappContext getAppContext()
@org.jetbrains.annotations.NotNull public final net.corda.testing.services.MockAttachmentStorage getAttachments() @org.jetbrains.annotations.NotNull public final net.corda.testing.services.MockAttachmentStorage getAttachments()
@org.jetbrains.annotations.NotNull public java.time.Clock getClock() @org.jetbrains.annotations.NotNull public java.time.Clock getClock()
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.ContractUpgradeService getContractUpgradeService() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
@ -4024,6 +4101,7 @@ public class net.corda.testing.node.MockServices extends java.lang.Object implem
@org.jetbrains.annotations.NotNull public static final net.corda.node.VersionInfo getMOCK_VERSION_INFO() @org.jetbrains.annotations.NotNull public static final net.corda.node.VersionInfo getMOCK_VERSION_INFO()
@org.jetbrains.annotations.NotNull public net.corda.core.node.NodeInfo getMyInfo() @org.jetbrains.annotations.NotNull public net.corda.core.node.NodeInfo getMyInfo()
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.NetworkMapCache getNetworkMapCache() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.NetworkMapCache getNetworkMapCache()
@org.jetbrains.annotations.NotNull public final net.corda.core.node.NetworkParameters getNetworkParameters()
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.TransactionVerifierService getTransactionVerifierService() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.TransactionVerifierService getTransactionVerifierService()
@org.jetbrains.annotations.NotNull public net.corda.node.services.api.WritableTransactionStorage getValidatedTransactions() @org.jetbrains.annotations.NotNull public net.corda.node.services.api.WritableTransactionStorage getValidatedTransactions()
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.VaultService getVaultService() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.VaultService getVaultService()
@ -4062,6 +4140,7 @@ public static final class net.corda.testing.node.MockServicesKt$createMockCordaS
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey) @org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.FilteredTransaction, java.security.PublicKey)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction) @org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey) @org.jetbrains.annotations.NotNull public net.corda.core.crypto.TransactionSignature createSignature(net.corda.core.transactions.SignedTransaction, java.security.PublicKey)
@org.jetbrains.annotations.NotNull public net.corda.core.cordapp.CordappContext getAppContext()
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.AttachmentStorage getAttachments() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.AttachmentStorage getAttachments()
@org.jetbrains.annotations.NotNull public java.time.Clock getClock() @org.jetbrains.annotations.NotNull public java.time.Clock getClock()
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.ContractUpgradeService getContractUpgradeService() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.ContractUpgradeService getContractUpgradeService()
@ -4070,6 +4149,7 @@ public static final class net.corda.testing.node.MockServicesKt$createMockCordaS
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.KeyManagementService getKeyManagementService() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.KeyManagementService getKeyManagementService()
@org.jetbrains.annotations.NotNull public net.corda.core.node.NodeInfo getMyInfo() @org.jetbrains.annotations.NotNull public net.corda.core.node.NodeInfo getMyInfo()
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.NetworkMapCache getNetworkMapCache() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.NetworkMapCache getNetworkMapCache()
@org.jetbrains.annotations.NotNull public net.corda.core.node.NetworkParameters getNetworkParameters()
@org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockServices getServiceHub() @org.jetbrains.annotations.NotNull public final net.corda.testing.node.MockServices getServiceHub()
@org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializeAsToken getServiceInstance() @org.jetbrains.annotations.NotNull public final net.corda.core.serialization.SerializeAsToken getServiceInstance()
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.TransactionVerifierService getTransactionVerifierService() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.TransactionVerifierService getTransactionVerifierService()
@ -4135,6 +4215,7 @@ public final class net.corda.testing.node.StartedMockNode extends java.lang.Obje
@org.jetbrains.annotations.NotNull public final rx.Observable registerInitiatedFlow(Class) @org.jetbrains.annotations.NotNull public final rx.Observable registerInitiatedFlow(Class)
public final void setMessagingServiceSpy(net.corda.testing.node.MessagingServiceSpy) public final void setMessagingServiceSpy(net.corda.testing.node.MessagingServiceSpy)
public final void stop() public final void stop()
public final Object transaction(kotlin.jvm.functions.Function0)
public static final net.corda.testing.node.StartedMockNode$Companion Companion public static final net.corda.testing.node.StartedMockNode$Companion Companion
## ##
public static final class net.corda.testing.node.StartedMockNode$Companion extends java.lang.Object public static final class net.corda.testing.node.StartedMockNode$Companion extends java.lang.Object
@ -4553,6 +4634,7 @@ public static final class net.corda.testing.dsl.TestTransactionDSLInterpreter$se
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.AttachmentStorage getAttachments() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.AttachmentStorage getAttachments()
@org.jetbrains.annotations.NotNull public net.corda.core.cordapp.CordappProvider getCordappProvider() @org.jetbrains.annotations.NotNull public net.corda.core.cordapp.CordappProvider getCordappProvider()
@org.jetbrains.annotations.NotNull public net.corda.core.node.services.IdentityService getIdentityService() @org.jetbrains.annotations.NotNull public net.corda.core.node.services.IdentityService getIdentityService()
@org.jetbrains.annotations.NotNull public net.corda.core.node.NetworkParameters getNetworkParameters()
@org.jetbrains.annotations.NotNull public net.corda.core.contracts.TransactionState loadState(net.corda.core.contracts.StateRef) @org.jetbrains.annotations.NotNull public net.corda.core.contracts.TransactionState loadState(net.corda.core.contracts.StateRef)
@org.jetbrains.annotations.NotNull public Set loadStates(Set) @org.jetbrains.annotations.NotNull public Set loadStates(Set)
## ##
@ -4640,6 +4722,7 @@ public final class net.corda.testing.services.MockAttachmentStorage extends net.
public boolean hasAttachment(net.corda.core.crypto.SecureHash) public boolean hasAttachment(net.corda.core.crypto.SecureHash)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream) @org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream, String, String) @org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash importAttachment(java.io.InputStream, String, String)
@org.jetbrains.annotations.NotNull public final net.corda.core.crypto.SecureHash importContractAttachment(List, String, java.io.InputStream)
@org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash importOrGetAttachment(java.io.InputStream) @org.jetbrains.annotations.NotNull public net.corda.core.crypto.SecureHash importOrGetAttachment(java.io.InputStream)
@org.jetbrains.annotations.Nullable public net.corda.core.contracts.Attachment openAttachment(net.corda.core.crypto.SecureHash) @org.jetbrains.annotations.Nullable public net.corda.core.contracts.Attachment openAttachment(net.corda.core.crypto.SecureHash)
@org.jetbrains.annotations.NotNull public List queryAttachments(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentSort) @org.jetbrains.annotations.NotNull public List queryAttachments(net.corda.core.node.services.vault.AttachmentQueryCriteria, net.corda.core.node.services.vault.AttachmentSort)

View File

@ -116,7 +116,6 @@ ext {
apply plugin: 'project-report' apply plugin: 'project-report'
apply plugin: 'com.github.ben-manes.versions' apply plugin: 'com.github.ben-manes.versions'
apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'net.corda.plugins.cordformation'
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'com.jfrog.artifactory'
@ -262,36 +261,6 @@ tasks.withType(Test) {
reports.html.destination = file("${reporting.baseDir}/${name}") reports.html.destination = file("${reporting.baseDir}/${name}")
} }
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
directory "./build/nodes"
node {
name "O=Controller,OU=corda,L=London,C=GB"
notary = [validating : true]
p2pPort 10002
cordapps = []
}
node {
name "O=Bank A,OU=corda,L=London,C=GB"
p2pPort 10012
rpcSettings {
address "localhost:10013"
adminAddress "localhost:10023"
}
webPort 10014
cordapps = []
}
node {
name "O=Bank B,OU=corda,L=London,C=GB"
p2pAddress "localhost:10007"
rpcSettings {
address "localhost:10018"
adminAddress "localhost:10028"
}
webAddress "localhost:10009"
cordapps = []
}
}
bintrayConfig { bintrayConfig {
user = System.getenv('CORDA_BINTRAY_USER') user = System.getenv('CORDA_BINTRAY_USER')
key = System.getenv('CORDA_BINTRAY_KEY') key = System.getenv('CORDA_BINTRAY_KEY')

View File

@ -10,6 +10,7 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.finance.USD import net.corda.finance.USD
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
@ -101,6 +102,7 @@ class JacksonSupportTest {
fun writeTransaction() { fun writeTransaction() {
val attachmentRef = SecureHash.randomSHA256() val attachmentRef = SecureHash.randomSHA256()
doReturn(attachmentRef).whenever(cordappProvider).getContractAttachmentID(DummyContract.PROGRAM_ID) doReturn(attachmentRef).whenever(cordappProvider).getContractAttachmentID(DummyContract.PROGRAM_ID)
doReturn(testNetworkParameters()).whenever(services).networkParameters
fun makeDummyTx(): SignedTransaction { fun makeDummyTx(): SignedTransaction {
val wtx = DummyContract.generateInitial(1, DUMMY_NOTARY, MINI_CORP.ref(1)) val wtx = DummyContract.generateInitial(1, DUMMY_NOTARY, MINI_CORP.ref(1))
.toWireTransaction(services) .toWireTransaction(services)

View File

@ -1,4 +1,4 @@
gradlePluginsVersion=4.0.0 gradlePluginsVersion=4.0.2
kotlinVersion=1.2.20 kotlinVersion=1.2.20
platformVersion=2 platformVersion=2
guavaVersion=21.0 guavaVersion=21.0

View File

@ -1,10 +1,14 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.DoNotImplement
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint.isSatisfiedBy
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.AttachmentWithContext
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
/** Constrain which contract-code-containing attachment can be used with a [ContractState]. */ /** Constrain which contract-code-containing attachment can be used with a [ContractState]. */
@CordaSerializable @CordaSerializable
@DoNotImplement
interface AttachmentConstraint { interface AttachmentConstraint {
/** Returns whether the given contract attachment can be used with the [ContractState] associated with this constraint object. */ /** Returns whether the given contract attachment can be used with the [ContractState] associated with this constraint object. */
fun isSatisfiedBy(attachment: Attachment): Boolean fun isSatisfiedBy(attachment: Attachment): Boolean
@ -20,6 +24,20 @@ data class HashAttachmentConstraint(val attachmentId: SecureHash) : AttachmentCo
override fun isSatisfiedBy(attachment: Attachment) = attachment.id == attachmentId override fun isSatisfiedBy(attachment: Attachment) = attachment.id == attachmentId
} }
/**
* An [AttachmentConstraint] that verifies that the hash of the attachment is in the network parameters whitelist.
* See: [net.corda.core.node.NetworkParameters.whitelistedContractImplementations]
* It allows for centralized control over the cordapps that can be used.
*/
object WhitelistedByZoneAttachmentConstraint : AttachmentConstraint {
override fun isSatisfiedBy(attachment: Attachment): Boolean {
return if (attachment is AttachmentWithContext) {
val whitelist = attachment.whitelistedContractImplementations ?: throw IllegalStateException("Unable to verify WhitelistedByZoneAttachmentConstraint - whitelist not specified")
attachment.id in (whitelist[attachment.stateContract] ?: emptyList())
} else false
}
}
/** /**
* This [AttachmentConstraint] is a convenience class that will be automatically resolved to a [HashAttachmentConstraint]. * This [AttachmentConstraint] is a convenience class that will be automatically resolved to a [HashAttachmentConstraint].
* The resolution occurs in [TransactionBuilder.toWireTransaction] and uses the [TransactionState.contract] value * The resolution occurs in [TransactionBuilder.toWireTransaction] and uses the [TransactionState.contract] value

View File

@ -6,7 +6,15 @@ import net.corda.core.serialization.CordaSerializable
* Wrap an attachment in this if it is to be used as an executable contract attachment * Wrap an attachment in this if it is to be used as an executable contract attachment
* *
* @property attachment The attachment representing the contract JAR * @property attachment The attachment representing the contract JAR
* @property contract The contract name contained within the JAR * @property contract The contract name contained within the JAR. A Contract attachment has to contain at least 1 contract.
* @property additionalContracts Additional contract names contained within the JAR.
*/ */
@CordaSerializable @CordaSerializable
class ContractAttachment(val attachment: Attachment, val contract: ContractClassName) : Attachment by attachment class ContractAttachment @JvmOverloads constructor (val attachment: Attachment, val contract: ContractClassName, val additionalContracts: Set<ContractClassName> = emptySet(), val uploader: String? = null) : Attachment by attachment {
val allContracts: Set<ContractClassName> get() = additionalContracts + contract
override fun toString(): String {
return "ContractAttachment(attachment=${attachment.id}, contracts='${allContracts}', uploader='${uploader}')"
}
}

View File

@ -22,6 +22,9 @@ sealed class TransactionVerificationException(val txId: SecureHash, message: Str
class MissingAttachmentRejection(txId: SecureHash, val contractClass: String) class MissingAttachmentRejection(txId: SecureHash, val contractClass: String)
: TransactionVerificationException(txId, "Contract constraints failed, could not find attachment for: $contractClass", null) : TransactionVerificationException(txId, "Contract constraints failed, could not find attachment for: $contractClass", null)
class ConflictingAttachmentsRejection(txId: SecureHash, contractClass: String)
: TransactionVerificationException(txId, "Contract constraints failed for: $contractClass, because multiple attachments providing this contract were attached.", null)
class ContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable) class ContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable)
: TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, could not create contract class: $contractClass", cause) : TransactionVerificationException(txId, "Contract verification failed: ${cause.message}, could not create contract class: $contractClass", cause)

View File

@ -13,6 +13,13 @@ import java.security.CodeSigner
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
// Possible attachment uploaders
const val DEPLOYED_CORDAPP_UPLOADER = "app"
const val RPC_UPLOADER = "rpc"
const val TEST_UPLOADER = "test"
const val P2P_UPLOADER = "p2p"
const val UNKNOWN_UPLOADER = "unknown"
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 {

View File

@ -0,0 +1,22 @@
package net.corda.core.internal
import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName
import net.corda.core.node.services.AttachmentId
/**
* Used only for passing to the Attachment constraint verification.
*/
class AttachmentWithContext(
val contractAttachment: ContractAttachment,
val stateContract: ContractClassName,
/** Required for verifying [WhitelistedByZoneAttachmentConstraint] */
val whitelistedContractImplementations: Map<String, List<AttachmentId>>?
) : Attachment by contractAttachment {
init {
require(stateContract in contractAttachment.allContracts) {
"This AttachmentWithContext was not initialised properly"
}
}
}

View File

@ -147,7 +147,7 @@ class FetchAttachmentsFlow(requests: Set<SecureHash>,
override fun maybeWriteToDisk(downloaded: List<Attachment>) { override fun maybeWriteToDisk(downloaded: List<Attachment>) {
for (attachment in downloaded) { for (attachment in downloaded) {
serviceHub.attachments.importAttachment(attachment.open()) serviceHub.attachments.importAttachment(attachment.open(), "$P2P_UPLOADER:${otherSideSession.counterparty.name}", null)
} }
} }

View File

@ -306,8 +306,8 @@ fun <T, U : T> uncheckedCast(obj: T) = obj as U
fun <K, V> Iterable<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = this.groupBy({ it.first }) { it.second } fun <K, V> Iterable<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = this.groupBy({ it.first }) { it.second }
/** Provide access to internal method for AttachmentClassLoaderTests */ /** Provide access to internal method for AttachmentClassLoaderTests */
fun TransactionBuilder.toWireTransaction(cordappProvider: CordappProvider, serializationContext: SerializationContext): WireTransaction { fun TransactionBuilder.toWireTransaction(services: ServicesForResolution, serializationContext: SerializationContext): WireTransaction {
return toWireTransactionWithContext(cordappProvider, serializationContext) return toWireTransactionWithContext(services, serializationContext)
} }
/** Provide access to internal method for AttachmentClassLoaderTests */ /** Provide access to internal method for AttachmentClassLoaderTests */

View File

@ -1,6 +1,7 @@
package net.corda.core.node package net.corda.core.node
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import java.time.Instant import java.time.Instant
@ -9,11 +10,13 @@ import java.time.Instant
* correctly interoperate with each other. * correctly interoperate with each other.
* @property minimumPlatformVersion Minimum version of Corda platform that is required for nodes in the network. * @property minimumPlatformVersion Minimum version of Corda platform that is required for nodes in the network.
* @property notaries List of well known and trusted notary identities with information on validation type. * @property notaries List of well known and trusted notary identities with information on validation type.
* @property maxMessageSize Maximum P2P message sent over the wire in bytes. * @property maxMessageSize This is currently ignored. However, it will be wired up in a future release.
* @property maxTransactionSize Maximum permitted transaction size in bytes. * @property maxTransactionSize Maximum permitted transaction size in bytes.
* @property modifiedTime Last modification time of network parameters set. * @property modifiedTime Last modification time of network parameters set.
* @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set * @property epoch Version number of the network parameters. Starting from 1, this will always increment on each new set
* of parameters. * of parameters.
* @property whitelistedContractImplementations List of whitelisted jars containing contract code for each contract class.
* This will be used by [net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint]. Read more about contract constraints here: <https://docs.corda.net/api-contract-constraints.html>
*/ */
// TODO Add eventHorizon - how many days a node can be offline before being automatically ejected from the network. // TODO Add eventHorizon - how many days a node can be offline before being automatically ejected from the network.
// It needs separate design. // It needs separate design.
@ -24,7 +27,8 @@ data class NetworkParameters(
val maxMessageSize: Int, val maxMessageSize: Int,
val maxTransactionSize: Int, val maxTransactionSize: Int,
val modifiedTime: Instant, val modifiedTime: Instant,
val epoch: Int val epoch: Int,
val whitelistedContractImplementations: Map<String, List<AttachmentId>>
) { ) {
init { init {
require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" } require(minimumPlatformVersion > 0) { "minimumPlatformVersion must be at least 1" }

View File

@ -33,6 +33,7 @@ interface AttachmentStorage {
* @throws IllegalArgumentException if the given byte stream is empty or a [java.util.jar.JarInputStream]. * @throws IllegalArgumentException if the given byte stream is empty or a [java.util.jar.JarInputStream].
* @throws IOException if something went wrong. * @throws IOException if something went wrong.
*/ */
@Deprecated("More attachment information is required", replaceWith = ReplaceWith("importAttachment(jar, uploader, filename)"))
@Throws(FileAlreadyExistsException::class, IOException::class) @Throws(FileAlreadyExistsException::class, IOException::class)
fun importAttachment(jar: InputStream): AttachmentId fun importAttachment(jar: InputStream): AttachmentId
@ -43,13 +44,14 @@ interface AttachmentStorage {
* @param filename Name of the file * @param filename Name of the file
*/ */
@Throws(FileAlreadyExistsException::class, IOException::class) @Throws(FileAlreadyExistsException::class, IOException::class)
fun importAttachment(jar: InputStream, uploader: String, filename: String): AttachmentId fun importAttachment(jar: InputStream, uploader: String, filename: String?): AttachmentId
/** /**
* Inserts or returns Attachment Id of attachment. Does not throw an exception if already uploaded. * Inserts or returns Attachment Id of attachment. Does not throw an exception if already uploaded.
* @param jar [InputStream] of Jar file * @param jar [InputStream] of Jar file
* @return [AttachmentId] of uploaded attachment * @return [AttachmentId] of uploaded attachment
*/ */
@Deprecated("More attachment information is required", replaceWith = ReplaceWith("importAttachment(jar, uploader, filename)"))
fun importOrGetAttachment(jar: InputStream): AttachmentId fun importOrGetAttachment(jar: InputStream): AttachmentId
/** /**

View File

@ -3,9 +3,11 @@ package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.AttachmentWithContext
import net.corda.core.internal.UpgradeCommand import net.corda.core.internal.UpgradeCommand
import net.corda.core.internal.castIfPossible import net.corda.core.internal.castIfPossible
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.node.NetworkParameters
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.Try import net.corda.core.utilities.Try
import java.security.PublicKey import java.security.PublicKey
@ -27,7 +29,7 @@ import java.util.function.Predicate
// currently sends this across to out-of-process verifiers. We'll need to change that first. // currently sends this across to out-of-process verifiers. We'll need to change that first.
// DOCSTART 1 // DOCSTART 1
@CordaSerializable @CordaSerializable
data class LedgerTransaction( data class LedgerTransaction @JvmOverloads constructor(
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */ /** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
override val inputs: List<StateAndRef<ContractState>>, override val inputs: List<StateAndRef<ContractState>>,
override val outputs: List<TransactionState<ContractState>>, override val outputs: List<TransactionState<ContractState>>,
@ -39,7 +41,8 @@ data class LedgerTransaction(
override val id: SecureHash, override val id: SecureHash,
override val notary: Party?, override val notary: Party?,
val timeWindow: TimeWindow?, val timeWindow: TimeWindow?,
val privacySalt: PrivacySalt val privacySalt: PrivacySalt,
private val networkParameters: NetworkParameters? = null
) : FullTransaction() { ) : FullTransaction() {
//DOCEND 1 //DOCEND 1
init { init {
@ -87,17 +90,29 @@ data class LedgerTransaction(
/** /**
* Verify that all contract constraints are valid for each state before running any contract code * Verify that all contract constraints are valid for each state before running any contract code
* *
* In case the transaction was created on this node then the attachments will contain the hash of the current cordapp jars.
* In case this verifies an older transaction or one originated on a different node, then this verifies that the attachments
* are valid.
*
* @throws TransactionVerificationException if the constraints fail to verify * @throws TransactionVerificationException if the constraints fail to verify
*/ */
private fun verifyConstraints() { private fun verifyConstraints() {
val contractAttachments = attachments.filterIsInstance<ContractAttachment>() val contractAttachments = attachments.filterIsInstance<ContractAttachment>()
(inputs.map { it.state } + outputs).forEach { state -> (inputs.map { it.state } + outputs).forEach { state ->
// Ordering of attachments matters - if two attachments contain the same named contract then the second val stateAttachments = contractAttachments.filter { state.contract in it.allContracts }
// will be shadowed by the first. if (stateAttachments.isEmpty()) throw TransactionVerificationException.MissingAttachmentRejection(id, state.contract)
val contractAttachment = contractAttachments.find { it.contract == state.contract }
?: throw TransactionVerificationException.MissingAttachmentRejection(id, state.contract)
if (!state.constraint.isSatisfiedBy(contractAttachment)) { val uniqueAttachmentsForStateContract = stateAttachments.distinctBy { it.id }
// In case multiple attachments have been added for the same contract, fail because this transaction will not be able to be verified
// because it will break the no-overlap rule that we have implemented in our Classloaders
if (uniqueAttachmentsForStateContract.size > 1) {
throw TransactionVerificationException.ConflictingAttachmentsRejection(id, state.contract)
}
val contractAttachment = uniqueAttachmentsForStateContract.first()
val constraintAttachment = AttachmentWithContext(contractAttachment, state.contract, networkParameters?.whitelistedContractImplementations)
if (!state.constraint.isSatisfiedBy(constraintAttachment)) {
throw TransactionVerificationException.ContractConstraintRejection(id, state.contract) throw TransactionVerificationException.ContractConstraintRejection(id, state.contract)
} }
} }
@ -403,5 +418,14 @@ data class LedgerTransaction(
* @throws IllegalArgumentException if no item matches the id. * @throws IllegalArgumentException if no item matches the id.
*/ */
fun getAttachment(id: SecureHash): Attachment = attachments.first { it.id == id } fun getAttachment(id: SecureHash): Attachment = attachments.first { it.id == id }
}
fun copy(inputs: List<StateAndRef<ContractState>>,
outputs: List<TransactionState<ContractState>>,
commands: List<CommandWithParties<CommandData>>,
attachments: List<Attachment>,
id: SecureHash,
notary: Party?,
timeWindow: TimeWindow?,
privacySalt: PrivacySalt
) = copy(inputs, outputs, commands, attachments, id, notary, timeWindow, privacySalt, null)
}

View File

@ -8,8 +8,10 @@ import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata import net.corda.core.crypto.SignatureMetadata
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.node.NetworkParameters
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationFactory import net.corda.core.serialization.SerializationFactory
@ -17,6 +19,7 @@ import java.security.PublicKey
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
import kotlin.collections.ArrayList
/** /**
* A TransactionBuilder is a transaction class that's mutable (unlike the others which are all immutable). It is * A TransactionBuilder is a transaction class that's mutable (unlike the others which are all immutable). It is
@ -42,18 +45,24 @@ open class TransactionBuilder(
) { ) {
constructor(notary: Party) : this(notary, (Strand.currentStrand() as? FlowStateMachine<*>)?.id?.uuid ?: UUID.randomUUID()) constructor(notary: Party) : this(notary, (Strand.currentStrand() as? FlowStateMachine<*>)?.id?.uuid ?: UUID.randomUUID())
private val inputsWithTransactionState = arrayListOf<TransactionState<ContractState>>()
/** /**
* Creates a copy of the builder. * Creates a copy of the builder.
*/ */
fun copy() = TransactionBuilder( fun copy(): TransactionBuilder {
notary = notary, val t = TransactionBuilder(
inputs = ArrayList(inputs), notary = notary,
attachments = ArrayList(attachments), inputs = ArrayList(inputs),
outputs = ArrayList(outputs), attachments = ArrayList(attachments),
commands = ArrayList(commands), outputs = ArrayList(outputs),
window = window, commands = ArrayList(commands),
privacySalt = privacySalt window = window,
) privacySalt = privacySalt
)
t.inputsWithTransactionState.addAll(this.inputsWithTransactionState)
return t
}
// DOCSTART 1 // DOCSTART 1
/** A more convenient way to add items to this transaction that calls the add* methods for you based on type */ /** A more convenient way to add items to this transaction that calls the add* methods for you based on type */
@ -83,31 +92,52 @@ open class TransactionBuilder(
* @returns A new [WireTransaction] that will be unaffected by further changes to this [TransactionBuilder]. * @returns A new [WireTransaction] that will be unaffected by further changes to this [TransactionBuilder].
*/ */
@Throws(MissingContractAttachments::class) @Throws(MissingContractAttachments::class)
fun toWireTransaction(services: ServicesForResolution): WireTransaction = toWireTransactionWithContext(services.cordappProvider) fun toWireTransaction(services: ServicesForResolution): WireTransaction = toWireTransactionWithContext(services)
internal fun toWireTransactionWithContext(cordappProvider: CordappProvider, serializationContext: SerializationContext? = null): WireTransaction { internal fun toWireTransactionWithContext(services: ServicesForResolution, serializationContext: SerializationContext? = null): WireTransaction {
// Resolves the AutomaticHashConstraints to HashAttachmentConstraints for convenience. The AutomaticHashConstraint
// allows for less boiler plate when constructing transactions since for the typical case the named contract // Resolves the AutomaticHashConstraints to HashAttachmentConstraints or WhitelistedByZoneAttachmentConstraint based on a global parameter.
// The AutomaticHashConstraint allows for less boiler plate when constructing transactions since for the typical case the named contract
// will be available when building the transaction. In exceptional cases the TransactionStates must be created // will be available when building the transaction. In exceptional cases the TransactionStates must be created
// with an explicit [AttachmentConstraint] // with an explicit [AttachmentConstraint]
val resolvedOutputs = outputs.map { state -> val resolvedOutputs = outputs.map { state ->
if (state.constraint is AutomaticHashConstraint) { when {
cordappProvider.getContractAttachmentID(state.contract)?.let { state.constraint !is AutomaticHashConstraint -> state
useWhitelistedByZoneAttachmentConstraint(state.contract, services.networkParameters) -> state.copy(constraint = WhitelistedByZoneAttachmentConstraint)
else -> services.cordappProvider.getContractAttachmentID(state.contract)?.let {
state.copy(constraint = HashAttachmentConstraint(it)) state.copy(constraint = HashAttachmentConstraint(it))
} ?: throw MissingContractAttachments(listOf(state)) } ?: throw MissingContractAttachments(listOf(state))
} else {
state
} }
} }
return SerializationFactory.defaultFactory.withCurrentContext(serializationContext) { return SerializationFactory.defaultFactory.withCurrentContext(serializationContext) {
WireTransaction(WireTransaction.createComponentGroups(inputs, resolvedOutputs, commands, attachments, notary, window), privacySalt) WireTransaction(WireTransaction.createComponentGroups(inputStates(), resolvedOutputs, commands, attachments + makeContractAttachments(services.cordappProvider), notary, window), privacySalt)
} }
} }
private fun useWhitelistedByZoneAttachmentConstraint(contractClassName: ContractClassName, networkParameters: NetworkParameters): Boolean {
return contractClassName in networkParameters.whitelistedContractImplementations.keys
}
/**
* The attachments added to the current transaction contain only the hashes of the current cordapps.
* NOT the hashes of the cordapps that were used when the input states were created ( in case they changed in the meantime)
* TODO - review this logic
*/
private fun makeContractAttachments(cordappProvider: CordappProvider): List<AttachmentId> {
return (inputsWithTransactionState + outputs).map { state ->
cordappProvider.getContractAttachmentID(state.contract)
?: throw MissingContractAttachments(listOf(state))
}.distinct()
}
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class) @Throws(AttachmentResolutionException::class, TransactionResolutionException::class)
fun toLedgerTransaction(services: ServiceHub) = toWireTransaction(services).toLedgerTransaction(services) fun toLedgerTransaction(services: ServiceHub) = toWireTransaction(services).toLedgerTransaction(services)
internal fun toLedgerTransactionWithContext(services: ServicesForResolution, serializationContext: SerializationContext) = toWireTransactionWithContext(services.cordappProvider, serializationContext).toLedgerTransaction(services) internal fun toLedgerTransactionWithContext(services: ServicesForResolution, serializationContext: SerializationContext): LedgerTransaction {
return toWireTransactionWithContext(services, serializationContext).toLedgerTransaction(services)
}
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class, TransactionVerificationException::class) @Throws(AttachmentResolutionException::class, TransactionResolutionException::class, TransactionVerificationException::class)
fun verify(services: ServiceHub) { fun verify(services: ServiceHub) {
toLedgerTransaction(services).verify() toLedgerTransaction(services).verify()
@ -117,6 +147,7 @@ open class TransactionBuilder(
val notary = stateAndRef.state.notary val notary = stateAndRef.state.notary
require(notary == this.notary) { "Input state requires notary \"$notary\" which does not match the transaction notary \"${this.notary}\"." } require(notary == this.notary) { "Input state requires notary \"$notary\" which does not match the transaction notary \"${this.notary}\"." }
inputs.add(stateAndRef.ref) inputs.add(stateAndRef.ref)
inputsWithTransactionState.add(stateAndRef.state)
return this return this
} }

View File

@ -5,6 +5,7 @@ import net.corda.core.contracts.ComponentGroupEnum.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
import net.corda.core.node.NetworkParameters
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -88,8 +89,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
resolveIdentity = { services.identityService.partyFromKey(it) }, resolveIdentity = { services.identityService.partyFromKey(it) },
resolveAttachment = { services.attachments.openAttachment(it) }, resolveAttachment = { services.attachments.openAttachment(it) },
resolveStateRef = { services.loadState(it) }, resolveStateRef = { services.loadState(it) },
resolveContractAttachment = { services.cordappProvider.getContractAttachmentID(it.contract) }, networkParameters = services.networkParameters
maxTransactionSize = services.networkParameters.maxTransactionSize
) )
} }
@ -108,17 +108,16 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
resolveStateRef: (StateRef) -> TransactionState<*>?, resolveStateRef: (StateRef) -> TransactionState<*>?,
resolveContractAttachment: (TransactionState<ContractState>) -> AttachmentId? resolveContractAttachment: (TransactionState<ContractState>) -> AttachmentId?
): LedgerTransaction { ): LedgerTransaction {
return toLedgerTransactionInternal(resolveIdentity, resolveAttachment, resolveStateRef, resolveContractAttachment, 10485760) return toLedgerTransactionInternal(resolveIdentity, resolveAttachment, resolveStateRef, null)
} }
private fun toLedgerTransactionInternal( private fun toLedgerTransactionInternal(
resolveIdentity: (PublicKey) -> Party?, resolveIdentity: (PublicKey) -> Party?,
resolveAttachment: (SecureHash) -> Attachment?, resolveAttachment: (SecureHash) -> Attachment?,
resolveStateRef: (StateRef) -> TransactionState<*>?, resolveStateRef: (StateRef) -> TransactionState<*>?,
resolveContractAttachment: (TransactionState<ContractState>) -> AttachmentId?, networkParameters: NetworkParameters?
maxTransactionSize: Int
): LedgerTransaction { ): LedgerTransaction {
// Look up public keys to authenticated identities. This is just a stub placeholder and will all change in future. // Look up public keys to authenticated identities.
val authenticatedArgs = commands.map { val authenticatedArgs = commands.map {
val parties = it.signers.mapNotNull { pk -> resolveIdentity(pk) } val parties = it.signers.mapNotNull { pk -> resolveIdentity(pk) }
CommandWithParties(it.signers, parties, it.value) CommandWithParties(it.signers, parties, it.value)
@ -126,12 +125,9 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
val resolvedInputs = inputs.map { ref -> val resolvedInputs = inputs.map { ref ->
resolveStateRef(ref)?.let { StateAndRef(it, ref) } ?: throw TransactionResolutionException(ref.txhash) resolveStateRef(ref)?.let { StateAndRef(it, ref) } ?: throw TransactionResolutionException(ref.txhash)
} }
// Open attachments specified in this transaction. If we haven't downloaded them, we fail. val attachments = attachments.map { resolveAttachment(it) ?: throw AttachmentResolutionException(it) }
val contractAttachments = findAttachmentContracts(resolvedInputs, resolveContractAttachment, resolveAttachment) val ltx = LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt, networkParameters)
// Order of attachments is important since contracts may refer to indexes so only append automatic attachments checkTransactionSize(ltx, networkParameters?.maxTransactionSize ?: 10485760)
val attachments = (attachments.map { resolveAttachment(it) ?: throw AttachmentResolutionException(it) } + contractAttachments).distinct()
val ltx = LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, notary, timeWindow, privacySalt)
checkTransactionSize(ltx, maxTransactionSize)
return ltx return ltx
} }
@ -266,19 +262,6 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
return buf.toString() return buf.toString()
} }
private fun findAttachmentContracts(resolvedInputs: List<StateAndRef<ContractState>>,
resolveContractAttachment: (TransactionState<ContractState>) -> AttachmentId?,
resolveAttachment: (SecureHash) -> Attachment?
): List<Attachment> {
val contractAttachments = (outputs + resolvedInputs.map { it.state }).map { Pair(it, resolveContractAttachment(it)) }
val missingAttachments = contractAttachments.filter { it.second == null }
return if (missingAttachments.isEmpty()) {
contractAttachments.map { ContractAttachment(resolveAttachment(it.second!!) ?: throw AttachmentResolutionException(it.second!!), it.first.contract) }
} else {
throw MissingContractAttachments(missingAttachments.map { it.first })
}
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other is WireTransaction) { if (other is WireTransaction) {
return (this.id == other.id) return (this.id == other.id)

View File

@ -1,9 +1,11 @@
package net.corda.core.contracts package net.corda.core.contracts
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.cordapp.CordappProvider import net.corda.core.cordapp.CordappProvider
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.allOnesHash
import net.corda.core.internal.UpgradeCommand import net.corda.core.internal.UpgradeCommand
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
@ -34,7 +36,9 @@ class DummyContractV2Tests {
@Test @Test
fun `upgrade from v1`() { fun `upgrade from v1`() {
val services = rigorousMock<ServicesForResolution>().also { val services = rigorousMock<ServicesForResolution>().also {
doReturn(rigorousMock<CordappProvider>()).whenever(it).cordappProvider doReturn(rigorousMock<CordappProvider>().also {
doReturn(allOnesHash).whenever(it).getContractAttachmentID(any())
}).whenever(it).cordappProvider
} }
val contractUpgrade = DummyContractV2() val contractUpgrade = DummyContractV2()
val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DummyContract.PROGRAM_ID, DUMMY_NOTARY, constraint = AlwaysAcceptAttachmentConstraint) val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DummyContract.PROGRAM_ID, DUMMY_NOTARY, constraint = AlwaysAcceptAttachmentConstraint)

View File

@ -10,7 +10,6 @@ import net.corda.core.identity.Party
import net.corda.node.services.api.IdentityServiceInternal import net.corda.node.services.api.IdentityServiceInternal
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.* import net.corda.testing.core.*
import net.corda.testing.internal.MockCordappProvider
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import org.junit.Before import org.junit.Before
@ -30,7 +29,7 @@ class LedgerTransactionQueryTests {
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
private val keyPair = generateKeyPair() private val keyPair = generateKeyPair()
private val services = MockServices(emptyList(), CordaX500Name("MegaCorp", "London", "GB"), private val services = MockServices(listOf("net.corda.testing.contracts"), CordaX500Name("MegaCorp", "London", "GB"),
rigorousMock<IdentityServiceInternal>().also { rigorousMock<IdentityServiceInternal>().also {
doReturn(null).whenever(it).partyFromKey(keyPair.public) doReturn(null).whenever(it).partyFromKey(keyPair.public)
}, keyPair) }, keyPair)

View File

@ -65,7 +65,7 @@ class TransactionEncumbranceTests {
} }
} }
private val ledgerServices = MockServices(emptyList(), MEGA_CORP.name, private val ledgerServices = MockServices(listOf("net.corda.core.transactions", "net.corda.finance.contracts.asset"), MEGA_CORP.name,
rigorousMock<IdentityServiceInternal>().also { rigorousMock<IdentityServiceInternal>().also {
doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY) doReturn(MEGA_CORP).whenever(it).partyFromKey(MEGA_CORP_PUBKEY)
}) })

View File

@ -1,5 +1,7 @@
package net.corda.core.transactions package net.corda.core.transactions
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
@ -112,7 +114,9 @@ class TransactionTests {
val inputs = emptyList<StateAndRef<*>>() val inputs = emptyList<StateAndRef<*>>()
val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB)) val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB))
val commands = emptyList<CommandWithParties<CommandData>>() val commands = emptyList<CommandWithParties<CommandData>>()
val attachments = listOf<Attachment>(ContractAttachment(rigorousMock(), DummyContract.PROGRAM_ID)) val attachments = listOf<Attachment>(ContractAttachment(rigorousMock<Attachment>().also {
doReturn(SecureHash.zeroHash).whenever(it).id
}, DummyContract.PROGRAM_ID))
val id = SecureHash.randomSHA256() val id = SecureHash.randomSHA256()
val timeWindow: TimeWindow? = null val timeWindow: TimeWindow? = null
val privacySalt: PrivacySalt = PrivacySalt() val privacySalt: PrivacySalt = PrivacySalt()

View File

@ -7,46 +7,48 @@ import net.corda.core.identity.CordaX500Name;
import net.corda.finance.contracts.ICommercialPaperState; import net.corda.finance.contracts.ICommercialPaperState;
import net.corda.finance.contracts.JavaCommercialPaper; import net.corda.finance.contracts.JavaCommercialPaper;
import net.corda.finance.contracts.asset.Cash; import net.corda.finance.contracts.asset.Cash;
import net.corda.testing.node.MockServices;
import net.corda.testing.core.TestIdentity; import net.corda.testing.core.TestIdentity;
import net.corda.testing.node.MockServices;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.security.PublicKey;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import static java.util.Collections.emptyList; import static java.util.Collections.singletonList;
import static net.corda.core.crypto.Crypto.generateKeyPair;
import static net.corda.finance.Currencies.DOLLARS; import static net.corda.finance.Currencies.DOLLARS;
import static net.corda.finance.Currencies.issuedBy; import static net.corda.finance.Currencies.issuedBy;
import static net.corda.finance.contracts.JavaCommercialPaper.JCP_PROGRAM_ID; import static net.corda.finance.contracts.JavaCommercialPaper.JCP_PROGRAM_ID;
import static net.corda.testing.core.TestConstants.*;
import static net.corda.testing.node.MockServicesKt.makeTestIdentityService; import static net.corda.testing.node.MockServicesKt.makeTestIdentityService;
import static net.corda.testing.node.NodeTestUtils.ledger; import static net.corda.testing.node.NodeTestUtils.ledger;
import static net.corda.testing.node.NodeTestUtils.transaction; import static net.corda.testing.node.NodeTestUtils.transaction;
import static net.corda.testing.core.TestConstants.ALICE_NAME;
import static net.corda.testing.core.TestConstants.BOB_NAME;
import static net.corda.testing.core.TestConstants.TEST_TX_TIME;
public class CommercialPaperTest { public class CommercialPaperTest {
private static final TestIdentity alice = new TestIdentity(ALICE_NAME, 70L); private static final TestIdentity alice = new TestIdentity(ALICE_NAME, 70L);
private static final TestIdentity bigCorp = new TestIdentity(new CordaX500Name("BigCorp", "New York", "GB")); private static final TestIdentity bigCorp = new TestIdentity(new CordaX500Name("BigCorp", "New York", "GB"));
private static final TestIdentity bob = new TestIdentity(BOB_NAME, 80L); private static final TestIdentity bob = new TestIdentity(BOB_NAME, 80L);
private static final TestIdentity megaCorp = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB")); private static final TestIdentity megaCorp = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB"));
private final byte[] defaultRef = {123}; private final byte[] defaultRef = {123};
private MockServices ledgerServices;
// DOCSTART 11 @Before
private final MockServices ledgerServices = new MockServices( public void setUp() {
// A list of packages to scan for cordapps // DOCSTART 11
emptyList(), ledgerServices = new MockServices(
// The identity represented by this set of mock services. Defaults to a test identity. // A list of packages to scan for cordapps
// You can also use the alternative parameter initialIdentityName which accepts a singletonList("net.corda.finance.contracts"),
// [CordaX500Name] // The identity represented by this set of mock services. Defaults to a test identity.
megaCorp, // You can also use the alternative parameter initialIdentityName which accepts a
// An implementation of [IdentityService], which contains a list of all identities known // [CordaX500Name]
// to the node. Use [makeTestIdentityService] which returns an implementation of megaCorp,
// [InMemoryIdentityService] with the given identities // An implementation of [IdentityService], which contains a list of all identities known
makeTestIdentityService(megaCorp.getIdentity()) // to the node. Use [makeTestIdentityService] which returns an implementation of
// [InMemoryIdentityService] with the given identities
makeTestIdentityService(megaCorp.getIdentity())
); );
// DOCEND 11 // DOCEND 11
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
// DOCSTART 12 // DOCSTART 12

View File

@ -1,7 +1,11 @@
package net.corda.docs.tutorial.testdsl package net.corda.docs.tutorial.testdsl
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.TransactionVerificationException import net.corda.core.contracts.TransactionVerificationException
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.node.services.IdentityService
import net.corda.core.utilities.days import net.corda.core.utilities.days
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.`issued by` import net.corda.finance.`issued by`
@ -11,6 +15,7 @@ import net.corda.finance.contracts.ICommercialPaperState
import net.corda.finance.contracts.asset.CASH import net.corda.finance.contracts.asset.CASH
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.testing.core.* import net.corda.testing.core.*
import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger import net.corda.testing.node.ledger
import net.corda.testing.node.makeTestIdentityService import net.corda.testing.node.makeTestIdentityService
@ -33,20 +38,20 @@ class CommercialPaperTest {
// DOCSTART 11 // DOCSTART 11
private val ledgerServices = MockServices( private val ledgerServices = MockServices(
// A list of packages to scan for cordapps // A list of packages to scan for cordapps
cordappPackages = emptyList(), listOf("net.corda.finance.contracts"),
// The identity represented by this set of mock services. Defaults to a test identity. // The identity represented by this set of mock services. Defaults to a test identity.
// You can also use the alternative parameter initialIdentityName which accepts a // You can also use the alternative parameter initialIdentityName which accepts a
// [CordaX500Name] // [CordaX500Name]
initialIdentity = megaCorp, megaCorp,
// An implementation of IdentityService, which contains a list of all identities known rigorousMock<IdentityService>().also {
// to the node. Use [makeTestIdentityService] which returns an implementation of doReturn(megaCorp.party).whenever(it).partyFromKey(megaCorp.publicKey)
// [InMemoryIdentityService] with the given identities doReturn(null).whenever(it).partyFromKey(bigCorp.publicKey)
identityService = makeTestIdentityService(megaCorp.identity) doReturn(null).whenever(it).partyFromKey(alice.publicKey)
) })
// DOCEND 11 // DOCEND 11
@Suppress("unused")
// DOCSTART 12 // DOCSTART 12
@Suppress("unused")
private val simpleLedgerServices = MockServices( private val simpleLedgerServices = MockServices(
// This is the identity of the node // This is the identity of the node
megaCorp, megaCorp,

View File

@ -88,7 +88,7 @@ class ObligationTests {
doReturn(MINI_CORP).whenever(it).partyFromKey(MINI_CORP_PUBKEY) doReturn(MINI_CORP).whenever(it).partyFromKey(MINI_CORP_PUBKEY)
} }
private val mockService = MockServices(listOf("net.corda.finance.contracts.asset"), MEGA_CORP.name, identityService) private val mockService = MockServices(listOf("net.corda.finance.contracts.asset"), MEGA_CORP.name, identityService)
private val ledgerServices get() = MockServices(emptyList(), MEGA_CORP.name, identityService) private val ledgerServices get() = MockServices(listOf("net.corda.finance.contracts.asset", "net.corda.testing.contracts"), MEGA_CORP.name, identityService)
private fun cashObligationTestRoots( private fun cashObligationTestRoots(
group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
) = group.apply { ) = group.apply {

View File

@ -34,6 +34,9 @@ dependencies {
// For AMQP serialisation. // For AMQP serialisation.
compile "org.apache.qpid:proton-j:0.21.0" compile "org.apache.qpid:proton-j:0.21.0"
// FastClasspathScanner: classpath scanning - needed for the NetworkBootstraper
compile 'io.github.lukehutch:fast-classpath-scanner:2.0.21'
// Pure-Java Snappy compression // Pure-Java Snappy compression
compile 'org.iq80.snappy:snappy:0.4' compile 'org.iq80.snappy:snappy:0.4'

View File

@ -1,7 +1,9 @@
package net.corda.nodeapi.internal package net.corda.nodeapi.internal
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@ -31,6 +33,10 @@ class AttachmentsClassLoader(attachments: List<Attachment>, parent: ClassLoader
} }
init { init {
require(attachments.mapNotNull { it as? ContractAttachment }.none { it.uploader != DEPLOYED_CORDAPP_UPLOADER }) {
"Attempting to load Contract Attachments downloaded from the network"
}
for (attachment in attachments) { for (attachment in attachments) {
attachment.openAsJAR().use { jar -> attachment.openAsJAR().use { jar ->
while (true) { while (true) {

View File

@ -0,0 +1,41 @@
package net.corda.nodeapi.internal
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractClassName
import net.corda.core.internal.copyTo
import net.corda.core.internal.deleteIfExists
import net.corda.core.internal.read
import java.io.File
import java.io.InputStream
import java.lang.reflect.Modifier
import java.net.URLClassLoader
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
/**
* Scans the jar for contracts.
* @returns: found contract class names or null if none found
*/
fun scanJarForContracts(cordappJarPath: String): List<ContractClassName> {
val currentClassLoader = Contract::class.java.classLoader
val scanResult = FastClasspathScanner().addClassLoader(currentClassLoader).overrideClasspath(cordappJarPath, Paths.get(Contract::class.java.protectionDomain.codeSource.location.toURI()).toString()).scan()
val contracts = (scanResult.getNamesOfClassesImplementing(Contract::class.qualifiedName) ).distinct()
// Only keep instantiable contracts
val classLoader = URLClassLoader(arrayOf(File(cordappJarPath).toURL()), currentClassLoader)
val concreteContracts = contracts.map(classLoader::loadClass).filter { !it.isInterface && !Modifier.isAbstract(it.modifiers) }
return concreteContracts.map { it.name }
}
fun <T> withContractsInJar(jarInputStream: InputStream, withContracts: (List<ContractClassName>, InputStream) -> T): T {
val tempFile = Files.createTempFile("attachment", ".jar")
try {
jarInputStream.copyTo(tempFile, StandardCopyOption.REPLACE_EXISTING)
val contracts = scanJarForContracts(tempFile.toAbsolutePath().toString())
return tempFile.read { withContracts(contracts, it) }
} finally {
tempFile.deleteIfExists()
}
}

View File

@ -1,25 +1,33 @@
package net.corda.nodeapi.internal.network package net.corda.nodeapi.internal.network
import com.google.common.hash.Hashing
import com.google.common.hash.HashingInputStream
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import net.corda.cordform.CordformNode import net.corda.cordform.CordformNode
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.parse
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.* import net.corda.core.internal.*
import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.fork
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.NotaryInfo import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.internal.SerializationEnvironmentImpl import net.corda.core.serialization.internal.SerializationEnvironmentImpl
import net.corda.core.serialization.internal._contextSerializationEnv import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.scanJarForContracts
import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT
import net.corda.nodeapi.internal.serialization.CordaSerializationMagic import net.corda.nodeapi.internal.serialization.CordaSerializationMagic
import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl
import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme
import net.corda.nodeapi.internal.serialization.kryo.kryoMagic import net.corda.nodeapi.internal.serialization.kryo.kryoMagic
import java.io.File
import java.io.PrintStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
@ -43,15 +51,17 @@ class NetworkBootstrapper {
) )
private const val LOGS_DIR_NAME = "logs" private const val LOGS_DIR_NAME = "logs"
private const val WHITELIST_FILE_NAME = "whitelist.txt"
@JvmStatic @JvmStatic
fun main(args: Array<String>) { fun main(args: Array<String>) {
val arg = args.singleOrNull() ?: throw IllegalArgumentException("Expecting single argument which is the nodes' parent directory") val baseNodeDirectory = args.firstOrNull() ?: throw IllegalArgumentException("Expecting first argument which is the nodes' parent directory")
NetworkBootstrapper().bootstrap(Paths.get(arg).toAbsolutePath().normalize()) val cordapps = if (args.size > 1) args.toList().drop(1) else null
NetworkBootstrapper().bootstrap(Paths.get(baseNodeDirectory).toAbsolutePath().normalize(), cordapps)
} }
} }
fun bootstrap(directory: Path) { fun bootstrap(directory: Path, cordapps: List<String>?) {
directory.createDirectories() directory.createDirectories()
println("Bootstrapping local network in $directory") println("Bootstrapping local network in $directory")
generateDirectoriesIfNeeded(directory) generateDirectoriesIfNeeded(directory)
@ -68,7 +78,10 @@ class NetworkBootstrapper {
println("Gathering notary identities") println("Gathering notary identities")
val notaryInfos = gatherNotaryInfos(nodeInfoFiles) val notaryInfos = gatherNotaryInfos(nodeInfoFiles)
println("Notary identities to be used in network-parameters file: ${notaryInfos.joinToString("; ") { it.prettyPrint() }}") println("Notary identities to be used in network-parameters file: ${notaryInfos.joinToString("; ") { it.prettyPrint() }}")
installNetworkParameters(notaryInfos, nodeDirs) val mergedWhiteList = generateWhitelist(directory / WHITELIST_FILE_NAME, cordapps?.distinct())
println("Updating whitelist.")
overwriteWhitelist(directory / WHITELIST_FILE_NAME, mergedWhiteList)
installNetworkParameters(notaryInfos, nodeDirs, mergedWhiteList)
println("Bootstrapping complete!") println("Bootstrapping complete!")
} finally { } finally {
_contextSerializationEnv.set(null) _contextSerializationEnv.set(null)
@ -84,8 +97,7 @@ class NetworkBootstrapper {
for (confFile in confFiles) { for (confFile in confFiles) {
val nodeName = confFile.fileName.toString().removeSuffix(".conf") val nodeName = confFile.fileName.toString().removeSuffix(".conf")
println("Generating directory for $nodeName") println("Generating directory for $nodeName")
val nodeDir = (directory / nodeName) val nodeDir = (directory / nodeName).createDirectories()
if (!nodeDir.exists()) { nodeDir.createDirectory() }
confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING) confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING)
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING) Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING)
} }
@ -157,7 +169,7 @@ class NetworkBootstrapper {
}.distinct() // We need distinct as nodes part of a distributed notary share the same notary identity }.distinct() // We need distinct as nodes part of a distributed notary share the same notary identity
} }
private fun installNetworkParameters(notaryInfos: List<NotaryInfo>, nodeDirs: List<Path>) { private fun installNetworkParameters(notaryInfos: List<NotaryInfo>, nodeDirs: List<Path>, whitelist: Map<String, List<AttachmentId>>) {
// TODO Add config for minimumPlatformVersion, maxMessageSize and maxTransactionSize // TODO Add config for minimumPlatformVersion, maxMessageSize and maxTransactionSize
val copier = NetworkParametersCopier(NetworkParameters( val copier = NetworkParametersCopier(NetworkParameters(
minimumPlatformVersion = 1, minimumPlatformVersion = 1,
@ -165,12 +177,58 @@ class NetworkBootstrapper {
modifiedTime = Instant.now(), modifiedTime = Instant.now(),
maxMessageSize = 10485760, maxMessageSize = 10485760,
maxTransactionSize = Int.MAX_VALUE, maxTransactionSize = Int.MAX_VALUE,
epoch = 1 epoch = 1,
whitelistedContractImplementations = whitelist
), overwriteFile = true) ), overwriteFile = true)
nodeDirs.forEach { copier.install(it) } nodeDirs.forEach { copier.install(it) }
} }
private fun generateWhitelist(whitelistFile: Path, cordapps: List<String>?): Map<String, List<AttachmentId>> {
val existingWhitelist = if (whitelistFile.exists()) readContractWhitelist(whitelistFile) else emptyMap()
println("Found existing whitelist: $existingWhitelist")
val newWhiteList = cordapps?.flatMap { cordappJarPath ->
val jarHash = getJarHash(cordappJarPath)
scanJarForContracts(cordappJarPath).map { contract ->
contract to jarHash
}
}?.toMap() ?: emptyMap()
println("Calculating whitelist for current cordapps: $newWhiteList")
val merged = (newWhiteList.keys + existingWhitelist.keys).map { contractClassName ->
val existing = existingWhitelist[contractClassName] ?: emptyList()
val newHash = newWhiteList[contractClassName]
contractClassName to (if (newHash == null || newHash in existing) existing else existing + newHash)
}.toMap()
println("Final whitelist: $merged")
return merged
}
private fun overwriteWhitelist(whitelistFile: Path, mergedWhiteList: Map<String, List<AttachmentId>>) {
PrintStream(whitelistFile.toFile().outputStream()).use { out ->
mergedWhiteList.forEach { (contract, attachments )->
out.println("${contract}:${attachments.joinToString(",")}")
}
}
}
private fun getJarHash(cordappPath: String): AttachmentId = File(cordappPath).inputStream().use { jar ->
val hs = HashingInputStream(Hashing.sha256(), jar)
hs.readBytes()
SecureHash.SHA256(hs.hash().asBytes())
}
private fun readContractWhitelist(file: Path): Map<String, List<AttachmentId>> = file.toFile().readLines()
.map { line -> line.split(":") }
.map { (contract, attachmentIds) ->
contract to (attachmentIds.split(",").map(::parse))
}.toMap()
private fun NotaryInfo.prettyPrint(): String = "${identity.name} (${if (validating) "" else "non-"}validating)" private fun NotaryInfo.prettyPrint(): String = "${identity.name} (${if (validating) "" else "non-"}validating)"
private fun NodeInfo.notaryIdentity(): Party { private fun NodeInfo.notaryIdentity(): Party {

View File

@ -21,12 +21,12 @@ class ContractAttachmentSerializer(factory: SerializerFactory) : CustomSerialize
} catch (e: Exception) { } catch (e: Exception) {
throw MissingAttachmentsException(listOf(obj.id)) throw MissingAttachmentsException(listOf(obj.id))
} }
return ContractAttachmentProxy(GeneratedAttachment(bytes), obj.contract) return ContractAttachmentProxy(GeneratedAttachment(bytes), obj.contract, obj.additionalContracts, obj.uploader)
} }
override fun fromProxy(proxy: ContractAttachmentProxy): ContractAttachment { override fun fromProxy(proxy: ContractAttachmentProxy): ContractAttachment {
return ContractAttachment(proxy.attachment, proxy.contract) return ContractAttachment(proxy.attachment, proxy.contract, proxy.contracts, proxy.uploader)
} }
data class ContractAttachmentProxy(val attachment: Attachment, val contract: ContractClassName) data class ContractAttachmentProxy(val attachment: Attachment, val contract: ContractClassName, val contracts: Set<ContractClassName>, val uploader: String?)
} }

View File

@ -12,6 +12,7 @@ import de.javakaffee.kryoserializers.BitSetSerializer
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
import de.javakaffee.kryoserializers.guava.* import de.javakaffee.kryoserializers.guava.*
import net.corda.core.contracts.ContractAttachment import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName
import net.corda.core.contracts.PrivacySalt import net.corda.core.contracts.PrivacySalt
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -206,29 +207,34 @@ object DefaultKryoCustomizer {
output.writeBytesWithLength(buffer.toByteArray()) output.writeBytesWithLength(buffer.toByteArray())
} }
output.writeString(obj.contract) output.writeString(obj.contract)
kryo.writeClassAndObject(output, obj.additionalContracts)
output.writeString(obj.uploader)
} }
override fun read(kryo: Kryo, input: Input, type: Class<ContractAttachment>): ContractAttachment { override fun read(kryo: Kryo, input: Input, type: Class<ContractAttachment>): ContractAttachment {
if (kryo.serializationContext() != null) { if (kryo.serializationContext() != null) {
val attachmentHash = SecureHash.SHA256(input.readBytes(32)) val attachmentHash = SecureHash.SHA256(input.readBytes(32))
val contract = input.readString() val contract = input.readString()
val additionalContracts = kryo.readClassAndObject(input) as Set<ContractClassName>
val uploader = input.readString()
val context = kryo.serializationContext()!! val context = kryo.serializationContext()!!
val attachmentStorage = context.serviceHub.attachments val attachmentStorage = context.serviceHub.attachments
val lazyAttachment = object : AbstractAttachment({ val lazyAttachment = object : AbstractAttachment({
val attachment = attachmentStorage.openAttachment(attachmentHash) ?: throw MissingAttachmentsException(listOf(attachmentHash)) val attachment = attachmentStorage.openAttachment(attachmentHash)
?: throw MissingAttachmentsException(listOf(attachmentHash))
attachment.open().readBytes() attachment.open().readBytes()
}) { }) {
override val id = attachmentHash override val id = attachmentHash
} }
return ContractAttachment(lazyAttachment, contract) return ContractAttachment(lazyAttachment, contract, additionalContracts, uploader)
} else { } else {
val attachment = GeneratedAttachment(input.readBytesWithLength()) val attachment = GeneratedAttachment(input.readBytesWithLength())
val contract = input.readString() val contract = input.readString()
val additionalContracts = kryo.readClassAndObject(input) as Set<ContractClassName>
return ContractAttachment(attachment, contract) val uploader = input.readString()
return ContractAttachment(attachment, contract, additionalContracts, uploader)
} }
} }
} }

View File

@ -13,6 +13,7 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappLoader
import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.CordappProviderImpl
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
@ -59,7 +60,8 @@ class AttachmentsClassLoaderStaticContractTests {
} }
private val serviceHub = rigorousMock<ServicesForResolution>().also { private val serviceHub = rigorousMock<ServicesForResolution>().also {
doReturn(CordappProviderImpl(CordappLoader.createWithTestPackages(listOf("net.corda.nodeapi.internal")), MockCordappConfigProvider(), MockAttachmentStorage())).whenever(it).cordappProvider doReturn(CordappProviderImpl(CordappLoader.createWithTestPackages(listOf("net.corda.nodeapi.internal")), MockCordappConfigProvider(), MockAttachmentStorage(), testNetworkParameters().whitelistedContractImplementations)).whenever(it).cordappProvider
doReturn(testNetworkParameters()).whenever(it).networkParameters
} }
@Test @Test

View File

@ -18,6 +18,7 @@ import net.corda.nodeapi.DummyContractBackdoor
import net.corda.nodeapi.internal.serialization.SerializeAsTokenContextImpl import net.corda.nodeapi.internal.serialization.SerializeAsTokenContextImpl
import net.corda.nodeapi.internal.serialization.attachmentsClassLoaderEnabledPropertyName import net.corda.nodeapi.internal.serialization.attachmentsClassLoaderEnabledPropertyName
import net.corda.nodeapi.internal.serialization.withTokenContext import net.corda.nodeapi.internal.serialization.withTokenContext
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
@ -58,12 +59,15 @@ class AttachmentsClassLoaderTests {
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
private val attachments = MockAttachmentStorage() private val attachments = MockAttachmentStorage()
private val cordappProvider = CordappProviderImpl(CordappLoader.createDevMode(listOf(ISOLATED_CONTRACTS_JAR_PATH)), MockCordappConfigProvider(), attachments) private val networkParameters = testNetworkParameters()
private val cordappProvider = CordappProviderImpl(CordappLoader.createDevMode(listOf(ISOLATED_CONTRACTS_JAR_PATH)), MockCordappConfigProvider(), attachments, networkParameters.whitelistedContractImplementations)
private val cordapp get() = cordappProvider.cordapps.first() private val cordapp get() = cordappProvider.cordapps.first()
private val attachmentId get() = cordappProvider.getCordappAttachmentId(cordapp)!! private val attachmentId get() = cordappProvider.getCordappAttachmentId(cordapp)!!
private val appContext get() = cordappProvider.getAppContext(cordapp) private val appContext get() = cordappProvider.getAppContext(cordapp)
private val serviceHub = rigorousMock<ServiceHub>().also { private val serviceHub = rigorousMock<ServiceHub>().also {
doReturn(attachments).whenever(it).attachments doReturn(attachments).whenever(it).attachments
doReturn(cordappProvider).whenever(it).cordappProvider
doReturn(networkParameters).whenever(it).networkParameters
} }
// These ClassLoaders work together to load 'AnotherDummyContract' in a disposable way, such that even though // These ClassLoaders work together to load 'AnotherDummyContract' in a disposable way, such that even though
@ -279,7 +283,7 @@ class AttachmentsClassLoaderTests {
.withClassLoader(child) .withClassLoader(child)
val bytes = run { val bytes = run {
val wireTransaction = tx.toWireTransaction(cordappProvider, context) val wireTransaction = tx.toWireTransaction(serviceHub, context)
wireTransaction.serialize(context = context) wireTransaction.serialize(context = context)
} }
val copiedWireTransaction = bytes.deserialize(context = context) val copiedWireTransaction = bytes.deserialize(context = context)
@ -303,7 +307,7 @@ class AttachmentsClassLoaderTests {
val outboundContext = SerializationFactory.defaultFactory.defaultContext val outboundContext = SerializationFactory.defaultFactory.defaultContext
.withServiceHub(serviceHub) .withServiceHub(serviceHub)
.withClassLoader(child) .withClassLoader(child)
val wireTransaction = tx.toWireTransaction(cordappProvider, outboundContext) val wireTransaction = tx.toWireTransaction(serviceHub, outboundContext)
wireTransaction.serialize(context = outboundContext) wireTransaction.serialize(context = outboundContext)
} }
// use empty attachmentStorage // use empty attachmentStorage

View File

@ -43,6 +43,7 @@ class ContractAttachmentSerializerTest {
assertEquals(contractAttachment.id, deserialized.attachment.id) assertEquals(contractAttachment.id, deserialized.attachment.id)
assertEquals(contractAttachment.contract, deserialized.contract) assertEquals(contractAttachment.contract, deserialized.contract)
assertEquals(contractAttachment.additionalContracts, deserialized.additionalContracts)
assertArrayEquals(contractAttachment.open().readBytes(), deserialized.open().readBytes()) assertArrayEquals(contractAttachment.open().readBytes(), deserialized.open().readBytes())
} }
@ -58,6 +59,7 @@ class ContractAttachmentSerializerTest {
assertEquals(contractAttachment.id, deserialized.attachment.id) assertEquals(contractAttachment.id, deserialized.attachment.id)
assertEquals(contractAttachment.contract, deserialized.contract) assertEquals(contractAttachment.contract, deserialized.contract)
assertEquals(contractAttachment.additionalContracts, deserialized.additionalContracts)
assertArrayEquals(contractAttachment.open().readBytes(), deserialized.open().readBytes()) assertArrayEquals(contractAttachment.open().readBytes(), deserialized.open().readBytes())
} }

View File

@ -6,6 +6,7 @@ import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.util.DefaultClassResolver import com.esotericsoftware.kryo.util.DefaultClassResolver
import com.esotericsoftware.kryo.util.MapReferenceResolver import com.esotericsoftware.kryo.util.MapReferenceResolver
import com.nhaarman.mockito_kotlin.* import com.nhaarman.mockito_kotlin.*
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.nodeapi.internal.AttachmentsClassLoader import net.corda.nodeapi.internal.AttachmentsClassLoader
@ -195,7 +196,7 @@ class CordaClassResolverTests {
CordaClassResolver(emptyWhitelistContext).getRegistration(DefaultSerializable::class.java) CordaClassResolver(emptyWhitelistContext).getRegistration(DefaultSerializable::class.java)
} }
private fun importJar(storage: AttachmentStorage) = AttachmentsClassLoaderTests.ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) } private fun importJar(storage: AttachmentStorage, uploader: String = DEPLOYED_CORDAPP_UPLOADER) = AttachmentsClassLoaderTests.ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it, uploader, "") }
@Test(expected = KryoException::class) @Test(expected = KryoException::class)
fun `Annotation does not work in conjunction with AttachmentClassLoader annotation`() { fun `Annotation does not work in conjunction with AttachmentClassLoader annotation`() {
@ -206,6 +207,15 @@ class CordaClassResolverTests {
CordaClassResolver(emptyWhitelistContext).getRegistration(attachedClass) CordaClassResolver(emptyWhitelistContext).getRegistration(attachedClass)
} }
@Test(expected = IllegalArgumentException::class)
fun `Attempt to load contract attachment with the incorrect uploader should fails with IAE`() {
val storage = MockAttachmentStorage()
val attachmentHash = importJar(storage, "some_uploader")
val classLoader = AttachmentsClassLoader(arrayOf(attachmentHash).map { storage.openAttachment(it)!! })
val attachedClass = Class.forName("net.corda.finance.contracts.isolated.AnotherDummyContract", true, classLoader)
CordaClassResolver(emptyWhitelistContext).getRegistration(attachedClass)
}
@Test @Test
fun `Annotation is inherited from interfaces`() { fun `Annotation is inherited from interfaces`() {
CordaClassResolver(emptyWhitelistContext).getRegistration(SerializableViaInterface::class.java) CordaClassResolver(emptyWhitelistContext).getRegistration(SerializableViaInterface::class.java)

View File

@ -523,7 +523,7 @@ class EvolvabilityTests {
val resource = "networkParams.<corda version>.<commit sha>" val resource = "networkParams.<corda version>.<commit sha>"
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
val networkParameters = NetworkParameters( val networkParameters = NetworkParameters(
3, listOf(NotaryInfo(DUMMY_NOTARY, false)),1000, 1000, Instant.EPOCH, 1 ) 3, listOf(NotaryInfo(DUMMY_NOTARY, false)),1000, 1000, Instant.EPOCH, 1, emptyMap())
val sf = testDefaultFactory() val sf = testDefaultFactory()
sf.register(net.corda.nodeapi.internal.serialization.amqp.custom.InstantSerializer(sf)) sf.register(net.corda.nodeapi.internal.serialization.amqp.custom.InstantSerializer(sf))

View File

@ -1071,6 +1071,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
val obj2 = serdes(obj, factory, factory2, expectedEqual = false, expectDeserializedEqual = false) val obj2 = serdes(obj, factory, factory2, expectedEqual = false, expectDeserializedEqual = false)
assertEquals(obj.id, obj2.attachment.id) assertEquals(obj.id, obj2.attachment.id)
assertEquals(obj.contract, obj2.contract) assertEquals(obj.contract, obj2.contract)
assertEquals(obj.additionalContracts, obj2.additionalContracts)
assertArrayEquals(obj.open().readBytes(), obj2.open().readBytes()) assertArrayEquals(obj.open().readBytes(), obj2.open().readBytes())
} }

View File

@ -167,9 +167,6 @@ dependencies {
compile 'commons-codec:commons-codec:1.10' compile 'commons-codec:commons-codec:1.10'
compile 'com.github.bft-smart:library:master-v1.1-beta-g6215ec8-87' compile 'com.github.bft-smart:library:master-v1.1-beta-g6215ec8-87'
// FastClasspathScanner: classpath scanning
compile 'io.github.lukehutch:fast-classpath-scanner:2.0.21'
// Apache Shiro: authentication, authorization and session management. // Apache Shiro: authentication, authorization and session management.
compile "org.apache.shiro:shiro-core:${shiro_version}" compile "org.apache.shiro:shiro-core:${shiro_version}"

View File

@ -49,7 +49,7 @@ class AttachmentLoadingTests {
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
private val attachments = MockAttachmentStorage() private val attachments = MockAttachmentStorage()
private val provider = CordappProviderImpl(CordappLoader.createDevMode(listOf(isolatedJAR)), MockCordappConfigProvider(), attachments) private val provider = CordappProviderImpl(CordappLoader.createDevMode(listOf(isolatedJAR)), MockCordappConfigProvider(), attachments, testNetworkParameters().whitelistedContractImplementations)
private val cordapp get() = provider.cordapps.first() private val cordapp get() = provider.cordapps.first()
private val attachmentId get() = provider.getCordappAttachmentId(cordapp)!! private val attachmentId get() = provider.getCordappAttachmentId(cordapp)!!
private val appContext get() = provider.getAppContext(cordapp) private val appContext get() = provider.getAppContext(cordapp)

View File

@ -48,7 +48,7 @@ class BFTNotaryServiceTests {
@Before @Before
fun before() { fun before() {
mockNet = InternalMockNetwork(emptyList()) mockNet = InternalMockNetwork(listOf("net.corda.testing.contracts"))
} }
@After @After

View File

@ -556,7 +556,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
checkpointStorage = DBCheckpointStorage() checkpointStorage = DBCheckpointStorage()
val metrics = MetricRegistry() val metrics = MetricRegistry()
attachments = NodeAttachmentService(metrics, configuration.attachmentContentCacheSizeBytes, configuration.attachmentCacheBound) attachments = NodeAttachmentService(metrics, configuration.attachmentContentCacheSizeBytes, configuration.attachmentCacheBound)
val cordappProvider = CordappProviderImpl(cordappLoader, CordappConfigFileProvider(), attachments) val cordappProvider = CordappProviderImpl(cordappLoader, CordappConfigFileProvider(), attachments, networkParameters.whitelistedContractImplementations)
val keyManagementService = makeKeyManagementService(identityService, keyPairs) val keyManagementService = makeKeyManagementService(identityService, keyPairs)
_services = ServiceHubInternalImpl( _services = ServiceHubInternalImpl(
identityService, identityService,

View File

@ -13,6 +13,7 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
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.internal.RPC_UPLOADER
import net.corda.core.internal.sign import net.corda.core.internal.sign
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
@ -189,7 +190,7 @@ internal class CordaRPCOpsImpl(
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.attachments.importAttachment(jar) services.attachments.importAttachment(jar, RPC_UPLOADER, null)
} }
} }

View File

@ -1,10 +1,12 @@
package net.corda.node.internal.cordapp package net.corda.node.internal.cordapp
import com.google.common.collect.HashBiMap import com.google.common.collect.HashBiMap
import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.ContractClassName
import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.Cordapp
import net.corda.core.cordapp.CordappContext import net.corda.core.cordapp.CordappContext
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.DEPLOYED_CORDAPP_UPLOADER
import net.corda.core.internal.cordapp.CordappConfigProvider import net.corda.core.internal.cordapp.CordappConfigProvider
import net.corda.core.internal.createCordappContext import net.corda.core.internal.createCordappContext
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
@ -17,7 +19,10 @@ import java.util.concurrent.ConcurrentHashMap
/** /**
* Cordapp provider and store. For querying CorDapps for their attachment and vice versa. * Cordapp provider and store. For querying CorDapps for their attachment and vice versa.
*/ */
open class CordappProviderImpl(private val cordappLoader: CordappLoader, private val cordappConfigProvider: CordappConfigProvider, attachmentStorage: AttachmentStorage) : SingletonSerializeAsToken(), CordappProviderInternal { open class CordappProviderImpl(private val cordappLoader: CordappLoader,
private val cordappConfigProvider: CordappConfigProvider,
attachmentStorage: AttachmentStorage,
private val whitelistedContractImplementations: Map<String, List<AttachmentId>>) : SingletonSerializeAsToken(), CordappProviderInternal {
companion object { companion object {
private val log = loggerFor<CordappProviderImpl>() private val log = loggerFor<CordappProviderImpl>()
@ -25,6 +30,34 @@ open class CordappProviderImpl(private val cordappLoader: CordappLoader, private
private val contextCache = ConcurrentHashMap<Cordapp, CordappContext>() private val contextCache = ConcurrentHashMap<Cordapp, CordappContext>()
/**
* Current known CorDapps loaded on this node
*/
override val cordapps get() = cordappLoader.cordapps
private val cordappAttachments = HashBiMap.create(loadContractsIntoAttachmentStore(attachmentStorage))
init {
verifyInstalledCordapps(attachmentStorage)
}
private fun verifyInstalledCordapps(attachmentStorage: AttachmentStorage) {
if (whitelistedContractImplementations.isEmpty()) {
log.warn("The network parameters don't specify any whitelisted contract implementations. Please contact your zone operator. See https://docs.corda.net/network-map.html")
return
}
// Verify that the installed contract classes correspond with the whitelist hash
// And warn if node is not using latest CorDapp
cordappAttachments.keys.map(attachmentStorage::openAttachment).mapNotNull { it as? ContractAttachment }.forEach { attch ->
(attch.allContracts intersect whitelistedContractImplementations.keys).forEach { contractClassName ->
when {
attch.id !in whitelistedContractImplementations[contractClassName]!! -> log.error("Contract $contractClassName found in attachment ${attch.id} is not whitelisted in the network parameters. If this is a production node contact your zone operator. See https://docs.corda.net/network-map.html")
attch.id != whitelistedContractImplementations[contractClassName]!!.last() -> log.warn("You are not using the latest CorDapp version for contract: $contractClassName. Please contact your zone operator.")
}
}
}
}
override fun getAppContext(): CordappContext { override fun getAppContext(): CordappContext {
// TODO: Use better supported APIs in Java 9 // TODO: Use better supported APIs in Java 9
@ -42,11 +75,6 @@ open class CordappProviderImpl(private val cordappLoader: CordappLoader, private
return getCordappForClass(contractClassName)?.let(this::getCordappAttachmentId) return getCordappForClass(contractClassName)?.let(this::getCordappAttachmentId)
} }
/**
* Current known CorDapps loaded on this node
*/
override val cordapps get() = cordappLoader.cordapps
private val cordappAttachments = HashBiMap.create(loadContractsIntoAttachmentStore(attachmentStorage))
/** /**
* Gets the attachment ID of this CorDapp. Only CorDapps with contracts have an attachment ID * Gets the attachment ID of this CorDapp. Only CorDapps with contracts have an attachment ID
* *
@ -55,11 +83,16 @@ open class CordappProviderImpl(private val cordappLoader: CordappLoader, private
*/ */
fun getCordappAttachmentId(cordapp: Cordapp): SecureHash? = cordappAttachments.inverse().get(cordapp.jarPath) fun getCordappAttachmentId(cordapp: Cordapp): SecureHash? = cordappAttachments.inverse().get(cordapp.jarPath)
private fun loadContractsIntoAttachmentStore(attachmentStorage: AttachmentStorage): Map<SecureHash, URL> { private fun loadContractsIntoAttachmentStore(attachmentStorage: AttachmentStorage): Map<SecureHash, URL> =
val cordappsWithAttachments = cordapps.filter { !it.contractClassNames.isEmpty() }.map { it.jarPath } cordapps.filter { !it.contractClassNames.isEmpty() }.map {
val attachmentIds = cordappsWithAttachments.map { it.openStream().use { attachmentStorage.importOrGetAttachment(it) } } it.jarPath.openStream().use { stream ->
return attachmentIds.zip(cordappsWithAttachments).toMap() try {
} attachmentStorage.importAttachment(stream, DEPLOYED_CORDAPP_UPLOADER, null)
} catch (faee: java.nio.file.FileAlreadyExistsException) {
AttachmentId.parse(faee.message!!)
}
} to it.jarPath
}.toMap()
/** /**
* Get the current cordapp context for the given CorDapp * Get the current cordapp context for the given CorDapp

View File

@ -8,8 +8,11 @@ import com.google.common.hash.HashingInputStream
import com.google.common.io.CountingInputStream import com.google.common.io.CountingInputStream
import net.corda.core.CordaRuntimeException import net.corda.core.CordaRuntimeException
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.AbstractAttachment import net.corda.core.internal.AbstractAttachment
import net.corda.core.internal.UNKNOWN_UPLOADER
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.VisibleForTesting
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
@ -24,6 +27,7 @@ import net.corda.node.utilities.NonInvalidatingWeightBasedCache
import net.corda.node.utilities.defaultCordaCacheConcurrencyLevel import net.corda.node.utilities.defaultCordaCacheConcurrencyLevel
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import net.corda.nodeapi.internal.persistence.currentDBSession import net.corda.nodeapi.internal.persistence.currentDBSession
import net.corda.nodeapi.internal.withContractsInJar
import java.io.* import java.io.*
import java.nio.file.Paths import java.nio.file.Paths
import java.time.Instant import java.time.Instant
@ -85,7 +89,14 @@ class NodeAttachmentService(
var uploader: String? = null, var uploader: String? = null,
@Column(name = "filename", updatable = false) @Column(name = "filename", updatable = false)
var filename: String? = null var filename: String? = null,
@ElementCollection
@Column(name = "contract_class_name")
@CollectionTable(name = "node_attachments_contract_class_name", joinColumns = arrayOf(
JoinColumn(name = "att_id", referencedColumnName = "att_id")),
foreignKey = ForeignKey(name = "FK__ctr_class__attachments"))
var contractClassNames: List<ContractClassName>? = null
) : Serializable ) : Serializable
@VisibleForTesting @VisibleForTesting
@ -196,23 +207,31 @@ class NodeAttachmentService(
// If repeatedly looking for non-existing attachments becomes a performance issue, this is either indicating a // If repeatedly looking for non-existing attachments becomes a performance issue, this is either indicating a
// a problem somewhere else or this needs to be revisited. // a problem somewhere else or this needs to be revisited.
private val attachmentContentCache = NonInvalidatingWeightBasedCache<SecureHash, Optional<ByteArray>>( private val attachmentContentCache = NonInvalidatingWeightBasedCache<SecureHash, Optional<Pair<Attachment, ByteArray>>>(
maxWeight = attachmentContentCacheSize, maxWeight = attachmentContentCacheSize,
concurrencyLevel = defaultCordaCacheConcurrencyLevel, concurrencyLevel = defaultCordaCacheConcurrencyLevel,
weigher = object : Weigher<SecureHash, Optional<ByteArray>> { weigher = object : Weigher<SecureHash, Optional<Pair<Attachment, ByteArray>>> {
override fun weigh(key: SecureHash, value: Optional<ByteArray>): Int { override fun weigh(key: SecureHash, value: Optional<Pair<Attachment, ByteArray>>): Int {
return key.size + if (value.isPresent) value.get().size else 0 return key.size + if (value.isPresent) value.get().second.size else 0
} }
}, },
loadFunction = { Optional.ofNullable(loadAttachmentContent(it)) } loadFunction = { Optional.ofNullable(loadAttachmentContent(it)) }
) )
private fun loadAttachmentContent(id: SecureHash): ByteArray? { private fun loadAttachmentContent(id: SecureHash): Pair<Attachment, ByteArray>? {
val attachment = currentDBSession().get(NodeAttachmentService.DBAttachment::class.java, id.toString()) val attachment = currentDBSession().get(NodeAttachmentService.DBAttachment::class.java, id.toString())
return attachment?.content ?: return null
val attachmentImpl = AttachmentImpl(id, { attachment.content }, checkAttachmentsOnLoad).let {
val contracts = attachment.contractClassNames
if (contracts != null && contracts.isNotEmpty()) {
ContractAttachment(it, contracts.first(), contracts.drop(1).toSet(), attachment.uploader)
} else {
it
}
}
return Pair(attachmentImpl, attachment.content)
} }
private val attachmentCache = NonInvalidatingCache<SecureHash, Optional<Attachment>>( private val attachmentCache = NonInvalidatingCache<SecureHash, Optional<Attachment>>(
attachmentCacheBound, attachmentCacheBound,
defaultCordaCacheConcurrencyLevel, defaultCordaCacheConcurrencyLevel,
@ -222,16 +241,7 @@ class NodeAttachmentService(
private fun createAttachment(key: SecureHash): Attachment? { private fun createAttachment(key: SecureHash): Attachment? {
val content = attachmentContentCache.get(key) val content = attachmentContentCache.get(key)
if (content.isPresent) { if (content.isPresent) {
return AttachmentImpl( return content.get().first
key,
{
attachmentContentCache
.get(key)
.orElseThrow {
IllegalArgumentException("No attachement impl should have been created for non existent content")
}
},
checkAttachmentsOnLoad)
} }
// if no attachement has been found, we don't want to cache that - it might arrive later // if no attachement has been found, we don't want to cache that - it might arrive later
attachmentContentCache.invalidate(key) attachmentContentCache.invalidate(key)
@ -248,10 +258,10 @@ class NodeAttachmentService(
} }
override fun importAttachment(jar: InputStream): AttachmentId { override fun importAttachment(jar: InputStream): AttachmentId {
return import(jar, null, null) return import(jar, UNKNOWN_UPLOADER, null)
} }
override fun importAttachment(jar: InputStream, uploader: String, filename: String): AttachmentId { override fun importAttachment(jar: InputStream, uploader: String, filename: String?): AttachmentId {
return import(jar, uploader, filename) return import(jar, uploader, filename)
} }
@ -263,47 +273,39 @@ class NodeAttachmentService(
return Pair(id, bytes) return Pair(id, bytes)
} }
override fun hasAttachment(attachmentId: AttachmentId): Boolean { override fun hasAttachment(attachmentId: AttachmentId): Boolean =
val session = currentDBSession() currentDBSession().find(NodeAttachmentService.DBAttachment::class.java, attachmentId.toString()) != null
val criteriaBuilder = session.criteriaBuilder
val criteriaQuery = criteriaBuilder.createQuery(Long::class.java)
val attachments = criteriaQuery.from(NodeAttachmentService.DBAttachment::class.java)
criteriaQuery.select(criteriaBuilder.count(criteriaQuery.from(NodeAttachmentService.DBAttachment::class.java)))
criteriaQuery.where(criteriaBuilder.equal(attachments.get<String>(DBAttachment::attId.name), attachmentId.toString()))
return (session.createQuery(criteriaQuery).singleResult > 0)
}
// TODO: PLT-147: The attachment should be randomised to prevent brute force guessing and thus privacy leaks. // TODO: PLT-147: The attachment should be randomised to prevent brute force guessing and thus privacy leaks.
private fun import(jar: InputStream, uploader: String?, filename: String?): AttachmentId { private fun import(jar: InputStream, uploader: String?, filename: String?): AttachmentId {
require(jar !is JarInputStream) return withContractsInJar(jar) { contractClassNames, inputStream ->
require(inputStream !is JarInputStream)
// Read the file into RAM, hashing it to find the ID as we go. The attachment must fit into memory. // Read the file into RAM, hashing it to find the ID as we go. The attachment must fit into memory.
// TODO: Switch to a two-phase insert so we can handle attachments larger than RAM. // TODO: Switch to a two-phase insert so we can handle attachments larger than RAM.
// To do this we must pipe stream into the database without knowing its hash, which we will learn only once // To do this we must pipe stream into the database without knowing its hash, which we will learn only once
// the insert/upload is complete. We can then query to see if it's a duplicate and if so, erase, and if not // the insert/upload is complete. We can then query to see if it's a duplicate and if so, erase, and if not
// set the hash field of the new attachment record. // set the hash field of the new attachment record.
val (id, bytes) = getAttachmentIdAndBytes(jar) val (id, bytes) = getAttachmentIdAndBytes(inputStream)
if (!hasAttachment(id)) { if (!hasAttachment(id)) {
checkIsAValidJAR(ByteArrayInputStream(bytes)) checkIsAValidJAR(ByteArrayInputStream(bytes))
val session = currentDBSession() val session = currentDBSession()
val attachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes, uploader = uploader, filename = filename) val attachment = NodeAttachmentService.DBAttachment(attId = id.toString(), content = bytes, uploader = uploader, filename = filename, contractClassNames = contractClassNames)
session.save(attachment) session.save(attachment)
attachmentCount.inc() attachmentCount.inc()
log.info("Stored new attachment $id") log.info("Stored new attachment $id")
return id id
} else { } else {
throw java.nio.file.FileAlreadyExistsException(id.toString()) throw java.nio.file.FileAlreadyExistsException(id.toString())
}
} }
} }
override fun importOrGetAttachment(jar: InputStream): AttachmentId { override fun importOrGetAttachment(jar: InputStream): AttachmentId = try {
try { importAttachment(jar)
return importAttachment(jar) } catch (faee: java.nio.file.FileAlreadyExistsException) {
} AttachmentId.parse(faee.message!!)
catch (faee: java.nio.file.FileAlreadyExistsException) {
return AttachmentId.parse(faee.message!!)
}
} }
override fun queryAttachments(criteria: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId> { override fun queryAttachments(criteria: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId> {
@ -328,5 +330,4 @@ class NodeAttachmentService(
return results.map { AttachmentId.parse(it.attId) } return results.map { AttachmentId.parse(it.attId) }
} }
} }

View File

@ -2,14 +2,18 @@ package net.corda.node.internal.cordapp
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import junit.framework.Assert.assertNull
import net.corda.core.internal.cordapp.CordappConfigProvider import net.corda.core.internal.cordapp.CordappConfigProvider
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.internal.MockCordappConfigProvider import net.corda.testing.internal.MockCordappConfigProvider
import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.net.URL
class CordappProviderImplTests { class CordappProviderImplTests {
private companion object { private companion object {
@ -25,6 +29,7 @@ class CordappProviderImplTests {
} }
private lateinit var attachmentStore: AttachmentStorage private lateinit var attachmentStore: AttachmentStorage
private val whitelistedContractImplementations = testNetworkParameters().whitelistedContractImplementations
@Before @Before
fun setup() { fun setup() {
@ -33,44 +38,40 @@ class CordappProviderImplTests {
@Test @Test
fun `isolated jar is loaded into the attachment store`() { fun `isolated jar is loaded into the attachment store`() {
val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) val provider = newCordappProvider(isolatedJAR)
val provider = CordappProviderImpl(loader, stubConfigProvider, attachmentStore)
val maybeAttachmentId = provider.getCordappAttachmentId(provider.cordapps.first()) val maybeAttachmentId = provider.getCordappAttachmentId(provider.cordapps.first())
Assert.assertNotNull(maybeAttachmentId) assertNotNull(maybeAttachmentId)
Assert.assertNotNull(attachmentStore.openAttachment(maybeAttachmentId!!)) assertNotNull(attachmentStore.openAttachment(maybeAttachmentId!!))
} }
@Test @Test
fun `empty jar is not loaded into the attachment store`() { fun `empty jar is not loaded into the attachment store`() {
val loader = CordappLoader.createDevMode(listOf(emptyJAR)) val provider = newCordappProvider(emptyJAR)
val provider = CordappProviderImpl(loader, stubConfigProvider, attachmentStore) assertNull(provider.getCordappAttachmentId(provider.cordapps.first()))
Assert.assertNull(provider.getCordappAttachmentId(provider.cordapps.first()))
} }
@Test @Test
fun `test that we find a cordapp class that is loaded into the store`() { fun `test that we find a cordapp class that is loaded into the store`() {
val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) val provider = newCordappProvider(isolatedJAR)
val provider = CordappProviderImpl(loader, stubConfigProvider, attachmentStore)
val className = "net.corda.finance.contracts.isolated.AnotherDummyContract" val className = "net.corda.finance.contracts.isolated.AnotherDummyContract"
val expected = provider.cordapps.first() val expected = provider.cordapps.first()
val actual = provider.getCordappForClass(className) val actual = provider.getCordappForClass(className)
Assert.assertNotNull(actual) assertNotNull(actual)
Assert.assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test @Test
fun `test that we find an attachment for a cordapp contract class`() { fun `test that we find an attachment for a cordapp contrat class`() {
val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) val provider = newCordappProvider(isolatedJAR)
val provider = CordappProviderImpl(loader, stubConfigProvider, attachmentStore)
val className = "net.corda.finance.contracts.isolated.AnotherDummyContract" val className = "net.corda.finance.contracts.isolated.AnotherDummyContract"
val expected = provider.getAppContext(provider.cordapps.first()).attachmentId val expected = provider.getAppContext(provider.cordapps.first()).attachmentId
val actual = provider.getContractAttachmentID(className) val actual = provider.getContractAttachmentID(className)
Assert.assertNotNull(actual) assertNotNull(actual)
Assert.assertEquals(actual!!, expected) assertEquals(actual!!, expected)
} }
@Test @Test
@ -78,10 +79,15 @@ class CordappProviderImplTests {
val configProvider = MockCordappConfigProvider() val configProvider = MockCordappConfigProvider()
configProvider.cordappConfigs.put(isolatedCordappName, validConfig) configProvider.cordappConfigs.put(isolatedCordappName, validConfig)
val loader = CordappLoader.createDevMode(listOf(isolatedJAR)) val loader = CordappLoader.createDevMode(listOf(isolatedJAR))
val provider = CordappProviderImpl(loader, configProvider, attachmentStore) val provider = CordappProviderImpl(loader, configProvider, attachmentStore, whitelistedContractImplementations)
val expected = provider.getAppContext(provider.cordapps.first()).config val expected = provider.getAppContext(provider.cordapps.first()).config
assertThat(expected.getString("key")).isEqualTo("value") assertThat(expected.getString("key")).isEqualTo("value")
} }
private fun newCordappProvider(vararg urls: URL): CordappProviderImpl {
val loader = CordappLoader.createDevMode(urls.toList())
return CordappProviderImpl(loader, stubConfigProvider, attachmentStore, whitelistedContractImplementations)
}
} }

View File

@ -62,7 +62,7 @@ open class MockServices private constructor(
cordappLoader: CordappLoader, cordappLoader: CordappLoader,
override val validatedTransactions: WritableTransactionStorage, override val validatedTransactions: WritableTransactionStorage,
override val identityService: IdentityService, override val identityService: IdentityService,
override val networkParameters: NetworkParameters, final override val networkParameters: NetworkParameters,
private val initialIdentity: TestIdentity, private val initialIdentity: TestIdentity,
private val moreKeys: Array<out KeyPair> private val moreKeys: Array<out KeyPair>
) : ServiceHub, StateLoader by validatedTransactions { ) : ServiceHub, StateLoader by validatedTransactions {
@ -226,7 +226,7 @@ open class MockServices private constructor(
return NodeInfo(listOf(NetworkHostAndPort("mock.node.services", 10000)), listOf(initialIdentity.identity), 1, serial = 1L) return NodeInfo(listOf(NetworkHostAndPort("mock.node.services", 10000)), listOf(initialIdentity.identity), 1, serial = 1L)
} }
override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2) override val transactionVerifierService: TransactionVerifierService get() = InMemoryTransactionVerifierService(2)
private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments) private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments, networkParameters.whitelistedContractImplementations)
override val cordappProvider: CordappProvider get() = mockCordappProvider override val cordappProvider: CordappProvider get() = mockCordappProvider
internal fun makeVaultService(hibernateConfig: HibernateConfiguration, schemaService: SchemaService): VaultServiceInternal { internal fun makeVaultService(hibernateConfig: HibernateConfiguration, schemaService: SchemaService): VaultServiceInternal {

View File

@ -39,7 +39,7 @@ class NetworkMapServer(private val cacheTimeout: Duration,
private val myHostNameValue: String = "test.host.name", private val myHostNameValue: String = "test.host.name",
vararg additionalServices: Any) : Closeable { vararg additionalServices: Any) : Closeable {
companion object { companion object {
private val stubNetworkParameters = NetworkParameters(1, emptyList(), 10485760, Int.MAX_VALUE, Instant.now(), 10) private val stubNetworkParameters = NetworkParameters(1, emptyList(), 10485760, Int.MAX_VALUE, Instant.now(), 10, emptyMap())
} }
private val server: Server private val server: Server

View File

@ -19,6 +19,7 @@ fun testNetworkParameters(
modifiedTime = modifiedTime, modifiedTime = modifiedTime,
maxMessageSize = maxMessageSize, maxMessageSize = maxMessageSize,
maxTransactionSize = maxTransactionSize, maxTransactionSize = maxTransactionSize,
epoch = epoch epoch = epoch,
whitelistedContractImplementations = emptyMap()
) )
} }

View File

@ -2,6 +2,7 @@ package net.corda.testing.internal
import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.ContractClassName
import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.Cordapp
import net.corda.core.internal.TEST_UPLOADER
import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappImpl
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
@ -14,9 +15,10 @@ import java.util.*
class MockCordappProvider( class MockCordappProvider(
cordappLoader: CordappLoader, cordappLoader: CordappLoader,
attachmentStorage: AttachmentStorage, attachmentStorage: AttachmentStorage,
val cordappConfigProvider: MockCordappConfigProvider = MockCordappConfigProvider() whitelistedContractImplementations: Map<String, List<AttachmentId>>,
) : CordappProviderImpl(cordappLoader, cordappConfigProvider, attachmentStorage) { cordappConfigProvider: MockCordappConfigProvider = MockCordappConfigProvider()
constructor(cordappLoader: CordappLoader, attachmentStorage: AttachmentStorage) : this(cordappLoader, attachmentStorage, MockCordappConfigProvider()) ) : CordappProviderImpl(cordappLoader, cordappConfigProvider, attachmentStorage, whitelistedContractImplementations) {
constructor(cordappLoader: CordappLoader, attachmentStorage: AttachmentStorage, whitelistedContractImplementations: Map<String, List<AttachmentId>>) : this(cordappLoader, attachmentStorage, whitelistedContractImplementations, MockCordappConfigProvider())
val cordappRegistry = mutableListOf<Pair<Cordapp, AttachmentId>>() val cordappRegistry = mutableListOf<Pair<Cordapp, AttachmentId>>()
@ -33,20 +35,21 @@ class MockCordappProvider(
customSchemas = emptySet(), customSchemas = emptySet(),
jarPath = Paths.get("").toUri().toURL()) jarPath = Paths.get("").toUri().toURL())
if (cordappRegistry.none { it.first.contractClassNames.contains(contractClassName) }) { if (cordappRegistry.none { it.first.contractClassNames.contains(contractClassName) }) {
cordappRegistry.add(Pair(cordapp, findOrImportAttachment(contractClassName.toByteArray(), attachments))) cordappRegistry.add(Pair(cordapp, findOrImportAttachment(listOf(contractClassName), contractClassName.toByteArray(), attachments)))
} }
} }
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? = cordappRegistry.find { it.first.contractClassNames.contains(contractClassName) }?.second ?: super.getContractAttachmentID(contractClassName) override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? = cordappRegistry.find { it.first.contractClassNames.contains(contractClassName) }?.second
?: super.getContractAttachmentID(contractClassName)
private fun findOrImportAttachment(data: ByteArray, attachments: MockAttachmentStorage): AttachmentId { private fun findOrImportAttachment(contractClassNames: List<ContractClassName>, data: ByteArray, attachments: MockAttachmentStorage): AttachmentId {
val existingAttachment = attachments.files.filter { val existingAttachment = attachments.files.filter {
Arrays.equals(it.value, data) Arrays.equals(it.value.second, data)
} }
return if (!existingAttachment.isEmpty()) { return if (!existingAttachment.isEmpty()) {
existingAttachment.keys.first() existingAttachment.keys.first()
} else { } else {
attachments.importAttachment(data.inputStream()) attachments.importContractAttachment(contractClassNames, TEST_UPLOADER, data.inputStream())
} }
} }
} }

View File

@ -1,14 +1,18 @@
package net.corda.testing.services package net.corda.testing.services
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.internal.AbstractAttachment import net.corda.core.internal.AbstractAttachment
import net.corda.core.internal.UNKNOWN_UPLOADER
import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.vault.AttachmentQueryCriteria import net.corda.core.node.services.vault.AttachmentQueryCriteria
import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.node.services.vault.AttachmentSort
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.nodeapi.internal.withContractsInJar
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import java.util.* import java.util.*
@ -24,31 +28,17 @@ class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
} }
} }
override fun importAttachment(jar: InputStream): AttachmentId { val files = HashMap<SecureHash, Pair<Attachment, ByteArray>>()
// JIS makes read()/readBytes() return bytes of the current file, but we want to hash the entire container here.
require(jar !is JarInputStream)
val bytes = getBytes(jar) override fun importAttachment(jar: InputStream): AttachmentId = importAttachment(jar, UNKNOWN_UPLOADER, null)
val sha256 = bytes.sha256() override fun importAttachment(jar: InputStream, uploader: String, filename: String?): AttachmentId {
if (!files.containsKey(sha256)) { return withContractsInJar(jar) { contractClassNames, inputStream ->
files[sha256] = bytes importAttachmentInternal(inputStream, uploader, filename, contractClassNames)
} }
return sha256
} }
override fun importAttachment(jar: InputStream, uploader: String, filename: String): AttachmentId { override fun openAttachment(id: SecureHash): Attachment? = files[id]?.first
return importAttachment(jar)
}
val files = HashMap<SecureHash, ByteArray>()
private class MockAttachment(dataLoader: () -> ByteArray, override val id: SecureHash) : AbstractAttachment(dataLoader)
override fun openAttachment(id: SecureHash): Attachment? {
val f = files[id] ?: return null
return MockAttachment({ f }, id)
}
override fun queryAttachments(criteria: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId> { override fun queryAttachments(criteria: AttachmentQueryCriteria, sorting: AttachmentSort?): List<AttachmentId> {
throw NotImplementedError("Querying for attachments not implemented") throw NotImplementedError("Querying for attachments not implemented")
@ -56,11 +46,6 @@ class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
override fun hasAttachment(attachmentId: AttachmentId) = files.containsKey(attachmentId) override fun hasAttachment(attachmentId: AttachmentId) = files.containsKey(attachmentId)
fun getAttachmentIdAndBytes(jar: InputStream): Pair<AttachmentId, ByteArray> {
val bytes = getBytes(jar)
return Pair(bytes.sha256(), bytes)
}
override fun importOrGetAttachment(jar: InputStream): AttachmentId { override fun importOrGetAttachment(jar: InputStream): AttachmentId {
try { try {
return importAttachment(jar) return importAttachment(jar)
@ -68,4 +53,25 @@ class MockAttachmentStorage : AttachmentStorage, SingletonSerializeAsToken() {
return AttachmentId.parse(faee.message!!) return AttachmentId.parse(faee.message!!)
} }
} }
fun importContractAttachment(contractClassNames: List<ContractClassName>, uploader: String, jar: InputStream): AttachmentId = importAttachmentInternal(jar, uploader, null, contractClassNames)
fun getAttachmentIdAndBytes(jar: InputStream): Pair<AttachmentId, ByteArray> = getBytes(jar).let { bytes -> Pair(bytes.sha256(), bytes) }
private class MockAttachment(dataLoader: () -> ByteArray, override val id: SecureHash) : AbstractAttachment(dataLoader)
private fun importAttachmentInternal(jar: InputStream, uploader: String, filename: String?, contractClassNames: List<ContractClassName>? = null): AttachmentId {
// JIS makes read()/readBytes() return bytes of the current file, but we want to hash the entire container here.
require(jar !is JarInputStream)
val bytes = getBytes(jar)
val sha256 = bytes.sha256()
if (sha256 !in files.keys) {
val baseAttachment = MockAttachment({ bytes }, sha256)
val attachment = if (contractClassNames == null || contractClassNames.isEmpty()) baseAttachment else ContractAttachment(baseAttachment, contractClassNames.first(), contractClassNames.toSet(), uploader)
files[sha256] = Pair(attachment, bytes)
}
return sha256
}
} }

View File

@ -145,7 +145,8 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
modifiedTime = Instant.now(), modifiedTime = Instant.now(),
maxMessageSize = 10485760, maxMessageSize = 10485760,
maxTransactionSize = Int.MAX_VALUE, maxTransactionSize = Int.MAX_VALUE,
epoch = 1 epoch = 1,
whitelistedContractImplementations = emptyMap()
)) ))
notaryIdentity = identity notaryIdentity = identity
networkParametersCopier = parametersCopier networkParametersCopier = parametersCopier

View File

@ -43,7 +43,7 @@ data class GeneratedLedger(
private val attachmentMap: Map<SecureHash, Attachment> by lazy { attachments.associateBy(Attachment::id) } private val attachmentMap: Map<SecureHash, Attachment> by lazy { attachments.associateBy(Attachment::id) }
private val identityMap: Map<PublicKey, Party> by lazy { identities.associateBy(Party::owningKey) } private val identityMap: Map<PublicKey, Party> by lazy { identities.associateBy(Party::owningKey) }
private val contractAttachmentMap: Map<String, ContractAttachment> by lazy { private val contractAttachmentMap: Map<String, ContractAttachment> by lazy {
attachments.mapNotNull { it as? ContractAttachment }.associateBy { it.contract } attachments.mapNotNull { it as? ContractAttachment }.flatMap { attch-> attch.allContracts.map { it to attch } }.toMap()
} }
private val services = object : ServicesForResolution { private val services = object : ServicesForResolution {

View File

@ -17,6 +17,7 @@ import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.driver.internal.NodeHandleInternal import net.corda.testing.driver.internal.NodeHandleInternal
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec
import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
@ -133,6 +134,7 @@ class VerifierTests {
} }
} }
@Ignore("CORDA-1022")
@Test @Test
fun `single verifier works with a node`() { fun `single verifier works with a node`() {
verifierDriver( verifierDriver(