From cd301c727ef19f072491a09aed1a8a76945e48c9 Mon Sep 17 00:00:00 2001 From: "rick.parker" Date: Tue, 20 Sep 2016 13:35:28 +0100 Subject: [PATCH] First working version with database persistence in MockNetwork/Node. --- .../kotlin/com/r3corda/node/internal/Node.kt | 1 + .../r3corda/demos/api/NodeInterestRates.kt | 18 ++- .../com/r3corda/simulation/Simulation.kt | 7 +- .../core/testing/NodeInterestRatesTest.kt | 115 ++++++++++++------ 4 files changed, 96 insertions(+), 45 deletions(-) diff --git a/node/src/main/kotlin/com/r3corda/node/internal/Node.kt b/node/src/main/kotlin/com/r3corda/node/internal/Node.kt index 7e4d5f145a..d82d9d33ca 100644 --- a/node/src/main/kotlin/com/r3corda/node/internal/Node.kt +++ b/node/src/main/kotlin/com/r3corda/node/internal/Node.kt @@ -236,6 +236,7 @@ class Node(val p2pAddr: HostAndPort, val webServerAddr: HostAndPort, // Wrap all API calls in a database transaction. val filterHolder = FilterHolder(DatabaseTransactionFilter(database)) addFilter(filterHolder, "/api/*", EnumSet.of(DispatcherType.REQUEST)) + addFilter(filterHolder, "/upload/*", EnumSet.of(DispatcherType.REQUEST)) } } diff --git a/src/main/kotlin/com/r3corda/demos/api/NodeInterestRates.kt b/src/main/kotlin/com/r3corda/demos/api/NodeInterestRates.kt index 3c250e24fd..b2c797476f 100644 --- a/src/main/kotlin/com/r3corda/demos/api/NodeInterestRates.kt +++ b/src/main/kotlin/com/r3corda/demos/api/NodeInterestRates.kt @@ -18,7 +18,9 @@ import com.r3corda.core.utilities.ProgressTracker import com.r3corda.node.services.api.AcceptsFileUpload import com.r3corda.node.services.api.ServiceHubInternal import com.r3corda.node.utilities.FiberBox +import com.r3corda.node.utilities.JDBCHashSet import com.r3corda.protocols.RatesFixProtocol.* +import com.r3corda.protocols.ServiceRequestMessage import com.r3corda.protocols.TwoPartyDealProtocol import java.io.InputStream import java.math.BigDecimal @@ -116,9 +118,10 @@ object NodeInterestRates { */ @ThreadSafe class Oracle(val identity: Party, private val signingKey: KeyPair, val clock: Clock) { - private class InnerState { - var container: FixContainer = FixContainer(emptyList()) + private class InnerState { + val fixes = JDBCHashSet("interest_rate_fixes") + var container: FixContainer = FixContainer(fixes) } private val mutex = FiberBox(InnerState()) @@ -126,6 +129,8 @@ object NodeInterestRates { set(value) { require(value.size > 0) mutex.write { + fixes.clear() + fixes.addAll(value.fixes) container = value } } @@ -185,9 +190,9 @@ object NodeInterestRates { class UnknownFix(val fix: FixOf) : RetryableException("Unknown fix: $fix") /** Fix container, for every fix name & date pair stores a tenor to interest rate map - [InterpolatingRateMap] */ - class FixContainer(fixes: List, val factory: InterpolatorFactory = CubicSplineInterpolator) { + class FixContainer(val fixes: Set, val factory: InterpolatorFactory = CubicSplineInterpolator) { private val container = buildContainer(fixes) - val size = fixes.size + val size: Int get() = fixes.size operator fun get(fixOf: FixOf): Fix? { val rates = container[fixOf.name to fixOf.forDay] @@ -195,7 +200,7 @@ object NodeInterestRates { return Fix(fixOf, fixValue) } - private fun buildContainer(fixes: List): Map, InterpolatingRateMap> { + private fun buildContainer(fixes: Set): Map, InterpolatingRateMap> { val tempContainer = HashMap, HashMap>() for (fix in fixes) { val fixOf = fix.of @@ -265,7 +270,8 @@ object NodeInterestRates { map(String::trim). // Filter out comment and empty lines. filterNot { it.startsWith("#") || it.isBlank() }. - map { parseFix(it) } + map { parseFix(it) }. + toSet() return FixContainer(fixes) } diff --git a/src/main/kotlin/com/r3corda/simulation/Simulation.kt b/src/main/kotlin/com/r3corda/simulation/Simulation.kt index 85aa576948..b7a2aa5e02 100644 --- a/src/main/kotlin/com/r3corda/simulation/Simulation.kt +++ b/src/main/kotlin/com/r3corda/simulation/Simulation.kt @@ -17,6 +17,7 @@ import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.network.NetworkMapService import com.r3corda.node.services.transactions.SimpleNotaryService import com.r3corda.node.utilities.AddOrRemove +import com.r3corda.node.utilities.databaseTransaction import com.r3corda.testing.node.* import rx.Observable import rx.subjects.PublishSubject @@ -147,7 +148,11 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean, return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, keyPair) { override fun start(): MockNetwork.MockNode { super.start() - findService().upload(javaClass.getResourceAsStream("example.rates.txt")) + javaClass.getResourceAsStream("example.rates.txt").use { + databaseTransaction(database) { + findService().upload(it) + } + } return this } } diff --git a/src/test/kotlin/com/r3corda/core/testing/NodeInterestRatesTest.kt b/src/test/kotlin/com/r3corda/core/testing/NodeInterestRatesTest.kt index b5e195450a..bfa48353fb 100644 --- a/src/test/kotlin/com/r3corda/core/testing/NodeInterestRatesTest.kt +++ b/src/test/kotlin/com/r3corda/core/testing/NodeInterestRatesTest.kt @@ -15,12 +15,17 @@ import com.r3corda.core.utilities.DUMMY_NOTARY import com.r3corda.core.utilities.LogHelper import com.r3corda.testing.node.MockNetwork import com.r3corda.demos.api.NodeInterestRates +import com.r3corda.node.services.transactions.PersistentUniquenessProvider +import com.r3corda.node.utilities.configureDatabase +import com.r3corda.node.utilities.databaseTransaction import com.r3corda.protocols.RatesFixProtocol import com.r3corda.testing.ALICE_PUBKEY import com.r3corda.testing.MEGA_CORP import com.r3corda.testing.MEGA_CORP_KEY -import org.junit.Assert -import org.junit.Test +import com.r3corda.testing.node.makeTestDataSourceProperties +import org.jetbrains.exposed.sql.Database +import org.junit.* +import java.io.Closeable import java.time.Clock import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -39,73 +44,107 @@ class NodeInterestRatesTest { val DUMMY_CASH_ISSUER = Party("Cash issuer", DUMMY_CASH_ISSUER_KEY.public) val clock = Clock.systemUTC() - val oracle = NodeInterestRates.Oracle(MEGA_CORP, MEGA_CORP_KEY, clock).apply { knownFixes = TEST_DATA } + lateinit var oracle: NodeInterestRates.Oracle + lateinit var dataSource: Closeable + lateinit var database: Database + + @Before + fun setUp() { + val dataSourceAndDatabase = configureDatabase(makeTestDataSourceProperties()) + dataSource=dataSourceAndDatabase.first + database=dataSourceAndDatabase.second + databaseTransaction(database) { + oracle = NodeInterestRates.Oracle(MEGA_CORP, MEGA_CORP_KEY, clock).apply { knownFixes = TEST_DATA } + } + } + + @After + fun tearDown() { + dataSource.close() + } @Test fun `query successfully`() { - val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") - val res = oracle.query(listOf(q), clock.instant()) - assertEquals(1, res.size) - assertEquals("0.678".bd, res[0].value) - assertEquals(q, res[0].of) + databaseTransaction(database) { + val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") + val res = oracle.query(listOf(q), clock.instant()) + assertEquals(1, res.size) + assertEquals("0.678".bd, res[0].value) + assertEquals(q, res[0].of) + } } @Test fun `query with one success and one missing`() { - val q1 = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") - val q2 = NodeInterestRates.parseFixOf("LIBOR 2016-03-15 1M") - val e = assertFailsWith { oracle.query(listOf(q1, q2), clock.instant()) } - assertEquals(e.fix, q2) + databaseTransaction(database) { + val q1 = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") + val q2 = NodeInterestRates.parseFixOf("LIBOR 2016-03-15 1M") + val e = assertFailsWith { oracle.query(listOf(q1, q2), clock.instant()) } + assertEquals(e.fix, q2) + } } @Test fun `query successfully with interpolated rate`() { - val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 5M") - val res = oracle.query(listOf(q), clock.instant()) - assertEquals(1, res.size) - Assert.assertEquals(0.7316228, res[0].value.toDouble(), 0.0000001) - assertEquals(q, res[0].of) + databaseTransaction(database) { + val q = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 5M") + val res = oracle.query(listOf(q), clock.instant()) + assertEquals(1, res.size) + Assert.assertEquals(0.7316228, res[0].value.toDouble(), 0.0000001) + assertEquals(q, res[0].of) + } } @Test fun `rate missing and unable to interpolate`() { - val q = NodeInterestRates.parseFixOf("EURIBOR 2016-03-15 3M") - assertFailsWith { oracle.query(listOf(q), clock.instant()) } + databaseTransaction(database) { + val q = NodeInterestRates.parseFixOf("EURIBOR 2016-03-15 3M") + assertFailsWith { oracle.query(listOf(q), clock.instant()) } + } } @Test fun `empty query`() { - assertFailsWith { oracle.query(emptyList(), clock.instant()) } + databaseTransaction(database) { + assertFailsWith { oracle.query(emptyList(), clock.instant()) } + } } @Test fun `refuse to sign with no relevant commands`() { - val tx = makeTX() - assertFailsWith { oracle.sign(tx.toWireTransaction()) } - tx.addCommand(Cash.Commands.Move(), ALICE_PUBKEY) - assertFailsWith { oracle.sign(tx.toWireTransaction()) } + databaseTransaction(database) { + val tx = makeTX() + assertFailsWith { oracle.sign(tx.toWireTransaction()) } + tx.addCommand(Cash.Commands.Move(), ALICE_PUBKEY) + assertFailsWith { oracle.sign(tx.toWireTransaction()) } + } } @Test fun `sign successfully`() { - val tx = makeTX() - val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")), clock.instant()).first() - tx.addCommand(fix, oracle.identity.owningKey) + databaseTransaction(database) { + val tx = makeTX() + val fix = oracle.query(listOf(NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")), clock.instant()).first() + tx.addCommand(fix, oracle.identity.owningKey) - // Sign successfully. - val signature = oracle.sign(tx.toWireTransaction()) - tx.checkAndAddSignature(signature) + // Sign successfully. + val signature = oracle.sign(tx.toWireTransaction()) + tx.checkAndAddSignature(signature) + } } @Test fun `do not sign with unknown fix`() { - val tx = makeTX() - val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") - val badFix = Fix(fixOf, "0.6789".bd) - tx.addCommand(badFix, oracle.identity.owningKey) + databaseTransaction(database) { + val tx = makeTX() + val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") + val badFix = Fix(fixOf, "0.6789".bd) + tx.addCommand(badFix, oracle.identity.owningKey) - val e1 = assertFailsWith { oracle.sign(tx.toWireTransaction()) } - assertEquals(fixOf, e1.fix) + val e1 = assertFailsWith { oracle.sign(tx.toWireTransaction()) } + assertEquals(fixOf, e1.fix) + } } @Test fun network() { val net = MockNetwork() val (n1, n2) = net.createTwoNodes() - n2.findService().oracle.knownFixes = TEST_DATA - + databaseTransaction(n2.database) { + n2.findService().oracle.knownFixes = TEST_DATA + } val tx = TransactionType.General.Builder(null) val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M") val protocol = RatesFixProtocol(tx, n2.info.identity, fixOf, "0.675".bd, "0.1".bd)