mirror of
https://github.com/corda/corda.git
synced 2025-06-03 08:00:57 +00:00
Merge branch 'master' into dynamic-loading
This commit is contained in:
commit
88a7406ec9
4
.idea/runConfigurations/All_tests.xml
generated
4
.idea/runConfigurations/All_tests.xml
generated
@ -1,7 +1,7 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests" type="JUnit" factoryName="JUnit">
|
<configuration default="false" name="All tests" type="JUnit" factoryName="JUnit">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
<module name="r3prototyping_test" />
|
<module name="" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
<option name="PACKAGE_NAME" value="" />
|
<option name="PACKAGE_NAME" value="" />
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<option name="ENV_VARIABLES" />
|
<option name="ENV_VARIABLES" />
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="moduleWithDependencies" />
|
<value defaultName="wholeProject" />
|
||||||
</option>
|
</option>
|
||||||
<envs />
|
<envs />
|
||||||
<patterns />
|
<patterns />
|
||||||
|
@ -8,6 +8,15 @@
|
|||||||
|
|
||||||
package contracts
|
package contracts
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||||
|
import com.fasterxml.jackson.databind.type.SimpleType
|
||||||
import core.*
|
import core.*
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.node.services.DummyTimestampingAuthority
|
import core.node.services.DummyTimestampingAuthority
|
||||||
@ -15,6 +24,7 @@ import org.apache.commons.jexl3.JexlBuilder
|
|||||||
import org.apache.commons.jexl3.MapContext
|
import org.apache.commons.jexl3.MapContext
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
|
import java.security.PublicKey
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -167,8 +177,6 @@ class InterestRateSwap() : Contract {
|
|||||||
val hashLegalDocs: String
|
val hashLegalDocs: String
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Expression(val expr: String)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Calculation data class is "mutable" through out the life of the swap, as in, it's the only thing that contains
|
* The Calculation data class is "mutable" through out the life of the swap, as in, it's the only thing that contains
|
||||||
* data that will changed from state to state (Recall that the design insists that everything is immutable, so we actually
|
* data that will changed from state to state (Recall that the design insists that everything is immutable, so we actually
|
||||||
@ -177,7 +185,7 @@ class InterestRateSwap() : Contract {
|
|||||||
data class Calculation(
|
data class Calculation(
|
||||||
val expression: Expression,
|
val expression: Expression,
|
||||||
val floatingLegPaymentSchedule: Map<LocalDate, FloatingRatePaymentEvent>,
|
val floatingLegPaymentSchedule: Map<LocalDate, FloatingRatePaymentEvent>,
|
||||||
val fixedLegpaymentSchedule: Map<LocalDate, FixedRatePaymentEvent>
|
val fixedLegPaymentSchedule: Map<LocalDate, FixedRatePaymentEvent>
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Gets the date of the next fixing.
|
* Gets the date of the next fixing.
|
||||||
@ -185,7 +193,7 @@ class InterestRateSwap() : Contract {
|
|||||||
*/
|
*/
|
||||||
fun nextFixingDate(): LocalDate? {
|
fun nextFixingDate(): LocalDate? {
|
||||||
return floatingLegPaymentSchedule.
|
return floatingLegPaymentSchedule.
|
||||||
filter { it.value.rate is OracleRetrievableReferenceRate }.// TODO - a better way to determine what fixings remain to be fixed
|
filter { it.value.rate is ReferenceRate }.// TODO - a better way to determine what fixings remain to be fixed
|
||||||
minBy { it.value.fixingDate.toEpochDay() }?.value?.fixingDate
|
minBy { it.value.fixingDate.toEpochDay() }?.value?.fixingDate
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +211,7 @@ class InterestRateSwap() : Contract {
|
|||||||
val newFloatingLPS = floatingLegPaymentSchedule + (paymentEvent.date to paymentEvent.withNewRate(newRate))
|
val newFloatingLPS = floatingLegPaymentSchedule + (paymentEvent.date to paymentEvent.withNewRate(newRate))
|
||||||
return Calculation(expression = expression,
|
return Calculation(expression = expression,
|
||||||
floatingLegPaymentSchedule = newFloatingLPS,
|
floatingLegPaymentSchedule = newFloatingLPS,
|
||||||
fixedLegpaymentSchedule = fixedLegpaymentSchedule)
|
fixedLegPaymentSchedule = fixedLegPaymentSchedule)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exportSchedule() {
|
fun exportSchedule() {
|
||||||
@ -297,7 +305,7 @@ class InterestRateSwap() : Contract {
|
|||||||
*/
|
*/
|
||||||
override fun verify(tx: TransactionForVerification) {
|
override fun verify(tx: TransactionForVerification) {
|
||||||
val command = tx.commands.requireSingleCommand<InterestRateSwap.Commands>()
|
val command = tx.commands.requireSingleCommand<InterestRateSwap.Commands>()
|
||||||
val time = tx.getTimestampBy(DummyTimestampingAuthority.identity)?.midpoint
|
val time = tx.commands.getTimestampByName("Mock Company 0", "Bank of Zurich")?.midpoint
|
||||||
if (time == null) throw IllegalArgumentException("must be timestamped")
|
if (time == null) throw IllegalArgumentException("must be timestamped")
|
||||||
|
|
||||||
val irs = tx.outStates.filterIsInstance<InterestRateSwap.State>().single()
|
val irs = tx.outStates.filterIsInstance<InterestRateSwap.State>().single()
|
||||||
@ -306,7 +314,7 @@ class InterestRateSwap() : Contract {
|
|||||||
requireThat {
|
requireThat {
|
||||||
"There are no in states for an agreement" by tx.inStates.isEmpty()
|
"There are no in states for an agreement" by tx.inStates.isEmpty()
|
||||||
"The fixed rate is non zero" by (irs.fixedLeg.fixedRate != FixedRate(PercentageRatioUnit("0.0")))
|
"The fixed rate is non zero" by (irs.fixedLeg.fixedRate != FixedRate(PercentageRatioUnit("0.0")))
|
||||||
"There are events in the fix schedule" by (irs.calculation.fixedLegpaymentSchedule.size > 0)
|
"There are events in the fix schedule" by (irs.calculation.fixedLegPaymentSchedule.size > 0)
|
||||||
"There are events in the float schedule" by (irs.calculation.floatingLegPaymentSchedule.size > 0)
|
"There are events in the float schedule" by (irs.calculation.floatingLegPaymentSchedule.size > 0)
|
||||||
// "There are fixes in the schedule" by (irs.calculation.floatingLegPaymentSchedule!!.size > 0)
|
// "There are fixes in the schedule" by (irs.calculation.floatingLegPaymentSchedule!!.size > 0)
|
||||||
// TODO: shortlist of other tests
|
// TODO: shortlist of other tests
|
||||||
@ -348,8 +356,14 @@ class InterestRateSwap() : Contract {
|
|||||||
val floatingLeg: FloatingLeg,
|
val floatingLeg: FloatingLeg,
|
||||||
val calculation: Calculation,
|
val calculation: Calculation,
|
||||||
val common: Common
|
val common: Common
|
||||||
) : ContractState {
|
) : LinearState {
|
||||||
override val contract = IRS_PROGRAM_ID
|
override val contract = IRS_PROGRAM_ID
|
||||||
|
override val thread = SecureHash.sha256(common.tradeID)
|
||||||
|
override val ref = common.tradeID
|
||||||
|
|
||||||
|
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
||||||
|
return (fixedLeg.fixedRatePayer.owningKey in ourKeys) || (floatingLeg.floatingRatePayer.owningKey in ourKeys)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For evaluating arbitrary java on the platform
|
* For evaluating arbitrary java on the platform
|
||||||
@ -408,9 +422,6 @@ class InterestRateSwap() : Contract {
|
|||||||
var floatingLegPaymentSchedule: MutableMap<LocalDate, FloatingRatePaymentEvent> = HashMap()
|
var floatingLegPaymentSchedule: MutableMap<LocalDate, FloatingRatePaymentEvent> = HashMap()
|
||||||
periodStartDate = floatingLeg.effectiveDate
|
periodStartDate = floatingLeg.effectiveDate
|
||||||
|
|
||||||
// TODO: Temporary until implemented via Rates Oracle.
|
|
||||||
val telerate = TelerateOracle("3750")
|
|
||||||
|
|
||||||
// Now create a schedule for the floating and fixes.
|
// Now create a schedule for the floating and fixes.
|
||||||
for (periodEndDate in dates) {
|
for (periodEndDate in dates) {
|
||||||
val paymentEvent = FloatingRatePaymentEvent(
|
val paymentEvent = FloatingRatePaymentEvent(
|
||||||
@ -421,8 +432,7 @@ class InterestRateSwap() : Contract {
|
|||||||
floatingLeg.dayCountBasisYear,
|
floatingLeg.dayCountBasisYear,
|
||||||
calcFixingDate(periodStartDate, floatingLeg.fixingPeriod, floatingLeg.fixingCalendar),
|
calcFixingDate(periodStartDate, floatingLeg.fixingPeriod, floatingLeg.fixingCalendar),
|
||||||
floatingLeg.notional,
|
floatingLeg.notional,
|
||||||
// TODO: OracleRetrievableReferenceRate will be replaced via oracle v soon.
|
ReferenceRate(floatingLeg.indexSource, floatingLeg.indexTenor, floatingLeg.index)
|
||||||
OracleRetrievableReferenceRate(telerate, floatingLeg.indexTenor, floatingLeg.index)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
floatingLegPaymentSchedule.put(periodEndDate, paymentEvent)
|
floatingLegPaymentSchedule.put(periodEndDate, paymentEvent)
|
||||||
|
@ -10,6 +10,6 @@ package contracts
|
|||||||
|
|
||||||
fun InterestRateSwap.State.exportIRSToCSV() : String =
|
fun InterestRateSwap.State.exportIRSToCSV() : String =
|
||||||
"Fixed Leg\n" + FixedRatePaymentEvent.CSVHeader + "\n" +
|
"Fixed Leg\n" + FixedRatePaymentEvent.CSVHeader + "\n" +
|
||||||
this.calculation.fixedLegpaymentSchedule.toSortedMap().values.map{ it.asCSV() }.joinToString("\n") + "\n" +
|
this.calculation.fixedLegPaymentSchedule.toSortedMap().values.map{ it.asCSV() }.joinToString("\n") + "\n" +
|
||||||
"Floating Leg\n" + FloatingRatePaymentEvent.CSVHeader + "\n" +
|
"Floating Leg\n" + FloatingRatePaymentEvent.CSVHeader + "\n" +
|
||||||
this.calculation.floatingLegPaymentSchedule.toSortedMap().values.map{ it.asCSV() }.joinToString("\n") + "\n"
|
this.calculation.floatingLegPaymentSchedule.toSortedMap().values.map{ it.asCSV() }.joinToString("\n") + "\n"
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
package contracts
|
package contracts
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParseException
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer
|
||||||
|
import com.fasterxml.jackson.databind.type.SimpleType
|
||||||
import core.Amount
|
import core.Amount
|
||||||
import core.Tenor
|
import core.Tenor
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
@ -50,43 +58,7 @@ open class FloatingRate: Rate(null)
|
|||||||
* So a reference rate is a rate that takes its value from a source at a given date
|
* So a reference rate is a rate that takes its value from a source at a given date
|
||||||
* e.g. LIBOR 6M as of 17 March 2016. Hence it requires a source (name) and a value date in the getAsOf(..) method.
|
* e.g. LIBOR 6M as of 17 March 2016. Hence it requires a source (name) and a value date in the getAsOf(..) method.
|
||||||
*/
|
*/
|
||||||
abstract class ReferenceRate(val name: String): FloatingRate() {
|
class ReferenceRate(val oracle: String, val tenor: Tenor, val name: String) : FloatingRate() {
|
||||||
abstract fun getAsOf(date: LocalDate?) : RatioUnit
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A concrete implementation of the above for testing purposes
|
|
||||||
*/
|
|
||||||
open class TestReferenceRate(val testrate: String) : ReferenceRate(testrate) {
|
|
||||||
override fun getAsOf(date: LocalDate?) : RatioUnit {
|
|
||||||
return testrate.percent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This represents a source of data.
|
|
||||||
*/
|
|
||||||
abstract class Oracle() { abstract fun retrieve(tenor: Tenor, date: LocalDate) : RatioUnit }
|
|
||||||
|
|
||||||
class ReutersOracle() : Oracle() {
|
|
||||||
override fun retrieve(tenor: Tenor, date: LocalDate): RatioUnit {
|
|
||||||
TODO("Reuters Oracle retrieval")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TelerateOracle(page: String) : Oracle() {
|
|
||||||
override fun retrieve(tenor: Tenor, date: LocalDate): RatioUnit {
|
|
||||||
TODO("Telerate Oracle retrieval")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Reference rate that is retrieved via an Oracle.
|
|
||||||
*/
|
|
||||||
open class OracleRetrievableReferenceRate(val oracle: Oracle, val tenor: Tenor, referenceRate: String) : ReferenceRate(referenceRate) {
|
|
||||||
override fun getAsOf(date: LocalDate?): RatioUnit {
|
|
||||||
return oracle.retrieve(tenor,date!!)
|
|
||||||
}
|
|
||||||
override fun toString(): String = "$name - $tenor"
|
override fun toString(): String = "$name - $tenor"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,4 +47,7 @@ dependencies {
|
|||||||
// Apache JEXL: An embeddable expression evaluation library.
|
// Apache JEXL: An embeddable expression evaluation library.
|
||||||
// This may be temporary until we experiment with other ways to do on-the-fly contract specialisation via an API.
|
// This may be temporary until we experiment with other ways to do on-the-fly contract specialisation via an API.
|
||||||
compile "org.apache.commons:commons-jexl3:3.0"
|
compile "org.apache.commons:commons-jexl3:3.0"
|
||||||
|
|
||||||
|
// For JSON
|
||||||
|
compile "com.fasterxml.jackson.core:jackson-databind:2.5.5"
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,14 @@
|
|||||||
|
|
||||||
package core
|
package core
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator
|
||||||
|
import com.fasterxml.jackson.core.JsonParser
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.time.DayOfWeek
|
import java.time.DayOfWeek
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@ -82,7 +90,25 @@ data class FixOf(val name: String, val forDay: LocalDate, val ofTenor: Tenor)
|
|||||||
/** A [Fix] represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx. */
|
/** A [Fix] represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx. */
|
||||||
data class Fix(val of: FixOf, val value: BigDecimal) : CommandData
|
data class Fix(val of: FixOf, val value: BigDecimal) : CommandData
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a textual expression of e.g. a formula
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@JsonDeserialize(using = ExpressionDeserializer::class)
|
||||||
|
@JsonSerialize(using = ExpressionSerializer::class)
|
||||||
|
data class Expression(val expr: String)
|
||||||
|
|
||||||
|
object ExpressionSerializer: JsonSerializer<Expression>() {
|
||||||
|
override fun serialize(expr: Expression, generator: JsonGenerator, provider: SerializerProvider) {
|
||||||
|
generator.writeString(expr.expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ExpressionDeserializer: JsonDeserializer<Expression>() {
|
||||||
|
override fun deserialize(parser: JsonParser, context: DeserializationContext): Expression {
|
||||||
|
return Expression(parser.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Placeholder class for the Tenor datatype - which is a standardised duration of time until maturity */
|
* Placeholder class for the Tenor datatype - which is a standardised duration of time until maturity */
|
||||||
|
@ -3,4 +3,49 @@
|
|||||||
LIBOR 2016-03-16 1M = 0.678
|
LIBOR 2016-03-16 1M = 0.678
|
||||||
LIBOR 2016-03-16 2M = 0.655
|
LIBOR 2016-03-16 2M = 0.655
|
||||||
EURIBOR 2016-03-15 1M = 0.123
|
EURIBOR 2016-03-15 1M = 0.123
|
||||||
EURIBOR 2016-03-15 2M = 0.111
|
EURIBOR 2016-03-15 2M = 0.111
|
||||||
|
|
||||||
|
LIBOR 2016-03-08 3M = 0.01
|
||||||
|
LIBOR 2016-06-08 3M = 0.01
|
||||||
|
LIBOR 2016-09-08 3M = 0.01
|
||||||
|
LIBOR 2016-12-08 3M = 0.01
|
||||||
|
LIBOR 2017-03-08 3M = 0.01
|
||||||
|
LIBOR 2017-06-08 3M = 0.01
|
||||||
|
LIBOR 2017-09-07 3M = 0.01
|
||||||
|
LIBOR 2017-12-07 3M = 0.01
|
||||||
|
LIBOR 2018-03-08 3M = 0.01
|
||||||
|
LIBOR 2018-06-07 3M = 0.01
|
||||||
|
LIBOR 2018-09-06 3M = 0.01
|
||||||
|
LIBOR 2018-12-06 3M = 0.01
|
||||||
|
LIBOR 2019-03-07 3M = 0.01
|
||||||
|
LIBOR 2019-06-06 3M = 0.01
|
||||||
|
LIBOR 2019-09-06 3M = 0.01
|
||||||
|
LIBOR 2019-12-06 3M = 0.01
|
||||||
|
LIBOR 2020-03-06 3M = 0.01
|
||||||
|
LIBOR 2020-06-08 3M = 0.01
|
||||||
|
LIBOR 2020-09-08 3M = 0.01
|
||||||
|
LIBOR 2020-12-08 3M = 0.01
|
||||||
|
LIBOR 2021-03-08 3M = 0.01
|
||||||
|
LIBOR 2021-06-08 3M = 0.01
|
||||||
|
LIBOR 2021-09-08 3M = 0.01
|
||||||
|
LIBOR 2021-12-08 3M = 0.01
|
||||||
|
LIBOR 2022-03-08 3M = 0.01
|
||||||
|
LIBOR 2022-06-08 3M = 0.01
|
||||||
|
LIBOR 2022-09-08 3M = 0.01
|
||||||
|
LIBOR 2022-12-08 3M = 0.01
|
||||||
|
LIBOR 2023-03-08 3M = 0.01
|
||||||
|
LIBOR 2023-06-08 3M = 0.01
|
||||||
|
LIBOR 2023-09-07 3M = 0.01
|
||||||
|
LIBOR 2023-12-07 3M = 0.01
|
||||||
|
LIBOR 2024-03-07 3M = 0.01
|
||||||
|
LIBOR 2024-06-06 3M = 0.01
|
||||||
|
LIBOR 2024-09-06 3M = 0.01
|
||||||
|
LIBOR 2024-12-06 3M = 0.01
|
||||||
|
LIBOR 2025-03-06 3M = 0.01
|
||||||
|
LIBOR 2025-06-06 3M = 0.01
|
||||||
|
LIBOR 2025-09-08 3M = 0.01
|
||||||
|
LIBOR 2025-12-08 3M = 0.01
|
||||||
|
LIBOR 2026-03-06 3M = 0.01
|
||||||
|
LIBOR 2026-06-08 3M = 0.01
|
||||||
|
LIBOR 2026-09-08 3M = 0.01
|
||||||
|
LIBOR 2026-12-08 3M = 0.01
|
||||||
|
@ -7,6 +7,7 @@ import core.crypto.SecureHash
|
|||||||
import core.node.AbstractNode
|
import core.node.AbstractNode
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.serialization.SerializedBytes
|
import core.serialization.SerializedBytes
|
||||||
|
import core.utilities.ANSIProgressRenderer
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.reflect.KParameter
|
import kotlin.reflect.KParameter
|
||||||
@ -87,6 +88,7 @@ class APIServerImpl(val node: AbstractNode): APIServer {
|
|||||||
}
|
}
|
||||||
// If we get here then we matched every parameter
|
// If we get here then we matched every parameter
|
||||||
val protocol = constructor.callBy(params) as ProtocolLogic<*>
|
val protocol = constructor.callBy(params) as ProtocolLogic<*>
|
||||||
|
ANSIProgressRenderer.progressTracker = protocol.progressTracker
|
||||||
val future = node.smm.add("api-call",protocol)
|
val future = node.smm.add("api-call",protocol)
|
||||||
return future
|
return future
|
||||||
}
|
}
|
||||||
|
33
src/main/kotlin/api/ResponseFilter.kt
Normal file
33
src/main/kotlin/api/ResponseFilter.kt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext
|
||||||
|
import javax.ws.rs.container.ContainerResponseContext
|
||||||
|
import javax.ws.rs.container.ContainerResponseFilter
|
||||||
|
import javax.ws.rs.ext.Provider
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This adds headers needed for cross site scripting on API clients
|
||||||
|
*/
|
||||||
|
@Provider
|
||||||
|
class ResponseFilter: ContainerResponseFilter {
|
||||||
|
override fun filter(requestContext: ContainerRequestContext, responseContext: ContainerResponseContext) {
|
||||||
|
val headers = responseContext.headers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO we need to revisit this for security reasons
|
||||||
|
*
|
||||||
|
* We don't want this scriptable from any web page anywhere, but for demo reasons
|
||||||
|
* we're making this really easy to access pending a proper security approach including
|
||||||
|
* access control and authentication at a network and software level
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
headers.add("Access-Control-Allow-Origin","*")
|
||||||
|
|
||||||
|
if(requestContext.method == "OPTIONS") {
|
||||||
|
headers.add("Access-Control-Allow-Headers", "Content-Type,Accept,Origin")
|
||||||
|
headers.add("Access-Control-Allow-Methods", "POST,PUT,GET,OPTIONS")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
|
|
||||||
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
|
|
||||||
* set forth therein.
|
|
||||||
*
|
|
||||||
* All other rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package core.messaging
|
|
||||||
|
|
||||||
import core.Party
|
|
||||||
import core.crypto.DummyPublicKey
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/** Info about a network node that has is operated by some sort of verified identity. */
|
|
||||||
data class LegallyIdentifiableNode(val address: SingleMessageRecipient, val identity: Party)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A network map contains lists of nodes on the network along with information about their identity keys, services
|
|
||||||
* they provide and host names or IP addresses where they can be connected to. A reasonable architecture for the
|
|
||||||
* network map service might be one like the Tor directory authorities, where several nodes linked by RAFT or Paxos
|
|
||||||
* elect a leader and that leader distributes signed documents describing the network layout. Those documents can
|
|
||||||
* then be cached by every node and thus a network map can be retrieved given only a single successful peer connection.
|
|
||||||
*
|
|
||||||
* This interface assumes fast, synchronous access to an in-memory map.
|
|
||||||
*/
|
|
||||||
interface NetworkMapService {
|
|
||||||
val timestampingNodes: List<LegallyIdentifiableNode>
|
|
||||||
val partyNodes: List<LegallyIdentifiableNode>
|
|
||||||
|
|
||||||
fun nodeForPartyName(name: String): LegallyIdentifiableNode? = partyNodes.singleOrNull { it.identity.name == name }
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Move this to the test tree once a real network map is implemented and this scaffolding is no longer needed.
|
|
||||||
class MockNetworkMapService : NetworkMapService {
|
|
||||||
|
|
||||||
data class MockAddress(val id: String): SingleMessageRecipient
|
|
||||||
|
|
||||||
override val timestampingNodes = Collections.synchronizedList(ArrayList<LegallyIdentifiableNode>())
|
|
||||||
override val partyNodes = Collections.synchronizedList(ArrayList<LegallyIdentifiableNode>())
|
|
||||||
|
|
||||||
init {
|
|
||||||
partyNodes.add(LegallyIdentifiableNode(MockAddress("excalibur:8080"), Party("Excalibur", DummyPublicKey("Excalibur"))))
|
|
||||||
partyNodes.add(LegallyIdentifiableNode(MockAddress("another:8080"), Party("ANOther", DummyPublicKey("ANOther"))))
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -168,10 +168,11 @@ class StateMachineManager(val serviceHub: ServiceHub, val runInThread: Executor)
|
|||||||
fun <T> add(loggerName: String, logic: ProtocolLogic<T>): ListenableFuture<T> {
|
fun <T> add(loggerName: String, logic: ProtocolLogic<T>): ListenableFuture<T> {
|
||||||
val logger = LoggerFactory.getLogger(loggerName)
|
val logger = LoggerFactory.getLogger(loggerName)
|
||||||
val fiber = ProtocolStateMachine(logic)
|
val fiber = ProtocolStateMachine(logic)
|
||||||
|
// Need to add before iterating in case of immediate completion
|
||||||
|
_stateMachines.add(logic)
|
||||||
iterateStateMachine(fiber, serviceHub.networkService, logger, null, null) {
|
iterateStateMachine(fiber, serviceHub.networkService, logger, null, null) {
|
||||||
it.start()
|
it.start()
|
||||||
}
|
}
|
||||||
_stateMachines.add(logic)
|
|
||||||
totalStartedProtocols.inc()
|
totalStartedProtocols.inc()
|
||||||
return fiber.resultFuture
|
return fiber.resultFuture
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,12 @@ import api.APIServer
|
|||||||
import api.APIServerImpl
|
import api.APIServerImpl
|
||||||
import com.codahale.metrics.MetricRegistry
|
import com.codahale.metrics.MetricRegistry
|
||||||
import contracts.*
|
import contracts.*
|
||||||
import core.*
|
import core.Contract
|
||||||
|
import core.Party
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.crypto.generateKeyPair
|
import core.crypto.generateKeyPair
|
||||||
import core.messaging.*
|
import core.messaging.MessagingService
|
||||||
|
import core.messaging.StateMachineManager
|
||||||
import core.node.services.*
|
import core.node.services.*
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
import core.serialization.serialize
|
import core.serialization.serialize
|
||||||
@ -68,7 +70,11 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
|
|||||||
override val clock: Clock get() = platformClock
|
override val clock: Clock get() = platformClock
|
||||||
}
|
}
|
||||||
|
|
||||||
val legallyIdentifableAddress: LegallyIdentifiableNode get() = LegallyIdentifiableNode(net.myAddress, storage.myLegalIdentity)
|
val legallyIdentifiableAddress: LegallyIdentifiableNode by lazy {
|
||||||
|
LegallyIdentifiableNode(net.myAddress, storage.myLegalIdentity, findMyLocation())
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun findMyLocation(): PhysicalLocation? = CityDatabase[configuration.nearestCity]
|
||||||
|
|
||||||
lateinit var storage: StorageService
|
lateinit var storage: StorageService
|
||||||
lateinit var smm: StateMachineManager
|
lateinit var smm: StateMachineManager
|
||||||
|
@ -9,9 +9,10 @@
|
|||||||
package core.node
|
package core.node
|
||||||
|
|
||||||
import api.Config
|
import api.Config
|
||||||
|
import api.ResponseFilter
|
||||||
import com.codahale.metrics.JmxReporter
|
import com.codahale.metrics.JmxReporter
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import core.messaging.LegallyIdentifiableNode
|
import core.node.services.LegallyIdentifiableNode
|
||||||
import core.messaging.MessagingService
|
import core.messaging.MessagingService
|
||||||
import core.node.services.ArtemisMessagingService
|
import core.node.services.ArtemisMessagingService
|
||||||
import core.node.servlets.AttachmentDownloadServlet
|
import core.node.servlets.AttachmentDownloadServlet
|
||||||
@ -94,6 +95,7 @@ class Node(dir: Path, val p2pAddr: HostAndPort, configuration: NodeConfiguration
|
|||||||
val resourceConfig = ResourceConfig()
|
val resourceConfig = ResourceConfig()
|
||||||
// Add your API provider classes (annotated for JAX-RS) here
|
// Add your API provider classes (annotated for JAX-RS) here
|
||||||
resourceConfig.register(Config(services))
|
resourceConfig.register(Config(services))
|
||||||
|
resourceConfig.register(ResponseFilter())
|
||||||
resourceConfig.register(api)
|
resourceConfig.register(api)
|
||||||
// Give the app a slightly better name in JMX rather than a randomly generated one and enable JMX
|
// Give the app a slightly better name in JMX rather than a randomly generated one and enable JMX
|
||||||
resourceConfig.addProperties(mapOf(ServerProperties.APPLICATION_NAME to "node.api",
|
resourceConfig.addProperties(mapOf(ServerProperties.APPLICATION_NAME to "node.api",
|
||||||
|
@ -15,6 +15,7 @@ import kotlin.reflect.KProperty
|
|||||||
interface NodeConfiguration {
|
interface NodeConfiguration {
|
||||||
val myLegalName: String
|
val myLegalName: String
|
||||||
val exportJMXto: String
|
val exportJMXto: String
|
||||||
|
val nearestCity: String
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow the use of "String by config" syntax. TODO: Make it more flexible.
|
// Allow the use of "String by config" syntax. TODO: Make it more flexible.
|
||||||
@ -23,4 +24,5 @@ operator fun Config.getValue(receiver: NodeConfigurationFromConfig, metadata: KP
|
|||||||
class NodeConfigurationFromConfig(val config: Config = ConfigFactory.load()) : NodeConfiguration {
|
class NodeConfigurationFromConfig(val config: Config = ConfigFactory.load()) : NodeConfiguration {
|
||||||
override val myLegalName: String by config
|
override val myLegalName: String by config
|
||||||
override val exportJMXto: String by config
|
override val exportJMXto: String by config
|
||||||
|
override val nearestCity: String by config
|
||||||
}
|
}
|
@ -135,43 +135,45 @@ class ArtemisMessagingService(val directory: Path, val myHostPort: HostAndPort)
|
|||||||
session.createQueue(myHostPort.toString(), "inbound", false)
|
session.createQueue(myHostPort.toString(), "inbound", false)
|
||||||
inboundConsumer = session.createConsumer("inbound").setMessageHandler { message: ClientMessage ->
|
inboundConsumer = session.createConsumer("inbound").setMessageHandler { message: ClientMessage ->
|
||||||
// This code runs for every inbound message.
|
// This code runs for every inbound message.
|
||||||
if (!message.containsProperty(TOPIC_PROPERTY)) {
|
try {
|
||||||
log.warn("Received message without a ${TOPIC_PROPERTY} property, ignoring")
|
if (!message.containsProperty(TOPIC_PROPERTY)) {
|
||||||
// TODO: Figure out whether we always need to acknowledge messages, even when invalid.
|
log.warn("Received message without a ${TOPIC_PROPERTY} property, ignoring")
|
||||||
return@setMessageHandler
|
return@setMessageHandler
|
||||||
}
|
}
|
||||||
val topic = message.getStringProperty(TOPIC_PROPERTY)
|
val topic = message.getStringProperty(TOPIC_PROPERTY)
|
||||||
// Because handlers is a COW list, the loop inside filter will operate on a snapshot. Handlers being added
|
// Because handlers is a COW list, the loop inside filter will operate on a snapshot. Handlers being added
|
||||||
// or removed whilst the filter is executing will not affect anything.
|
// or removed whilst the filter is executing will not affect anything.
|
||||||
val deliverTo = handlers.filter { if (it.topic.isBlank()) true else it.topic == topic }
|
val deliverTo = handlers.filter { if (it.topic.isBlank()) true else it.topic == topic }
|
||||||
|
|
||||||
if (deliverTo.isEmpty()) {
|
if (deliverTo.isEmpty()) {
|
||||||
// This should probably be downgraded to a trace in future, so the protocol can evolve with new topics
|
// This should probably be downgraded to a trace in future, so the protocol can evolve with new topics
|
||||||
// without causing log spam.
|
// without causing log spam.
|
||||||
log.warn("Received message for $topic that doesn't have any registered handlers.")
|
log.warn("Received message for $topic that doesn't have any registered handlers.")
|
||||||
return@setMessageHandler
|
return@setMessageHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
val bits = ByteArray(message.bodySize)
|
val bits = ByteArray(message.bodySize)
|
||||||
message.bodyBuffer.readBytes(bits)
|
message.bodyBuffer.readBytes(bits)
|
||||||
|
|
||||||
val msg = object : Message {
|
val msg = object : Message {
|
||||||
override val topic = topic
|
override val topic = topic
|
||||||
override val data: ByteArray = bits
|
override val data: ByteArray = bits
|
||||||
override val debugTimestamp: Instant = Instant.ofEpochMilli(message.timestamp)
|
override val debugTimestamp: Instant = Instant.ofEpochMilli(message.timestamp)
|
||||||
override val debugMessageID: String = message.messageID.toString()
|
override val debugMessageID: String = message.messageID.toString()
|
||||||
override fun serialise(): ByteArray = bits
|
override fun serialise(): ByteArray = bits
|
||||||
}
|
}
|
||||||
for (handler in deliverTo) {
|
for (handler in deliverTo) {
|
||||||
(handler.executor ?: RunOnCallerThread).execute {
|
(handler.executor ?: RunOnCallerThread).execute {
|
||||||
try {
|
try {
|
||||||
handler.callback(msg, handler)
|
handler.callback(msg, handler)
|
||||||
} catch(e: Exception) {
|
} catch(e: Exception) {
|
||||||
log.error("Caught exception whilst executing message handler for $topic", e)
|
log.error("Caught exception whilst executing message handler for $topic", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
message.acknowledge()
|
||||||
}
|
}
|
||||||
message.acknowledge()
|
|
||||||
}
|
}
|
||||||
session.start()
|
session.start()
|
||||||
|
|
||||||
|
106
src/main/kotlin/core/node/services/NetworkMapService.kt
Normal file
106
src/main/kotlin/core/node/services/NetworkMapService.kt
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
|
||||||
|
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
|
||||||
|
* set forth therein.
|
||||||
|
*
|
||||||
|
* All other rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package core.node.services
|
||||||
|
|
||||||
|
import core.Party
|
||||||
|
import core.crypto.DummyPublicKey
|
||||||
|
import core.messaging.SingleMessageRecipient
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/** Info about a network node that has operated by some sort of verified identity. */
|
||||||
|
data class LegallyIdentifiableNode(val address: SingleMessageRecipient, val identity: Party,
|
||||||
|
val physicalLocation: PhysicalLocation? = null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A network map contains lists of nodes on the network along with information about their identity keys, services
|
||||||
|
* they provide and host names or IP addresses where they can be connected to. A reasonable architecture for the
|
||||||
|
* network map service might be one like the Tor directory authorities, where several nodes linked by RAFT or Paxos
|
||||||
|
* elect a leader and that leader distributes signed documents describing the network layout. Those documents can
|
||||||
|
* then be cached by every node and thus a network map can be retrieved given only a single successful peer connection.
|
||||||
|
*
|
||||||
|
* This interface assumes fast, synchronous access to an in-memory map.
|
||||||
|
*/
|
||||||
|
interface NetworkMapService {
|
||||||
|
val timestampingNodes: List<LegallyIdentifiableNode>
|
||||||
|
val ratesOracleNodes: List<LegallyIdentifiableNode>
|
||||||
|
val partyNodes: List<LegallyIdentifiableNode>
|
||||||
|
|
||||||
|
fun nodeForPartyName(name: String): LegallyIdentifiableNode? = partyNodes.singleOrNull { it.identity.name == name }
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move this to the test tree once a real network map is implemented and this scaffolding is no longer needed.
|
||||||
|
class MockNetworkMapService : NetworkMapService {
|
||||||
|
data class MockAddress(val id: String): SingleMessageRecipient
|
||||||
|
|
||||||
|
override val timestampingNodes = Collections.synchronizedList(ArrayList<LegallyIdentifiableNode>())
|
||||||
|
override val ratesOracleNodes = Collections.synchronizedList(ArrayList<LegallyIdentifiableNode>())
|
||||||
|
override val partyNodes = Collections.synchronizedList(ArrayList<LegallyIdentifiableNode>())
|
||||||
|
|
||||||
|
init {
|
||||||
|
partyNodes.add(LegallyIdentifiableNode(MockAddress("excalibur:8080"), Party("Excalibur", DummyPublicKey("Excalibur"))))
|
||||||
|
partyNodes.add(LegallyIdentifiableNode(MockAddress("another:8080"), Party("ANOther", DummyPublicKey("ANOther"))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A latitude/longitude pair. */
|
||||||
|
data class WorldCoordinate(val latitude: Double, val longitude: Double) {
|
||||||
|
init {
|
||||||
|
require(latitude in -90..90)
|
||||||
|
require(longitude in -180..180)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to screen coordinates using the Mercator projection. You should have a world map image that
|
||||||
|
* you know the precise extents of for this function to work.
|
||||||
|
*
|
||||||
|
* Note that no world map ever has latitude extents of -90 to 90 because at these extremes the mapping tends
|
||||||
|
* to infinity. Google Maps, for example, uses a square map image, and square maps yield latitude extents
|
||||||
|
* of 85.0511 to -85.0511 = arctan(sinh(π)).
|
||||||
|
*/
|
||||||
|
fun project(screenWidth: Double, screenHeight: Double, topLatitude: Double, bottomLatitude: Double,
|
||||||
|
leftLongitude: Double, rightLongitude: Double): Pair<Double, Double> {
|
||||||
|
require(latitude in bottomLatitude..topLatitude)
|
||||||
|
require(longitude in leftLongitude..rightLongitude)
|
||||||
|
|
||||||
|
fun deg2rad(deg: Double) = deg * Math.PI / 180.0
|
||||||
|
val leftLngRad = deg2rad(leftLongitude)
|
||||||
|
val rightLngRad = deg2rad(rightLongitude)
|
||||||
|
fun longitudeToScreenX(lng: Double) = screenWidth * (deg2rad(lng) - leftLngRad) / (rightLngRad - leftLngRad)
|
||||||
|
fun screenYRelative(latDeg: Double) = Math.log(Math.tan(latDeg / 360.0 * Math.PI + Math.PI / 4))
|
||||||
|
val topLatRel = screenYRelative(topLatitude)
|
||||||
|
val bottomLatRel = screenYRelative(bottomLatitude)
|
||||||
|
fun latitudeToScreenY(lat: Double) = screenHeight * (screenYRelative(lat) - topLatRel) / (bottomLatRel - topLatRel)
|
||||||
|
return Pair(longitudeToScreenX(longitude), latitudeToScreenY(latitude))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A labelled [WorldCoordinate], where the label is human meaningful. For example, the name of the nearest city.
|
||||||
|
* Labels should not refer to non-landmarks, for example, they should not contain the names of organisations.
|
||||||
|
*/
|
||||||
|
data class PhysicalLocation(val coordinate: WorldCoordinate, val description: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple lookup table of city names to their coordinates. Lookups are case insensitive.
|
||||||
|
*/
|
||||||
|
object CityDatabase {
|
||||||
|
private val cityMap = HashMap<String, PhysicalLocation>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
javaClass.getResourceAsStream("cities.txt").bufferedReader().useLines { lines ->
|
||||||
|
for (line in lines) {
|
||||||
|
if (line.startsWith("#")) continue
|
||||||
|
val (name, lng, lat) = line.split('\t')
|
||||||
|
cityMap[name.toLowerCase()] = PhysicalLocation(WorldCoordinate(lat.toDouble(), lng.toDouble()), name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(name: String) = cityMap[name.toLowerCase()]
|
||||||
|
}
|
@ -13,7 +13,7 @@ import contracts.Cash
|
|||||||
import core.*
|
import core.*
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.messaging.MessagingService
|
import core.messaging.MessagingService
|
||||||
import core.messaging.NetworkMapService
|
import core.node.services.NetworkMapService
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
|
@ -11,17 +11,22 @@ package core.testing
|
|||||||
import com.google.common.util.concurrent.Futures
|
import com.google.common.util.concurrent.Futures
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import com.google.common.util.concurrent.MoreExecutors
|
import com.google.common.util.concurrent.MoreExecutors
|
||||||
import core.node.services.DummyTimestampingAuthority
|
|
||||||
import core.ThreadBox
|
import core.ThreadBox
|
||||||
import core.crypto.sha256
|
import core.crypto.sha256
|
||||||
import core.messaging.*
|
import core.messaging.*
|
||||||
|
import core.node.services.DummyTimestampingAuthority
|
||||||
|
import core.node.services.LegallyIdentifiableNode
|
||||||
import core.node.services.NodeTimestamperService
|
import core.node.services.NodeTimestamperService
|
||||||
import core.utilities.loggerFor
|
import core.utilities.loggerFor
|
||||||
|
import rx.Observable
|
||||||
|
import rx.subjects.PublishSubject
|
||||||
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import java.util.concurrent.LinkedBlockingQueue
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
import kotlin.concurrent.schedule
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,8 +71,33 @@ class InMemoryMessagingNetwork {
|
|||||||
return Builder(manuallyPumped, Handle(id))
|
return Builder(manuallyPumped, Handle(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _allMessages = PublishSubject.create<Triple<SingleMessageRecipient, Message, MessageRecipients>>()
|
||||||
|
/** A stream of (sender, message, recipients) triples */
|
||||||
|
val allMessages: Observable<Triple<SingleMessageRecipient, Message, MessageRecipients>> = _allMessages
|
||||||
|
|
||||||
|
interface LatencyCalculator {
|
||||||
|
fun between(sender: SingleMessageRecipient, receiver: SingleMessageRecipient): Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This can be set to an object which can inject artificial latency between sender/recipient pairs. */
|
||||||
|
@Volatile var latencyCalculator: LatencyCalculator? = null
|
||||||
|
private val timer = Timer()
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun msgSend(message: Message, recipients: MessageRecipients) {
|
private fun msgSend(from: InMemoryMessaging, message: Message, recipients: MessageRecipients) {
|
||||||
|
val calc = latencyCalculator
|
||||||
|
if (calc != null && recipients is SingleMessageRecipient) {
|
||||||
|
// Inject some artificial latency.
|
||||||
|
timer.schedule(calc.between(from.myAddress, recipients).toMillis()) {
|
||||||
|
msgSendInternal(from, message, recipients)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msgSendInternal(from, message, recipients)
|
||||||
|
}
|
||||||
|
_allMessages.onNext(Triple(from.myAddress, message, recipients))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun msgSendInternal(from: InMemoryMessaging, message: Message, recipients: MessageRecipients) {
|
||||||
when (recipients) {
|
when (recipients) {
|
||||||
is Handle -> getQueueForHandle(recipients).add(message)
|
is Handle -> getQueueForHandle(recipients).add(message)
|
||||||
|
|
||||||
@ -173,7 +203,7 @@ class InMemoryMessagingNetwork {
|
|||||||
Pair(handler, items)
|
Pair(handler, items)
|
||||||
}
|
}
|
||||||
for (it in items)
|
for (it in items)
|
||||||
msgSend(it, handle)
|
msgSend(this, it, handle)
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +214,7 @@ class InMemoryMessagingNetwork {
|
|||||||
|
|
||||||
override fun send(message: Message, target: MessageRecipients) {
|
override fun send(message: Message, target: MessageRecipients) {
|
||||||
check(running)
|
check(running)
|
||||||
msgSend(message, target)
|
msgSend(this, message, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
|
@ -11,11 +11,13 @@ package core.testing
|
|||||||
import com.google.common.jimfs.Jimfs
|
import com.google.common.jimfs.Jimfs
|
||||||
import com.google.common.util.concurrent.MoreExecutors
|
import com.google.common.util.concurrent.MoreExecutors
|
||||||
import core.Party
|
import core.Party
|
||||||
import core.messaging.LegallyIdentifiableNode
|
|
||||||
import core.messaging.MessagingService
|
import core.messaging.MessagingService
|
||||||
|
import core.messaging.SingleMessageRecipient
|
||||||
import core.node.AbstractNode
|
import core.node.AbstractNode
|
||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
import core.node.services.FixedIdentityService
|
import core.node.services.FixedIdentityService
|
||||||
|
import core.node.services.LegallyIdentifiableNode
|
||||||
|
import core.node.services.PhysicalLocation
|
||||||
import core.utilities.loggerFor
|
import core.utilities.loggerFor
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@ -33,7 +35,8 @@ import java.util.concurrent.Executors
|
|||||||
* for message exchanges to take place (and associated handlers to run), you must call the [runNetwork]
|
* for message exchanges to take place (and associated handlers to run), you must call the [runNetwork]
|
||||||
* method.
|
* method.
|
||||||
*/
|
*/
|
||||||
class MockNetwork(private val threadPerNode: Boolean = false) {
|
class MockNetwork(private val threadPerNode: Boolean = false,
|
||||||
|
private val defaultFactory: Factory = MockNetwork.DefaultFactory) {
|
||||||
private var counter = 0
|
private var counter = 0
|
||||||
val filesystem = Jimfs.newFileSystem(com.google.common.jimfs.Configuration.unix())
|
val filesystem = Jimfs.newFileSystem(com.google.common.jimfs.Configuration.unix())
|
||||||
val messagingNetwork = InMemoryMessagingNetwork()
|
val messagingNetwork = InMemoryMessagingNetwork()
|
||||||
@ -48,6 +51,19 @@ class MockNetwork(private val threadPerNode: Boolean = false) {
|
|||||||
Files.createDirectory(filesystem.getPath("/nodes"))
|
Files.createDirectory(filesystem.getPath("/nodes"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Allows customisation of how nodes are created. */
|
||||||
|
interface Factory {
|
||||||
|
fun create(dir: Path, config: NodeConfiguration, network: MockNetwork,
|
||||||
|
timestamperAddr: LegallyIdentifiableNode?): MockNode
|
||||||
|
}
|
||||||
|
|
||||||
|
object DefaultFactory : Factory {
|
||||||
|
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork,
|
||||||
|
timestamperAddr: LegallyIdentifiableNode?): MockNode {
|
||||||
|
return MockNode(dir, config, network, timestamperAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open class MockNode(dir: Path, config: NodeConfiguration, val mockNet: MockNetwork,
|
open class MockNode(dir: Path, config: NodeConfiguration, val mockNet: MockNetwork,
|
||||||
withTimestamper: LegallyIdentifiableNode?, val forcedID: Int = -1) : AbstractNode(dir, config, withTimestamper, Clock.systemUTC()) {
|
withTimestamper: LegallyIdentifiableNode?, val forcedID: Int = -1) : AbstractNode(dir, config, withTimestamper, Clock.systemUTC()) {
|
||||||
override val log: Logger = loggerFor<MockNode>()
|
override val log: Logger = loggerFor<MockNode>()
|
||||||
@ -69,6 +85,9 @@ class MockNetwork(private val threadPerNode: Boolean = false) {
|
|||||||
|
|
||||||
override fun makeIdentityService() = FixedIdentityService(mockNet.identities)
|
override fun makeIdentityService() = FixedIdentityService(mockNet.identities)
|
||||||
|
|
||||||
|
// There is no need to slow down the unit tests by initialising CityDatabase
|
||||||
|
override fun findMyLocation(): PhysicalLocation? = null
|
||||||
|
|
||||||
override fun start(): MockNode {
|
override fun start(): MockNode {
|
||||||
super.start()
|
super.start()
|
||||||
mockNet.identities.add(storage.myLegalIdentity)
|
mockNet.identities.add(storage.myLegalIdentity)
|
||||||
@ -77,8 +96,7 @@ class MockNetwork(private val threadPerNode: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a started node, optionally created by the passed factory method */
|
/** Returns a started node, optionally created by the passed factory method */
|
||||||
fun createNode(withTimestamper: LegallyIdentifiableNode?, forcedID: Int = -1,
|
fun createNode(withTimestamper: LegallyIdentifiableNode?, forcedID: Int = -1, nodeFactory: Factory = defaultFactory): MockNode {
|
||||||
factory: ((Path, NodeConfiguration, network: MockNetwork, LegallyIdentifiableNode?) -> MockNode)? = null): MockNode {
|
|
||||||
val newNode = forcedID == -1
|
val newNode = forcedID == -1
|
||||||
val id = if (newNode) counter++ else forcedID
|
val id = if (newNode) counter++ else forcedID
|
||||||
|
|
||||||
@ -88,9 +106,9 @@ class MockNetwork(private val threadPerNode: Boolean = false) {
|
|||||||
val config = object : NodeConfiguration {
|
val config = object : NodeConfiguration {
|
||||||
override val myLegalName: String = "Mock Company $id"
|
override val myLegalName: String = "Mock Company $id"
|
||||||
override val exportJMXto: String = ""
|
override val exportJMXto: String = ""
|
||||||
|
override val nearestCity: String = "Atlantis"
|
||||||
}
|
}
|
||||||
val fac = factory ?: { p, n, n2, l -> MockNode(p, n, n2, l, id) }
|
val node = nodeFactory.create(path, config, this, withTimestamper).start()
|
||||||
val node = fac(path, config, this, withTimestamper).start()
|
|
||||||
_nodes.add(node)
|
_nodes.add(node)
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
@ -112,8 +130,10 @@ class MockNetwork(private val threadPerNode: Boolean = false) {
|
|||||||
/**
|
/**
|
||||||
* Sets up a two node network in which the first node runs a timestamping service and the other doesn't.
|
* Sets up a two node network in which the first node runs a timestamping service and the other doesn't.
|
||||||
*/
|
*/
|
||||||
fun createTwoNodes(factory: ((Path, NodeConfiguration, network: MockNetwork, LegallyIdentifiableNode?) -> MockNode)? = null): Pair<MockNode, MockNode> {
|
fun createTwoNodes(nodeFactory: Factory = defaultFactory): Pair<MockNode, MockNode> {
|
||||||
require(nodes.isEmpty())
|
require(nodes.isEmpty())
|
||||||
return Pair(createNode(null, -1, factory), createNode(nodes[0].legallyIdentifableAddress, -1, factory))
|
return Pair(createNode(null, -1, nodeFactory), createNode(nodes[0].legallyIdentifiableAddress, -1, nodeFactory))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addressToNode(address: SingleMessageRecipient): MockNode = nodes.single { it.net.myAddress == address }
|
||||||
}
|
}
|
34
src/main/kotlin/demos/DemoClock.kt
Normal file
34
src/main/kotlin/demos/DemoClock.kt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package demos
|
||||||
|
|
||||||
|
import java.time.*
|
||||||
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Clock] that can have the date advanced for use in demos
|
||||||
|
*/
|
||||||
|
@ThreadSafe
|
||||||
|
class DemoClock(private var delegateClock: Clock = Clock.systemUTC()) : Clock() {
|
||||||
|
|
||||||
|
@Synchronized fun updateDate(date: LocalDate): Boolean {
|
||||||
|
val currentDate = LocalDate.now(this)
|
||||||
|
if(currentDate.isBefore(date)) {
|
||||||
|
// It's ok to increment
|
||||||
|
delegateClock = Clock.offset(delegateClock, Duration.between(currentDate.atStartOfDay(),date.atStartOfDay()))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized override fun instant(): Instant {
|
||||||
|
return delegateClock.instant()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized override fun withZone(zone: ZoneId): Clock {
|
||||||
|
return DemoClock(delegateClock.withZone(zone))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized override fun getZone(): ZoneId {
|
||||||
|
return delegateClock.zone
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -10,10 +10,10 @@ package demos
|
|||||||
|
|
||||||
import contracts.Cash
|
import contracts.Cash
|
||||||
import core.*
|
import core.*
|
||||||
import core.messaging.LegallyIdentifiableNode
|
|
||||||
import core.node.Node
|
import core.node.Node
|
||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
import core.node.services.ArtemisMessagingService
|
import core.node.services.ArtemisMessagingService
|
||||||
|
import core.node.services.LegallyIdentifiableNode
|
||||||
import core.node.services.NodeInterestRates
|
import core.node.services.NodeInterestRates
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
import core.utilities.ANSIProgressRenderer
|
import core.utilities.ANSIProgressRenderer
|
||||||
@ -71,6 +71,7 @@ fun main(args: Array<String>) {
|
|||||||
val config = object : NodeConfiguration {
|
val config = object : NodeConfiguration {
|
||||||
override val myLegalName: String = "Rate fix demo node"
|
override val myLegalName: String = "Rate fix demo node"
|
||||||
override val exportJMXto: String = "http"
|
override val exportJMXto: String = "http"
|
||||||
|
override val nearestCity: String = "Atlantis"
|
||||||
}
|
}
|
||||||
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, null).start() }
|
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, config, null).start() }
|
||||||
|
|
||||||
|
@ -15,12 +15,12 @@ import contracts.CommercialPaper
|
|||||||
import core.*
|
import core.*
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.crypto.generateKeyPair
|
import core.crypto.generateKeyPair
|
||||||
import core.messaging.LegallyIdentifiableNode
|
|
||||||
import core.messaging.SingleMessageRecipient
|
import core.messaging.SingleMessageRecipient
|
||||||
import core.node.Node
|
import core.node.Node
|
||||||
import core.node.NodeConfiguration
|
import core.node.NodeConfiguration
|
||||||
import core.node.NodeConfigurationFromConfig
|
import core.node.NodeConfigurationFromConfig
|
||||||
import core.node.services.ArtemisMessagingService
|
import core.node.services.ArtemisMessagingService
|
||||||
|
import core.node.services.LegallyIdentifiableNode
|
||||||
import core.node.services.NodeAttachmentService
|
import core.node.services.NodeAttachmentService
|
||||||
import core.node.services.NodeWalletService
|
import core.node.services.NodeWalletService
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
|
@ -11,7 +11,7 @@ package protocols
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import core.*
|
import core.*
|
||||||
import core.crypto.DigitalSignature
|
import core.crypto.DigitalSignature
|
||||||
import core.messaging.LegallyIdentifiableNode
|
import core.node.services.LegallyIdentifiableNode
|
||||||
import core.messaging.SingleMessageRecipient
|
import core.messaging.SingleMessageRecipient
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.utilities.ProgressTracker
|
import core.utilities.ProgressTracker
|
||||||
@ -50,10 +50,13 @@ open class RatesFixProtocol(protected val tx: TransactionBuilder,
|
|||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call() {
|
override fun call() {
|
||||||
|
progressTracker.currentStep = progressTracker.steps[0]
|
||||||
val fix = query()
|
val fix = query()
|
||||||
|
progressTracker.currentStep = WORKING
|
||||||
checkFixIsNearExpected(fix)
|
checkFixIsNearExpected(fix)
|
||||||
tx.addCommand(fix, oracle.identity.owningKey)
|
tx.addCommand(fix, oracle.identity.owningKey)
|
||||||
beforeSigning(fix)
|
beforeSigning(fix)
|
||||||
|
progressTracker.currentStep = SIGNING
|
||||||
tx.addSignatureUnchecked(sign())
|
tx.addSignatureUnchecked(sign())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import core.Party
|
import core.Party
|
||||||
import core.WireTransaction
|
import core.WireTransaction
|
||||||
import core.crypto.DigitalSignature
|
import core.crypto.DigitalSignature
|
||||||
import core.messaging.LegallyIdentifiableNode
|
import core.node.services.LegallyIdentifiableNode
|
||||||
import core.messaging.MessageRecipients
|
import core.messaging.MessageRecipients
|
||||||
import core.messaging.StateMachineManager
|
import core.messaging.StateMachineManager
|
||||||
import core.node.services.NodeTimestamperService
|
import core.node.services.NodeTimestamperService
|
||||||
@ -20,6 +20,7 @@ import core.node.services.TimestamperService
|
|||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.random63BitValue
|
import core.random63BitValue
|
||||||
import core.serialization.SerializedBytes
|
import core.serialization.SerializedBytes
|
||||||
|
import core.utilities.ProgressTracker
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TimestampingProtocol class is the client code that talks to a [NodeTimestamperService] on some remote node. It is a
|
* The TimestampingProtocol class is the client code that talks to a [NodeTimestamperService] on some remote node. It is a
|
||||||
@ -31,7 +32,8 @@ import core.serialization.SerializedBytes
|
|||||||
* a network message: use it only from spare application threads that don't have to respond to anything.
|
* a network message: use it only from spare application threads that don't have to respond to anything.
|
||||||
*/
|
*/
|
||||||
class TimestampingProtocol(private val node: LegallyIdentifiableNode,
|
class TimestampingProtocol(private val node: LegallyIdentifiableNode,
|
||||||
private val wtxBytes: SerializedBytes<WireTransaction>) : ProtocolLogic<DigitalSignature.LegallyIdentifiable>() {
|
private val wtxBytes: SerializedBytes<WireTransaction>,
|
||||||
|
override val progressTracker: ProgressTracker = TimestampingProtocol.tracker()) : ProtocolLogic<DigitalSignature.LegallyIdentifiable>() {
|
||||||
|
|
||||||
class Client(private val stateMachineManager: StateMachineManager, private val node: LegallyIdentifiableNode) : TimestamperService {
|
class Client(private val stateMachineManager: StateMachineManager, private val node: LegallyIdentifiableNode) : TimestamperService {
|
||||||
override val identity: Party = node.identity
|
override val identity: Party = node.identity
|
||||||
@ -41,9 +43,18 @@ class TimestampingProtocol(private val node: LegallyIdentifiableNode,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
object REQUESTING : ProgressTracker.Step("Requesting signature by timestamping service")
|
||||||
|
object VALIDATING : ProgressTracker.Step("Validating received signature from timestamping service")
|
||||||
|
|
||||||
|
fun tracker() = ProgressTracker(REQUESTING, VALIDATING)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): DigitalSignature.LegallyIdentifiable {
|
override fun call(): DigitalSignature.LegallyIdentifiable {
|
||||||
|
progressTracker.currentStep = REQUESTING
|
||||||
val sessionID = random63BitValue()
|
val sessionID = random63BitValue()
|
||||||
val replyTopic = "${NodeTimestamperService.TIMESTAMPING_PROTOCOL_TOPIC}.$sessionID"
|
val replyTopic = "${NodeTimestamperService.TIMESTAMPING_PROTOCOL_TOPIC}.$sessionID"
|
||||||
val req = Request(wtxBytes, serviceHub.networkService.myAddress, replyTopic)
|
val req = Request(wtxBytes, serviceHub.networkService.myAddress, replyTopic)
|
||||||
@ -52,6 +63,7 @@ class TimestampingProtocol(private val node: LegallyIdentifiableNode,
|
|||||||
NodeTimestamperService.TIMESTAMPING_PROTOCOL_TOPIC, node.address, 0, sessionID, req)
|
NodeTimestamperService.TIMESTAMPING_PROTOCOL_TOPIC, node.address, 0, sessionID, req)
|
||||||
|
|
||||||
// Check that the timestamping authority gave us back a valid signature and didn't break somehow
|
// Check that the timestamping authority gave us back a valid signature and didn't break somehow
|
||||||
|
progressTracker.currentStep = VALIDATING
|
||||||
maybeSignature.validate { sig ->
|
maybeSignature.validate { sig ->
|
||||||
sig.verifyWithECDSA(wtxBytes)
|
sig.verifyWithECDSA(wtxBytes)
|
||||||
return sig
|
return sig
|
||||||
|
@ -15,7 +15,7 @@ import contracts.sumCashBy
|
|||||||
import core.*
|
import core.*
|
||||||
import core.crypto.DigitalSignature
|
import core.crypto.DigitalSignature
|
||||||
import core.crypto.signWithECDSA
|
import core.crypto.signWithECDSA
|
||||||
import core.messaging.LegallyIdentifiableNode
|
import core.node.services.LegallyIdentifiableNode
|
||||||
import core.messaging.SingleMessageRecipient
|
import core.messaging.SingleMessageRecipient
|
||||||
import core.messaging.StateMachineManager
|
import core.messaging.StateMachineManager
|
||||||
import protocols.TimestampingProtocol
|
import protocols.TimestampingProtocol
|
||||||
|
756
src/main/resources/core/node/services/cities.txt
Normal file
756
src/main/resources/core/node/services/cities.txt
Normal file
@ -0,0 +1,756 @@
|
|||||||
|
# name longitude latitude
|
||||||
|
Shanghai 121.47 31.23
|
||||||
|
Bombay 72.82 18.96
|
||||||
|
Karachi 67.01 24.86
|
||||||
|
Buenos Aires -58.37 -34.61
|
||||||
|
Delhi 77.21 28.67
|
||||||
|
Istanbul 29 41.1
|
||||||
|
Manila 120.97 14.62
|
||||||
|
Sao Paulo -46.63 -23.53
|
||||||
|
Moscow 37.62 55.75
|
||||||
|
Dhaka 90.39 23.7
|
||||||
|
Soul 126.99 37.56
|
||||||
|
Lagos 3.35 6.5
|
||||||
|
Kinshasa 15.32 -4.31
|
||||||
|
Tokyo 139.77 35.67
|
||||||
|
Mexico City -99.14 19.43
|
||||||
|
Jakarta 106.83 -6.18
|
||||||
|
New York -73.94 40.67
|
||||||
|
Tehran 51.43 35.67
|
||||||
|
Cairo 31.25 30.06
|
||||||
|
Lima -77.05 -12.07
|
||||||
|
Peking 116.4 39.93
|
||||||
|
London -0.1 51.52
|
||||||
|
Bogota -74.09 4.63
|
||||||
|
Lahore 74.35 31.56
|
||||||
|
Rio de Janeiro -43.2 -22.91
|
||||||
|
Bangkok 100.5 13.73
|
||||||
|
Bagdad 44.44 33.33
|
||||||
|
Bangalore 77.56 12.97
|
||||||
|
Santiago -70.64 -33.46
|
||||||
|
Calcutta 88.36 22.57
|
||||||
|
Singapore 103.85 1.3
|
||||||
|
Toronto -79.38 43.65
|
||||||
|
Rangoon 96.15 16.79
|
||||||
|
Ibadan 3.93 7.38
|
||||||
|
Riyadh 46.77 24.65
|
||||||
|
Madras 80.27 13.09
|
||||||
|
Chongqing 106.58 29.57
|
||||||
|
Ho Chi Minh City 106.69 10.78
|
||||||
|
Xian 108.9 34.27
|
||||||
|
Wuhan 114.27 30.58
|
||||||
|
Alexandria 29.95 31.22
|
||||||
|
Saint Petersburg 30.32 59.93
|
||||||
|
Hyderabad 78.48 17.4
|
||||||
|
Chengdu 104.07 30.67
|
||||||
|
Abidjan -4.03 5.33
|
||||||
|
Ankara 32.85 39.93
|
||||||
|
Ahmadabad 72.58 23.03
|
||||||
|
Los Angeles -118.41 34.11
|
||||||
|
Tianjin 117.2 39.13
|
||||||
|
Chattagam 91.81 22.33
|
||||||
|
Sydney 151.21 -33.87
|
||||||
|
Yokohama 139.62 35.47
|
||||||
|
Melbourne 144.96 -37.81
|
||||||
|
Shenyang 123.45 41.8
|
||||||
|
Cape Town 18.46 -33.93
|
||||||
|
Berlin 13.38 52.52
|
||||||
|
Pusan 129.03 35.11
|
||||||
|
Montreal -73.57 45.52
|
||||||
|
Harbin 126.65 45.75
|
||||||
|
Durban 30.99 -29.87
|
||||||
|
Gizeh 31.21 30.01
|
||||||
|
Nanjing 118.78 32.05
|
||||||
|
Casablanca -7.62 33.6
|
||||||
|
Pune 73.84 18.53
|
||||||
|
Addis Abeba 38.74 9.03
|
||||||
|
Pyongyang 125.75 39.02
|
||||||
|
Surat 72.82 21.2
|
||||||
|
Madrid -3.71 40.42
|
||||||
|
Guangzhou 113.25 23.12
|
||||||
|
Jiddah 39.17 21.5
|
||||||
|
Kanpur 80.33 26.47
|
||||||
|
Nairobi 36.82 -1.29
|
||||||
|
Jaipur 75.8 26.92
|
||||||
|
Dar es Salaam 39.28 -6.82
|
||||||
|
Salvador -38.5 -12.97
|
||||||
|
Chicago -87.68 41.84
|
||||||
|
Taiyuan 112.55 37.87
|
||||||
|
al-Mawsil 43.14 36.34
|
||||||
|
Faisalabad 73.11 31.41
|
||||||
|
Changchun 125.35 43.87
|
||||||
|
Izmir 27.15 38.43
|
||||||
|
Taibei 121.45 25.02
|
||||||
|
Osaka 135.5 34.68
|
||||||
|
Lakhnau 80.92 26.85
|
||||||
|
Kiev 30.52 50.43
|
||||||
|
Luanda 13.24 -8.82
|
||||||
|
Inchon 126.64 37.48
|
||||||
|
Rome 12.5 41.89
|
||||||
|
Dakar -17.48 14.72
|
||||||
|
Belo Horizonte -43.94 -19.92
|
||||||
|
Fortaleza -38.59 -3.78
|
||||||
|
Mashhad 59.57 36.27
|
||||||
|
Maracaibo -71.66 10.73
|
||||||
|
Kabul 69.17 34.53
|
||||||
|
Santo Domingo -69.91 18.48
|
||||||
|
Taegu 128.6 35.87
|
||||||
|
Brasilia -47.91 -15.78
|
||||||
|
Umm Durman 32.48 15.65
|
||||||
|
Nagpur 79.08 21.16
|
||||||
|
Surabaya 112.74 -7.24
|
||||||
|
Kano 8.52 12
|
||||||
|
Medellin -75.54 6.29
|
||||||
|
Accra -0.2 5.56
|
||||||
|
Nagoya 136.91 35.15
|
||||||
|
Benin 5.62 6.34
|
||||||
|
Shijiazhuang 114.48 38.05
|
||||||
|
Guayaquil -79.9 -2.21
|
||||||
|
Changsha 112.97 28.2
|
||||||
|
Houston -95.39 29.77
|
||||||
|
Khartoum 32.52 15.58
|
||||||
|
Paris 2.34 48.86
|
||||||
|
Cali -76.52 3.44
|
||||||
|
Algiers 3.04 36.77
|
||||||
|
Jinan 117 36.67
|
||||||
|
Havanna -82.39 23.13
|
||||||
|
Tashkent 69.3 41.31
|
||||||
|
Dalian 121.65 38.92
|
||||||
|
Jilin 126.55 43.85
|
||||||
|
Nanchang 115.88 28.68
|
||||||
|
Zhengzhou 113.67 34.75
|
||||||
|
Vancouver -123.13 49.28
|
||||||
|
Johannesburg 28.04 -26.19
|
||||||
|
Bayrut 35.5 33.89
|
||||||
|
Douala 9.71 4.06
|
||||||
|
Jiulong 114.17 22.32
|
||||||
|
Caracas -66.93 10.54
|
||||||
|
Kaduna 7.44 10.52
|
||||||
|
Bucharest 26.1 44.44
|
||||||
|
Ecatepec -99.05 19.6
|
||||||
|
Sapporo 141.34 43.06
|
||||||
|
Port Harcourt 7.01 4.81
|
||||||
|
Hangzhou 120.17 30.25
|
||||||
|
Rawalpindi 73.04 33.6
|
||||||
|
San'a 44.21 15.38
|
||||||
|
Conakry -13.67 9.55
|
||||||
|
Curitiba -49.29 -25.42
|
||||||
|
al-Basrah 47.82 30.53
|
||||||
|
Brisbane 153.02 -27.46
|
||||||
|
Xinyang 114.07 32.13
|
||||||
|
Medan 98.67 3.59
|
||||||
|
Indore 75.86 22.72
|
||||||
|
Manaus -60.02 -3.12
|
||||||
|
Kumasi -1.63 6.69
|
||||||
|
Hamburg 10 53.55
|
||||||
|
Rabat -6.84 34.02
|
||||||
|
Minsk 27.55 53.91
|
||||||
|
Patna 85.13 25.62
|
||||||
|
Valencia -67.98 10.23
|
||||||
|
Bhopal 77.4 23.24
|
||||||
|
Soweto 27.84 -26.28
|
||||||
|
Warsaw 21.02 52.26
|
||||||
|
Qingdao 120.32 36.07
|
||||||
|
Vienna 16.37 48.22
|
||||||
|
Yaounde 11.52 3.87
|
||||||
|
Dubai 55.33 25.27
|
||||||
|
Thana 72.97 19.2
|
||||||
|
Aleppo 37.17 36.23
|
||||||
|
Bekasi 106.97 -6.22
|
||||||
|
Budapest 19.08 47.51
|
||||||
|
Bamako -7.99 12.65
|
||||||
|
Ludhiana 75.84 30.91
|
||||||
|
Harare 31.05 -17.82
|
||||||
|
Esfahan 51.68 32.68
|
||||||
|
Pretoria 28.22 -25.73
|
||||||
|
Barcelona 2.17 41.4
|
||||||
|
Lubumbashi 27.48 -11.66
|
||||||
|
Bandung 107.6 -6.91
|
||||||
|
Guadalajara -103.35 20.67
|
||||||
|
Tangshan 118.19 39.62
|
||||||
|
Muqdisho 45.33 2.05
|
||||||
|
Phoenix -112.07 33.54
|
||||||
|
Damascus 36.32 33.5
|
||||||
|
Quito -78.5 -0.19
|
||||||
|
Agra 78.01 27.19
|
||||||
|
Urumqi 87.58 43.8
|
||||||
|
Davao 125.63 7.11
|
||||||
|
Santa Cruz -63.21 -17.77
|
||||||
|
Antananarivo 47.51 -18.89
|
||||||
|
Kobe 135.17 34.68
|
||||||
|
Juarez -106.49 31.74
|
||||||
|
Tijuana -117.02 32.53
|
||||||
|
Recife -34.92 -8.08
|
||||||
|
Multan 71.45 30.2
|
||||||
|
Ha Noi 105.84 21.03
|
||||||
|
Gaoxiong 120.27 22.63
|
||||||
|
Belem -48.5 -1.44
|
||||||
|
Cordoba -64.19 -31.4
|
||||||
|
Kampala 32.58 0.32
|
||||||
|
Lome 1.35 6.17
|
||||||
|
Hyderabad 68.37 25.38
|
||||||
|
Suzhou 120.62 31.3
|
||||||
|
Vadodara 73.18 22.31
|
||||||
|
Gujranwala 74.18 32.16
|
||||||
|
Bursa 29.08 40.2
|
||||||
|
Mbuji-Mayi 23.59 -6.13
|
||||||
|
Pimpri 73.8 18.62
|
||||||
|
Karaj 50.97 35.8
|
||||||
|
Kyoto 135.75 35.01
|
||||||
|
Tangerang 106.63 -6.18
|
||||||
|
Aba 7.35 5.1
|
||||||
|
Kharkiv 36.22 49.98
|
||||||
|
Puebla -98.22 19.05
|
||||||
|
Nashik 73.78 20.01
|
||||||
|
Kuala Lumpur 101.71 3.16
|
||||||
|
Philadelphia -75.13 40.01
|
||||||
|
Fukuoka 130.41 33.59
|
||||||
|
Taejon 127.43 36.33
|
||||||
|
Lanzhou 103.68 36.05
|
||||||
|
Mecca 39.82 21.43
|
||||||
|
Shantou 116.67 23.37
|
||||||
|
Koyang 126.93 37.7
|
||||||
|
Hefei 117.28 31.85
|
||||||
|
Novosibirsk 82.93 55.04
|
||||||
|
Porto Alegre -51.22 -30.04
|
||||||
|
Adana 35.32 37
|
||||||
|
Makasar 119.41 -5.14
|
||||||
|
Tabriz 46.3 38.08
|
||||||
|
Narayanganj 90.5 23.62
|
||||||
|
Faridabad 77.3 28.38
|
||||||
|
Fushun 123.88 41.87
|
||||||
|
Phnum Penh 104.92 11.57
|
||||||
|
Luoyang 112.47 34.68
|
||||||
|
Khulna 89.56 22.84
|
||||||
|
Depok 106.83 -6.39
|
||||||
|
Lusaka 28.29 -15.42
|
||||||
|
Ghaziabad 77.41 28.66
|
||||||
|
Handan 114.48 36.58
|
||||||
|
San Antonio -98.51 29.46
|
||||||
|
Kawasaki 139.7 35.53
|
||||||
|
Kwangju 126.91 35.16
|
||||||
|
Peshawar 71.54 34.01
|
||||||
|
Rajkot 70.79 22.31
|
||||||
|
Suwon 127.01 37.26
|
||||||
|
Mandalay 96.09 21.98
|
||||||
|
Almaty 76.92 43.32
|
||||||
|
Munich 11.58 48.14
|
||||||
|
Mirat 77.7 28.99
|
||||||
|
Baotou 110.05 40.6
|
||||||
|
Milan 9.19 45.48
|
||||||
|
Rongcheng 116.34 23.54
|
||||||
|
Kalyan 73.16 19.25
|
||||||
|
Montevideo -56.17 -34.87
|
||||||
|
Xianggangdao 114.14 22.27
|
||||||
|
Yekaterinburg 60.6 56.85
|
||||||
|
Ouagadougou -1.53 12.37
|
||||||
|
Guarulhos -46.49 -23.46
|
||||||
|
Semarang 110.42 -6.97
|
||||||
|
Xuzhou 117.18 34.27
|
||||||
|
Perth 115.84 -31.96
|
||||||
|
Dallas -96.77 32.79
|
||||||
|
Stockholm 18.07 59.33
|
||||||
|
Palembang 104.75 -2.99
|
||||||
|
San Diego -117.14 32.81
|
||||||
|
Goiania -49.26 -16.72
|
||||||
|
Gaziantep 37.39 37.07
|
||||||
|
Nizhniy Novgorod 44 56.33
|
||||||
|
Shiraz 52.57 29.63
|
||||||
|
Rosario -60.67 -32.94
|
||||||
|
Fuzhou 119.3 26.08
|
||||||
|
Nezahualcoyotl -99.03 19.41
|
||||||
|
Saitama 139.64 35.87
|
||||||
|
Shenzhen 114.13 22.53
|
||||||
|
Yerevan 44.52 40.17
|
||||||
|
Tripoli 13.18 32.87
|
||||||
|
Anshan 122.95 41.12
|
||||||
|
Varanasi 83.01 25.32
|
||||||
|
Guiyang 106.72 26.58
|
||||||
|
Baku 49.86 40.39
|
||||||
|
Wuxi 120.3 31.58
|
||||||
|
Prague 14.43 50.08
|
||||||
|
Brazzaville 15.26 -4.25
|
||||||
|
Subang Jaya 101.53 3.15
|
||||||
|
Leon -101.69 21.12
|
||||||
|
Hiroshima 132.44 34.39
|
||||||
|
Amritsar 74.87 31.64
|
||||||
|
Huainan 116.98 32.63
|
||||||
|
Barranquilla -74.8 10.96
|
||||||
|
Monrovia -10.8 6.31
|
||||||
|
'Amman 35.93 31.95
|
||||||
|
Tbilisi 44.79 41.72
|
||||||
|
Abuja 7.49 9.06
|
||||||
|
Aurangabad 75.32 19.89
|
||||||
|
Sofia 23.31 42.69
|
||||||
|
Omsk 73.4 55
|
||||||
|
Monterrey -100.32 25.67
|
||||||
|
Port Elizabeth 25.59 -33.96
|
||||||
|
Navi Mumbai 73.06 19.11
|
||||||
|
Maputo 32.57 -25.95
|
||||||
|
Allahabad 81.84 25.45
|
||||||
|
Samara 50.15 53.2
|
||||||
|
Belgrade 20.5 44.83
|
||||||
|
Campinas -47.08 -22.91
|
||||||
|
Sholapur 75.89 17.67
|
||||||
|
Kazan 49.13 55.75
|
||||||
|
Irbil 44.01 36.18
|
||||||
|
Barquisimeto -69.3 10.05
|
||||||
|
K?benhavn 12.58 55.67
|
||||||
|
Xianyang 108.7 34.37
|
||||||
|
Baoding 115.48 38.87
|
||||||
|
Guatemala -90.55 14.63
|
||||||
|
Maceio -35.75 -9.65
|
||||||
|
Nova Iguacu -43.47 -22.74
|
||||||
|
Kunming 102.7 25.05
|
||||||
|
Taizhong 120.68 24.15
|
||||||
|
Maiduguri 13.16 11.85
|
||||||
|
Datong 113.3 40.08
|
||||||
|
Dublin -6.25 53.33
|
||||||
|
Jabalpur 79.94 23.17
|
||||||
|
Visakhapatnam 83.3 17.73
|
||||||
|
Rostov-na-Donu 39.71 47.24
|
||||||
|
Dnipropetrovs'k 34.98 48.45
|
||||||
|
Shubra-El-Khema 31.25 30.11
|
||||||
|
Srinagar 74.79 34.09
|
||||||
|
Benxi 123.75 41.33
|
||||||
|
Brussels 4.33 50.83
|
||||||
|
al-Madinah 39.59 24.48
|
||||||
|
Adelaide 138.6 -34.93
|
||||||
|
Zapopan -103.4 20.72
|
||||||
|
Chelyabinsk 61.43 55.15
|
||||||
|
Haora 88.33 22.58
|
||||||
|
Calgary -114.06 51.05
|
||||||
|
Sendai 140.89 38.26
|
||||||
|
Tegucigalpa -87.22 14.09
|
||||||
|
Ranchi 85.33 23.36
|
||||||
|
Songnam 127.15 37.44
|
||||||
|
Ilorin 4.55 8.49
|
||||||
|
Fez -5 34.05
|
||||||
|
Ufa 56.04 54.78
|
||||||
|
Klang 101.45 3.04
|
||||||
|
Chandigarh 76.78 30.75
|
||||||
|
Ahvaz 48.72 31.28
|
||||||
|
Koyampattur 76.96 11.01
|
||||||
|
Cologne 6.97 50.95
|
||||||
|
Qom 50.95 34.65
|
||||||
|
Odesa 30.73 46.47
|
||||||
|
Donetsk 37.82 48
|
||||||
|
Jodhpur 73.02 26.29
|
||||||
|
Sao Luis -44.3 -2.5
|
||||||
|
Sao Goncalo -43.07 -22.84
|
||||||
|
Kitakyushu 130.86 33.88
|
||||||
|
Huaibei 116.75 33.95
|
||||||
|
Perm 56.25 58
|
||||||
|
Changzhou 119.97 31.78
|
||||||
|
Maisuru 76.65 12.31
|
||||||
|
Guwahati 91.75 26.19
|
||||||
|
Volgograd 44.48 48.71
|
||||||
|
Konya 32.48 37.88
|
||||||
|
Naples 14.27 40.85
|
||||||
|
Vijayawada 80.63 16.52
|
||||||
|
Ulsan 129.31 35.55
|
||||||
|
San Jose -121.85 37.3
|
||||||
|
Birmingham -1.91 52.48
|
||||||
|
Chiba 140.11 35.61
|
||||||
|
Ciudad Guayana -62.62 8.37
|
||||||
|
Kolwezi 25.66 -10.7
|
||||||
|
Padang 100.35 -0.95
|
||||||
|
Managua -86.27 12.15
|
||||||
|
Mendoza -68.83 -32.89
|
||||||
|
Gwalior 78.17 26.23
|
||||||
|
Biskek 74.57 42.87
|
||||||
|
Kathmandu 85.31 27.71
|
||||||
|
El Alto -68.17 -16.5
|
||||||
|
Niamey 2.12 13.52
|
||||||
|
Kigali 30.06 -1.94
|
||||||
|
Qiqihar 124 47.35
|
||||||
|
Ulaanbaatar 106.91 47.93
|
||||||
|
Krasnoyarsk 93.06 56.02
|
||||||
|
Madurai 78.12 9.92
|
||||||
|
Edmonton -113.54 53.57
|
||||||
|
Asgabat 58.38 37.95
|
||||||
|
al-H?artum Bah?ri 32.52 15.64
|
||||||
|
Arequipa -71.53 -16.39
|
||||||
|
Marrakesh -8 31.63
|
||||||
|
Bandar Lampung 105.27 -5.44
|
||||||
|
Pingdingshan 113.3 33.73
|
||||||
|
Cartagena -75.5 10.4
|
||||||
|
Hubli 75.13 15.36
|
||||||
|
La Paz -68.15 -16.5
|
||||||
|
Wenzhou 120.65 28.02
|
||||||
|
Ottawa -75.71 45.42
|
||||||
|
Johor Bahru 103.75 1.48
|
||||||
|
Mombasa 39.66 -4.04
|
||||||
|
Lilongwe 33.8 -13.97
|
||||||
|
Turin 7.68 45.08
|
||||||
|
Duque de Caxias -43.31 -22.77
|
||||||
|
Abu Dhabi 54.37 24.48
|
||||||
|
Jalandhar 75.57 31.33
|
||||||
|
Warri 5.76 5.52
|
||||||
|
Valencia -0.39 39.48
|
||||||
|
Oslo 10.75 59.91
|
||||||
|
Taian 117.12 36.2
|
||||||
|
ad-Dammam 50.1 26.43
|
||||||
|
Mira Bhayandar 72.85 19.29
|
||||||
|
Salem 78.16 11.67
|
||||||
|
Pietermaritzburg 30.39 -29.61
|
||||||
|
Naucalpan -99.23 19.48
|
||||||
|
H?ims 36.72 34.73
|
||||||
|
Bhubaneswar 85.84 20.27
|
||||||
|
Hamamatsu 137.73 34.72
|
||||||
|
Saratov 46.03 51.57
|
||||||
|
Detroit -83.1 42.38
|
||||||
|
Kirkuk 44.39 35.47
|
||||||
|
Sakai 135.48 34.57
|
||||||
|
Onitsha 6.78 6.14
|
||||||
|
Quetta 67.02 30.21
|
||||||
|
Aligarh 78.06 27.89
|
||||||
|
Voronezh 39.26 51.72
|
||||||
|
Freetown -13.24 8.49
|
||||||
|
Tucuman -65.22 -26.83
|
||||||
|
Bogor 106.79 -6.58
|
||||||
|
Niigata 139.04 37.92
|
||||||
|
Thiruvananthapuram 76.95 8.51
|
||||||
|
Jacksonville -81.66 30.33
|
||||||
|
Bareli 79.41 28.36
|
||||||
|
Cebu 123.9 10.32
|
||||||
|
Kota 75.83 25.18
|
||||||
|
Natal -35.22 -5.8
|
||||||
|
Shihung 126.89 37.46
|
||||||
|
Puchon 126.77 37.48
|
||||||
|
Tiruchchirappalli 78.69 10.81
|
||||||
|
Trujillo -79.03 -8.11
|
||||||
|
Sharjah 55.41 25.37
|
||||||
|
Kermanshah 47.06 34.38
|
||||||
|
Qinhuangdao 119.62 39.93
|
||||||
|
Anyang 114.35 36.08
|
||||||
|
Bhiwandi 73.05 19.3
|
||||||
|
an-Najaf 44.34 32
|
||||||
|
Sao Bernardo do Campo -46.54 -23.71
|
||||||
|
Teresina -42.8 -5.1
|
||||||
|
Nanning 108.32 22.82
|
||||||
|
Antalya 30.71 36.89
|
||||||
|
Campo Grande -54.63 -20.45
|
||||||
|
Indianapolis -86.15 39.78
|
||||||
|
Jaboatao -35.02 -8.11
|
||||||
|
Zaporizhzhya 35.17 47.85
|
||||||
|
Hohhot 111.64 40.82
|
||||||
|
Marseille 5.37 43.31
|
||||||
|
Moradabad 78.76 28.84
|
||||||
|
Zhangjiakou 114.93 40.83
|
||||||
|
Liuzhou 109.25 24.28
|
||||||
|
Nouakchott -15.98 18.09
|
||||||
|
Rajshahi 88.59 24.37
|
||||||
|
Yantai 121.4 37.53
|
||||||
|
Tainan 120.19 23
|
||||||
|
Xining 101.77 36.62
|
||||||
|
Port-au-Prince -72.34 18.54
|
||||||
|
Hegang 130.37 47.4
|
||||||
|
Akure 5.19 7.25
|
||||||
|
N'Djamena 15.05 12.11
|
||||||
|
Guadalupe -100.26 25.68
|
||||||
|
Cracow 19.96 50.06
|
||||||
|
Malang 112.62 -7.98
|
||||||
|
Hengyang 112.62 26.89
|
||||||
|
Athens 23.73 37.98
|
||||||
|
Puyang 114.98 35.7
|
||||||
|
San Francisco -122.45 37.77
|
||||||
|
Jerusalem 35.22 31.78
|
||||||
|
Amsterdam 4.89 52.37
|
||||||
|
?odz 19.46 51.77
|
||||||
|
Merida -89.62 20.97
|
||||||
|
Austin -97.75 30.31
|
||||||
|
Abeokuta 3.35 7.16
|
||||||
|
Xinxiang 113.87 35.32
|
||||||
|
Raipur 81.63 21.24
|
||||||
|
Tunis 10.22 36.84
|
||||||
|
Columbus -82.99 39.99
|
||||||
|
Chihuahua -106.08 28.63
|
||||||
|
L'viv 24 49.83
|
||||||
|
Cotonou 2.44 6.36
|
||||||
|
Pekan Baru 101.43 0.56
|
||||||
|
Blantyre 34.99 -15.79
|
||||||
|
La Plata -57.96 -34.92
|
||||||
|
Bulawayo 28.58 -20.17
|
||||||
|
Tangier -5.81 35.79
|
||||||
|
Kayseri 35.48 38.74
|
||||||
|
Tolyatti 49.51 53.48
|
||||||
|
Foshan 113.12 23.03
|
||||||
|
Ningbo 121.55 29.88
|
||||||
|
Langfang 116.68 39.52
|
||||||
|
Ampang Jaya 101.77 3.15
|
||||||
|
Liaoyang 123.18 41.28
|
||||||
|
Riga 24.13 56.97
|
||||||
|
Changzhi 111.75 35.22
|
||||||
|
Kryvyy Rih 33.35 47.92
|
||||||
|
Libreville 9.45 0.39
|
||||||
|
Chonju 127.14 35.83
|
||||||
|
Fort Worth -97.34 32.75
|
||||||
|
as-Sulaymaniyah 45.43 35.56
|
||||||
|
Osasco -46.78 -23.53
|
||||||
|
Zamboanga 122.08 6.92
|
||||||
|
Tlalnepantla -99.19 19.54
|
||||||
|
Gorakhpur 83.36 26.76
|
||||||
|
San Luis Potosi -100.98 22.16
|
||||||
|
Sevilla -5.98 37.4
|
||||||
|
Zhuzhou 113.15 27.83
|
||||||
|
Zagreb 15.97 45.8
|
||||||
|
Huangshi 115.1 30.22
|
||||||
|
Puente Alto -70.57 -33.61
|
||||||
|
Shaoguan 113.58 24.8
|
||||||
|
Matola 32.46 -25.97
|
||||||
|
Guilin 110.28 25.28
|
||||||
|
Aguascalientes -102.3 21.88
|
||||||
|
Shizuoka 138.39 34.98
|
||||||
|
Benghazi 20.07 32.12
|
||||||
|
Fuxin 121.65 42.01
|
||||||
|
Joao Pessoa -34.86 -7.12
|
||||||
|
Ipoh 101.07 4.6
|
||||||
|
Contagem -44.1 -19.91
|
||||||
|
Dushanbe 68.78 38.57
|
||||||
|
Zhanjiang 110.38 21.2
|
||||||
|
Xingtai 114.49 37.07
|
||||||
|
Okayama 133.92 34.67
|
||||||
|
Yogyakarta 110.37 -7.78
|
||||||
|
Bhilai 81.38 21.21
|
||||||
|
Zigong 104.78 29.4
|
||||||
|
Mudanjiang 129.6 44.58
|
||||||
|
Wahran -0.62 35.7
|
||||||
|
Enugu 7.51 6.44
|
||||||
|
Santo Andre -46.53 -23.65
|
||||||
|
Colombo 79.85 6.93
|
||||||
|
Chimalhuacan -98.96 19.44
|
||||||
|
Shatian 114.19 22.38
|
||||||
|
Memphis -90.01 35.11
|
||||||
|
Kumamoto 130.71 32.8
|
||||||
|
Sao Jose dos Campos -45.88 -23.2
|
||||||
|
Zhangdian 118.06 36.8
|
||||||
|
Acapulco -99.92 16.85
|
||||||
|
Xiangtan 112.9 27.85
|
||||||
|
Quebec -71.23 46.82
|
||||||
|
Dasmarinas 120.93 14.33
|
||||||
|
Zaria 7.71 11.08
|
||||||
|
Nantong 120.82 32.02
|
||||||
|
Charlotte -80.83 35.2
|
||||||
|
Pointe Noire 11.87 -4.77
|
||||||
|
Shaoyang 111.2 27
|
||||||
|
Queretaro -100.4 20.59
|
||||||
|
Hamilton -79.85 43.26
|
||||||
|
Islamabad 73.06 33.72
|
||||||
|
Panjin 122.05 41.18
|
||||||
|
Saltillo -101 25.42
|
||||||
|
Ansan 126.86 37.35
|
||||||
|
Jamshedpur 86.2 22.79
|
||||||
|
Zaragoza -0.89 41.65
|
||||||
|
Cancun -86.83 21.17
|
||||||
|
Dandong 124.4 40.13
|
||||||
|
Frankfurt 8.68 50.12
|
||||||
|
Palermo 13.36 38.12
|
||||||
|
Haikou 110.32 20.05
|
||||||
|
'Adan 45.03 12.79
|
||||||
|
Amravati 77.76 20.95
|
||||||
|
Winnipeg -97.17 49.88
|
||||||
|
Sagamihara 139.38 35.58
|
||||||
|
Zhangzhou 117.67 24.52
|
||||||
|
Gazzah 34.44 31.53
|
||||||
|
Kataka 85.88 20.47
|
||||||
|
El Paso -106.44 31.85
|
||||||
|
Krasnodar 38.98 45.03
|
||||||
|
Kuching 110.34 1.55
|
||||||
|
Wroc?aw 17.03 51.11
|
||||||
|
Asmara 38.94 15.33
|
||||||
|
Zhenjiang 119.43 32.22
|
||||||
|
Baltimore -76.61 39.3
|
||||||
|
Benoni 28.33 -26.15
|
||||||
|
Mersin 34.63 36.81
|
||||||
|
Izhevsk 53.23 56.85
|
||||||
|
Yancheng 120.12 33.39
|
||||||
|
Hermosillo -110.97 29.07
|
||||||
|
Yuanlong 114.02 22.44
|
||||||
|
Uberlandia -48.28 -18.9
|
||||||
|
Ulyanovsk 48.4 54.33
|
||||||
|
Bouake -5.03 7.69
|
||||||
|
Santiago -70.69 19.48
|
||||||
|
Mexicali -115.47 32.65
|
||||||
|
Hai Phong 106.68 20.86
|
||||||
|
Anyang 126.92 37.39
|
||||||
|
Dadiangas 125.25 6.1
|
||||||
|
Morelia -101.18 19.72
|
||||||
|
Oshogbo 4.56 7.77
|
||||||
|
Chongju 127.5 36.64
|
||||||
|
Jos 8.89 9.93
|
||||||
|
al-'Ayn 55.74 24.23
|
||||||
|
Sorocaba -47.47 -23.49
|
||||||
|
Bikaner 73.32 28.03
|
||||||
|
Taizhou 119.9 32.49
|
||||||
|
Antipolo 121.18 14.59
|
||||||
|
Xiamen 118.08 24.45
|
||||||
|
Cochabamba -66.17 -17.38
|
||||||
|
Culiacan -107.39 24.8
|
||||||
|
Yingkou 122.28 40.67
|
||||||
|
Kagoshima 130.56 31.59
|
||||||
|
Siping 124.33 43.17
|
||||||
|
Orumiyeh 45 37.53
|
||||||
|
Luancheng 114.65 37.88
|
||||||
|
Diyarbak?r 40.23 37.92
|
||||||
|
Yaroslavl 39.87 57.62
|
||||||
|
Mixco -90.6 14.64
|
||||||
|
Banjarmasin 114.59 -3.33
|
||||||
|
Chisinau 28.83 47.03
|
||||||
|
Djibouti 43.15 11.59
|
||||||
|
Seattle -122.35 47.62
|
||||||
|
Stuttgart 9.19 48.79
|
||||||
|
Khabarovsk 135.12 48.42
|
||||||
|
Rotterdam 4.48 51.93
|
||||||
|
Jinzhou 121.1 41.12
|
||||||
|
Kisangani 25.19 0.53
|
||||||
|
San Pedro Sula -88.03 15.47
|
||||||
|
Bengbu 117.33 32.95
|
||||||
|
Irkutsk 104.24 52.33
|
||||||
|
Shihezi 86.03 44.3
|
||||||
|
Maracay -67.47 10.33
|
||||||
|
Cucuta -72.51 7.88
|
||||||
|
Bhavnagar 72.13 21.79
|
||||||
|
Port Said 32.29 31.26
|
||||||
|
Denver -104.87 39.77
|
||||||
|
Genoa 8.93 44.42
|
||||||
|
Jiangmen 113.08 22.58
|
||||||
|
Dortmund 7.48 51.51
|
||||||
|
Barnaul 83.75 53.36
|
||||||
|
Washington -77.02 38.91
|
||||||
|
Veracruz -96.14 19.19
|
||||||
|
Ribeirao Preto -47.8 -21.17
|
||||||
|
Vladivostok 131.9 43.13
|
||||||
|
Mar del Plata -57.58 -38
|
||||||
|
Boston -71.02 42.34
|
||||||
|
Eskisehir 30.52 39.79
|
||||||
|
Warangal 79.58 18.01
|
||||||
|
Zahedan 60.83 29.5
|
||||||
|
Essen 7 51.47
|
||||||
|
Dusseldorf 6.79 51.24
|
||||||
|
Kaifeng 114.35 34.85
|
||||||
|
Kingston -76.8 17.99
|
||||||
|
Glasgow -4.27 55.87
|
||||||
|
Funabashi 139.99 35.7
|
||||||
|
Shah Alam 101.56 3.07
|
||||||
|
Maoming 110.87 21.92
|
||||||
|
Hachioji 139.33 35.66
|
||||||
|
Meknes -5.56 33.9
|
||||||
|
Hamhung 127.54 39.91
|
||||||
|
Villa Nueva -90.59 14.53
|
||||||
|
Sargodha 72.67 32.08
|
||||||
|
Las Vegas -115.22 36.21
|
||||||
|
Resht 49.63 37.3
|
||||||
|
Cangzhou 116.87 38.32
|
||||||
|
Tanggu 117.67 39
|
||||||
|
Helsinki 24.94 60.17
|
||||||
|
Malaga -4.42 36.72
|
||||||
|
Milwaukee -87.97 43.06
|
||||||
|
Nashville -86.78 36.17
|
||||||
|
Ife 4.56 7.48
|
||||||
|
Changde 111.68 29.03
|
||||||
|
at-Ta'if 40.38 21.26
|
||||||
|
Surakarta 110.82 -7.57
|
||||||
|
Poznan 16.9 52.4
|
||||||
|
Barcelona -64.72 10.13
|
||||||
|
Bloemfontein 26.23 -29.15
|
||||||
|
Lopez Mateos -99.26 19.57
|
||||||
|
Bangui 18.56 4.36
|
||||||
|
Reynosa -98.28 26.08
|
||||||
|
Xigong 114.25 22.33
|
||||||
|
Cuiaba -56.09 -15.61
|
||||||
|
Shiliguri 88.42 26.73
|
||||||
|
Oklahoma City -97.51 35.47
|
||||||
|
Louisville -85.74 38.22
|
||||||
|
Jiamusi 130.35 46.83
|
||||||
|
Huaiyin 119.03 33.58
|
||||||
|
Welkom 26.73 -27.97
|
||||||
|
Kolhapur 74.22 16.7
|
||||||
|
Ulhasnagar 73.15 19.23
|
||||||
|
Rajpur 88.44 22.44
|
||||||
|
Bremen 8.81 53.08
|
||||||
|
San Salvador -89.19 13.69
|
||||||
|
Maanshan 118.48 31.73
|
||||||
|
Tembisa 28.22 -25.99
|
||||||
|
Banqiao 121.44 25.02
|
||||||
|
Toluca -99.67 19.29
|
||||||
|
Portland -122.66 45.54
|
||||||
|
Gold Coast 153.44 -28.07
|
||||||
|
Kota Kinabalu 116.07 5.97
|
||||||
|
Vilnius 25.27 54.7
|
||||||
|
Agadir -9.61 30.42
|
||||||
|
Ajmer 74.64 26.45
|
||||||
|
Orenburg 55.1 51.78
|
||||||
|
Neijiang 105.05 29.58
|
||||||
|
Salta -65.41 -24.79
|
||||||
|
Guntur 80.44 16.31
|
||||||
|
Novokuznetsk 87.1 53.75
|
||||||
|
Yangzhou 119.43 32.4
|
||||||
|
Durgapur 87.31 23.5
|
||||||
|
Shashi 112.23 30.32
|
||||||
|
Asuncion -57.63 -25.3
|
||||||
|
Aparecida de Goiania -49.24 -16.82
|
||||||
|
Ribeirao das Neves -44.08 -19.76
|
||||||
|
Petaling Jaya 101.62 3.1
|
||||||
|
Sangli-Miraj 74.57 16.86
|
||||||
|
Dehra Dun 78.05 30.34
|
||||||
|
Maturin -63.17 9.75
|
||||||
|
Torreon -103.43 25.55
|
||||||
|
Jiaozuo 113.22 35.25
|
||||||
|
Zhuhai 113.57 22.28
|
||||||
|
Nanded 77.29 19.17
|
||||||
|
Suez 32.54 29.98
|
||||||
|
Tyumen 65.53 57.15
|
||||||
|
Albuquerque -106.62 35.12
|
||||||
|
Cagayan 124.67 8.45
|
||||||
|
Mwanza 32.89 -2.52
|
||||||
|
Petare -66.83 10.52
|
||||||
|
Soledad -74.77 10.92
|
||||||
|
Uijongbu 127.04 37.74
|
||||||
|
Yueyang 113.1 29.38
|
||||||
|
Feira de Santana -38.97 -12.25
|
||||||
|
Ta'izz 44.04 13.6
|
||||||
|
Tucson -110.89 32.2
|
||||||
|
Naberezhnyye Chelny 52.32 55.69
|
||||||
|
Kerman 57.08 30.3
|
||||||
|
Matsuyama 132.77 33.84
|
||||||
|
Garoua 13.39 9.3
|
||||||
|
Tlaquepaque -103.32 20.64
|
||||||
|
Tuxtla Gutierrez -93.12 16.75
|
||||||
|
Jamnagar 70.07 22.47
|
||||||
|
Jammu 74.85 32.71
|
||||||
|
Gulbarga 76.82 17.34
|
||||||
|
Chiclayo -79.84 -6.76
|
||||||
|
Hanover 9.73 52.4
|
||||||
|
Bucaramanga -73.13 7.13
|
||||||
|
Bahawalpur 71.67 29.39
|
||||||
|
Goteborg 12.01 57.72
|
||||||
|
Zhunmen 113.98 22.41
|
||||||
|
Bhatpara 88.42 22.89
|
||||||
|
Ryazan 39.74 54.62
|
||||||
|
Calamba 121.15 14.21
|
||||||
|
Changwon 128.62 35.27
|
||||||
|
Aracaju -37.07 -10.91
|
||||||
|
Zunyi 106.92 27.7
|
||||||
|
Lipetsk 39.62 52.62
|
||||||
|
Dresden 13.74 51.05
|
||||||
|
Saharanpur 77.54 29.97
|
||||||
|
H?amah 36.73 35.15
|
||||||
|
Niyala 24.89 12.06
|
||||||
|
San Nicolas de los Garza -100.3 25.75
|
||||||
|
Higashiosaka 135.59 34.67
|
||||||
|
al-H?illah 44.43 32.48
|
||||||
|
Leipzig 12.4 51.35
|
||||||
|
Xuchang 113.82 34.02
|
||||||
|
Wuhu 118.37 31.35
|
||||||
|
Boma 13.05 -5.85
|
||||||
|
Kananga 22.4 -5.89
|
||||||
|
Mykolayiv 32 46.97
|
||||||
|
Atlanta -84.42 33.76
|
||||||
|
Londrina -51.18 -23.3
|
||||||
|
Tabuk 36.57 28.39
|
||||||
|
Cuautitlan Izcalli -99.25 19.65
|
||||||
|
Nuremberg 11.05 49.45
|
||||||
|
Santa Fe -60.69 -31.6
|
||||||
|
Joinville -48.84 -26.32
|
||||||
|
Zurich 8.55 47.36
|
@ -1,2 +1,3 @@
|
|||||||
myLegalName = "Vast Global MegaCorp, Ltd"
|
myLegalName = "Vast Global MegaCorp, Ltd"
|
||||||
exportJMXto = "http"
|
exportJMXto = "http"
|
||||||
|
nearestCity = "The Moon"
|
@ -74,11 +74,11 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
|||||||
//expression = "( fixedLeg.notional * (fixedLeg.fixedRate)) - (floatingLeg.notional * (rateSchedule.get(context.getDate('currentDate'))))",
|
//expression = "( fixedLeg.notional * (fixedLeg.fixedRate)) - (floatingLeg.notional * (rateSchedule.get(context.getDate('currentDate'))))",
|
||||||
|
|
||||||
// How it's ended up looking, which I think is now broken but it's a WIP.
|
// How it's ended up looking, which I think is now broken but it's a WIP.
|
||||||
expression = InterestRateSwap.Expression("( fixedLeg.notional.pennies * (fixedLeg.fixedRate.ratioUnit.value)) -" +
|
expression = Expression("( fixedLeg.notional.pennies * (fixedLeg.fixedRate.ratioUnit.value)) -" +
|
||||||
"(floatingLeg.notional.pennies * (calculation.fixingSchedule.get(context.getDate('currentDate')).rate.ratioUnit.value))"),
|
"(floatingLeg.notional.pennies * (calculation.fixingSchedule.get(context.getDate('currentDate')).rate.ratioUnit.value))"),
|
||||||
|
|
||||||
floatingLegPaymentSchedule = HashMap(),
|
floatingLegPaymentSchedule = HashMap(),
|
||||||
fixedLegpaymentSchedule = HashMap()
|
fixedLegPaymentSchedule = HashMap()
|
||||||
)
|
)
|
||||||
|
|
||||||
val EUR = currency("EUR")
|
val EUR = currency("EUR")
|
||||||
@ -94,13 +94,13 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
|||||||
valuationDate = "Every Local Business Day",
|
valuationDate = "Every Local Business Day",
|
||||||
notificationTime = "2:00pm London",
|
notificationTime = "2:00pm London",
|
||||||
resolutionTime = "2:00pm London time on the first LocalBusiness Day following the date on which the notice is given ",
|
resolutionTime = "2:00pm London time on the first LocalBusiness Day following the date on which the notice is given ",
|
||||||
interestRate = OracleRetrievableReferenceRate(TelerateOracle("T3270"), Tenor("6M"), "EONIA"),
|
interestRate = ReferenceRate("T3270", Tenor("6M"), "EONIA"),
|
||||||
addressForTransfers = "",
|
addressForTransfers = "",
|
||||||
exposure = UnknownType(),
|
exposure = UnknownType(),
|
||||||
localBusinessDay = BusinessCalendar.getInstance("London"),
|
localBusinessDay = BusinessCalendar.getInstance("London"),
|
||||||
tradeID = "trade1",
|
tradeID = "trade1",
|
||||||
hashLegalDocs = "put hash here",
|
hashLegalDocs = "put hash here",
|
||||||
dailyInterestAmount = InterestRateSwap.Expression("(CashAmount * InterestRate ) / (fixedLeg.notional.currency.currencyCode.equals('GBP')) ? 365 : 360")
|
dailyInterestAmount = Expression("(CashAmount * InterestRate ) / (fixedLeg.notional.currency.currencyCode.equals('GBP')) ? 365 : 360")
|
||||||
)
|
)
|
||||||
|
|
||||||
InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common)
|
InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common)
|
||||||
@ -165,11 +165,11 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
|||||||
//expression = "( fixedLeg.notional * (fixedLeg.fixedRate)) - (floatingLeg.notional * (rateSchedule.get(context.getDate('currentDate'))))",
|
//expression = "( fixedLeg.notional * (fixedLeg.fixedRate)) - (floatingLeg.notional * (rateSchedule.get(context.getDate('currentDate'))))",
|
||||||
|
|
||||||
// How it's ended up looking, which I think is now broken but it's a WIP.
|
// How it's ended up looking, which I think is now broken but it's a WIP.
|
||||||
expression = InterestRateSwap.Expression("( fixedLeg.notional.pennies * (fixedLeg.fixedRate.ratioUnit.value)) -" +
|
expression = Expression("( fixedLeg.notional.pennies * (fixedLeg.fixedRate.ratioUnit.value)) -" +
|
||||||
"(floatingLeg.notional.pennies * (calculation.fixingSchedule.get(context.getDate('currentDate')).rate.ratioUnit.value))"),
|
"(floatingLeg.notional.pennies * (calculation.fixingSchedule.get(context.getDate('currentDate')).rate.ratioUnit.value))"),
|
||||||
|
|
||||||
floatingLegPaymentSchedule = HashMap(),
|
floatingLegPaymentSchedule = HashMap(),
|
||||||
fixedLegpaymentSchedule = HashMap()
|
fixedLegPaymentSchedule = HashMap()
|
||||||
)
|
)
|
||||||
|
|
||||||
val EUR = currency("EUR")
|
val EUR = currency("EUR")
|
||||||
@ -185,13 +185,13 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
|||||||
valuationDate = "Every Local Business Day",
|
valuationDate = "Every Local Business Day",
|
||||||
notificationTime = "2:00pm London",
|
notificationTime = "2:00pm London",
|
||||||
resolutionTime = "2:00pm London time on the first LocalBusiness Day following the date on which the notice is given ",
|
resolutionTime = "2:00pm London time on the first LocalBusiness Day following the date on which the notice is given ",
|
||||||
interestRate = OracleRetrievableReferenceRate(TelerateOracle("T3270"), Tenor("6M"), "EONIA"),
|
interestRate = ReferenceRate("T3270", Tenor("6M"), "EONIA"),
|
||||||
addressForTransfers = "",
|
addressForTransfers = "",
|
||||||
exposure = UnknownType(),
|
exposure = UnknownType(),
|
||||||
localBusinessDay = BusinessCalendar.getInstance("London"),
|
localBusinessDay = BusinessCalendar.getInstance("London"),
|
||||||
tradeID = "trade1",
|
tradeID = "trade1",
|
||||||
hashLegalDocs = "put hash here",
|
hashLegalDocs = "put hash here",
|
||||||
dailyInterestAmount = InterestRateSwap.Expression("(CashAmount * InterestRate ) / (fixedLeg.notional.currency.currencyCode.equals('GBP')) ? 365 : 360")
|
dailyInterestAmount = Expression("(CashAmount * InterestRate ) / (fixedLeg.notional.currency.currencyCode.equals('GBP')) ? 365 : 360")
|
||||||
)
|
)
|
||||||
|
|
||||||
return InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common)
|
return InterestRateSwap.State(fixedLeg = fixedLeg, floatingLeg = floatingLeg, calculation = calculation, common = common)
|
||||||
@ -335,12 +335,6 @@ class IRSTests {
|
|||||||
// TODO: r1*r2 ?
|
// TODO: r1*r2 ?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `reference rate testing`() {
|
|
||||||
val r1 = TestReferenceRate("5")
|
|
||||||
assert(100 * r1.getAsOf(null) == 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `expression calculation testing`() {
|
fun `expression calculation testing`() {
|
||||||
val dummyIRS = singleIRS()
|
val dummyIRS = singleIRS()
|
||||||
@ -366,7 +360,7 @@ class IRSTests {
|
|||||||
|
|
||||||
for (i in stuffToPrint) {
|
for (i in stuffToPrint) {
|
||||||
println(i)
|
println(i)
|
||||||
var z = dummyIRS.evaluateCalculation(LocalDate.of(2016, 9, 12), InterestRateSwap.Expression(i))
|
var z = dummyIRS.evaluateCalculation(LocalDate.of(2016, 9, 12), Expression(i))
|
||||||
println(z.javaClass)
|
println(z.javaClass)
|
||||||
println(z)
|
println(z)
|
||||||
println("-----------")
|
println("-----------")
|
||||||
|
@ -11,8 +11,8 @@ package core
|
|||||||
import com.codahale.metrics.MetricRegistry
|
import com.codahale.metrics.MetricRegistry
|
||||||
import core.crypto.*
|
import core.crypto.*
|
||||||
import core.messaging.MessagingService
|
import core.messaging.MessagingService
|
||||||
import core.messaging.MockNetworkMapService
|
import core.node.services.MockNetworkMapService
|
||||||
import core.messaging.NetworkMapService
|
import core.node.services.NetworkMapService
|
||||||
import core.node.services.*
|
import core.node.services.*
|
||||||
import core.serialization.SerializedBytes
|
import core.serialization.SerializedBytes
|
||||||
import core.serialization.deserialize
|
import core.serialization.deserialize
|
||||||
|
@ -8,22 +8,25 @@
|
|||||||
|
|
||||||
package core.messaging
|
package core.messaging
|
||||||
|
|
||||||
import protocols.FetchAttachmentsProtocol
|
|
||||||
import protocols.FetchDataProtocol
|
|
||||||
import core.Attachment
|
import core.Attachment
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.crypto.sha256
|
import core.crypto.sha256
|
||||||
import core.testing.MockNetwork
|
import core.node.NodeConfiguration
|
||||||
|
import core.node.services.LegallyIdentifiableNode
|
||||||
import core.node.services.NodeAttachmentService
|
import core.node.services.NodeAttachmentService
|
||||||
import core.serialization.OpaqueBytes
|
import core.serialization.OpaqueBytes
|
||||||
|
import core.testing.MockNetwork
|
||||||
import core.testutils.rootCauseExceptions
|
import core.testutils.rootCauseExceptions
|
||||||
import core.utilities.BriefLogFormatter
|
import core.utilities.BriefLogFormatter
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import protocols.FetchAttachmentsProtocol
|
||||||
|
import protocols.FetchDataProtocol
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardOpenOption
|
import java.nio.file.StandardOpenOption
|
||||||
import java.util.jar.JarOutputStream
|
import java.util.jar.JarOutputStream
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
@ -90,16 +93,18 @@ class AttachmentTests {
|
|||||||
@Test
|
@Test
|
||||||
fun maliciousResponse() {
|
fun maliciousResponse() {
|
||||||
// Make a node that doesn't do sanity checking at load time.
|
// Make a node that doesn't do sanity checking at load time.
|
||||||
val n0 = network.createNode(null) { path, config, mock, ts ->
|
val n0 = network.createNode(null, nodeFactory = object : MockNetwork.Factory {
|
||||||
object : MockNetwork.MockNode(path, config, mock, ts) {
|
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: LegallyIdentifiableNode?): MockNetwork.MockNode {
|
||||||
override fun start(): MockNetwork.MockNode {
|
return object : MockNetwork.MockNode(dir, config, network, timestamperAddr) {
|
||||||
super.start()
|
override fun start(): MockNetwork.MockNode {
|
||||||
(storage.attachments as NodeAttachmentService).checkAttachmentsOnLoad = false
|
super.start()
|
||||||
return this
|
(storage.attachments as NodeAttachmentService).checkAttachmentsOnLoad = false
|
||||||
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
val n1 = network.createNode(n0.legallyIdentifableAddress)
|
val n1 = network.createNode(n0.legallyIdentifiableAddress)
|
||||||
|
|
||||||
// Insert an attachment into node zero's store directly.
|
// Insert an attachment into node zero's store directly.
|
||||||
val id = n0.storage.attachments.importAttachment(ByteArrayInputStream(fakeAttachment()))
|
val id = n0.storage.attachments.importAttachment(ByteArrayInputStream(fakeAttachment()))
|
||||||
|
@ -12,6 +12,7 @@ import contracts.Cash
|
|||||||
import contracts.CommercialPaper
|
import contracts.CommercialPaper
|
||||||
import core.*
|
import core.*
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
|
import core.node.NodeConfiguration
|
||||||
import core.node.services.*
|
import core.node.services.*
|
||||||
import core.testing.InMemoryMessagingNetwork
|
import core.testing.InMemoryMessagingNetwork
|
||||||
import core.testing.MockNetwork
|
import core.testing.MockNetwork
|
||||||
@ -65,7 +66,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
|||||||
transactionGroupFor<ContractState> {
|
transactionGroupFor<ContractState> {
|
||||||
val (aliceNode, bobNode) = net.createTwoNodes()
|
val (aliceNode, bobNode) = net.createTwoNodes()
|
||||||
(bobNode.wallet as NodeWalletService).fillWithSomeTestCash(2000.DOLLARS)
|
(bobNode.wallet as NodeWalletService).fillWithSomeTestCash(2000.DOLLARS)
|
||||||
val alicesFakePaper = fillUpForSeller(false, aliceNode.legallyIdentifableAddress.identity, null).second
|
val alicesFakePaper = fillUpForSeller(false, aliceNode.legallyIdentifiableAddress.identity, null).second
|
||||||
|
|
||||||
insertFakeTransactions(alicesFakePaper, aliceNode.services, aliceNode.storage.myLegalIdentityKey)
|
insertFakeTransactions(alicesFakePaper, aliceNode.services, aliceNode.storage.myLegalIdentityKey)
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
|||||||
|
|
||||||
val aliceResult = TwoPartyTradeProtocol.runSeller(
|
val aliceResult = TwoPartyTradeProtocol.runSeller(
|
||||||
aliceNode.smm,
|
aliceNode.smm,
|
||||||
aliceNode.legallyIdentifableAddress,
|
aliceNode.legallyIdentifiableAddress,
|
||||||
bobNode.net.myAddress,
|
bobNode.net.myAddress,
|
||||||
lookup("alice's paper"),
|
lookup("alice's paper"),
|
||||||
1000.DOLLARS,
|
1000.DOLLARS,
|
||||||
@ -82,7 +83,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
|||||||
)
|
)
|
||||||
val bobResult = TwoPartyTradeProtocol.runBuyer(
|
val bobResult = TwoPartyTradeProtocol.runBuyer(
|
||||||
bobNode.smm,
|
bobNode.smm,
|
||||||
aliceNode.legallyIdentifableAddress,
|
aliceNode.legallyIdentifiableAddress,
|
||||||
aliceNode.net.myAddress,
|
aliceNode.net.myAddress,
|
||||||
1000.DOLLARS,
|
1000.DOLLARS,
|
||||||
CommercialPaper.State::class.java,
|
CommercialPaper.State::class.java,
|
||||||
@ -104,7 +105,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
|||||||
var (aliceNode, bobNode) = net.createTwoNodes()
|
var (aliceNode, bobNode) = net.createTwoNodes()
|
||||||
val aliceAddr = aliceNode.net.myAddress
|
val aliceAddr = aliceNode.net.myAddress
|
||||||
val bobAddr = bobNode.net.myAddress as InMemoryMessagingNetwork.Handle
|
val bobAddr = bobNode.net.myAddress as InMemoryMessagingNetwork.Handle
|
||||||
val timestamperAddr = aliceNode.legallyIdentifableAddress
|
val timestamperAddr = aliceNode.legallyIdentifiableAddress
|
||||||
|
|
||||||
(bobNode.wallet as NodeWalletService).fillWithSomeTestCash(2000.DOLLARS)
|
(bobNode.wallet as NodeWalletService).fillWithSomeTestCash(2000.DOLLARS)
|
||||||
val alicesFakePaper = fillUpForSeller(false, timestamperAddr.identity, null).second
|
val alicesFakePaper = fillUpForSeller(false, timestamperAddr.identity, null).second
|
||||||
@ -163,16 +164,18 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
|||||||
|
|
||||||
// ... bring the node back up ... the act of constructing the SMM will re-register the message handlers
|
// ... bring the node back up ... the act of constructing the SMM will re-register the message handlers
|
||||||
// that Bob was waiting on before the reboot occurred.
|
// that Bob was waiting on before the reboot occurred.
|
||||||
bobNode = net.createNode(timestamperAddr, bobAddr.id) { path, nodeConfiguration, net, timestamper ->
|
bobNode = net.createNode(timestamperAddr, bobAddr.id, object : MockNetwork.Factory {
|
||||||
object : MockNetwork.MockNode(path, nodeConfiguration, net, timestamper, bobAddr.id) {
|
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: LegallyIdentifiableNode?): MockNetwork.MockNode {
|
||||||
override fun initialiseStorageService(dir: Path): StorageService {
|
return object : MockNetwork.MockNode(dir, config, net, timestamperAddr, bobAddr.id) {
|
||||||
val ss = super.initialiseStorageService(dir)
|
override fun initialiseStorageService(dir: Path): StorageService {
|
||||||
val smMap = ss.stateMachines
|
val ss = super.initialiseStorageService(dir)
|
||||||
smMap.putAll(savedCheckpoints)
|
val smMap = ss.stateMachines
|
||||||
return ss
|
smMap.putAll(savedCheckpoints)
|
||||||
|
return ss
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
// Find the future representing the result of this state machine again.
|
// Find the future representing the result of this state machine again.
|
||||||
var bobFuture = bobNode.smm.findStateMachines(TwoPartyTradeProtocol.Buyer::class.java).single().second
|
var bobFuture = bobNode.smm.findStateMachines(TwoPartyTradeProtocol.Buyer::class.java).single().second
|
||||||
@ -192,22 +195,24 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
|||||||
// of gets and puts.
|
// of gets and puts.
|
||||||
private fun makeNodeWithTracking(name: String): MockNetwork.MockNode {
|
private fun makeNodeWithTracking(name: String): MockNetwork.MockNode {
|
||||||
// Create a node in the mock network ...
|
// Create a node in the mock network ...
|
||||||
return net.createNode(null) { path, config, net, tsNode ->
|
return net.createNode(null, nodeFactory = object : MockNetwork.Factory {
|
||||||
object : MockNetwork.MockNode(path, config, net, tsNode) {
|
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, timestamperAddr: LegallyIdentifiableNode?): MockNetwork.MockNode {
|
||||||
// That constructs the storage service object in a customised way ...
|
return object : MockNetwork.MockNode(dir, config, network, timestamperAddr) {
|
||||||
override fun constructStorageService(attachments: NodeAttachmentService, keypair: KeyPair, identity: Party): StorageServiceImpl {
|
// That constructs the storage service object in a customised way ...
|
||||||
// To use RecordingMaps instead of ordinary HashMaps.
|
override fun constructStorageService(attachments: NodeAttachmentService, keypair: KeyPair, identity: Party): StorageServiceImpl {
|
||||||
return StorageServiceImpl(attachments, keypair, identity, { tableName -> name })
|
// To use RecordingMaps instead of ordinary HashMaps.
|
||||||
|
return StorageServiceImpl(attachments, keypair, identity, { tableName -> name })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun checkDependenciesOfSaleAssetAreResolved() {
|
fun checkDependenciesOfSaleAssetAreResolved() {
|
||||||
transactionGroupFor<ContractState> {
|
transactionGroupFor<ContractState> {
|
||||||
val aliceNode = makeNodeWithTracking("alice")
|
val aliceNode = makeNodeWithTracking("alice")
|
||||||
val timestamperAddr = aliceNode.legallyIdentifableAddress
|
val timestamperAddr = aliceNode.legallyIdentifiableAddress
|
||||||
val bobNode = makeNodeWithTracking("bob")
|
val bobNode = makeNodeWithTracking("bob")
|
||||||
|
|
||||||
// Insert a prospectus type attachment into the commercial paper transaction.
|
// Insert a prospectus type attachment into the commercial paper transaction.
|
||||||
@ -318,7 +323,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
|
|||||||
var (aliceNode, bobNode) = net.createTwoNodes()
|
var (aliceNode, bobNode) = net.createTwoNodes()
|
||||||
val aliceAddr = aliceNode.net.myAddress
|
val aliceAddr = aliceNode.net.myAddress
|
||||||
val bobAddr = bobNode.net.myAddress as InMemoryMessagingNetwork.Handle
|
val bobAddr = bobNode.net.myAddress as InMemoryMessagingNetwork.Handle
|
||||||
val timestamperAddr = aliceNode.legallyIdentifableAddress
|
val timestamperAddr = aliceNode.legallyIdentifiableAddress
|
||||||
|
|
||||||
val bobKey = bobNode.keyManagement.freshKey()
|
val bobKey = bobNode.keyManagement.freshKey()
|
||||||
val bobsBadCash = fillUpForBuyer(bobError, bobKey.public).second
|
val bobsBadCash = fillUpForBuyer(bobError, bobKey.public).second
|
||||||
|
@ -12,9 +12,7 @@ import co.paralleluniverse.fibers.Suspendable
|
|||||||
import core.*
|
import core.*
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.messaging.*
|
import core.messaging.*
|
||||||
import core.node.services.NodeTimestamperService
|
import core.node.services.*
|
||||||
import core.node.services.ServiceHub
|
|
||||||
import core.node.services.TimestampingError
|
|
||||||
import core.protocols.ProtocolLogic
|
import core.protocols.ProtocolLogic
|
||||||
import core.serialization.serialize
|
import core.serialization.serialize
|
||||||
import core.testing.InMemoryMessagingNetwork
|
import core.testing.InMemoryMessagingNetwork
|
||||||
|
@ -9,12 +9,11 @@
|
|||||||
package core.node.services
|
package core.node.services
|
||||||
|
|
||||||
import contracts.Cash
|
import contracts.Cash
|
||||||
import core.*
|
|
||||||
import core.testing.MockNetwork
|
|
||||||
import core.DOLLARS
|
import core.DOLLARS
|
||||||
import core.Fix
|
import core.Fix
|
||||||
import core.TransactionBuilder
|
import core.TransactionBuilder
|
||||||
import core.bd
|
import core.bd
|
||||||
|
import core.testing.MockNetwork
|
||||||
import core.testutils.*
|
import core.testutils.*
|
||||||
import core.utilities.BriefLogFormatter
|
import core.utilities.BriefLogFormatter
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -86,7 +85,7 @@ class NodeInterestRatesTest {
|
|||||||
|
|
||||||
val tx = TransactionBuilder()
|
val tx = TransactionBuilder()
|
||||||
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
val fixOf = NodeInterestRates.parseFixOf("LIBOR 2016-03-16 1M")
|
||||||
val protocol = RatesFixProtocol(tx, n2.legallyIdentifableAddress, fixOf, "0.675".bd, "0.1".bd)
|
val protocol = RatesFixProtocol(tx, n2.legallyIdentifiableAddress, fixOf, "0.675".bd, "0.1".bd)
|
||||||
BriefLogFormatter.initVerbose("rates")
|
BriefLogFormatter.initVerbose("rates")
|
||||||
val future = n1.smm.add("rates", protocol)
|
val future = n1.smm.add("rates", protocol)
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ object TestUtils {
|
|||||||
val keypair = generateKeyPair()
|
val keypair = generateKeyPair()
|
||||||
val keypair2 = generateKeyPair()
|
val keypair2 = generateKeyPair()
|
||||||
val keypair3 = generateKeyPair()
|
val keypair3 = generateKeyPair()
|
||||||
|
val keypair4 = generateKeyPair()
|
||||||
|
val keypair5 = generateKeyPair()
|
||||||
}
|
}
|
||||||
// A dummy time at which we will be pretending test transactions are created.
|
// A dummy time at which we will be pretending test transactions are created.
|
||||||
val TEST_TX_TIME = Instant.parse("2015-04-17T12:00:00.00Z")
|
val TEST_TX_TIME = Instant.parse("2015-04-17T12:00:00.00Z")
|
||||||
@ -49,6 +51,14 @@ val MEGA_CORP_PUBKEY = MEGA_CORP_KEY.public
|
|||||||
val MINI_CORP_KEY = TestUtils.keypair2
|
val MINI_CORP_KEY = TestUtils.keypair2
|
||||||
val MINI_CORP_PUBKEY = MINI_CORP_KEY.public
|
val MINI_CORP_PUBKEY = MINI_CORP_KEY.public
|
||||||
|
|
||||||
|
// TODO remove once mock API is retired
|
||||||
|
val EXCALIBUR_BANK_KEY = TestUtils.keypair4
|
||||||
|
val EXCALIBUR_BANK_PUBKEY = EXCALIBUR_BANK_KEY.public
|
||||||
|
|
||||||
|
// TODO remove once mock API is retired
|
||||||
|
val A_N_OTHER_BANK_KEY = TestUtils.keypair5
|
||||||
|
val A_N_OTHER_BANK_PUBKEY = A_N_OTHER_BANK_KEY.public
|
||||||
|
|
||||||
val ORACLE_KEY = TestUtils.keypair3
|
val ORACLE_KEY = TestUtils.keypair3
|
||||||
val ORACLE_PUBKEY = ORACLE_KEY.public
|
val ORACLE_PUBKEY = ORACLE_KEY.public
|
||||||
|
|
||||||
@ -64,11 +74,17 @@ val BOB = BOB_KEY.public
|
|||||||
val MEGA_CORP = Party("MegaCorp", MEGA_CORP_PUBKEY)
|
val MEGA_CORP = Party("MegaCorp", MEGA_CORP_PUBKEY)
|
||||||
val MINI_CORP = Party("MiniCorp", MINI_CORP_PUBKEY)
|
val MINI_CORP = Party("MiniCorp", MINI_CORP_PUBKEY)
|
||||||
|
|
||||||
val ALL_TEST_KEYS = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, ALICE_KEY, BOB_KEY, DummyTimestampingAuthority.key)
|
// TODO remove once mock API is retired
|
||||||
|
val EXCALIBUR_BANK = Party("Excalibur", EXCALIBUR_BANK_PUBKEY)
|
||||||
|
val A_N_OTHER_BANK = Party("ANOther",A_N_OTHER_BANK_PUBKEY)
|
||||||
|
|
||||||
|
val ALL_TEST_KEYS = listOf(MEGA_CORP_KEY, MINI_CORP_KEY, ALICE_KEY, BOB_KEY, EXCALIBUR_BANK_KEY, A_N_OTHER_BANK_KEY, DummyTimestampingAuthority.key)
|
||||||
|
|
||||||
val TEST_KEYS_TO_CORP_MAP: Map<PublicKey, Party> = mapOf(
|
val TEST_KEYS_TO_CORP_MAP: Map<PublicKey, Party> = mapOf(
|
||||||
MEGA_CORP_PUBKEY to MEGA_CORP,
|
MEGA_CORP_PUBKEY to MEGA_CORP,
|
||||||
MINI_CORP_PUBKEY to MINI_CORP,
|
MINI_CORP_PUBKEY to MINI_CORP,
|
||||||
|
EXCALIBUR_BANK_PUBKEY to EXCALIBUR_BANK,
|
||||||
|
A_N_OTHER_BANK_PUBKEY to A_N_OTHER_BANK,
|
||||||
DUMMY_TIMESTAMPER.identity.owningKey to DUMMY_TIMESTAMPER.identity
|
DUMMY_TIMESTAMPER.identity.owningKey to DUMMY_TIMESTAMPER.identity
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user