mirror of
https://github.com/corda/corda.git
synced 2024-12-21 13:57:54 +00:00
Rearranging interfaces and implementations. Notary fix for the cash tests.
This commit is contained in:
parent
7d0ce00978
commit
422d65cc54
@ -0,0 +1,31 @@
|
||||
package core.node.services
|
||||
|
||||
import core.Party
|
||||
import core.StateRef
|
||||
import core.WireTransaction
|
||||
import core.crypto.SecureHash
|
||||
|
||||
/**
|
||||
* A service that records input states of the given transaction and provides conflict information
|
||||
* if any of the inputs have already been used in another transaction
|
||||
*/
|
||||
interface UniquenessProvider {
|
||||
/** Commits all input states of the given transaction */
|
||||
fun commit(tx: WireTransaction, callerIdentity: Party)
|
||||
|
||||
/** Specifies the consuming transaction for every conflicting state */
|
||||
data class Conflict(val stateHistory: Map<StateRef, ConsumingTx>)
|
||||
|
||||
/**
|
||||
* Specifies the transaction id, the position of the consumed state in the inputs, and
|
||||
* the caller identity requesting the commit
|
||||
*
|
||||
* TODO: need to do more design work to prevent privacy problems: knowing the id of a
|
||||
* transaction, by the rules of our system the party can obtain it and see its contents.
|
||||
* This allows a party to just submit invalid transactions with outputs it was aware of and
|
||||
* find out where exactly they were spent.
|
||||
*/
|
||||
data class ConsumingTx(val id: SecureHash, val inputIndex: Int, val requestingParty: Party)
|
||||
}
|
||||
|
||||
class UniquenessException(val error: UniquenessProvider.Conflict) : Exception()
|
@ -5,11 +5,10 @@ import core.Party
|
||||
import core.TimestampCommand
|
||||
import core.WireTransaction
|
||||
import core.crypto.DigitalSignature
|
||||
import core.crypto.SignedData
|
||||
import core.messaging.SingleMessageRecipient
|
||||
import core.node.NodeInfo
|
||||
import core.node.services.NotaryError
|
||||
import core.node.services.NotaryException
|
||||
import core.node.services.NotaryService
|
||||
import core.node.services.UniquenessProvider
|
||||
import core.protocols.ProtocolLogic
|
||||
import core.random63BitValue
|
||||
import core.serialization.SerializedBytes
|
||||
@ -45,13 +44,13 @@ class NotaryProtocol(private val wtx: WireTransaction,
|
||||
|
||||
val sessionID = random63BitValue()
|
||||
val request = SignRequest(wtx.serialized, serviceHub.storageService.myLegalIdentity, serviceHub.networkService.myAddress, sessionID)
|
||||
val response = sendAndReceive<NotaryService.Result>(TOPIC, notaryNode.address, 0, sessionID, request)
|
||||
val response = sendAndReceive<Result>(TOPIC, notaryNode.address, 0, sessionID, request)
|
||||
|
||||
val notaryResult = validateResponse(response)
|
||||
return notaryResult.sig ?: throw NotaryException(notaryResult.error!!)
|
||||
}
|
||||
|
||||
private fun validateResponse(response: UntrustworthyData<NotaryService.Result>): NotaryService.Result {
|
||||
private fun validateResponse(response: UntrustworthyData<Result>): Result {
|
||||
progressTracker.currentStep = VALIDATING
|
||||
|
||||
response.validate {
|
||||
@ -91,4 +90,27 @@ class NotaryProtocol(private val wtx: WireTransaction,
|
||||
val callerIdentity: Party,
|
||||
replyTo: SingleMessageRecipient,
|
||||
sessionID: Long) : AbstractRequestMessage(replyTo, sessionID)
|
||||
|
||||
data class Result private constructor(val sig: DigitalSignature.LegallyIdentifiable?, val error: NotaryError?) {
|
||||
companion object {
|
||||
fun withError(error: NotaryError) = Result(null, error)
|
||||
fun noError(sig: DigitalSignature.LegallyIdentifiable) = Result(sig, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NotaryException(val error: NotaryError) : Exception()
|
||||
|
||||
sealed class NotaryError {
|
||||
class Conflict(val tx: WireTransaction, val conflict: SignedData<UniquenessProvider.Conflict>) : NotaryError() {
|
||||
override fun toString() = "One or more input states for transaction ${tx.id} have been used in another transaction"
|
||||
}
|
||||
|
||||
class MoreThanOneTimestamp : NotaryError()
|
||||
|
||||
/** Thrown if the timestamp command in the transaction doesn't list this Notary as a signer */
|
||||
class NotForMe : NotaryError()
|
||||
|
||||
/** Thrown if the time specified in the timestamp command is outside the allowed tolerance */
|
||||
class TimestampInvalid : NotaryError()
|
||||
}
|
@ -4,35 +4,9 @@ import core.Party
|
||||
import core.StateRef
|
||||
import core.ThreadBox
|
||||
import core.WireTransaction
|
||||
import core.crypto.SecureHash
|
||||
import java.util.*
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
|
||||
/**
|
||||
* A service that records input states of the given transaction and provides conflict information
|
||||
* if any of the inputs have already been used in another transaction
|
||||
*/
|
||||
interface UniquenessProvider {
|
||||
/** Commits all input states of the given transaction */
|
||||
fun commit(tx: WireTransaction, callerIdentity: Party)
|
||||
|
||||
/** Specifies the consuming transaction for every conflicting state */
|
||||
data class Conflict(val stateHistory: Map<StateRef, ConsumingTx>)
|
||||
|
||||
/**
|
||||
* Specifies the transaction id, the position of the consumed state in the inputs, and
|
||||
* the caller identity requesting the commit
|
||||
*
|
||||
* TODO: need to do more design work to prevent privacy problems: knowing the id of a
|
||||
* transaction, by the rules of our system the party can obtain it and see its contents.
|
||||
* This allows a party to just submit invalid transactions with outputs it was aware of and
|
||||
* find out where exactly they were spent.
|
||||
*/
|
||||
data class ConsumingTx(val id: SecureHash, val inputIndex: Int, val requestingParty: Party)
|
||||
}
|
||||
|
||||
class UniquenessException(val error: UniquenessProvider.Conflict) : Exception()
|
||||
|
||||
/** A dummy Uniqueness provider that stores the whole history of consumed states in memory */
|
||||
@ThreadSafe
|
||||
class InMemoryUniquenessProvider() : UniquenessProvider {
|
@ -12,6 +12,8 @@ import core.serialization.SerializedBytes
|
||||
import core.serialization.deserialize
|
||||
import core.serialization.serialize
|
||||
import core.utilities.loggerFor
|
||||
import protocols.NotaryError
|
||||
import protocols.NotaryException
|
||||
import protocols.NotaryProtocol
|
||||
import java.security.KeyPair
|
||||
|
||||
@ -51,17 +53,17 @@ class NotaryService(net: MessagingService,
|
||||
*
|
||||
* TODO: the notary service should only be able to see timestamp commands and inputs
|
||||
*/
|
||||
fun processRequest(txBits: SerializedBytes<WireTransaction>, reqIdentity: Party): Result {
|
||||
fun processRequest(txBits: SerializedBytes<WireTransaction>, reqIdentity: Party): NotaryProtocol.Result {
|
||||
val wtx = txBits.deserialize()
|
||||
try {
|
||||
validateTimestamp(wtx)
|
||||
commitInputStates(wtx, reqIdentity)
|
||||
} catch(e: NotaryException) {
|
||||
return Result.withError(e.error)
|
||||
return NotaryProtocol.Result.withError(e.error)
|
||||
}
|
||||
|
||||
val sig = sign(txBits)
|
||||
return Result.noError(sig)
|
||||
return NotaryProtocol.Result.noError(sig)
|
||||
}
|
||||
|
||||
private fun validateTimestamp(tx: WireTransaction) {
|
||||
@ -90,26 +92,5 @@ class NotaryService(net: MessagingService,
|
||||
return signingKey.signWithECDSA(bits, identity)
|
||||
}
|
||||
|
||||
data class Result private constructor(val sig: DigitalSignature.LegallyIdentifiable?, val error: NotaryError?) {
|
||||
companion object {
|
||||
fun withError(error: NotaryError) = Result(null, error)
|
||||
fun noError(sig: DigitalSignature.LegallyIdentifiable) = Result(sig, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NotaryException(val error: NotaryError) : Exception()
|
||||
|
||||
sealed class NotaryError {
|
||||
class Conflict(val tx: WireTransaction, val conflict: SignedData<UniquenessProvider.Conflict>) : NotaryError() {
|
||||
override fun toString() = "One or more input states for transaction ${tx.id} have been used in another transaction"
|
||||
}
|
||||
|
||||
class MoreThanOneTimestamp : NotaryError()
|
||||
|
||||
/** Thrown if the timestamp command in the transaction doesn't list this Notary as a signer */
|
||||
class NotForMe : NotaryError()
|
||||
|
||||
/** Thrown if the time specified in the timestamp command is outside the allowed tolerance */
|
||||
class TimestampInvalid : NotaryError()
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
import contracts.Cash
|
||||
import contracts.DummyContract
|
||||
import contracts.InsufficientBalanceException
|
||||
import contracts.cash.CashIssuanceDefinition
|
||||
import core.*
|
||||
import core.crypto.SecureHash
|
||||
import core.serialization.OpaqueBytes
|
||||
@ -109,7 +108,7 @@ class CashTests {
|
||||
// Test issuance from the issuance definition
|
||||
val issuanceDef = Cash.IssuanceDefinition(MINI_CORP.ref(12, 34), USD)
|
||||
val templatePtx = TransactionBuilder()
|
||||
Cash().generateIssue(templatePtx, issuanceDef, 100.DOLLARS.pennies, owner = DUMMY_PUBKEY_1)
|
||||
Cash().generateIssue(templatePtx, issuanceDef, 100.DOLLARS.pennies, owner = DUMMY_PUBKEY_1, notary = DUMMY_NOTARY)
|
||||
assertTrue(templatePtx.inputStates().isEmpty())
|
||||
assertEquals(ptx.outputStates()[0], templatePtx.outputStates()[0])
|
||||
|
||||
@ -432,9 +431,9 @@ class CashTests {
|
||||
*/
|
||||
@Test
|
||||
fun aggregation() {
|
||||
val fiveThousandDollarsFromMega = Cash.State(MEGA_CORP.ref(2), 5000.DOLLARS, MEGA_CORP_PUBKEY)
|
||||
val twoThousandDollarsFromMega = Cash.State(MEGA_CORP.ref(2), 2000.DOLLARS, MINI_CORP_PUBKEY)
|
||||
val oneThousandDollarsFromMini = Cash.State(MINI_CORP.ref(3), 1000.DOLLARS, MEGA_CORP_PUBKEY)
|
||||
val fiveThousandDollarsFromMega = Cash.State(MEGA_CORP.ref(2), 5000.DOLLARS, MEGA_CORP_PUBKEY, DUMMY_NOTARY)
|
||||
val twoThousandDollarsFromMega = Cash.State(MEGA_CORP.ref(2), 2000.DOLLARS, MINI_CORP_PUBKEY, DUMMY_NOTARY)
|
||||
val oneThousandDollarsFromMini = Cash.State(MINI_CORP.ref(3), 1000.DOLLARS, MEGA_CORP_PUBKEY, DUMMY_NOTARY)
|
||||
|
||||
// Obviously it must be possible to aggregate states with themselves
|
||||
assertEquals(fiveThousandDollarsFromMega.issuanceDef, fiveThousandDollarsFromMega.issuanceDef)
|
||||
@ -448,7 +447,7 @@ class CashTests {
|
||||
|
||||
// States cannot be aggregated if the currency differs
|
||||
assertNotEquals(oneThousandDollarsFromMini.issuanceDef,
|
||||
Cash.State(MINI_CORP.ref(3), 1000.POUNDS, MEGA_CORP_PUBKEY).issuanceDef)
|
||||
Cash.State(MINI_CORP.ref(3), 1000.POUNDS, MEGA_CORP_PUBKEY, DUMMY_NOTARY).issuanceDef)
|
||||
|
||||
// States cannot be aggregated if the reference differs
|
||||
assertNotEquals(fiveThousandDollarsFromMega.issuanceDef, fiveThousandDollarsFromMega.copy(deposit = MEGA_CORP.ref(1)).issuanceDef)
|
||||
|
@ -8,6 +8,8 @@ import core.testutils.DUMMY_NOTARY_KEY
|
||||
import core.testutils.issueState
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import protocols.NotaryError
|
||||
import protocols.NotaryException
|
||||
import protocols.NotaryProtocol
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.ExecutionException
|
||||
|
Loading…
Reference in New Issue
Block a user