mirror of
https://github.com/corda/corda.git
synced 2025-02-20 17:33:15 +00:00
First working version with database persistence in MockNetwork/Node.
This commit is contained in:
parent
a15a2b1d9f
commit
cd301c727e
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Fix>())
|
||||
|
||||
private class InnerState {
|
||||
val fixes = JDBCHashSet<Fix>("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<Fix>, val factory: InterpolatorFactory = CubicSplineInterpolator) {
|
||||
class FixContainer(val fixes: Set<Fix>, 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<Fix>): Map<Pair<String, LocalDate>, InterpolatingRateMap> {
|
||||
private fun buildContainer(fixes: Set<Fix>): Map<Pair<String, LocalDate>, InterpolatingRateMap> {
|
||||
val tempContainer = HashMap<Pair<String, LocalDate>, HashMap<Tenor, BigDecimal>>()
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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<NodeInterestRates.Service>().upload(javaClass.getResourceAsStream("example.rates.txt"))
|
||||
javaClass.getResourceAsStream("example.rates.txt").use {
|
||||
databaseTransaction(database) {
|
||||
findService<NodeInterestRates.Service>().upload(it)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
@ -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<NodeInterestRates.UnknownFix> { 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<NodeInterestRates.UnknownFix> { 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<NodeInterestRates.UnknownFix> { oracle.query(listOf(q), clock.instant()) }
|
||||
databaseTransaction(database) {
|
||||
val q = NodeInterestRates.parseFixOf("EURIBOR 2016-03-15 3M")
|
||||
assertFailsWith<NodeInterestRates.UnknownFix> { oracle.query(listOf(q), clock.instant()) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun `empty query`() {
|
||||
assertFailsWith<IllegalArgumentException> { oracle.query(emptyList(), clock.instant()) }
|
||||
databaseTransaction(database) {
|
||||
assertFailsWith<IllegalArgumentException> { oracle.query(emptyList(), clock.instant()) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun `refuse to sign with no relevant commands`() {
|
||||
val tx = makeTX()
|
||||
assertFailsWith<IllegalArgumentException> { oracle.sign(tx.toWireTransaction()) }
|
||||
tx.addCommand(Cash.Commands.Move(), ALICE_PUBKEY)
|
||||
assertFailsWith<IllegalArgumentException> { oracle.sign(tx.toWireTransaction()) }
|
||||
databaseTransaction(database) {
|
||||
val tx = makeTX()
|
||||
assertFailsWith<IllegalArgumentException> { oracle.sign(tx.toWireTransaction()) }
|
||||
tx.addCommand(Cash.Commands.Move(), ALICE_PUBKEY)
|
||||
assertFailsWith<IllegalArgumentException> { 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<NodeInterestRates.UnknownFix> { oracle.sign(tx.toWireTransaction()) }
|
||||
assertEquals(fixOf, e1.fix)
|
||||
val e1 = assertFailsWith<NodeInterestRates.UnknownFix> { oracle.sign(tx.toWireTransaction()) }
|
||||
assertEquals(fixOf, e1.fix)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun network() {
|
||||
val net = MockNetwork()
|
||||
val (n1, n2) = net.createTwoNodes()
|
||||
n2.findService<NodeInterestRates.Service>().oracle.knownFixes = TEST_DATA
|
||||
|
||||
databaseTransaction(n2.database) {
|
||||
n2.findService<NodeInterestRates.Service>().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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user