mirror of
https://github.com/corda/corda.git
synced 2025-05-31 06:31:08 +00:00
Merge pull request #1842 from corda/tlil/reformat-2017-oct
Reformat Oct 2017
This commit is contained in:
commit
0d15e70d51
@ -28,7 +28,7 @@ class CanonicalizerPlugin implements Plugin<Project> {
|
||||
output.setMethod(ZipOutputStream.DEFLATED)
|
||||
|
||||
entries.each {
|
||||
def newEntry = new ZipEntry( it.name )
|
||||
def newEntry = new ZipEntry(it.name)
|
||||
|
||||
newEntry.setLastModifiedTime(zeroTime)
|
||||
newEntry.setCreationTime(zeroTime)
|
||||
|
@ -119,12 +119,14 @@ object JacksonSupport {
|
||||
* match an identity known from the network map. If true, the name is matched more leniently but if the match
|
||||
* is ambiguous a [JsonParseException] is thrown.
|
||||
*/
|
||||
@JvmStatic @JvmOverloads
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun createDefaultMapper(rpc: CordaRPCOps, factory: JsonFactory = JsonFactory(),
|
||||
fuzzyIdentityMatch: Boolean = false): ObjectMapper = configureMapper(RpcObjectMapper(rpc, factory, fuzzyIdentityMatch))
|
||||
|
||||
/** For testing or situations where deserialising parties is not required */
|
||||
@JvmStatic @JvmOverloads
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun createNonRpcMapper(factory: JsonFactory = JsonFactory()): ObjectMapper = configureMapper(NoPartyObjectMapper(factory))
|
||||
|
||||
/**
|
||||
@ -134,7 +136,8 @@ object JacksonSupport {
|
||||
* match an identity known from the network map. If true, the name is matched more leniently but if the match
|
||||
* is ambiguous a [JsonParseException] is thrown.
|
||||
*/
|
||||
@JvmStatic @JvmOverloads
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun createInMemoryMapper(identityService: IdentityService, factory: JsonFactory = JsonFactory(),
|
||||
fuzzyIdentityMatch: Boolean = false) = configureMapper(IdentityObjectMapper(identityService, factory, fuzzyIdentityMatch))
|
||||
|
||||
@ -227,7 +230,7 @@ object JacksonSupport {
|
||||
|
||||
return try {
|
||||
CordaX500Name.parse(parser.text)
|
||||
} catch(ex: IllegalArgumentException) {
|
||||
} catch (ex: IllegalArgumentException) {
|
||||
throw JsonParseException(parser, "Invalid Corda X.500 name ${parser.text}: ${ex.message}", ex)
|
||||
}
|
||||
}
|
||||
@ -310,7 +313,7 @@ object JacksonSupport {
|
||||
// Attempt parsing as a currency token. TODO: This needs thought about how to extend to other token types.
|
||||
val currency = Currency.getInstance(token)
|
||||
return Amount(quantity, currency)
|
||||
} catch(e2: Exception) {
|
||||
} catch (e2: Exception) {
|
||||
throw JsonParseException(parser, "Invalid amount ${parser.text}", e2)
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ open class StringToMethodCallParser<in T : Any> @JvmOverloads constructor(
|
||||
val methodParamNames: Map<String, List<String>> = targetType.declaredMethods.mapNotNull {
|
||||
try {
|
||||
it.name to paramNamesFromMethod(it)
|
||||
} catch(e: KotlinReflectionInternalError) {
|
||||
} catch (e: KotlinReflectionInternalError) {
|
||||
// Kotlin reflection doesn't support every method that can exist on an object (in particular, reified
|
||||
// inline methods) so we just ignore those here.
|
||||
null
|
||||
@ -175,7 +175,7 @@ open class StringToMethodCallParser<in T : Any> @JvmOverloads constructor(
|
||||
try {
|
||||
val args = parseArguments(name, paramNamesFromMethod(method).zip(method.parameterTypes), argStr)
|
||||
return ParsedMethodCall(target, method, args)
|
||||
} catch(e: UnparseableCallException) {
|
||||
} catch (e: UnparseableCallException) {
|
||||
if (index == methods.size - 1)
|
||||
throw e
|
||||
}
|
||||
@ -198,7 +198,7 @@ open class StringToMethodCallParser<in T : Any> @JvmOverloads constructor(
|
||||
val entry = tree[argName] ?: throw UnparseableCallException.MissingParameter(methodNameHint, argName, args)
|
||||
try {
|
||||
om.readValue(entry.traverse(om), argType)
|
||||
} catch(e: Exception) {
|
||||
} catch (e: Exception) {
|
||||
throw UnparseableCallException.FailedParse(e)
|
||||
}
|
||||
}
|
||||
@ -212,16 +212,17 @@ open class StringToMethodCallParser<in T : Any> @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
/** Returns a string-to-string map of commands to a string describing available parameter types. */
|
||||
val availableCommands: Map<String, String> get() {
|
||||
return methodMap.entries().map { entry ->
|
||||
val (name, args) = entry // TODO: Kotlin 1.1
|
||||
val argStr = if (args.parameterCount == 0) "" else {
|
||||
val paramNames = methodParamNames[name]!!
|
||||
val typeNames = args.parameters.map { it.type.simpleName }
|
||||
val paramTypes = paramNames.zip(typeNames)
|
||||
paramTypes.map { "${it.first}: ${it.second}" }.joinToString(", ")
|
||||
}
|
||||
Pair(name, argStr)
|
||||
}.toMap()
|
||||
}
|
||||
val availableCommands: Map<String, String>
|
||||
get() {
|
||||
return methodMap.entries().map { entry ->
|
||||
val (name, args) = entry // TODO: Kotlin 1.1
|
||||
val argStr = if (args.parameterCount == 0) "" else {
|
||||
val paramNames = methodParamNames[name]!!
|
||||
val typeNames = args.parameters.map { it.type.simpleName }
|
||||
val paramTypes = paramNames.zip(typeNames)
|
||||
paramTypes.map { "${it.first}: ${it.second}" }.joinToString(", ")
|
||||
}
|
||||
Pair(name, argStr)
|
||||
}.toMap()
|
||||
}
|
||||
}
|
||||
|
@ -92,10 +92,10 @@ class JacksonSupportTest : TestDependencyInjectionBase() {
|
||||
fun writeTransaction() {
|
||||
val attachmentRef = SecureHash.randomSHA256()
|
||||
whenever(cordappProvider.getContractAttachmentID(DummyContract.PROGRAM_ID))
|
||||
.thenReturn(attachmentRef)
|
||||
.thenReturn(attachmentRef)
|
||||
fun makeDummyTx(): SignedTransaction {
|
||||
val wtx = DummyContract.generateInitial(1, DUMMY_NOTARY, MINI_CORP.ref(1))
|
||||
.toWireTransaction(services)
|
||||
.toWireTransaction(services)
|
||||
val signatures = TransactionSignature(
|
||||
ByteArray(1),
|
||||
ALICE_PUBKEY,
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:JvmName("ModelsUtils")
|
||||
|
||||
package net.corda.client.jfx.model
|
||||
|
||||
import javafx.beans.property.ObjectProperty
|
||||
|
@ -38,7 +38,7 @@ class NetworkIdentityModel {
|
||||
})
|
||||
|
||||
val notaries: ObservableList<Party> = networkIdentities.map {
|
||||
it.legalIdentitiesAndCerts.find { it.name.commonName?.let { ServiceType.parse(it).isNotary() } ?: false }
|
||||
it.legalIdentitiesAndCerts.find { it.name.commonName?.let { ServiceType.parse(it).isNotary() } == true }
|
||||
}.map { it?.party }.filterNotNull()
|
||||
|
||||
val notaryNodes: ObservableList<NodeInfo> = notaries.map { rpcProxy.value?.nodeInfoFromParty(it) }.filterNotNull()
|
||||
|
@ -57,8 +57,8 @@ class NodeMonitorModel {
|
||||
*/
|
||||
fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) {
|
||||
val client = CordaRPCClient(
|
||||
nodeHostAndPort,
|
||||
CordaRPCClientConfiguration.DEFAULT.copy(
|
||||
nodeHostAndPort,
|
||||
CordaRPCClientConfiguration.DEFAULT.copy(
|
||||
connectionMaxRetryInterval = 10.seconds
|
||||
)
|
||||
)
|
||||
|
@ -253,14 +253,15 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
|
||||
}
|
||||
}
|
||||
|
||||
override val size: Int get() {
|
||||
recalculateOffsets()
|
||||
if (nestedIndexOffsets.size > 0) {
|
||||
return nestedIndexOffsets.last()
|
||||
} else {
|
||||
return 0
|
||||
override val size: Int
|
||||
get() {
|
||||
recalculateOffsets()
|
||||
if (nestedIndexOffsets.size > 0) {
|
||||
return nestedIndexOffsets.last()
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSourceIndex(index: Int): Int {
|
||||
throw UnsupportedOperationException("Source index not supported in concatenation")
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:JvmName("ObservableFold")
|
||||
|
||||
package net.corda.client.jfx.utils
|
||||
|
||||
import javafx.application.Platform
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:JvmName("ObservableUtilities")
|
||||
|
||||
package net.corda.client.jfx.utils
|
||||
|
||||
import javafx.application.Platform
|
||||
|
@ -50,18 +50,19 @@ open class ReadOnlyBackedObservableMapBase<K, A, B> : ObservableMap<K, A> {
|
||||
|
||||
override fun isEmpty() = backingMap.isEmpty()
|
||||
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<K, A>> get() = backingMap.entries.fold(mutableSetOf()) { set, entry ->
|
||||
set.add(object : MutableMap.MutableEntry<K, A> {
|
||||
override var value: A = entry.value.first
|
||||
override val key = entry.key
|
||||
override fun setValue(newValue: A): A {
|
||||
val old = value
|
||||
value = newValue
|
||||
return old
|
||||
}
|
||||
})
|
||||
set
|
||||
}
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<K, A>>
|
||||
get() = backingMap.entries.fold(mutableSetOf()) { set, entry ->
|
||||
set.add(object : MutableMap.MutableEntry<K, A> {
|
||||
override var value: A = entry.value.first
|
||||
override val key = entry.key
|
||||
override fun setValue(newValue: A): A {
|
||||
val old = value
|
||||
value = newValue
|
||||
return old
|
||||
}
|
||||
})
|
||||
set
|
||||
}
|
||||
override val keys: MutableSet<K> get() = backingMap.keys
|
||||
override val values: MutableCollection<A> get() = ArrayList(backingMap.values.map { it.first })
|
||||
|
||||
|
@ -50,7 +50,7 @@ open class EventGenerator(val parties: List<Party>, val currencies: List<Currenc
|
||||
* [Generator]s for incoming/outgoing events of starting different cash flows. It invokes flows that throw exceptions
|
||||
* for use in explorer flow triage. Exceptions are of kind spending/exiting too much cash.
|
||||
*/
|
||||
class ErrorFlowsEventGenerator(parties: List<Party>, currencies: List<Currency>, notary: Party): EventGenerator(parties, currencies, notary) {
|
||||
class ErrorFlowsEventGenerator(parties: List<Party>, currencies: List<Currency>, notary: Party) : EventGenerator(parties, currencies, notary) {
|
||||
enum class IssuerEvents {
|
||||
NORMAL_EXIT,
|
||||
EXIT_ERROR
|
||||
@ -62,7 +62,7 @@ class ErrorFlowsEventGenerator(parties: List<Party>, currencies: List<Currency>,
|
||||
when (errorType) {
|
||||
IssuerEvents.NORMAL_EXIT -> {
|
||||
println("Normal exit")
|
||||
if (currencyMap[ccy]!! <= amount) addToMap(ccy, -amount)
|
||||
if (currencyMap[ccy]!! <= amount) addToMap(ccy, -amount)
|
||||
ExitRequest(Amount(amount, ccy), issueRef) // It may fail at the beginning, but we don't care.
|
||||
}
|
||||
IssuerEvents.EXIT_ERROR -> {
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:JvmName("Generators")
|
||||
|
||||
package net.corda.client.mock
|
||||
|
||||
import net.corda.core.contracts.Amount
|
||||
|
@ -76,10 +76,12 @@ class RPCStabilityTests {
|
||||
rpcDriver {
|
||||
Try.on { startRpcClient<RPCOps>(NetworkHostAndPort("localhost", 9999)).get() }
|
||||
val server = startRpcServer<RPCOps>(ops = DummyOps)
|
||||
Try.on { startRpcClient<RPCOps>(
|
||||
server.get().broker.hostAndPort!!,
|
||||
configuration = RPCClientConfiguration.default.copy(minimumServerProtocolVersion = 1)
|
||||
).get() }
|
||||
Try.on {
|
||||
startRpcClient<RPCOps>(
|
||||
server.get().broker.hostAndPort!!,
|
||||
configuration = RPCClientConfiguration.default.copy(minimumServerProtocolVersion = 1)
|
||||
).get()
|
||||
}
|
||||
}
|
||||
}
|
||||
repeat(5) {
|
||||
@ -173,7 +175,7 @@ class RPCStabilityTests {
|
||||
}
|
||||
}
|
||||
|
||||
interface LeakObservableOps: RPCOps {
|
||||
interface LeakObservableOps : RPCOps {
|
||||
fun leakObservable(): Observable<Nothing>
|
||||
}
|
||||
|
||||
@ -249,6 +251,7 @@ class RPCStabilityTests {
|
||||
val trackSubscriberCountObservable = UnicastSubject.create<Unit>().share().
|
||||
doOnSubscribe { subscriberCount.incrementAndGet() }.
|
||||
doOnUnsubscribe { subscriberCount.decrementAndGet() }
|
||||
|
||||
override fun subscribe(): Observable<Unit> {
|
||||
return trackSubscriberCountObservable
|
||||
}
|
||||
@ -261,7 +264,7 @@ class RPCStabilityTests {
|
||||
).get()
|
||||
|
||||
val numberOfClients = 4
|
||||
val clients = (1 .. numberOfClients).map {
|
||||
val clients = (1..numberOfClients).map {
|
||||
startRandomRpcClient<TrackSubscriberOps>(server.broker.hostAndPort!!)
|
||||
}.transpose().get()
|
||||
|
||||
@ -272,7 +275,7 @@ class RPCStabilityTests {
|
||||
clients[0].destroyForcibly()
|
||||
pollUntilClientNumber(server, numberOfClients - 1)
|
||||
// Kill the rest
|
||||
(1 .. numberOfClients - 1).forEach {
|
||||
(1..numberOfClients - 1).forEach {
|
||||
clients[it].destroyForcibly()
|
||||
}
|
||||
pollUntilClientNumber(server, 0)
|
||||
@ -284,6 +287,7 @@ class RPCStabilityTests {
|
||||
interface SlowConsumerRPCOps : RPCOps {
|
||||
fun streamAtInterval(interval: Duration, size: Int): Observable<ByteArray>
|
||||
}
|
||||
|
||||
class SlowConsumerRPCOpsImpl : SlowConsumerRPCOps {
|
||||
override val protocolVersion = 0
|
||||
|
||||
@ -292,6 +296,7 @@ class RPCStabilityTests {
|
||||
return Observable.interval(interval.toMillis(), TimeUnit.MILLISECONDS).map { chunk }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `slow consumers are kicked`() {
|
||||
rpcDriver {
|
||||
|
@ -79,6 +79,7 @@ class RPCClientProxyHandler(
|
||||
STARTED,
|
||||
FINISHED
|
||||
}
|
||||
|
||||
private val lifeCycle = LifeCycle(State.UNSTARTED)
|
||||
|
||||
private companion object {
|
||||
|
@ -67,7 +67,7 @@ public class StandaloneCordaRPCJavaClientTest {
|
||||
try {
|
||||
connection.close();
|
||||
} finally {
|
||||
if(notary != null) {
|
||||
if (notary != null) {
|
||||
notary.close();
|
||||
}
|
||||
}
|
||||
|
@ -114,14 +114,14 @@ class StandaloneCordaRPClientTest {
|
||||
@Test
|
||||
fun `test starting flow`() {
|
||||
rpcProxy.startFlow(::CashIssueFlow, 127.POUNDS, OpaqueBytes.of(0), notaryNodeIdentity)
|
||||
.returnValue.getOrThrow(timeout)
|
||||
.returnValue.getOrThrow(timeout)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test starting tracked flow`() {
|
||||
var trackCount = 0
|
||||
val handle = rpcProxy.startTrackedFlow(
|
||||
::CashIssueFlow, 429.DOLLARS, OpaqueBytes.of(0), notaryNodeIdentity
|
||||
::CashIssueFlow, 429.DOLLARS, OpaqueBytes.of(0), notaryNodeIdentity
|
||||
)
|
||||
val updateLatch = CountDownLatch(1)
|
||||
handle.progress.subscribe { msg ->
|
||||
@ -156,7 +156,7 @@ class StandaloneCordaRPClientTest {
|
||||
|
||||
// Now issue some cash
|
||||
rpcProxy.startFlow(::CashIssueFlow, 513.SWISS_FRANCS, OpaqueBytes.of(0), notaryNodeIdentity)
|
||||
.returnValue.getOrThrow(timeout)
|
||||
.returnValue.getOrThrow(timeout)
|
||||
updateLatch.await()
|
||||
assertEquals(1, updateCount.get())
|
||||
}
|
||||
|
@ -20,10 +20,13 @@ open class AbstractRPCTest {
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic @Parameterized.Parameters(name = "Mode = {0}")
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "Mode = {0}")
|
||||
fun defaultModes() = modes(RPCTestMode.InVm, RPCTestMode.Netty)
|
||||
|
||||
fun modes(vararg modes: RPCTestMode) = listOf(*modes).map { arrayOf(it) }
|
||||
}
|
||||
|
||||
@Parameterized.Parameter
|
||||
lateinit var mode: RPCTestMode
|
||||
|
||||
|
@ -26,9 +26,11 @@ import java.util.concurrent.TimeUnit
|
||||
@RunWith(Parameterized::class)
|
||||
class RPCPerformanceTests : AbstractRPCTest() {
|
||||
companion object {
|
||||
@JvmStatic @Parameterized.Parameters(name = "Mode = {0}")
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "Mode = {0}")
|
||||
fun modes() = modes(RPCTestMode.Netty)
|
||||
}
|
||||
|
||||
private interface TestOps : RPCOps {
|
||||
fun simpleReply(input: ByteArray, sizeOfReply: Int): ByteArray
|
||||
}
|
||||
@ -60,7 +62,7 @@ class RPCPerformanceTests : AbstractRPCTest() {
|
||||
val executor = Executors.newFixedThreadPool(4)
|
||||
val N = 10000
|
||||
val latch = CountDownLatch(N)
|
||||
for (i in 1 .. N) {
|
||||
for (i in 1..N) {
|
||||
executor.submit {
|
||||
proxy.ops.simpleReply(ByteArray(1024), 1024)
|
||||
latch.countDown()
|
||||
@ -155,10 +157,12 @@ class RPCPerformanceTests : AbstractRPCTest() {
|
||||
data class BigMessagesResult(
|
||||
val Mbps: Double
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `big messages`() {
|
||||
warmup()
|
||||
measure(listOf(1)) { clientParallelism -> // TODO this hangs with more parallelism
|
||||
measure(listOf(1)) { clientParallelism ->
|
||||
// TODO this hangs with more parallelism
|
||||
rpcDriver {
|
||||
val proxy = testProxy(
|
||||
RPCClientConfiguration.default,
|
||||
|
@ -79,7 +79,7 @@ class RPCPermissionsTests : AbstractRPCTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check ALL is implemented the correct way round` () {
|
||||
fun `check ALL is implemented the correct way round`() {
|
||||
rpcDriver {
|
||||
val joeUser = userOf("joe", setOf(DUMMY_FLOW))
|
||||
val proxy = testProxyFor(joeUser)
|
||||
|
@ -13,6 +13,7 @@ class RepeatingBytesInputStream(val bytesToRepeat: ByteArray, val numberOfBytes:
|
||||
return bytesToRepeat[(numberOfBytes - bytesLeft) % bytesToRepeat.size].toInt()
|
||||
}
|
||||
}
|
||||
|
||||
override fun read(byteArray: ByteArray, offset: Int, length: Int): Int {
|
||||
val lastIdx = Math.min(Math.min(offset + length, byteArray.size), offset + bytesLeft)
|
||||
for (i in offset until lastIdx) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:JvmName("ConcurrencyUtils")
|
||||
|
||||
package net.corda.core.concurrent
|
||||
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
|
@ -167,7 +167,7 @@ data class Amount<T : Any>(val quantity: Long, val displayTokenSize: BigDecimal,
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e: Exception) {
|
||||
} catch (e: Exception) {
|
||||
throw IllegalArgumentException("Could not parse $input as a currency", e)
|
||||
}
|
||||
throw IllegalArgumentException("Did not recognise the currency in $input or could not parse")
|
||||
|
@ -34,7 +34,7 @@ inline fun <R> requireThat(body: Requirements.() -> R) = Requirements.body()
|
||||
|
||||
/** Filters the command list by type, party and public key all at once. */
|
||||
inline fun <reified T : CommandData> Collection<CommandWithParties<CommandData>>.select(signer: PublicKey? = null,
|
||||
party: AbstractParty? = null) =
|
||||
party: AbstractParty? = null) =
|
||||
filter { it.value is T }.
|
||||
filter { if (signer == null) true else signer in it.signers }.
|
||||
filter { if (party == null) true else party in it.signingParties }.
|
||||
@ -44,7 +44,7 @@ inline fun <reified T : CommandData> Collection<CommandWithParties<CommandData>>
|
||||
|
||||
/** Filters the command list by type, parties and public keys all at once. */
|
||||
inline fun <reified T : CommandData> Collection<CommandWithParties<CommandData>>.select(signers: Collection<PublicKey>?,
|
||||
parties: Collection<Party>?) =
|
||||
parties: Collection<Party>?) =
|
||||
filter { it.value is T }.
|
||||
filter { if (signers == null) true else it.signers.containsAll(signers) }.
|
||||
filter { if (parties == null) true else it.signingParties.containsAll(parties) }.
|
||||
|
@ -85,6 +85,7 @@ abstract class TimeWindow {
|
||||
init {
|
||||
require(fromTime < untilTime) { "fromTime must be earlier than untilTime" }
|
||||
}
|
||||
|
||||
override val midpoint: Instant get() = fromTime + (fromTime until untilTime) / 2
|
||||
override fun contains(instant: Instant): Boolean = instant >= fromTime && instant < untilTime
|
||||
override fun toString(): String = "[$fromTime, $untilTime)"
|
||||
|
@ -31,6 +31,8 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
|
||||
object CordaObjectIdentifier {
|
||||
// UUID-based OID
|
||||
// TODO: Register for an OID space and issue our own shorter OID.
|
||||
@JvmField val COMPOSITE_KEY = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791002")
|
||||
@JvmField val COMPOSITE_SIGNATURE = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791003")
|
||||
@JvmField
|
||||
val COMPOSITE_KEY = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791002")
|
||||
@JvmField
|
||||
val COMPOSITE_SIGNATURE = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791003")
|
||||
}
|
||||
|
@ -774,9 +774,10 @@ object Crypto {
|
||||
// it forms, by itself, the new private key, which in turn is used to compute the new public key.
|
||||
val pointQ = FixedPointCombMultiplier().multiply(parameterSpec.g, deterministicD)
|
||||
// This is unlikely to happen, but we should check for point at infinity.
|
||||
if (pointQ.isInfinity)
|
||||
if (pointQ.isInfinity) {
|
||||
// Instead of throwing an exception, we retry with SHA256(seed).
|
||||
return deriveKeyPairECDSA(parameterSpec, privateKey, seed.sha256().bytes)
|
||||
}
|
||||
val publicKeySpec = ECPublicKeySpec(pointQ, parameterSpec)
|
||||
val publicKeyD = BCECPublicKey(privateKey.algorithm, publicKeySpec, BouncyCastleProvider.CONFIGURATION)
|
||||
|
||||
@ -849,6 +850,7 @@ object Crypto {
|
||||
override fun generatePublic(keyInfo: SubjectPublicKeyInfo?): PublicKey? {
|
||||
return keyInfo?.let { decodePublicKey(signatureScheme, it.encoded) }
|
||||
}
|
||||
|
||||
override fun generatePrivate(keyInfo: PrivateKeyInfo?): PrivateKey? {
|
||||
return keyInfo?.let { decodePrivateKey(signatureScheme, it.encoded) }
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ fun PrivateKey.sign(bytesToSign: ByteArray, publicKey: PublicKey) = DigitalSigna
|
||||
*/
|
||||
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class)
|
||||
fun KeyPair.sign(bytesToSign: ByteArray) = private.sign(bytesToSign, public)
|
||||
|
||||
fun KeyPair.sign(bytesToSign: OpaqueBytes) = sign(bytesToSign.bytes)
|
||||
/**
|
||||
* Helper function for signing a [SignableData] object.
|
||||
@ -72,18 +73,18 @@ fun PublicKey.verify(content: ByteArray, signature: DigitalSignature) = Crypto.d
|
||||
* @return whether the signature is correct for this key.
|
||||
*/
|
||||
@Throws(IllegalStateException::class, SignatureException::class, IllegalArgumentException::class)
|
||||
fun PublicKey.isValid(content: ByteArray, signature: DigitalSignature) : Boolean {
|
||||
fun PublicKey.isValid(content: ByteArray, signature: DigitalSignature): Boolean {
|
||||
if (this is CompositeKey)
|
||||
throw IllegalStateException("Verification of CompositeKey signatures currently not supported.") // TODO CompositeSignature verification.
|
||||
return Crypto.isValid(this, signature.bytes, content)
|
||||
}
|
||||
|
||||
/** Render a public key to its hash (in Base58) of its serialised form using the DL prefix. */
|
||||
fun PublicKey.toStringShort(): String = "DL" + this.toSHA256Bytes().toBase58()
|
||||
fun PublicKey.toStringShort(): String = "DL" + this.toSHA256Bytes().toBase58()
|
||||
|
||||
val PublicKey.keys: Set<PublicKey> get() = (this as? CompositeKey)?.leafKeys ?: setOf(this)
|
||||
|
||||
fun PublicKey.isFulfilledBy(otherKey: PublicKey): Boolean = isFulfilledBy(setOf(otherKey))
|
||||
fun PublicKey.isFulfilledBy(otherKey: PublicKey): Boolean = isFulfilledBy(setOf(otherKey))
|
||||
fun PublicKey.isFulfilledBy(otherKeys: Iterable<PublicKey>): Boolean = (this as? CompositeKey)?.isFulfilledBy(otherKeys) ?: (this in otherKeys)
|
||||
|
||||
/** Checks whether any of the given [keys] matches a leaf on the [CompositeKey] tree or a single [PublicKey]. */
|
||||
|
@ -23,6 +23,7 @@ open class DigitalSignature(bytes: ByteArray) : OpaqueBytes(bytes) {
|
||||
*/
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun verify(content: ByteArray) = by.verify(content, this)
|
||||
|
||||
/**
|
||||
* Utility to simplify the act of verifying a signature.
|
||||
*
|
||||
@ -32,6 +33,7 @@ open class DigitalSignature(bytes: ByteArray) : OpaqueBytes(bytes) {
|
||||
*/
|
||||
@Throws(InvalidKeyException::class, SignatureException::class)
|
||||
fun verify(content: OpaqueBytes) = by.verify(content.bytes, this)
|
||||
|
||||
/**
|
||||
* Utility to simplify the act of verifying a signature. In comparison to [verify] doesn't throw an
|
||||
* exception, making it more suitable where a boolean is required, but normally you should use the function
|
||||
|
@ -34,11 +34,18 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic fun sha256(bytes: ByteArray) = SHA256(MessageDigest.getInstance("SHA-256").digest(bytes))
|
||||
@JvmStatic fun sha256Twice(bytes: ByteArray) = sha256(sha256(bytes).bytes)
|
||||
@JvmStatic fun sha256(str: String) = sha256(str.toByteArray())
|
||||
@JvmStatic
|
||||
fun sha256(bytes: ByteArray) = SHA256(MessageDigest.getInstance("SHA-256").digest(bytes))
|
||||
|
||||
@JvmStatic
|
||||
fun sha256Twice(bytes: ByteArray) = sha256(sha256(bytes).bytes)
|
||||
|
||||
@JvmStatic
|
||||
fun sha256(str: String) = sha256(str.toByteArray())
|
||||
|
||||
@JvmStatic
|
||||
fun randomSHA256() = sha256(newSecureRandom().generateSeed(32))
|
||||
|
||||
@JvmStatic fun randomSHA256() = sha256(newSecureRandom().generateSeed(32))
|
||||
val zeroHash = SecureHash.SHA256(ByteArray(32, { 0.toByte() }))
|
||||
val allOnesHash = SecureHash.SHA256(ByteArray(32, { 255.toByte() }))
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import java.util.*
|
||||
* This is similar to [DigitalSignature.WithKey], but targeted to DLT transaction signatures.
|
||||
*/
|
||||
@CordaSerializable
|
||||
class TransactionSignature(bytes: ByteArray, val by: PublicKey, val signatureMetadata: SignatureMetadata): DigitalSignature(bytes) {
|
||||
class TransactionSignature(bytes: ByteArray, val by: PublicKey, val signatureMetadata: SignatureMetadata) : DigitalSignature(bytes) {
|
||||
/**
|
||||
* Function to verify a [SignableData] object's signature.
|
||||
* Note that [SignableData] contains the id of the transaction and extra metadata, such as DLT's platform version.
|
||||
|
@ -136,7 +136,8 @@ abstract class AbstractStateReplacementFlow {
|
||||
// We use Void? instead of Unit? as that's what you'd use in Java.
|
||||
abstract class Acceptor<in T>(val initiatingSession: FlowSession,
|
||||
override val progressTracker: ProgressTracker = Acceptor.tracker()) : FlowLogic<Void?>() {
|
||||
constructor(initiatingSession: FlowSession) : this(initiatingSession, Acceptor.tracker())
|
||||
constructor(initiatingSession: FlowSession) : this(initiatingSession, Acceptor.tracker())
|
||||
|
||||
companion object {
|
||||
object VERIFYING : ProgressTracker.Step("Verifying state replacement proposal")
|
||||
object APPROVING : ProgressTracker.Step("State replacement approved")
|
||||
|
@ -61,11 +61,12 @@ import java.security.PublicKey
|
||||
* just in the states. If null, the default well known identity of the node is used.
|
||||
*/
|
||||
// TODO: AbstractStateReplacementFlow needs updating to use this flow.
|
||||
class CollectSignaturesFlow @JvmOverloads constructor (val partiallySignedTx: SignedTransaction,
|
||||
val sessionsToCollectFrom: Collection<FlowSession>,
|
||||
val myOptionalKeys: Iterable<PublicKey>?,
|
||||
override val progressTracker: ProgressTracker = CollectSignaturesFlow.tracker()) : FlowLogic<SignedTransaction>() {
|
||||
class CollectSignaturesFlow @JvmOverloads constructor(val partiallySignedTx: SignedTransaction,
|
||||
val sessionsToCollectFrom: Collection<FlowSession>,
|
||||
val myOptionalKeys: Iterable<PublicKey>?,
|
||||
override val progressTracker: ProgressTracker = CollectSignaturesFlow.tracker()) : FlowLogic<SignedTransaction>() {
|
||||
@JvmOverloads constructor(partiallySignedTx: SignedTransaction, sessionsToCollectFrom: Collection<FlowSession>, progressTracker: ProgressTracker = CollectSignaturesFlow.tracker()) : this(partiallySignedTx, sessionsToCollectFrom, null, progressTracker)
|
||||
|
||||
companion object {
|
||||
object COLLECTING : ProgressTracker.Step("Collecting signatures from counter-parties.")
|
||||
object VERIFYING : ProgressTracker.Step("Verifying collected signatures.")
|
||||
@ -134,6 +135,7 @@ class CollectSignaturesFlow @JvmOverloads constructor (val partiallySignedTx: Si
|
||||
class CollectSignatureFlow(val partiallySignedTx: SignedTransaction, val session: FlowSession, val signingKeys: List<PublicKey>) : FlowLogic<List<TransactionSignature>>() {
|
||||
constructor(partiallySignedTx: SignedTransaction, session: FlowSession, vararg signingKeys: PublicKey) :
|
||||
this(partiallySignedTx, session, listOf(*signingKeys))
|
||||
|
||||
@Suspendable
|
||||
override fun call(): List<TransactionSignature> {
|
||||
// SendTransactionFlow allows counterparty to access our data to resolve the transaction.
|
||||
@ -224,7 +226,7 @@ abstract class SignTransactionFlow(val otherSideSession: FlowSession,
|
||||
// Perform some custom verification over the transaction.
|
||||
try {
|
||||
checkTransaction(stx)
|
||||
} catch(e: Exception) {
|
||||
} catch (e: Exception) {
|
||||
if (e is IllegalStateException || e is IllegalArgumentException || e is AssertionError)
|
||||
throw FlowException(e)
|
||||
else
|
||||
|
@ -28,7 +28,7 @@ object ContractUpgradeFlow {
|
||||
val stateAndRef: StateAndRef<*>,
|
||||
private val upgradedContractClass: Class<out UpgradedContract<*, *>>
|
||||
) : FlowLogic<Void?>() {
|
||||
// DOCEND 1
|
||||
// DOCEND 1
|
||||
@Suspendable
|
||||
override fun call(): Void? {
|
||||
val upgrade = upgradedContractClass.newInstance()
|
||||
@ -50,7 +50,7 @@ object ContractUpgradeFlow {
|
||||
class Deauthorise(val stateRef: StateRef) : FlowLogic<Void?>() {
|
||||
@Suspendable
|
||||
override fun call(): Void? {
|
||||
//DOCEND 2
|
||||
//DOCEND 2
|
||||
serviceHub.contractUpgradeService.removeAuthorisedContractUpgrade(stateRef)
|
||||
return null
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ import net.corda.core.utilities.ProgressTracker
|
||||
*/
|
||||
@InitiatingFlow
|
||||
class FinalityFlow(val transaction: SignedTransaction,
|
||||
private val extraRecipients: Set<Party>,
|
||||
override val progressTracker: ProgressTracker) : FlowLogic<SignedTransaction>() {
|
||||
private val extraRecipients: Set<Party>,
|
||||
override val progressTracker: ProgressTracker) : FlowLogic<SignedTransaction>() {
|
||||
constructor(transaction: SignedTransaction, extraParticipants: Set<Party>) : this(transaction, extraParticipants, tracker())
|
||||
constructor(transaction: SignedTransaction) : this(transaction, emptySet(), tracker())
|
||||
constructor(transaction: SignedTransaction, progressTracker: ProgressTracker) : this(transaction, emptySet(), progressTracker)
|
||||
@ -88,7 +88,7 @@ class FinalityFlow(val transaction: SignedTransaction,
|
||||
private fun hasNoNotarySignature(stx: SignedTransaction): Boolean {
|
||||
val notaryKey = stx.tx.notary?.owningKey
|
||||
val signers = stx.sigs.map { it.by }.toSet()
|
||||
return !(notaryKey?.isFulfilledBy(signers) ?: false)
|
||||
return notaryKey?.isFulfilledBy(signers) != true
|
||||
}
|
||||
|
||||
private fun getPartiesToSend(ltx: LedgerTransaction): Set<Party> {
|
||||
|
@ -16,18 +16,22 @@ sealed class FlowInitiator : Principal {
|
||||
data class RPC(val username: String) : FlowInitiator() {
|
||||
override fun getName(): String = username
|
||||
}
|
||||
|
||||
/** Started when we get new session initiation request. */
|
||||
data class Peer(val party: Party) : FlowInitiator() {
|
||||
override fun getName(): String = party.name.toString()
|
||||
}
|
||||
|
||||
/** Started by a CordaService. */
|
||||
data class Service(val serviceClassName: String) : FlowInitiator() {
|
||||
override fun getName(): String = serviceClassName
|
||||
}
|
||||
|
||||
/** Started as scheduled activity. */
|
||||
data class Scheduled(val scheduledState: ScheduledStateRef) : FlowInitiator() {
|
||||
override fun getName(): String = "Scheduler"
|
||||
}
|
||||
|
||||
// TODO When proper ssh access enabled, add username/use RPC?
|
||||
object Shell : FlowInitiator() {
|
||||
override fun getName(): String = "Shell User"
|
||||
|
@ -75,6 +75,7 @@ abstract class FlowSession {
|
||||
inline fun <reified R : Any> sendAndReceive(payload: Any): UntrustworthyData<R> {
|
||||
return sendAndReceive(R::class.java, payload)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes and queues the given [payload] object for sending to the [counterparty]. Suspends until a response
|
||||
* is received, which must be of the given [receiveType]. Remember that when receiving data from other parties the data
|
||||
@ -100,6 +101,7 @@ abstract class FlowSession {
|
||||
inline fun <reified R : Any> receive(): UntrustworthyData<R> {
|
||||
return receive(R::class.java)
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspends until [counterparty] sends us a message of type [receiveType].
|
||||
*
|
||||
|
@ -173,5 +173,5 @@ sealed class NotaryError {
|
||||
override fun toString() = cause.toString()
|
||||
}
|
||||
|
||||
object WrongNotary: NotaryError()
|
||||
object WrongNotary : NotaryError()
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import java.security.SignatureException
|
||||
* This flow is a combination of [FlowSession.receive], resolve and [SignedTransaction.verify]. This flow will receive the
|
||||
* [SignedTransaction] and perform the resolution back-and-forth required to check the dependencies and download any missing
|
||||
* attachments. The flow will return the [SignedTransaction] after it is resolved and then verified using [SignedTransaction.verify].
|
||||
*
|
||||
*
|
||||
* @param otherSideSession session to the other side which is calling [SendTransactionFlow].
|
||||
* @param checkSufficientSignatures if true checks all required signatures are present. See [SignedTransaction.verify].
|
||||
*/
|
||||
|
@ -13,6 +13,7 @@ import java.security.PublicKey
|
||||
abstract class AbstractParty(val owningKey: PublicKey) {
|
||||
/** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */
|
||||
override fun equals(other: Any?): Boolean = other === this || other is AbstractParty && other.owningKey == owningKey
|
||||
|
||||
override fun hashCode(): Int = owningKey.hashCode()
|
||||
abstract fun nameOrNull(): CordaX500Name?
|
||||
|
||||
|
@ -81,7 +81,7 @@ data class CordaX500Name(val commonName: String?,
|
||||
private val countryCodes: Set<String> = ImmutableSet.copyOf(Locale.getISOCountries())
|
||||
|
||||
@JvmStatic
|
||||
fun build(principal: X500Principal) : CordaX500Name {
|
||||
fun build(principal: X500Principal): CordaX500Name {
|
||||
val x500Name = X500Name.getInstance(principal.encoded)
|
||||
val attrsMap: Map<ASN1ObjectIdentifier, ASN1Encodable> = x500Name.rdNs
|
||||
.flatMap { it.typesAndValues.asList() }
|
||||
@ -109,7 +109,7 @@ data class CordaX500Name(val commonName: String?,
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parse(name: String) : CordaX500Name = build(X500Principal(name))
|
||||
fun parse(name: String): CordaX500Name = build(X500Principal(name))
|
||||
}
|
||||
|
||||
@Transient
|
||||
|
@ -29,6 +29,7 @@ import java.security.cert.X509Certificate
|
||||
class Party(val name: CordaX500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
|
||||
constructor(certificate: X509Certificate)
|
||||
: this(CordaX500Name.build(certificate.subjectX500Principal), Crypto.toSupportedPublicKey(certificate.publicKey))
|
||||
|
||||
override fun nameOrNull(): CordaX500Name = name
|
||||
fun anonymise(): AnonymousParty = AnonymousParty(owningKey)
|
||||
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
||||
|
@ -11,7 +11,9 @@ import java.security.cert.*
|
||||
*/
|
||||
@CordaSerializable
|
||||
class PartyAndCertificate(val certPath: CertPath) {
|
||||
@Transient val certificate: X509Certificate
|
||||
@Transient
|
||||
val certificate: X509Certificate
|
||||
|
||||
init {
|
||||
require(certPath.type == "X.509") { "Only X.509 certificates supported" }
|
||||
val certs = certPath.certificates
|
||||
@ -19,7 +21,8 @@ class PartyAndCertificate(val certPath: CertPath) {
|
||||
certificate = certs[0] as X509Certificate
|
||||
}
|
||||
|
||||
@Transient val party: Party = Party(certificate)
|
||||
@Transient
|
||||
val party: Party = Party(certificate)
|
||||
|
||||
val owningKey: PublicKey get() = party.owningKey
|
||||
val name: CordaX500Name get() = party.name
|
||||
|
@ -13,22 +13,38 @@ object Emoji {
|
||||
(System.getenv("TERM_PROGRAM") == "JediTerm" && System.getProperty("java.vendor") == "JetBrains s.r.o")
|
||||
}
|
||||
|
||||
@JvmStatic val CODE_SANTA_CLAUS: String = codePointsString(0x1F385)
|
||||
@JvmStatic val CODE_DIAMOND: String = codePointsString(0x1F537)
|
||||
@JvmStatic val CODE_BAG_OF_CASH: String = codePointsString(0x1F4B0)
|
||||
@JvmStatic val CODE_NEWSPAPER: String = codePointsString(0x1F4F0)
|
||||
@JvmStatic val CODE_RIGHT_ARROW: String = codePointsString(0x27A1, 0xFE0F)
|
||||
@JvmStatic val CODE_LEFT_ARROW: String = codePointsString(0x2B05, 0xFE0F)
|
||||
@JvmStatic val CODE_GREEN_TICK: String = codePointsString(0x2705)
|
||||
@JvmStatic val CODE_PAPERCLIP: String = codePointsString(0x1F4CE)
|
||||
@JvmStatic val CODE_COOL_GUY: String = codePointsString(0x1F60E)
|
||||
@JvmStatic val CODE_NO_ENTRY: String = codePointsString(0x1F6AB)
|
||||
@JvmStatic val CODE_SKULL_AND_CROSSBONES: String = codePointsString(0x2620)
|
||||
@JvmStatic val CODE_BOOKS: String = codePointsString(0x1F4DA)
|
||||
@JvmStatic val CODE_SLEEPING_FACE: String = codePointsString(0x1F634)
|
||||
@JvmStatic val CODE_LIGHTBULB: String = codePointsString(0x1F4A1)
|
||||
@JvmStatic val CODE_FREE: String = codePointsString(0x1F193)
|
||||
@JvmStatic val CODE_SOON: String = codePointsString(0x1F51C)
|
||||
@JvmStatic
|
||||
val CODE_SANTA_CLAUS: String = codePointsString(0x1F385)
|
||||
@JvmStatic
|
||||
val CODE_DIAMOND: String = codePointsString(0x1F537)
|
||||
@JvmStatic
|
||||
val CODE_BAG_OF_CASH: String = codePointsString(0x1F4B0)
|
||||
@JvmStatic
|
||||
val CODE_NEWSPAPER: String = codePointsString(0x1F4F0)
|
||||
@JvmStatic
|
||||
val CODE_RIGHT_ARROW: String = codePointsString(0x27A1, 0xFE0F)
|
||||
@JvmStatic
|
||||
val CODE_LEFT_ARROW: String = codePointsString(0x2B05, 0xFE0F)
|
||||
@JvmStatic
|
||||
val CODE_GREEN_TICK: String = codePointsString(0x2705)
|
||||
@JvmStatic
|
||||
val CODE_PAPERCLIP: String = codePointsString(0x1F4CE)
|
||||
@JvmStatic
|
||||
val CODE_COOL_GUY: String = codePointsString(0x1F60E)
|
||||
@JvmStatic
|
||||
val CODE_NO_ENTRY: String = codePointsString(0x1F6AB)
|
||||
@JvmStatic
|
||||
val CODE_SKULL_AND_CROSSBONES: String = codePointsString(0x2620)
|
||||
@JvmStatic
|
||||
val CODE_BOOKS: String = codePointsString(0x1F4DA)
|
||||
@JvmStatic
|
||||
val CODE_SLEEPING_FACE: String = codePointsString(0x1F634)
|
||||
@JvmStatic
|
||||
val CODE_LIGHTBULB: String = codePointsString(0x1F4A1)
|
||||
@JvmStatic
|
||||
val CODE_FREE: String = codePointsString(0x1F193)
|
||||
@JvmStatic
|
||||
val CODE_SOON: String = codePointsString(0x1F51C)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -49,6 +49,7 @@ operator fun Duration.times(multiplicand: Long): Duration = multipliedBy(multipl
|
||||
* separator problems.
|
||||
*/
|
||||
operator fun Path.div(other: String): Path = resolve(other)
|
||||
|
||||
operator fun String.div(other: String): Path = Paths.get(this) / other
|
||||
|
||||
/**
|
||||
@ -104,6 +105,7 @@ fun Path.copyToDirectory(targetDir: Path, vararg options: CopyOption): Path {
|
||||
Files.copy(this, targetFile, *options)
|
||||
return targetFile
|
||||
}
|
||||
|
||||
fun Path.moveTo(target: Path, vararg options: CopyOption): Path = Files.move(this, target, *options)
|
||||
fun Path.isRegularFile(vararg options: LinkOption): Boolean = Files.isRegularFile(this, *options)
|
||||
fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(this, *options)
|
||||
@ -238,10 +240,13 @@ fun <T> Class<T>.castIfPossible(obj: Any): T? = if (isInstance(obj)) cast(obj) e
|
||||
|
||||
/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [Class]. */
|
||||
fun <T> Class<*>.staticField(name: String): DeclaredField<T> = DeclaredField(this, name, null)
|
||||
|
||||
/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [KClass]. */
|
||||
fun <T> KClass<*>.staticField(name: String): DeclaredField<T> = DeclaredField(java, name, null)
|
||||
|
||||
/** @suppress Returns a [DeclaredField] wrapper around the declared (possibly non-public) instance field of the receiver object. */
|
||||
fun <T> Any.declaredField(name: String): DeclaredField<T> = DeclaredField(javaClass, name, this)
|
||||
|
||||
/**
|
||||
* Returns a [DeclaredField] wrapper around the (possibly non-public) instance field of the receiver object, but declared
|
||||
* in its superclass [clazz].
|
||||
@ -250,7 +255,7 @@ fun <T> Any.declaredField(name: String): DeclaredField<T> = DeclaredField(javaCl
|
||||
fun <T> Any.declaredField(clazz: KClass<*>, name: String): DeclaredField<T> = DeclaredField(clazz.java, name, this)
|
||||
|
||||
/** creates a new instance if not a Kotlin object */
|
||||
fun <T: Any> KClass<T>.objectOrNewInstance(): T {
|
||||
fun <T : Any> KClass<T>.objectOrNewInstance(): T {
|
||||
return this.objectInstance ?: this.createInstance()
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ class LazyPool<A>(
|
||||
STARTED,
|
||||
FINISHED
|
||||
}
|
||||
|
||||
private val lifeCycle = LifeCycle(State.STARTED)
|
||||
|
||||
private fun clearIfNeeded(instance: A): A {
|
||||
|
@ -18,6 +18,7 @@ class LazyStickyPool<A : Any>(
|
||||
private class InstanceBox<A> {
|
||||
var instance: LinkedBlockingQueue<A>? = null
|
||||
}
|
||||
|
||||
private val random = Random()
|
||||
private val boxes = Array(size) { InstanceBox<A>() }
|
||||
|
||||
|
@ -28,6 +28,7 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
|
||||
constructor(signedTransaction: SignedTransaction, otherSide: FlowSession) : this(dependencyIDs(signedTransaction), otherSide) {
|
||||
this.signedTransaction = signedTransaction
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun dependencyIDs(stx: SignedTransaction) = stx.inputs.map { it.txhash }.toSet()
|
||||
|
||||
|
@ -6,7 +6,7 @@ import kotlin.reflect.KProperty
|
||||
* A write-once property to be used as delegate for Kotlin var properties. The expectation is that this is initialised
|
||||
* prior to the spawning of any threads that may access it and so there's no need for it to be volatile.
|
||||
*/
|
||||
class WriteOnceProperty<T : Any>(private val defaultValue:T? = null) {
|
||||
class WriteOnceProperty<T : Any>(private val defaultValue: T? = null) {
|
||||
private var v: T? = defaultValue
|
||||
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>) = v ?: throw IllegalStateException("Write-once property $property not set.")
|
||||
|
@ -46,6 +46,7 @@ class X509EdDSAEngine : Signature {
|
||||
override fun engineSetParameter(params: AlgorithmParameterSpec) = engine.setParameter(params)
|
||||
@Suppress("DEPRECATION")
|
||||
override fun engineGetParameter(param: String): Any = engine.getParameter(param)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun engineSetParameter(param: String, value: Any?) = engine.setParameter(param, value)
|
||||
}
|
||||
|
@ -104,12 +104,15 @@ interface CordaRPCOps : RPCOps {
|
||||
fun <T : ContractState> vaultQuery(contractStateType: Class<out T>): Vault.Page<T> {
|
||||
return vaultQueryBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType)
|
||||
}
|
||||
|
||||
fun <T : ContractState> vaultQueryByCriteria(criteria: QueryCriteria, contractStateType: Class<out T>): Vault.Page<T> {
|
||||
return vaultQueryBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType)
|
||||
}
|
||||
|
||||
fun <T : ContractState> vaultQueryByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): Vault.Page<T> {
|
||||
return vaultQueryBy(criteria, paging, Sort(emptySet()), contractStateType)
|
||||
}
|
||||
|
||||
fun <T : ContractState> vaultQueryByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): Vault.Page<T> {
|
||||
return vaultQueryBy(criteria, PageSpecification(), sorting, contractStateType)
|
||||
}
|
||||
@ -142,12 +145,15 @@ interface CordaRPCOps : RPCOps {
|
||||
fun <T : ContractState> vaultTrack(contractStateType: Class<out T>): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
||||
return vaultTrackBy(QueryCriteria.VaultQueryCriteria(), PageSpecification(), Sort(emptySet()), contractStateType)
|
||||
}
|
||||
|
||||
fun <T : ContractState> vaultTrackByCriteria(contractStateType: Class<out T>, criteria: QueryCriteria): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
||||
return vaultTrackBy(criteria, PageSpecification(), Sort(emptySet()), contractStateType)
|
||||
}
|
||||
|
||||
fun <T : ContractState> vaultTrackByWithPagingSpec(contractStateType: Class<out T>, criteria: QueryCriteria, paging: PageSpecification): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
||||
return vaultTrackBy(criteria, paging, Sort(emptySet()), contractStateType)
|
||||
}
|
||||
|
||||
fun <T : ContractState> vaultTrackByWithSorting(contractStateType: Class<out T>, criteria: QueryCriteria, sorting: Sort): DataFeed<Vault.Page<T>, Vault.Update<T>> {
|
||||
return vaultTrackBy(criteria, PageSpecification(), sorting, contractStateType)
|
||||
}
|
||||
@ -250,6 +256,7 @@ interface CordaRPCOps : RPCOps {
|
||||
* @return well known identity, if found.
|
||||
*/
|
||||
fun wellKnownPartyFromAnonymous(party: AbstractParty): Party?
|
||||
|
||||
/** Returns the [Party] corresponding to the given key, if found. */
|
||||
fun partyFromKey(key: PublicKey): Party?
|
||||
|
||||
@ -310,7 +317,7 @@ inline fun <T, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
|
||||
flowConstructor: () -> R
|
||||
): FlowHandle<T> = startFlowDynamic(R::class.java)
|
||||
|
||||
inline fun <T , A, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
|
||||
inline fun <T, A, reified R : FlowLogic<T>> CordaRPCOps.startFlow(
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
flowConstructor: (A) -> R,
|
||||
arg0: A
|
||||
|
@ -43,7 +43,7 @@ data class FlowHandleImpl<A>(
|
||||
override val id: StateMachineRunId,
|
||||
override val returnValue: CordaFuture<A>) : FlowHandle<A> {
|
||||
|
||||
// Remember to add @Throws to FlowHandle.close() if this throws an exception.
|
||||
// Remember to add @Throws to FlowHandle.close() if this throws an exception.
|
||||
override fun close() {
|
||||
returnValue.cancel(false)
|
||||
}
|
||||
|
@ -35,9 +35,10 @@ data class NodeInfo(val addresses: List<NetworkHostAndPort>,
|
||||
* are porting code from earlier versions of Corda that expected a single party per node, just use the first item
|
||||
* in the returned list.
|
||||
*/
|
||||
val legalIdentities: List<Party> get() {
|
||||
return _legalIdentities ?: legalIdentitiesAndCerts.map { it.party }.also { _legalIdentities = it }
|
||||
}
|
||||
val legalIdentities: List<Party>
|
||||
get() {
|
||||
return _legalIdentities ?: legalIdentitiesAndCerts.map { it.party }.also { _legalIdentities = it }
|
||||
}
|
||||
|
||||
/** Returns true if [party] is one of the identities of this node, else false. */
|
||||
fun isLegalIdentity(party: Party): Boolean = party in legalIdentities
|
||||
|
@ -8,6 +8,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
*/
|
||||
sealed class PartyInfo {
|
||||
abstract val party: Party
|
||||
data class SingleNode(override val party: Party, val addresses: List<NetworkHostAndPort>): PartyInfo()
|
||||
data class DistributedNode(override val party: Party): PartyInfo()
|
||||
|
||||
data class SingleNode(override val party: Party, val addresses: List<NetworkHostAndPort>) : PartyInfo()
|
||||
data class DistributedNode(override val party: Party) : PartyInfo()
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
|
||||
|
||||
companion object {
|
||||
val NoUpdate = Update(emptySet(), emptySet(), type = Vault.UpdateType.GENERAL)
|
||||
val NoNotaryUpdate = Vault.Update(emptySet(), emptySet(), type = Vault.UpdateType.NOTARY_CHANGE)
|
||||
val NoNotaryUpdate = Vault.Update(emptySet(), emptySet(), type = Vault.UpdateType.NOTARY_CHANGE)
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
|
@ -49,12 +49,12 @@ sealed class QueryCriteria {
|
||||
/**
|
||||
* VaultQueryCriteria: provides query by attributes defined in [VaultSchema.VaultStates]
|
||||
*/
|
||||
data class VaultQueryCriteria @JvmOverloads constructor (override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
override val contractStateTypes: Set<Class<out ContractState>>? = null,
|
||||
val stateRefs: List<StateRef>? = null,
|
||||
val notary: List<AbstractParty>? = null,
|
||||
val softLockingCondition: SoftLockingCondition? = null,
|
||||
val timeCondition: TimeCondition? = null) : CommonQueryCriteria() {
|
||||
data class VaultQueryCriteria @JvmOverloads constructor(override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
override val contractStateTypes: Set<Class<out ContractState>>? = null,
|
||||
val stateRefs: List<StateRef>? = null,
|
||||
val notary: List<AbstractParty>? = null,
|
||||
val softLockingCondition: SoftLockingCondition? = null,
|
||||
val timeCondition: TimeCondition? = null) : CommonQueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
super.visit(parser)
|
||||
return parser.parseCriteria(this)
|
||||
@ -69,10 +69,10 @@ sealed class QueryCriteria {
|
||||
val externalId: List<String>? = null,
|
||||
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
override val contractStateTypes: Set<Class<out ContractState>>? = null) : CommonQueryCriteria() {
|
||||
constructor(participants: List<AbstractParty>? = null,
|
||||
linearId: List<UniqueIdentifier>? = null,
|
||||
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
contractStateTypes: Set<Class<out ContractState>>? = null) : this(participants, linearId?.map { it.id }, linearId?.mapNotNull { it.externalId }, status, contractStateTypes)
|
||||
constructor(participants: List<AbstractParty>? = null,
|
||||
linearId: List<UniqueIdentifier>? = null,
|
||||
status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
contractStateTypes: Set<Class<out ContractState>>? = null) : this(participants, linearId?.map { it.id }, linearId?.mapNotNull { it.externalId }, status, contractStateTypes)
|
||||
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
super.visit(parser)
|
||||
@ -80,13 +80,13 @@ sealed class QueryCriteria {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FungibleStateQueryCriteria: provides query by attributes defined in [VaultSchema.VaultFungibleState]
|
||||
*
|
||||
* Valid TokenType implementations defined by Amount<T> are
|
||||
* [Currency] as used in [Cash] contract state
|
||||
* [Commodity] as used in [CommodityContract] state
|
||||
*/
|
||||
/**
|
||||
* FungibleStateQueryCriteria: provides query by attributes defined in [VaultSchema.VaultFungibleState]
|
||||
*
|
||||
* Valid TokenType implementations defined by Amount<T> are
|
||||
* [Currency] as used in [Cash] contract state
|
||||
* [Commodity] as used in [CommodityContract] state
|
||||
*/
|
||||
data class FungibleAssetQueryCriteria @JvmOverloads constructor(val participants: List<AbstractParty>? = null,
|
||||
val owner: List<AbstractParty>? = null,
|
||||
val quantity: ColumnPredicate<Long>? = null,
|
||||
@ -94,11 +94,11 @@ sealed class QueryCriteria {
|
||||
val issuerRef: List<OpaqueBytes>? = null,
|
||||
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
override val contractStateTypes: Set<Class<out ContractState>>? = null) : CommonQueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
super.visit(parser)
|
||||
return parser.parseCriteria(this)
|
||||
}
|
||||
}
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
super.visit(parser)
|
||||
return parser.parseCriteria(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* VaultCustomQueryCriteria: provides query by custom attributes defined in a contracts
|
||||
@ -111,9 +111,9 @@ sealed class QueryCriteria {
|
||||
* Refer to [CommercialPaper.State] for a concrete example.
|
||||
*/
|
||||
data class VaultCustomQueryCriteria<L : PersistentState> @JvmOverloads constructor
|
||||
(val expression: CriteriaExpression<L, Boolean>,
|
||||
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
override val contractStateTypes: Set<Class<out ContractState>>? = null) : CommonQueryCriteria() {
|
||||
(val expression: CriteriaExpression<L, Boolean>,
|
||||
override val status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED,
|
||||
override val contractStateTypes: Set<Class<out ContractState>>? = null) : CommonQueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
super.visit(parser)
|
||||
return parser.parseCriteria(this)
|
||||
@ -121,13 +121,13 @@ sealed class QueryCriteria {
|
||||
}
|
||||
|
||||
// enable composition of [QueryCriteria]
|
||||
private data class AndComposition(val a: QueryCriteria, val b: QueryCriteria): QueryCriteria() {
|
||||
private data class AndComposition(val a: QueryCriteria, val b: QueryCriteria) : QueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
return parser.parseAnd(this.a, this.b)
|
||||
}
|
||||
}
|
||||
|
||||
private data class OrComposition(val a: QueryCriteria, val b: QueryCriteria): QueryCriteria() {
|
||||
private data class OrComposition(val a: QueryCriteria, val b: QueryCriteria) : QueryCriteria() {
|
||||
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
|
||||
return parser.parseOr(this.a, this.b)
|
||||
}
|
||||
@ -148,9 +148,9 @@ interface IQueryCriteriaParser {
|
||||
fun parseCriteria(criteria: QueryCriteria.CommonQueryCriteria): Collection<Predicate>
|
||||
fun parseCriteria(criteria: QueryCriteria.FungibleAssetQueryCriteria): Collection<Predicate>
|
||||
fun parseCriteria(criteria: QueryCriteria.LinearStateQueryCriteria): Collection<Predicate>
|
||||
fun <L: PersistentState> parseCriteria(criteria: QueryCriteria.VaultCustomQueryCriteria<L>): Collection<Predicate>
|
||||
fun <L : PersistentState> parseCriteria(criteria: QueryCriteria.VaultCustomQueryCriteria<L>): Collection<Predicate>
|
||||
fun parseCriteria(criteria: QueryCriteria.VaultQueryCriteria): Collection<Predicate>
|
||||
fun parseOr(left: QueryCriteria, right: QueryCriteria): Collection<Predicate>
|
||||
fun parseAnd(left: QueryCriteria, right: QueryCriteria): Collection<Predicate>
|
||||
fun parse(criteria: QueryCriteria, sorting: Sort? = null) : Collection<Predicate>
|
||||
fun parse(criteria: QueryCriteria, sorting: Sort? = null): Collection<Predicate>
|
||||
}
|
||||
|
@ -170,8 +170,8 @@ data class Sort(val columns: Collection<SortColumn>) {
|
||||
|
||||
@CordaSerializable
|
||||
data class SortColumn(
|
||||
val sortAttribute: SortAttribute,
|
||||
val direction: Sort.Direction = Sort.Direction.ASC)
|
||||
val sortAttribute: SortAttribute,
|
||||
val direction: Sort.Direction = Sort.Direction.ASC)
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
@ -199,8 +199,9 @@ object Builder {
|
||||
|
||||
fun <R> Field.predicate(predicate: ColumnPredicate<R>) = CriteriaExpression.ColumnPredicateExpression(Column<Any, R>(this), predicate)
|
||||
|
||||
fun <O, R> KProperty1<O, R?>.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column<O, R>>? = null, orderBy: Sort.Direction? = null)
|
||||
fun <O, R> KProperty1<O, R?>.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column<O, R>>? = null, orderBy: Sort.Direction? = null)
|
||||
= CriteriaExpression.AggregateFunctionExpression(Column(this), predicate, groupByColumns, orderBy)
|
||||
|
||||
fun <R> Field.functionPredicate(predicate: ColumnPredicate<R>, groupByColumns: List<Column<Any, R>>? = null, orderBy: Sort.Direction? = null)
|
||||
= CriteriaExpression.AggregateFunctionExpression(Column<Any, R>(this), predicate, groupByColumns, orderBy)
|
||||
|
||||
@ -217,15 +218,32 @@ object Builder {
|
||||
fun <O, R : Comparable<R>> KProperty1<O, R?>.`in`(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection))
|
||||
fun <O, R : Comparable<R>> KProperty1<O, R?>.notIn(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection))
|
||||
|
||||
@JvmStatic fun <R> Field.equal(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value))
|
||||
@JvmStatic fun <R> Field.notEqual(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value))
|
||||
@JvmStatic fun <R : Comparable<R>> Field.lessThan(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN, value)
|
||||
@JvmStatic fun <R : Comparable<R>> Field.lessThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN_OR_EQUAL, value)
|
||||
@JvmStatic fun <R : Comparable<R>> Field.greaterThan(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN, value)
|
||||
@JvmStatic fun <R : Comparable<R>> Field.greaterThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL, value)
|
||||
@JvmStatic fun <R : Comparable<R>> Field.between(from: R, to: R) = predicate(ColumnPredicate.Between(from, to))
|
||||
@JvmStatic fun <R : Comparable<R>> Field.`in`(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection))
|
||||
@JvmStatic fun <R : Comparable<R>> Field.notIn(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection))
|
||||
@JvmStatic
|
||||
fun <R> Field.equal(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value))
|
||||
|
||||
@JvmStatic
|
||||
fun <R> Field.notEqual(value: R) = predicate(ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value))
|
||||
|
||||
@JvmStatic
|
||||
fun <R : Comparable<R>> Field.lessThan(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN, value)
|
||||
|
||||
@JvmStatic
|
||||
fun <R : Comparable<R>> Field.lessThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.LESS_THAN_OR_EQUAL, value)
|
||||
|
||||
@JvmStatic
|
||||
fun <R : Comparable<R>> Field.greaterThan(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN, value)
|
||||
|
||||
@JvmStatic
|
||||
fun <R : Comparable<R>> Field.greaterThanOrEqual(value: R) = comparePredicate(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL, value)
|
||||
|
||||
@JvmStatic
|
||||
fun <R : Comparable<R>> Field.between(from: R, to: R) = predicate(ColumnPredicate.Between(from, to))
|
||||
|
||||
@JvmStatic
|
||||
fun <R : Comparable<R>> Field.`in`(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.IN, collection))
|
||||
|
||||
@JvmStatic
|
||||
fun <R : Comparable<R>> Field.notIn(collection: Collection<R>) = predicate(ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection))
|
||||
|
||||
fun <R> equal(value: R) = ColumnPredicate.EqualityComparison(EqualityComparisonOperator.EQUAL, value)
|
||||
fun <R> notEqual(value: R) = ColumnPredicate.EqualityComparison(EqualityComparisonOperator.NOT_EQUAL, value)
|
||||
@ -238,45 +256,57 @@ object Builder {
|
||||
fun <R : Comparable<R>> notIn(collection: Collection<R>) = ColumnPredicate.CollectionExpression(CollectionOperator.NOT_IN, collection)
|
||||
|
||||
fun <O> KProperty1<O, String?>.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
|
||||
@JvmStatic fun Field.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
|
||||
@JvmStatic
|
||||
fun Field.like(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.LIKE, string))
|
||||
|
||||
fun <O> KProperty1<O, String?>.notLike(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string))
|
||||
@JvmStatic fun Field.notLike(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string))
|
||||
@JvmStatic
|
||||
fun Field.notLike(string: String) = predicate(ColumnPredicate.Likeness(LikenessOperator.NOT_LIKE, string))
|
||||
|
||||
fun <O, R> KProperty1<O, R?>.isNull() = predicate(ColumnPredicate.NullExpression(NullOperator.IS_NULL))
|
||||
@JvmStatic fun Field.isNull() = predicate(ColumnPredicate.NullExpression<Any>(NullOperator.IS_NULL))
|
||||
@JvmStatic
|
||||
fun Field.isNull() = predicate(ColumnPredicate.NullExpression<Any>(NullOperator.IS_NULL))
|
||||
|
||||
fun <O, R> KProperty1<O, R?>.notNull() = predicate(ColumnPredicate.NullExpression(NullOperator.NOT_NULL))
|
||||
@JvmStatic fun Field.notNull() = predicate(ColumnPredicate.NullExpression<Any>(NullOperator.NOT_NULL))
|
||||
@JvmStatic
|
||||
fun Field.notNull() = predicate(ColumnPredicate.NullExpression<Any>(NullOperator.NOT_NULL))
|
||||
|
||||
/** aggregate functions */
|
||||
fun <O, R> KProperty1<O, R?>.sum(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
|
||||
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.SUM), groupByColumns?.map { Column(it) }, orderBy)
|
||||
@JvmStatic @JvmOverloads
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <R> Field.sum(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
|
||||
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.SUM), groupByColumns?.map { Column<Any,R>(it) }, orderBy)
|
||||
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.SUM), groupByColumns?.map { Column<Any, R>(it) }, orderBy)
|
||||
|
||||
fun <O, R> KProperty1<O, R?>.count() = functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.COUNT))
|
||||
@JvmStatic fun Field.count() = functionPredicate(ColumnPredicate.AggregateFunction<Any>(AggregateFunctionType.COUNT))
|
||||
@JvmStatic
|
||||
fun Field.count() = functionPredicate(ColumnPredicate.AggregateFunction<Any>(AggregateFunctionType.COUNT))
|
||||
|
||||
fun <O, R> KProperty1<O, R?>.avg(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
|
||||
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.AVG), groupByColumns?.map { Column(it) }, orderBy)
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <R> Field.avg(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
|
||||
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.AVG), groupByColumns?.map { Column<Any,R>(it) }, orderBy)
|
||||
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.AVG), groupByColumns?.map { Column<Any, R>(it) }, orderBy)
|
||||
|
||||
fun <O, R> KProperty1<O, R?>.min(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
|
||||
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.MIN), groupByColumns?.map { Column(it) }, orderBy)
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <R> Field.min(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
|
||||
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MIN), groupByColumns?.map { Column<Any,R>(it) }, orderBy)
|
||||
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MIN), groupByColumns?.map { Column<Any, R>(it) }, orderBy)
|
||||
|
||||
fun <O, R> KProperty1<O, R?>.max(groupByColumns: List<KProperty1<O, R>>? = null, orderBy: Sort.Direction? = null) =
|
||||
functionPredicate(ColumnPredicate.AggregateFunction(AggregateFunctionType.MAX), groupByColumns?.map { Column(it) }, orderBy)
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun <R> Field.max(groupByColumns: List<Field>? = null, orderBy: Sort.Direction? = null) =
|
||||
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MAX), groupByColumns?.map { Column<Any,R>(it) }, orderBy)
|
||||
functionPredicate(ColumnPredicate.AggregateFunction<R>(AggregateFunctionType.MAX), groupByColumns?.map { Column<Any, R>(it) }, orderBy)
|
||||
}
|
||||
|
||||
inline fun <A> builder(block: Builder.() -> A) = block(Builder)
|
||||
|
@ -38,9 +38,9 @@ object CommonSchemaV1 : MappedSchema(schemaFamily = CommonSchema.javaClass, vers
|
||||
|
||||
) : PersistentState() {
|
||||
constructor(uid: UniqueIdentifier, _participants: Set<AbstractParty>)
|
||||
: this(participants = _participants.toMutableSet(),
|
||||
externalId = uid.externalId,
|
||||
uuid = uid.id)
|
||||
: this(participants = _participants.toMutableSet(),
|
||||
externalId = uid.externalId,
|
||||
uuid = uid.id)
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
|
@ -31,8 +31,8 @@ object NodeInfoSchemaV1 : MappedSchema(
|
||||
@Column(name = "legal_identities_certs")
|
||||
@ManyToMany(cascade = arrayOf(CascadeType.ALL))
|
||||
@JoinTable(name = "link_nodeinfo_party",
|
||||
joinColumns = arrayOf(JoinColumn(name="node_info_id")),
|
||||
inverseJoinColumns = arrayOf(JoinColumn(name="party_name")))
|
||||
joinColumns = arrayOf(JoinColumn(name = "node_info_id")),
|
||||
inverseJoinColumns = arrayOf(JoinColumn(name = "party_name")))
|
||||
val legalIdentitiesAndCerts: List<DBPartyAndCertificate>,
|
||||
|
||||
@Column(name = "platform_version")
|
||||
@ -64,14 +64,15 @@ object NodeInfoSchemaV1 : MappedSchema(
|
||||
|
||||
@Entity
|
||||
data class DBHostAndPort(
|
||||
@EmbeddedId
|
||||
private val pk: PKHostAndPort
|
||||
@EmbeddedId
|
||||
private val pk: PKHostAndPort
|
||||
) {
|
||||
companion object {
|
||||
fun fromHostAndPort(hostAndPort: NetworkHostAndPort) = DBHostAndPort(
|
||||
PKHostAndPort(hostAndPort.host, hostAndPort.port)
|
||||
)
|
||||
}
|
||||
|
||||
fun toHostAndPort(): NetworkHostAndPort {
|
||||
return NetworkHostAndPort(this.pk.host!!, this.pk.port!!)
|
||||
}
|
||||
|
@ -49,7 +49,8 @@ open class MappedSchema(schemaFamily: Class<*>,
|
||||
* A super class for all mapped states exported to a schema that ensures the [StateRef] appears on the database row. The
|
||||
* [StateRef] will be set to the correct value by the framework (there's no need to set during mapping generation by the state itself).
|
||||
*/
|
||||
@MappedSuperclass @CordaSerializable open class PersistentState(@EmbeddedId var stateRef: PersistentStateRef? = null) : StatePersistable
|
||||
@MappedSuperclass
|
||||
@CordaSerializable open class PersistentState(@EmbeddedId var stateRef: PersistentStateRef? = null) : StatePersistable
|
||||
|
||||
/**
|
||||
* Embedded [StateRef] representation used in state mapping.
|
||||
|
@ -115,6 +115,7 @@ interface SerializationContext {
|
||||
* The use case we are serializing or deserializing for. See [UseCase].
|
||||
*/
|
||||
val useCase: UseCase
|
||||
|
||||
/**
|
||||
* Helper method to return a new context based on this context with the property added.
|
||||
*/
|
||||
|
@ -57,7 +57,7 @@ data class LedgerTransaction(
|
||||
}
|
||||
|
||||
private val contracts: Map<ContractClassName, Try<Contract>> = (inputs.map { it.state.contract } + outputs.map { it.contract })
|
||||
.toSet().map { it to createContractFor(it) }.toMap()
|
||||
.toSet().map { it to createContractFor(it) }.toMap()
|
||||
|
||||
val inputStates: List<ContractState> get() = inputs.map { it.state.data }
|
||||
|
||||
@ -79,8 +79,7 @@ data class LedgerTransaction(
|
||||
// TODO: make contract upgrade transactions have a separate type
|
||||
if (commands.any { it.value is UpgradeCommand }) {
|
||||
verifyContractUpgrade()
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
verifyContracts()
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ abstract class TraversableTransaction(open val componentGroups: List<ComponentGr
|
||||
* - list of each attachment that is present
|
||||
* - The notary [Party], if present (list with one element)
|
||||
* - The time-window of the transaction, if present (list with one element)
|
||||
*/
|
||||
*/
|
||||
val availableComponentGroups: List<List<Any>>
|
||||
get() {
|
||||
val result = mutableListOf(inputs, outputs, commands, attachments)
|
||||
@ -138,7 +138,7 @@ class FilteredTransaction private constructor(
|
||||
fun updateFilteredComponents() {
|
||||
wtx.inputs.forEachIndexed { internalIndex, it -> filter(it, ComponentGroupEnum.INPUTS_GROUP.ordinal, internalIndex) }
|
||||
wtx.outputs.forEachIndexed { internalIndex, it -> filter(it, ComponentGroupEnum.OUTPUTS_GROUP.ordinal, internalIndex) }
|
||||
wtx.commands.forEachIndexed { internalIndex, it -> filter(it, ComponentGroupEnum.COMMANDS_GROUP.ordinal, internalIndex) }
|
||||
wtx.commands.forEachIndexed { internalIndex, it -> filter(it, ComponentGroupEnum.COMMANDS_GROUP.ordinal, internalIndex) }
|
||||
wtx.attachments.forEachIndexed { internalIndex, it -> filter(it, ComponentGroupEnum.ATTACHMENTS_GROUP.ordinal, internalIndex) }
|
||||
if (wtx.notary != null) filter(wtx.notary, ComponentGroupEnum.NOTARY_GROUP.ordinal, 0)
|
||||
if (wtx.timeWindow != null) filter(wtx.timeWindow, ComponentGroupEnum.TIMEWINDOW_GROUP.ordinal, 0)
|
||||
@ -147,7 +147,7 @@ class FilteredTransaction private constructor(
|
||||
// we decide to filter and attach this field to a FilteredTransaction.
|
||||
// An example would be to redact certain contract state types, but otherwise leave a transaction alone,
|
||||
// including the unknown new components.
|
||||
wtx.componentGroups.filter { it.groupIndex >= ComponentGroupEnum.values().size }.forEach { componentGroup -> componentGroup.components.forEachIndexed { internalIndex, component-> filter(component, componentGroup.groupIndex, internalIndex) }}
|
||||
wtx.componentGroups.filter { it.groupIndex >= ComponentGroupEnum.values().size }.forEach { componentGroup -> componentGroup.components.forEachIndexed { internalIndex, component -> filter(component, componentGroup.groupIndex, internalIndex) } }
|
||||
}
|
||||
|
||||
fun createPartialMerkleTree(componentGroupIndex: Int) = PartialMerkleTree.build(MerkleTree.getMerkleTree(wtx.availableComponentHashes[componentGroupIndex]!!), filteredComponentHashes[componentGroupIndex]!!)
|
||||
@ -156,7 +156,7 @@ class FilteredTransaction private constructor(
|
||||
updateFilteredComponents()
|
||||
val filteredComponentGroups: MutableList<FilteredComponentGroup> = mutableListOf()
|
||||
filteredSerialisedComponents.forEach { (groupIndex, value) ->
|
||||
filteredComponentGroups.add(FilteredComponentGroup(groupIndex, value, filteredComponentNonces[groupIndex]!!, createPartialMerkleTree(groupIndex) ))
|
||||
filteredComponentGroups.add(FilteredComponentGroup(groupIndex, value, filteredComponentNonces[groupIndex]!!, createPartialMerkleTree(groupIndex)))
|
||||
}
|
||||
return filteredComponentGroups
|
||||
}
|
||||
@ -183,7 +183,7 @@ class FilteredTransaction private constructor(
|
||||
|
||||
// Compute partial Merkle roots for each filtered component and verify each of the partial Merkle trees.
|
||||
filteredComponentGroups.forEach { (groupIndex, components, nonces, groupPartialTree) ->
|
||||
verificationCheck(groupIndex < groupHashes.size ) { "There is no matching component group hash for group $groupIndex" }
|
||||
verificationCheck(groupIndex < groupHashes.size) { "There is no matching component group hash for group $groupIndex" }
|
||||
val groupMerkleRoot = groupHashes[groupIndex]
|
||||
verificationCheck(groupMerkleRoot == PartialMerkleTree.rootAndUsedHashes(groupPartialTree.root, mutableListOf())) { "Partial Merkle tree root and advertised full Merkle tree root for component group $groupIndex do not match" }
|
||||
verificationCheck(groupPartialTree.verify(groupMerkleRoot, components.mapIndexed { index, component -> componentHash(nonces[index], component) })) { "Visible components in group $groupIndex cannot be verified against their partial Merkle tree" }
|
||||
@ -226,7 +226,7 @@ class FilteredTransaction private constructor(
|
||||
"Did not receive components for group ${componentGroupEnum.ordinal} and cannot verify they didn't exist in the original wire transaction"
|
||||
}
|
||||
} else {
|
||||
visibilityCheck(group.groupIndex < groupHashes.size ) { "There is no matching component group hash for group ${group.groupIndex}" }
|
||||
visibilityCheck(group.groupIndex < groupHashes.size) { "There is no matching component group hash for group ${group.groupIndex}" }
|
||||
val groupPartialRoot = groupHashes[group.groupIndex]
|
||||
val groupFullRoot = MerkleTree.getMerkleTree(group.components.mapIndexed { index, component -> componentHash(group.nonces[index], component) }).hash
|
||||
visibilityCheck(groupPartialRoot == groupFullRoot) { "The partial Merkle tree root does not match with the received root for group ${group.groupIndex}" }
|
||||
@ -253,7 +253,7 @@ class FilteredTransaction private constructor(
|
||||
* This is similar to [ComponentGroup], but it also includes the corresponding nonce per component.
|
||||
*/
|
||||
@CordaSerializable
|
||||
data class FilteredComponentGroup(override val groupIndex: Int, override val components: List<OpaqueBytes>, val nonces: List<SecureHash>, val partialMerkleTree: PartialMerkleTree): ComponentGroup(groupIndex, components) {
|
||||
data class FilteredComponentGroup(override val groupIndex: Int, override val components: List<OpaqueBytes>, val nonces: List<SecureHash>, val partialMerkleTree: PartialMerkleTree) : ComponentGroup(groupIndex, components) {
|
||||
init {
|
||||
check(components.size == nonces.size) { "Size of transaction components and nonces do not match" }
|
||||
}
|
||||
|
@ -12,4 +12,4 @@ import net.corda.core.serialization.CordaSerializable
|
||||
*/
|
||||
@CordaSerializable
|
||||
class MissingContractAttachments(val states: List<TransactionState<ContractState>>)
|
||||
: FlowException("Cannot find contract attachments for ${states.map { it.contract }.distinct() }")
|
||||
: FlowException("Cannot find contract attachments for ${states.map { it.contract }.distinct()}")
|
@ -47,7 +47,8 @@ data class SignedTransaction(val txBits: SerializedBytes<CoreTransaction>,
|
||||
}
|
||||
|
||||
/** Cache the deserialized form of the transaction. This is useful when building a transaction or collecting signatures. */
|
||||
@Volatile @Transient private var cachedTransaction: CoreTransaction? = null
|
||||
@Volatile
|
||||
@Transient private var cachedTransaction: CoreTransaction? = null
|
||||
|
||||
/** Lazily calculated access to the deserialised/hashed transaction data. */
|
||||
private val transaction: CoreTransaction get() = cachedTransaction ?: txBits.deserialize().apply { cachedTransaction = this }
|
||||
|
@ -38,8 +38,8 @@ open class TransactionBuilder(
|
||||
protected val commands: MutableList<Command<*>> = arrayListOf(),
|
||||
protected var window: TimeWindow? = null,
|
||||
protected var privacySalt: PrivacySalt = PrivacySalt()
|
||||
) {
|
||||
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())
|
||||
|
||||
/**
|
||||
* Creates a copy of the builder.
|
||||
@ -179,6 +179,7 @@ open class TransactionBuilder(
|
||||
|
||||
// Accessors that yield immutable snapshots.
|
||||
fun inputStates(): List<StateRef> = ArrayList(inputs)
|
||||
|
||||
fun attachments(): List<SecureHash> = ArrayList(attachments)
|
||||
fun outputStates(): List<TransactionState<*>> = ArrayList(outputs)
|
||||
fun commands(): List<Command<*>> = ArrayList(commands)
|
||||
|
@ -63,15 +63,16 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
override val id: SecureHash get() = merkleTree.hash
|
||||
|
||||
/** Public keys that need to be fulfilled by signatures in order for the transaction to be valid. */
|
||||
val requiredSigningKeys: Set<PublicKey> get() {
|
||||
val commandKeys = commands.flatMap { it.signers }.toSet()
|
||||
// TODO: prevent notary field from being set if there are no inputs and no timestamp.
|
||||
return if (notary != null && (inputs.isNotEmpty() || timeWindow != null)) {
|
||||
commandKeys + notary.owningKey
|
||||
} else {
|
||||
commandKeys
|
||||
val requiredSigningKeys: Set<PublicKey>
|
||||
get() {
|
||||
val commandKeys = commands.flatMap { it.signers }.toSet()
|
||||
// TODO: prevent notary field from being set if there are no inputs and no timestamp.
|
||||
return if (notary != null && (inputs.isNotEmpty() || timeWindow != null)) {
|
||||
commandKeys + notary.owningKey
|
||||
} else {
|
||||
commandKeys
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up identities and attachments from storage to generate a [LedgerTransaction]. A transaction is expected to
|
||||
@ -84,7 +85,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
fun toLedgerTransaction(services: ServicesForResolution): LedgerTransaction {
|
||||
return toLedgerTransaction(
|
||||
resolveIdentity = { services.identityService.partyFromKey(it) },
|
||||
resolveAttachment = { services.attachments.openAttachment(it)},
|
||||
resolveAttachment = { services.attachments.openAttachment(it) },
|
||||
resolveStateRef = { services.loadState(it) },
|
||||
resolveContractAttachment = { services.cordappProvider.getContractAttachmentID(it.contract) }
|
||||
)
|
||||
@ -123,7 +124,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
* Build filtered transaction using provided filtering functions.
|
||||
*/
|
||||
fun buildFilteredTransaction(filtering: Predicate<Any>): FilteredTransaction =
|
||||
FilteredTransaction.buildFilteredTransaction(this, filtering)
|
||||
FilteredTransaction.buildFilteredTransaction(this, filtering)
|
||||
|
||||
/**
|
||||
* Builds whole Merkle tree for a transaction.
|
||||
@ -236,7 +237,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
|
||||
): 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()) {
|
||||
return if (missingAttachments.isEmpty()) {
|
||||
contractAttachments.map { ContractAttachment(resolveAttachment(it.second!!) ?: throw AttachmentResolutionException(it.second!!), it.first.contract) }
|
||||
} else {
|
||||
throw MissingContractAttachments(missingAttachments.map { it.first })
|
||||
|
@ -25,6 +25,7 @@ sealed class ByteSequence : Comparable<ByteSequence> {
|
||||
* The start position of the sequence within the byte array.
|
||||
*/
|
||||
abstract val offset: Int
|
||||
|
||||
/** Returns a [ByteArrayInputStream] of the bytes */
|
||||
fun open() = ByteArrayInputStream(bytes, offset, size)
|
||||
|
||||
|
@ -66,5 +66,6 @@ fun String.hexToBase64(): String = hexToByteArray().toBase64()
|
||||
// TODO: follow the crypto-conditions ASN.1 spec, some changes are needed to be compatible with the condition
|
||||
// structure, e.g. mapping a PublicKey to a condition with the specific feature (ED25519).
|
||||
fun parsePublicKeyBase58(base58String: String): PublicKey = base58String.base58ToByteArray().deserialize<PublicKey>()
|
||||
|
||||
fun PublicKey.toBase58String(): String = this.serialize().bytes.toBase58()
|
||||
fun PublicKey.toSHA256Bytes(): ByteArray = this.serialize().bytes.sha256().bytes // TODO: decide on the format of hashed key (encoded Vs serialised).
|
||||
|
@ -27,7 +27,7 @@ data class NetworkHostAndPort(val host: String, val port: Int) {
|
||||
fun parse(str: String): NetworkHostAndPort {
|
||||
val uri = try {
|
||||
URI(null, str, null, null, null)
|
||||
} catch(ex: URISyntaxException) {
|
||||
} catch (ex: URISyntaxException) {
|
||||
throw IllegalArgumentException("Host and port syntax is invalid, expected host:port")
|
||||
}
|
||||
require(uri.host != null) { NetworkHostAndPort.UNPARSEABLE_ADDRESS_FORMAT.format(str) }
|
||||
|
@ -48,11 +48,13 @@ class NonEmptySet<T> private constructor(private val elements: Set<T>) : Set<T>
|
||||
|
||||
/** Returns the first element of the set. */
|
||||
fun head(): T = elements.iterator().next()
|
||||
|
||||
override fun isEmpty(): Boolean = false
|
||||
override fun iterator() = object : Iterator<T> by elements.iterator() {}
|
||||
|
||||
// Following methods are not delegated by Kotlin's Class delegation
|
||||
override fun forEach(action: Consumer<in T>) = elements.forEach(action)
|
||||
|
||||
override fun stream(): Stream<T> = elements.stream()
|
||||
override fun parallelStream(): Stream<T> = elements.parallelStream()
|
||||
override fun spliterator(): Spliterator<T> = elements.spliterator()
|
||||
|
@ -45,11 +45,11 @@ class CompatibleTransactionTests : TestDependencyInjectionBase() {
|
||||
// Do not add attachments (empty list).
|
||||
private val componentGroupsA by lazy {
|
||||
listOf(
|
||||
inputGroup,
|
||||
outputGroup,
|
||||
commandGroup,
|
||||
notaryGroup,
|
||||
timeWindowGroup
|
||||
inputGroup,
|
||||
outputGroup,
|
||||
commandGroup,
|
||||
notaryGroup,
|
||||
timeWindowGroup
|
||||
)
|
||||
}
|
||||
private val wireTransactionA by lazy { WireTransaction(componentGroups = componentGroupsA, privacySalt = privacySalt) }
|
||||
@ -199,6 +199,7 @@ class CompatibleTransactionTests : TestDependencyInjectionBase() {
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
val ftxInputs = wireTransactionA.buildFilteredTransaction(Predicate(::filtering)) // Inputs only filtered.
|
||||
ftxInputs.verify()
|
||||
ftxInputs.checkAllComponentsVisible(INPUTS_GROUP)
|
||||
@ -210,6 +211,7 @@ class CompatibleTransactionTests : TestDependencyInjectionBase() {
|
||||
|
||||
// Filter one input only.
|
||||
fun filteringOneInput(elem: Any) = elem == inputs[0]
|
||||
|
||||
val ftxOneInput = wireTransactionA.buildFilteredTransaction(Predicate(::filteringOneInput)) // First input only filtered.
|
||||
ftxOneInput.verify()
|
||||
assertFailsWith<ComponentVisibilityException> { ftxOneInput.checkAllComponentsVisible(INPUTS_GROUP) }
|
||||
|
@ -20,7 +20,7 @@ class X509NameConstraintsTest {
|
||||
|
||||
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<KeyStore, KeyStore> {
|
||||
val rootKeys = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality= "London", country = "GB"), rootKeys)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Root CA", organisation = "R3 Ltd", locality = "London", country = "GB"), rootKeys)
|
||||
|
||||
val intermediateCAKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootKeys, CordaX500Name(commonName = "Corda Intermediate CA", organisation = "R3 Ltd", locality = "London", country = "GB"), intermediateCAKeyPair.public)
|
||||
|
@ -161,14 +161,14 @@ class ContractUpgradeFlowTest {
|
||||
assertFailsWith(UnexpectedFlowEndException::class) { rejectedFuture.getOrThrow() }
|
||||
|
||||
// Party B authorise the contract state upgrade, and immediately deauthorise the same.
|
||||
rpcB.startFlow( { stateAndRef, upgrade -> ContractUpgradeFlow.Authorise(stateAndRef, upgrade ) },
|
||||
rpcB.startFlow({ stateAndRef, upgrade -> ContractUpgradeFlow.Authorise(stateAndRef, upgrade) },
|
||||
btx!!.tx.outRef<ContractState>(0),
|
||||
DummyContractV2::class.java).returnValue
|
||||
rpcB.startFlow( { stateRef -> ContractUpgradeFlow.Deauthorise(stateRef) },
|
||||
rpcB.startFlow({ stateRef -> ContractUpgradeFlow.Deauthorise(stateRef) },
|
||||
btx.tx.outRef<ContractState>(0).ref).returnValue
|
||||
|
||||
// The request is expected to be rejected because party B has subsequently deauthorised and a previously authorised upgrade.
|
||||
val deauthorisedFuture = rpcA.startFlow( {stateAndRef, upgrade -> ContractUpgradeFlow.Initiate(stateAndRef, upgrade) },
|
||||
val deauthorisedFuture = rpcA.startFlow({ stateAndRef, upgrade -> ContractUpgradeFlow.Initiate(stateAndRef, upgrade) },
|
||||
atx.tx.outRef<DummyContract.State>(0),
|
||||
DummyContractV2::class.java).returnValue
|
||||
|
||||
@ -176,7 +176,7 @@ class ContractUpgradeFlowTest {
|
||||
assertFailsWith(UnexpectedFlowEndException::class) { deauthorisedFuture.getOrThrow() }
|
||||
|
||||
// Party B authorise the contract state upgrade.
|
||||
rpcB.startFlow( { stateAndRef, upgrade -> ContractUpgradeFlow.Authorise(stateAndRef, upgrade ) },
|
||||
rpcB.startFlow({ stateAndRef, upgrade -> ContractUpgradeFlow.Authorise(stateAndRef, upgrade) },
|
||||
btx.tx.outRef<ContractState>(0),
|
||||
DummyContractV2::class.java).returnValue
|
||||
|
||||
|
@ -15,11 +15,11 @@ import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class TestX509Key(algorithmId: AlgorithmId, key: BitArray) : X509Key() {
|
||||
init {
|
||||
this.algid = algorithmId
|
||||
this.setKey(key)
|
||||
this.encode()
|
||||
}
|
||||
init {
|
||||
this.algid = algorithmId
|
||||
this.setKey(key)
|
||||
this.encode()
|
||||
}
|
||||
}
|
||||
|
||||
class X509EdDSAEngineTest {
|
||||
|
@ -45,6 +45,7 @@ private fun Attachment.extractContent() = ByteArrayOutputStream().apply { extrac
|
||||
private fun StartedNode<*>.saveAttachment(content: String) = database.transaction {
|
||||
attachments.importAttachment(createAttachmentData(content).inputStream())
|
||||
}
|
||||
|
||||
private fun StartedNode<*>.hackAttachment(attachmentId: SecureHash, content: String) = database.transaction {
|
||||
updateAttachment(attachmentId, createAttachmentData(content))
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ fun recordUsedInstrumentedCallStack() {
|
||||
val throwable = Throwable()
|
||||
var index = 0
|
||||
while (true) {
|
||||
require (index < throwable.stackTrace.size) { "Can't find getStack call" }
|
||||
require(index < throwable.stackTrace.size) { "Can't find getStack call" }
|
||||
val stackElement = throwable.stackTrace[index]
|
||||
if (stackElement.className == "co.paralleluniverse.fibers.Stack" && stackElement.methodName == "getStack") {
|
||||
break
|
||||
@ -129,7 +129,7 @@ class QuasarInstrumentationHookAgent {
|
||||
// The separator append is a hack, it causes a package with an empty name to be added to the exclude tree,
|
||||
// which practically causes that level of the tree to be always expanded in the output globs.
|
||||
val expand = arguments.expand?.let { PackageTree.fromStrings(it.map { "$it${arguments.separator}" }, arguments.separator) }
|
||||
val truncatedTree = truncate?.let { scannedTree.truncate(it)} ?: scannedTree
|
||||
val truncatedTree = truncate?.let { scannedTree.truncate(it) } ?: scannedTree
|
||||
val expandedTree = expand?.let { alwaysExcludedTree.merge(it) } ?: alwaysExcludedTree
|
||||
val globs = truncatedTree.toGlobs(expandedTree)
|
||||
globs.forEach {
|
||||
@ -152,7 +152,7 @@ object QuasarInstrumentationHook : ClassFileTransformer {
|
||||
val instrumentMap = mapOf<String, (CtClass) -> Unit>(
|
||||
"co/paralleluniverse/fibers/Stack" to { clazz ->
|
||||
// This is called on each suspend, we hook into it to get the stack trace of actually used Suspendables
|
||||
val getStackMethod = clazz.methods.single { it.name == "getStack" }
|
||||
val getStackMethod = clazz.methods.single { it.name == "getStack" }
|
||||
getStackMethod.insertBefore(
|
||||
"$hookClassName.${::recordUsedInstrumentedCallStack.name}();"
|
||||
)
|
||||
@ -194,7 +194,7 @@ object QuasarInstrumentationHook : ClassFileTransformer {
|
||||
throwable.printStackTrace(System.out)
|
||||
classfileBuffer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Glob(val parts: List<String>, val isFull: Boolean) {
|
||||
@ -271,6 +271,7 @@ data class PackageTree(val branches: Map<String, PackageTree>) {
|
||||
val exclude: PackageTree,
|
||||
val globSoFar: List<String>
|
||||
)
|
||||
|
||||
val toExpandList = LinkedList(listOf(State(this, excludeTree, emptyList())))
|
||||
val globs = ArrayList<Glob>()
|
||||
while (true) {
|
||||
|
@ -43,7 +43,6 @@ public class CandidacyStatus {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param signature
|
||||
* @return true if the input was absent from the underlying map
|
||||
*/
|
||||
@ -52,7 +51,6 @@ public class CandidacyStatus {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param methodSignature
|
||||
* @return true if the input was absent from the underlying map
|
||||
*/
|
||||
@ -62,7 +60,7 @@ public class CandidacyStatus {
|
||||
|
||||
/**
|
||||
* Static factory method
|
||||
*
|
||||
*
|
||||
* @param startingSet
|
||||
* @return a candidacy status based on the starting set
|
||||
*/
|
||||
@ -81,7 +79,7 @@ public class CandidacyStatus {
|
||||
|
||||
/**
|
||||
* Static factory method
|
||||
*
|
||||
*
|
||||
* @return a candidacy status based on the starting set
|
||||
*/
|
||||
public static CandidacyStatus of() {
|
||||
@ -90,8 +88,8 @@ public class CandidacyStatus {
|
||||
|
||||
/**
|
||||
* Add additional methods that are known to be deterministic
|
||||
*
|
||||
* @param methodNames
|
||||
*
|
||||
* @param methodNames
|
||||
*/
|
||||
public void addKnownDeterministicMethods(final Set<String> methodNames) {
|
||||
for (String known : methodNames) {
|
||||
@ -101,7 +99,7 @@ public class CandidacyStatus {
|
||||
|
||||
/**
|
||||
* Getter method for candidate methods
|
||||
*
|
||||
*
|
||||
* @param methodSignature
|
||||
* @return the candidate method corresponding to a method signature
|
||||
*/
|
||||
@ -149,10 +147,10 @@ public class CandidacyStatus {
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the recursive depth of this classloading process, throwing a
|
||||
* Increases the recursive depth of this classloading process, throwing a
|
||||
* ClassNotFoundException if it becomes too high
|
||||
*
|
||||
* @throws ClassNotFoundException
|
||||
*
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
public void incRecursiveCount() throws ClassNotFoundException {
|
||||
if (recursiveDepth >= MAX_CLASSLOADING_RECURSIVE_DEPTH - 1) {
|
||||
@ -174,7 +172,7 @@ public class CandidacyStatus {
|
||||
out.add(candidateName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -6,15 +6,14 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* A candidate method that is under evaluation. Candidate methods have one of the following states:
|
||||
*
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>{@link CandidateMethod.State#DETERMINISTIC} - It's deterministic and therefore is allowed to be loaded.</li>
|
||||
* <li>{@link CandidateMethod.State#DISALLOWED} - It's not deterministic and won't be allowed to be loaded.</li>
|
||||
* <li>{@link CandidateMethod.State#SCANNED} - We're not sure if it's deterministic or not.</li>
|
||||
* <li>{@link CandidateMethod.State#DETERMINISTIC} - It's deterministic and therefore is allowed to be loaded.</li>
|
||||
* <li>{@link CandidateMethod.State#DISALLOWED} - It's not deterministic and won't be allowed to be loaded.</li>
|
||||
* <li>{@link CandidateMethod.State#SCANNED} - We're not sure if it's deterministic or not.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* CandidateMethods themselves reference other CandidateMethods which are be checked for their deterministic state
|
||||
*
|
||||
*/
|
||||
public final class CandidateMethod {
|
||||
|
||||
@ -43,7 +42,7 @@ public final class CandidateMethod {
|
||||
|
||||
private final Set<CandidateMethod> referencedCandidateMethods = new HashSet<>();
|
||||
|
||||
|
||||
|
||||
public State getCurrentState() {
|
||||
return currentState;
|
||||
}
|
||||
@ -59,7 +58,7 @@ public final class CandidateMethod {
|
||||
|
||||
public void deterministic() {
|
||||
if (currentState == State.DISALLOWED) {
|
||||
throw new IllegalArgumentException("Method "+ internalMethodName +" attempted to transition from DISALLOWED to DETERMINISTIC");
|
||||
throw new IllegalArgumentException("Method " + internalMethodName + " attempted to transition from DISALLOWED to DETERMINISTIC");
|
||||
}
|
||||
currentState = State.DETERMINISTIC;
|
||||
}
|
||||
@ -79,7 +78,7 @@ public final class CandidateMethod {
|
||||
public String getInternalMethodName() {
|
||||
return internalMethodName;
|
||||
}
|
||||
|
||||
|
||||
public void addReferencedCandidateMethod(final CandidateMethod referenceCandidateMethod) {
|
||||
referencedCandidateMethods.add(referenceCandidateMethod);
|
||||
}
|
||||
@ -94,8 +93,9 @@ public final class CandidateMethod {
|
||||
|
||||
/**
|
||||
* This factory constructor is only called for methods that are known to be deterministic in advance
|
||||
*
|
||||
* @param methodSignature
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static CandidateMethod proven(String methodSignature) {
|
||||
final CandidateMethod provenCandidateMethod = new CandidateMethod(methodSignature);
|
||||
|
@ -1,11 +1,11 @@
|
||||
package net.corda.sandbox;
|
||||
|
||||
import static net.corda.sandbox.Utils.*;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public final class SandboxAwareClassWriter extends ClassWriter {
|
||||
@ -25,17 +25,15 @@ public final class SandboxAwareClassWriter extends ClassWriter {
|
||||
* without actually loading any class, or to take into account the class
|
||||
* that is currently being generated by this ClassWriter, which can of
|
||||
* course not be loaded since it is under construction.
|
||||
*
|
||||
* @param type1
|
||||
* the internal name of a class.
|
||||
* @param type2
|
||||
* the internal name of another class.
|
||||
*
|
||||
* @param type1 the internal name of a class.
|
||||
* @param type2 the internal name of another class.
|
||||
* @return the internal name of the common super class of the two given
|
||||
* classes.
|
||||
* classes.
|
||||
*/
|
||||
@Override
|
||||
public String getCommonSuperClass(final String type1, final String type2) {
|
||||
if (OBJECT.equals(type1) || OBJECT.equals(type2)
|
||||
if (OBJECT.equals(type1) || OBJECT.equals(type2)
|
||||
|| OBJECT.equals(unsandboxNameIfNeedBe(type1)) || OBJECT.equals(unsandboxNameIfNeedBe(type2))) {
|
||||
return OBJECT;
|
||||
}
|
||||
@ -58,7 +56,7 @@ public final class SandboxAwareClassWriter extends ClassWriter {
|
||||
c = Class.forName(type1.replace('/', '.'), false, loader);
|
||||
d = Class.forName(type2.replace('/', '.'), false, loader);
|
||||
} catch (Exception e) {
|
||||
|
||||
|
||||
c = Class.forName(unsandboxNameIfNeedBe(type1).replace('/', '.'), false, loader);
|
||||
d = Class.forName(unsandboxNameIfNeedBe(type2).replace('/', '.'), false, loader);
|
||||
|
||||
|
@ -3,7 +3,6 @@ package net.corda.sandbox;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public final class SandboxRemapper extends Remapper {
|
||||
|
@ -4,7 +4,6 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public final class Utils {
|
||||
@ -12,7 +11,7 @@ public final class Utils {
|
||||
public final static String SANDBOX_PREFIX_INTERNAL = "sandbox/";
|
||||
|
||||
public final static String CLASSFILE_NAME_SUFFIX = "^(.*)\\.class$";
|
||||
|
||||
|
||||
public static final Pattern JAVA_LANG_PATTERN_INTERNAL = Pattern.compile("^java/lang/(.*)");
|
||||
|
||||
public static final Pattern SANDBOX_PATTERN_INTERNAL = Pattern.compile("^" + SANDBOX_PREFIX_INTERNAL + "(.*)");
|
||||
@ -28,13 +27,13 @@ public final class Utils {
|
||||
public static final Pattern CLASSNAME_PATTERN_QUALIFIED = Pattern.compile("([^\\.]+)\\.");
|
||||
|
||||
public static final String OBJECT = "java/lang/Object";
|
||||
|
||||
|
||||
public static final String THROWABLE = "java/lang/Throwable";
|
||||
|
||||
|
||||
public static final String ERROR = "java/lang/Error";
|
||||
|
||||
|
||||
public static final String THREAD_DEATH = "java/lang/ThreadDeath";
|
||||
|
||||
|
||||
// Hide constructor
|
||||
private Utils() {
|
||||
}
|
||||
@ -43,6 +42,7 @@ public final class Utils {
|
||||
* Helper method that converts from the internal class name format (as used in the
|
||||
* Constant Pool) to a fully-qualified class name. No obvious library method to do this
|
||||
* appears to exist, hence this code. If one exists, rip this out.
|
||||
*
|
||||
* @param classInternalName
|
||||
* @return
|
||||
*/
|
||||
@ -52,12 +52,11 @@ public final class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes in an internal method name but needs to return a qualified
|
||||
* This method takes in an internal method name but needs to return a qualified
|
||||
* classname (suitable for loading)
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param internalMethodName
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static String convertInternalMethodNameToQualifiedClassName(final String internalMethodName) {
|
||||
final Matcher classMatch = CLASSNAME_PATTERN_QUALIFIED.matcher(internalMethodName);
|
||||
@ -72,6 +71,7 @@ public final class Utils {
|
||||
* Helper method that converts from a fully-qualified class name to the internal class
|
||||
* name format (as used in the Constant Pool). No obvious library method to do this
|
||||
* appears to exist, hence this code. If one exists, rip this out.
|
||||
*
|
||||
* @param qualifiedClassName
|
||||
* @return
|
||||
*/
|
||||
@ -81,7 +81,7 @@ public final class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method potentially rewrites the classname.
|
||||
* This method potentially rewrites the classname.
|
||||
*
|
||||
* @param internalClassname - specified in internal form
|
||||
* @return
|
||||
@ -102,9 +102,8 @@ public final class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param qualifiedTypeName
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static String sandboxQualifiedTypeName(final String qualifiedTypeName) {
|
||||
final String internal = convertQualifiedClassNameToInternalForm(qualifiedTypeName);
|
||||
@ -118,7 +117,7 @@ public final class Utils {
|
||||
/**
|
||||
* This method removes the sandboxing prefix from a method or type name, if it has
|
||||
* one, otherwise it returns the input string.
|
||||
*
|
||||
*
|
||||
* @param internalClassname
|
||||
* @return the internal classname, unsandboxed if that was required
|
||||
*/
|
||||
@ -131,7 +130,6 @@ public final class Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param desc - internal
|
||||
* @return the rewritten desc string
|
||||
*/
|
||||
@ -169,9 +167,9 @@ public final class Utils {
|
||||
* loading. This should not attempt to load a classname that starts with java. as
|
||||
* the only permissable classes have already been transformed into sandboxed
|
||||
* methods
|
||||
*
|
||||
*
|
||||
* @param qualifiedClassName
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
public static boolean shouldAttemptToTransitivelyLoad(final String qualifiedClassName) {
|
||||
return !JAVA_PATTERN_QUALIFIED.asPredicate().test(qualifiedClassName);
|
||||
@ -179,7 +177,7 @@ public final class Utils {
|
||||
|
||||
/**
|
||||
* Helper method that determines whether this class requires sandboxing
|
||||
*
|
||||
*
|
||||
* @param clazzName - specified in internal form
|
||||
* @return true if the class should be sandboxed
|
||||
*/
|
||||
|
@ -2,6 +2,7 @@ package net.corda.sandbox;
|
||||
|
||||
import net.corda.sandbox.visitors.CostInstrumentingMethodVisitor;
|
||||
import net.corda.sandbox.visitors.WhitelistCheckingClassVisitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
@ -9,13 +10,13 @@ import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
import org.objectweb.asm.commons.ClassRemapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public final class WhitelistClassLoader extends ClassLoader {
|
||||
@ -61,8 +62,8 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method. Throws URISyntaxException currently, as this method is
|
||||
* called with user data, so a checked exception is not unreasonable. Could use a
|
||||
* Static factory method. Throws URISyntaxException currently, as this method is
|
||||
* called with user data, so a checked exception is not unreasonable. Could use a
|
||||
* runtime exception instead.
|
||||
*
|
||||
* @param auxiliaryClassPath
|
||||
@ -70,7 +71,7 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
* methods to be deterministic, instead the classloader
|
||||
* will remove all non-deterministic methods.
|
||||
* @return a suitably constructed whitelisting classloader
|
||||
* @throws URISyntaxException
|
||||
* @throws URISyntaxException
|
||||
*/
|
||||
public static WhitelistClassLoader of(final String auxiliaryClassPath, final boolean stripNonDeterministic) throws URISyntaxException {
|
||||
final WhitelistClassLoader out = new WhitelistClassLoader(stripNonDeterministic);
|
||||
@ -96,10 +97,10 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
|
||||
/**
|
||||
* Static factory method. Used for recursive classloading
|
||||
*
|
||||
*
|
||||
* @param other
|
||||
* @return a suitably constructed whitelisting classloader based on the state
|
||||
* of the passed classloader
|
||||
* of the passed classloader
|
||||
*/
|
||||
public static WhitelistClassLoader of(final WhitelistClassLoader other) {
|
||||
final WhitelistClassLoader out = new WhitelistClassLoader(other);
|
||||
@ -110,7 +111,7 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
/**
|
||||
* Helper method that adds a jar to the path to be searched
|
||||
*
|
||||
* @param knownGoodJar
|
||||
* @param knownGoodJar
|
||||
*/
|
||||
void addJarToSandbox(final Path knownGoodJar) {
|
||||
fileSystemSearchPath.add(knownGoodJar);
|
||||
@ -120,9 +121,9 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
* Setup the auxiliary classpath so that classes that are not on the original
|
||||
* classpath can be scanned for.
|
||||
* Note that this this method hardcodes Unix conventions, so won't work on e.g. Windows
|
||||
*
|
||||
*
|
||||
* @param auxiliaryClassPath
|
||||
* @throws URISyntaxException
|
||||
* @throws URISyntaxException
|
||||
*/
|
||||
void setupClasspath(final String auxiliaryClassPath) throws URISyntaxException {
|
||||
for (String entry : auxiliaryClassPath.split(":")) {
|
||||
@ -136,11 +137,10 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param qualifiedClassName
|
||||
* @return a class object that has been whitelist checked and is known to be
|
||||
* deterministic
|
||||
* @throws ClassNotFoundException
|
||||
* deterministic
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
@Override
|
||||
public Class<?> findClass(final String qualifiedClassName) throws ClassNotFoundException {
|
||||
@ -244,10 +244,10 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
* around a limitation of the ASM library that does not integrate cleanly with Java 7
|
||||
* NIO.2 Path APIs. This method also performs a couple of basic sanity check on the
|
||||
* class file (e.g. that it exists, is a regular file and is readable).
|
||||
*
|
||||
*
|
||||
* @param internalClassName
|
||||
* @return a path object that corresponds to a class that has been found
|
||||
* @throws ClassNotFoundException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
Path locateClassfileDir(final String internalClassName) throws ClassNotFoundException {
|
||||
// Check the primaryClasspathSearchPath
|
||||
@ -300,7 +300,7 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
/**
|
||||
* Creates a jar archive of all the transformed classes that this classloader
|
||||
* has loaded.
|
||||
*
|
||||
*
|
||||
* @return true on success, false on failure
|
||||
* @throws java.io.IOException
|
||||
* @throws java.net.URISyntaxException
|
||||
@ -328,7 +328,8 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
|
||||
/**
|
||||
* Getter method for the reason for failure
|
||||
* @return
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public WhitelistClassloadingException reason() {
|
||||
return candidacyStatus.getReason();
|
||||
@ -336,7 +337,8 @@ public final class WhitelistClassLoader extends ClassLoader {
|
||||
|
||||
/**
|
||||
* Getter method for the method candidacy status
|
||||
* @return
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public CandidacyStatus getCandidacyStatus() {
|
||||
return candidacyStatus;
|
||||
|
@ -4,7 +4,7 @@ package net.corda.sandbox;
|
||||
*
|
||||
*/
|
||||
public class WhitelistClassloadingException extends Exception {
|
||||
|
||||
|
||||
public WhitelistClassloadingException() {
|
||||
super();
|
||||
}
|
||||
@ -22,10 +22,10 @@ public class WhitelistClassloadingException extends Exception {
|
||||
}
|
||||
|
||||
protected WhitelistClassloadingException(String message, Throwable cause,
|
||||
boolean enableSuppression,
|
||||
boolean writableStackTrace) {
|
||||
boolean enableSuppression,
|
||||
boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This class is the runtime representation of a running contract.
|
||||
*
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public class Contract {
|
||||
|
@ -3,17 +3,17 @@ package net.corda.sandbox.costing;
|
||||
/**
|
||||
* This interface is to decouple the actual executable code from the entry point and
|
||||
* how vetted deterministic code will be used inside the sandbox
|
||||
*
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public interface ContractExecutor {
|
||||
/**
|
||||
* Executes a smart contract
|
||||
*
|
||||
*
|
||||
* @param contract the contract to be executed
|
||||
*/
|
||||
void execute(Contract contract);
|
||||
|
||||
|
||||
/**
|
||||
* Checks to see if the supplied Contract is suitable
|
||||
*
|
||||
|
@ -4,7 +4,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public class RuntimeCostAccounter {
|
||||
|
@ -2,6 +2,7 @@ package net.corda.sandbox.tools;
|
||||
|
||||
import net.corda.sandbox.WhitelistClassLoader;
|
||||
import net.corda.sandbox.visitors.SandboxPathVisitor;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
@ -18,7 +19,6 @@ import joptsimple.OptionSet;
|
||||
* This class takes in an exploded set of JRE classes, and a whitelist, and rewrites all
|
||||
* classes (note: not methods) that have at least one whitelisted method to create a
|
||||
* sandboxed version of the class.
|
||||
*
|
||||
*/
|
||||
// java8.scan.java.lang_and_util java8.interfaces_for_compat java8 sandbox
|
||||
public final class SandboxCreator {
|
||||
@ -30,7 +30,7 @@ public final class SandboxCreator {
|
||||
private final String outputJarName;
|
||||
private final WhitelistClassLoader wlcl;
|
||||
private final boolean hasInputJar;
|
||||
|
||||
|
||||
private final static OptionParser parser = new OptionParser();
|
||||
|
||||
private static void usage() {
|
||||
@ -53,7 +53,7 @@ public final class SandboxCreator {
|
||||
|
||||
static String unpackJar(final String zipFilePath) throws IOException {
|
||||
final Path tmpDir = Files.createTempDirectory(Paths.get("/tmp"), "wlcl-extract");
|
||||
|
||||
|
||||
try (final ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath))) {
|
||||
ZipEntry entry = zipIn.getNextEntry();
|
||||
|
||||
@ -68,13 +68,13 @@ public final class SandboxCreator {
|
||||
entry = zipIn.getNextEntry();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return tmpDir.toString();
|
||||
}
|
||||
|
||||
|
||||
void cleanup() {
|
||||
if (hasInputJar) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,10 +107,9 @@ public final class SandboxCreator {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param basePath
|
||||
* @param packageName
|
||||
* @throws IOException
|
||||
* @throws IOException
|
||||
*/
|
||||
void walk() throws IOException {
|
||||
final Path scanDir = Paths.get(basePathName);
|
||||
|
@ -9,7 +9,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public final class CostInstrumentingMethodVisitor extends GeneratorAdapter {
|
||||
@ -34,9 +33,10 @@ public final class CostInstrumentingMethodVisitor extends GeneratorAdapter {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method replaces MONITORENTER / MONITOREXIT opcodes with POP - basically
|
||||
* This method replaces MONITORENTER / MONITOREXIT opcodes with POP - basically
|
||||
* stripping the synchronization out of any sandboxed code.
|
||||
* @param opcode
|
||||
*
|
||||
* @param opcode
|
||||
*/
|
||||
@Override
|
||||
public void visitInsn(final int opcode) {
|
||||
@ -60,7 +60,7 @@ public final class CostInstrumentingMethodVisitor extends GeneratorAdapter {
|
||||
* For our purposes this is a NEWARRAY opcode.
|
||||
*
|
||||
* @param opcode
|
||||
* @param operand
|
||||
* @param operand
|
||||
*/
|
||||
@Override
|
||||
public void visitIntInsn(final int opcode, final int operand) {
|
||||
@ -103,11 +103,11 @@ public final class CostInstrumentingMethodVisitor extends GeneratorAdapter {
|
||||
/**
|
||||
* This method is called when visiting an opcode with a single operand, that
|
||||
* is a type (represented here as a String).
|
||||
*
|
||||
* <p>
|
||||
* For our purposes this is either a NEW opcode or a ANEWARRAY
|
||||
*
|
||||
* @param opcode
|
||||
* @param type
|
||||
* @param opcode
|
||||
* @param type
|
||||
*/
|
||||
@Override
|
||||
public void visitTypeInsn(final int opcode, final String type) {
|
||||
|
@ -11,5 +11,5 @@ class DefinitelyDisallowedMethodVisitor extends MethodVisitor {
|
||||
DefinitelyDisallowedMethodVisitor(MethodVisitor baseMethodVisitor) {
|
||||
super(Opcodes.ASM5, baseMethodVisitor);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -2,15 +2,17 @@ package net.corda.sandbox.visitors;
|
||||
|
||||
import net.corda.sandbox.Utils;
|
||||
import net.corda.sandbox.WhitelistClassLoader;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This helper class visits each file (represented as a Path) in some directory
|
||||
* tree containing classes to be sandboxed.
|
||||
*
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public final class SandboxPathVisitor extends SimpleFileVisitor<Path> {
|
||||
@ -30,10 +32,10 @@ public final class SandboxPathVisitor extends SimpleFileVisitor<Path> {
|
||||
public FileVisitResult visitFile(final Path path, final BasicFileAttributes attr) {
|
||||
// Check that this is a class file
|
||||
if (!path.toString().matches(Utils.CLASSFILE_NAME_SUFFIX)) {
|
||||
System.out.println("Skipping: "+ path);
|
||||
System.out.println("Skipping: " + path);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
// Check to see if this path corresponds to an allowedClass
|
||||
final String classFileName = startFrom.relativize(path).toString().replace(".class", "");
|
||||
|
||||
|
@ -2,10 +2,12 @@ package net.corda.sandbox.visitors;
|
||||
|
||||
import net.corda.sandbox.WhitelistClassLoader;
|
||||
import net.corda.sandbox.CandidacyStatus;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.corda.sandbox.CandidateMethod;
|
||||
import net.corda.sandbox.Utils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@ -14,6 +16,7 @@ import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
@ -57,7 +60,7 @@ public final class WhitelistCheckingClassVisitor extends ClassVisitor {
|
||||
/**
|
||||
* We initially take the method passed in and store an internal representation of
|
||||
* the method signature in the our CandidacyStatus working set.
|
||||
*
|
||||
* <p>
|
||||
* We then get an ASM MethodVisitor (which can read the byte code of the method) and pass that to our
|
||||
* custom method visitor which perform additional checks.
|
||||
*
|
||||
@ -66,7 +69,7 @@ public final class WhitelistCheckingClassVisitor extends ClassVisitor {
|
||||
* @param desc
|
||||
* @param signature
|
||||
* @param exceptions
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
|
||||
@ -77,7 +80,7 @@ public final class WhitelistCheckingClassVisitor extends ClassVisitor {
|
||||
// Force new access control flags - for now just strictfp for deterministic
|
||||
// compliance to IEEE 754
|
||||
final int maskedAccess = access | ACC_STRICT;
|
||||
|
||||
|
||||
final String internalName = classname + "." + name + ":" + desc;
|
||||
internalMethodNames.add(internalName);
|
||||
candidacyStatus.putIfAbsent(internalName);
|
||||
@ -151,10 +154,10 @@ public final class WhitelistCheckingClassVisitor extends ClassVisitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the name of a class and attempts to load it using a WLCL.
|
||||
*
|
||||
* Take the name of a class and attempts to load it using a WLCL.
|
||||
*
|
||||
* @param qualifiedClassname
|
||||
* @return
|
||||
* @return
|
||||
*/
|
||||
CandidateMethod.State resolveState(final String qualifiedClassname) {
|
||||
Class<?> clz = null;
|
||||
|
@ -13,7 +13,6 @@ import org.objectweb.asm.Label;
|
||||
/**
|
||||
* A MethodVisitor which checks method instructions in order to determine if this
|
||||
* method is deterministic or not
|
||||
*
|
||||
*/
|
||||
final class WhitelistCheckingMethodVisitor extends MethodVisitor {
|
||||
|
||||
@ -29,7 +28,7 @@ final class WhitelistCheckingMethodVisitor extends MethodVisitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a method instruction. A method instruction is an instruction that
|
||||
* Visits a method instruction. A method instruction is an instruction that
|
||||
* invokes a method.
|
||||
* <p>
|
||||
* Some method instructions are by their nature un-deterministic, so we set those methods to have a
|
||||
@ -84,15 +83,15 @@ final class WhitelistCheckingMethodVisitor extends MethodVisitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently a no-op.
|
||||
*
|
||||
* Currently a no-op.
|
||||
* <p>
|
||||
* The JVMspec seems to permit the possibility of using a backwards branch in a
|
||||
* tableswitch to try to create an infinite loop. However, it seems to be
|
||||
* tableswitch to try to create an infinite loop. However, it seems to be
|
||||
* impossible in practice - the specification of StackMapFrame seems to prevent
|
||||
* it in modern classfile formats, and even by explicitly generating a version
|
||||
* 49 (Java 5) classfile, the verifier seems to be specifically resistant to a
|
||||
* backwards branch from a tableswitch.
|
||||
*
|
||||
* backwards branch from a tableswitch.
|
||||
* <p>
|
||||
* We could still add a belt-and-braces static instrumentation to protect
|
||||
* against this but it currently seems unnecessary - at worse it is a branch that
|
||||
* should count against the branch limit, or an explicit disallow of a backwards
|
||||
@ -102,7 +101,7 @@ final class WhitelistCheckingMethodVisitor extends MethodVisitor {
|
||||
* @param min
|
||||
* @param max
|
||||
* @param dflt
|
||||
* @param labels
|
||||
* @param labels
|
||||
*/
|
||||
@Override
|
||||
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
|
||||
@ -112,11 +111,11 @@ final class WhitelistCheckingMethodVisitor extends MethodVisitor {
|
||||
/**
|
||||
* Visits an invokedynamic instruction - which is specifically disallowed for
|
||||
* deterministic apps.
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
* @param desc
|
||||
* @param bsm
|
||||
* @param bsmArgs
|
||||
* @param bsmArgs
|
||||
*/
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(final String name, final String desc, final Handle bsm, final Object... bsmArgs) {
|
||||
|
@ -4,7 +4,7 @@ package sandbox.net.corda.sandbox.costing;
|
||||
* A helper class that just forwards any static sandboxed calls to the real runtime
|
||||
* cost accounting class. This removes the need to special case the accounting
|
||||
* method calls during rewriting of method names
|
||||
*
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public class RuntimeCostAccounter {
|
||||
|
@ -22,7 +22,7 @@ public class TestUtils {
|
||||
// Copy resource jar to tmp dir
|
||||
tmpdir = Files.createTempDirectory("wlcl-tmp-test");
|
||||
Path copiedJar = tmpdir.resolve("tmp-resource.jar");
|
||||
try(final InputStream in = TestUtils.class.getResourceAsStream(resourcePathToJar)) {
|
||||
try (final InputStream in = TestUtils.class.getResourceAsStream(resourcePathToJar)) {
|
||||
Files.copy(in, copiedJar, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
final FileSystem fs = FileSystems.newFileSystem(copiedJar, null);
|
||||
@ -33,20 +33,20 @@ public class TestUtils {
|
||||
public static Path copySandboxJarToTmpDir(final String resourcePathToJar) throws IOException {
|
||||
|
||||
Path sandboxJar = tmpdir.resolve("tmp-sandbox.jar");
|
||||
try(final InputStream in = TestUtils.class.getResourceAsStream(resourcePathToJar)) {
|
||||
try (final InputStream in = TestUtils.class.getResourceAsStream(resourcePathToJar)) {
|
||||
Files.copy(in, sandboxJar, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
final FileSystem sandboxFs = FileSystems.newFileSystem(sandboxJar, null);
|
||||
tmpFileSystems.add(sandboxFs);
|
||||
return sandboxFs.getRootDirectories().iterator().next();
|
||||
}
|
||||
|
||||
|
||||
public static Path getJarFSRoot() {
|
||||
return jarFSDir;
|
||||
}
|
||||
|
||||
public static void cleanupTmpJar() throws IOException {
|
||||
for (FileSystem fs: tmpFileSystems) {
|
||||
for (FileSystem fs : tmpFileSystems) {
|
||||
fs.close();
|
||||
}
|
||||
tmpFileSystems.clear();
|
||||
@ -92,15 +92,15 @@ public class TestUtils {
|
||||
// Helper for finding the correct offsets if they change
|
||||
public static void printBytes(byte[] data) {
|
||||
byte[] datum = new byte[1];
|
||||
for (int i=0; i < data.length; i++) {
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
datum[0] = data[i];
|
||||
System.out.println(i +" : "+ DatatypeConverter.printHexBinary(datum));
|
||||
System.out.println(i + " : " + DatatypeConverter.printHexBinary(datum));
|
||||
}
|
||||
}
|
||||
|
||||
public static int findOffset(byte[] classBytes, byte[] originalSeq) {
|
||||
int offset = 0;
|
||||
for (int i=415; i < classBytes.length; i++) {
|
||||
for (int i = 415; i < classBytes.length; i++) {
|
||||
if (classBytes[i] != originalSeq[offset]) {
|
||||
offset = 0;
|
||||
continue;
|
||||
@ -110,7 +110,7 @@ public class TestUtils {
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ public class TestUtils {
|
||||
return wlcl.instrumentWithCosts(basic, hashSet);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static final class MyClassloader extends ClassLoader {
|
||||
|
||||
public Class<?> byPath(Path p) throws IOException {
|
||||
|
@ -1,10 +1,13 @@
|
||||
package net.corda.sandbox;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
public class WhitelistClassLoaderTest {
|
||||
@ -123,8 +126,8 @@ public class WhitelistClassLoaderTest {
|
||||
final Class<?> clz = wlcl.loadClass("resource.ThrowExceptions");
|
||||
assertNotNull("ThrowExceptions class could not be transformed and loaded", clz);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO Test cases that terminate when other resource limits are broken
|
||||
@Test
|
||||
public void when_too_much_memory_is_allocated_then_thread_dies() throws Exception {
|
||||
|
@ -11,7 +11,6 @@ import java.util.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public class DeterministicClassInstrumenterTest {
|
||||
@ -66,7 +65,7 @@ public class DeterministicClassInstrumenterTest {
|
||||
// TestUtils.printBytes(basic);
|
||||
final int origOffset = TestUtils.findOffset(basic, originalSeq);
|
||||
final int tmfdOffset = TestUtils.findOffset(tfmd, tfmdSeq);
|
||||
|
||||
|
||||
for (int i = 0; i < originalSeq.length; i++) {
|
||||
assertEquals(originalSeq[i], basic[origOffset + i]);
|
||||
assertEquals(tfmdSeq[i], tfmd[tmfdOffset + i]);
|
||||
|
@ -1,19 +1,24 @@
|
||||
package net.corda.sandbox.costing;
|
||||
|
||||
import net.corda.sandbox.TestUtils;
|
||||
|
||||
import static net.corda.sandbox.TestUtils.*;
|
||||
|
||||
import net.corda.sandbox.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
public class SandboxedRewritingTest {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package sandbox.greymalkin;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ben
|
||||
*/
|
||||
// Simple hack for now, generalise to lambdas later...
|
||||
|
@ -101,9 +101,7 @@ public class JavaCommercialPaper implements Contract {
|
||||
if (issuance != null ? !issuance.equals(state.issuance) : state.issuance != null) return false;
|
||||
if (owner != null ? !owner.equals(state.owner) : state.owner != null) return false;
|
||||
if (faceValue != null ? !faceValue.equals(state.faceValue) : state.faceValue != null) return false;
|
||||
if (maturityDate != null ? !maturityDate.equals(state.maturityDate) : state.maturityDate != null)
|
||||
return false;
|
||||
return true;
|
||||
return maturityDate != null ? maturityDate.equals(state.maturityDate) : state.maturityDate == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user