Minor: provide a random63BitValue() function and use it instead of the previously duplicated code. Update docs.

This commit is contained in:
Mike Hearn 2015-12-15 13:16:13 +01:00
parent 020a594a60
commit 06ee9db3f6
4 changed files with 18 additions and 18 deletions

View File

@ -140,8 +140,8 @@ Let's unpack what this code does:
- Two of the classes are simply wrappers for parameters to the trade; things like what is being sold, what the price
of the asset is, how much the buyer is willing to pay and so on. The ``myKeyPair`` field is simply the public key
that the seller wishes the buyer to send the cash to. The session ID field is sent from buyer to seller when the
trade is being set up and is just a big random number. It's used to keep messages separated on the network, and stop
malicious entities trying to interfere with the message stream.
trade is being set up and is used to keep messages separated on the network, and stop malicious entities trying to
interfere with the message stream.
- The other two classes define empty abstract classes called ``Buyer`` and ``Seller``. These inherit from a class
called ``ProtocolStateMachine`` and provide two type parameters: the arguments class we just defined for each side
and the type of the object that the protocol finally produces (this doesn't have to be identical for each side, even
@ -149,6 +149,9 @@ Let's unpack what this code does:
- Finally it simply defines a static method that creates an instance of an object that inherits from this base class
and returns it, with a ``StateMachineManager`` as an instance. The Impl class will be defined below.
.. note:: Session IDs keep different traffic streams separated, so for security they must be large and random enough
to be unguessable. 63 bits is good enough.
Alright, so using this protocol shouldn't be too hard: in the simplest case we can just pass in the details of the trade
to either runBuyer or runSeller, depending on who we are, and then call ``.get()`` on the resulting future to block the
calling thread until the protocol has finished. Or we could register a callback on the returned future that will be
@ -179,7 +182,6 @@ Implementing the seller
private class TwoPartyTradeProtocolImpl(private val smm: StateMachineManager) : TwoPartyTradeProtocol() {
companion object {
val TRADE_TOPIC = "com.r3cev.protocols.trade"
fun makeSessionID() = Math.abs(SecureRandom.getInstanceStrong().nextLong())
}
class SellerImpl : Seller() {
@ -206,10 +208,7 @@ Implementing the seller
We start with a skeleton on which we will build the protocol. Putting things in a *companion object* in Kotlin is like
declaring them as static members in Java. Here, we define a "topic" that will identify trade related messages that
arrive at a node (see :doc:`messaging` for details), and a convenience function to pick a large random session ID.
.. note:: Session IDs keep different traffic streams separated, so for security they must be large and random enough
to be unguessable. 63 bits is good enough.
arrive at a node (see :doc:`messaging` for details).
The runSeller and runBuyer methods simply start the state machines, passing in a reference to the classes and the topics
each side will use.
@ -240,7 +239,7 @@ Next we add some code to the ``SellerImpl.call`` method:
.. sourcecode:: kotlin
val sessionID = makeSessionID()
val sessionID = random63BitValue()
// Make the first message we'll send to kick off the protocol.
val hello = SellerTradeInfo(args.assetToSell, args.price, args.myKeyPair.public, sessionID)

View File

@ -16,7 +16,6 @@ import core.serialization.deserialize
import core.utilities.trace
import java.security.KeyPair
import java.security.PublicKey
import java.security.SecureRandom
/**
* This asset trading protocol has two parties (B and S for buyer and seller) and the following steps:
@ -73,7 +72,6 @@ abstract class TwoPartyTradeProtocol {
private class TwoPartyTradeProtocolImpl(private val smm: StateMachineManager) : TwoPartyTradeProtocol() {
companion object {
val TRADE_TOPIC = "com.r3cev.protocols.trade"
fun makeSessionID() = Math.abs(SecureRandom.getInstanceStrong().nextLong())
}
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
@ -92,7 +90,7 @@ private class TwoPartyTradeProtocolImpl(private val smm: StateMachineManager) :
// learn more about the protocol state machine framework.
class SellerImpl : Seller() {
override fun call(args: SellerInitialArgs): Pair<TimestampedWireTransaction, LedgerTransaction> {
val sessionID = makeSessionID()
val sessionID = random63BitValue()
// Make the first message we'll send to kick off the protocol.
val hello = SellerTradeInfo(args.assetToSell, args.price, args.myKeyPair.public, sessionID)

View File

@ -13,6 +13,7 @@ import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import com.google.common.util.concurrent.SettableFuture
import org.slf4j.Logger
import java.security.SecureRandom
import java.time.Duration
import java.util.*
import java.util.concurrent.Executor
@ -44,6 +45,12 @@ val Int.hours: Duration get() = Duration.ofHours(this.toLong())
val Int.minutes: Duration get() = Duration.ofMinutes(this.toLong())
val Int.seconds: Duration get() = Duration.ofSeconds(this.toLong())
/**
* Returns a random positive long generated using a secure RNG. This function sacrifies a bit of entropy in order to
* avoid potential bugs where the value is used in a context where negative numbers are not expected.
*/
fun random63BitValue(): Long = Math.abs(SecureRandom.getInstanceStrong().nextLong())
fun <T> ListenableFuture<T>.whenComplete(executor: Executor? = null, body: () -> Unit) {
addListener(Runnable { body() }, executor ?: MoreExecutors.directExecutor())
}

View File

@ -12,15 +12,11 @@ import com.google.common.util.concurrent.MoreExecutors
import contracts.Cash
import contracts.CommercialPaper
import contracts.protocols.TwoPartyTradeProtocol
import core.ContractState
import core.DOLLARS
import core.StateAndRef
import core.days
import core.*
import core.testutils.*
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.security.SecureRandom
import java.util.concurrent.Executors
import java.util.logging.Formatter
import java.util.logging.Level
@ -77,7 +73,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
val tpSeller = TwoPartyTradeProtocol.create(StateMachineManager(alicesServices, backgroundThread))
val tpBuyer = TwoPartyTradeProtocol.create(StateMachineManager(bobsServices, backgroundThread))
val buyerSessionID = SecureRandom.getInstanceStrong().nextLong()
val buyerSessionID = random63BitValue()
val aliceResult = tpSeller.runSeller(
bobsAddress,
@ -134,7 +130,7 @@ class TwoPartyTradeProtocolTests : TestWithInMemoryNetwork() {
val smmBuyer = StateMachineManager(bobsServices, MoreExecutors.directExecutor())
val tpBuyer = TwoPartyTradeProtocol.create(smmBuyer)
val buyerSessionID = SecureRandom.getInstanceStrong().nextLong()
val buyerSessionID = random63BitValue()
tpSeller.runSeller(
bobsAddress,