mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
Introduce OpaqueBytes, a simple wrapper around the JVM level byte[] which fixes hashCode and equals to use value-type identity rather than instance identity. Allows for more natural code.
This commit is contained in:
parent
12f5ddb0aa
commit
8f46fb4406
@ -5,7 +5,6 @@ import java.util.*
|
|||||||
//
|
//
|
||||||
// Cash
|
// Cash
|
||||||
|
|
||||||
|
|
||||||
// TODO: Think about state merging: when does it make sense to merge multiple cash states from the same issuer?
|
// TODO: Think about state merging: when does it make sense to merge multiple cash states from the same issuer?
|
||||||
// TODO: Does multi-currency also make sense? Probably?
|
// TODO: Does multi-currency also make sense? Probably?
|
||||||
// TODO: Implement a generate function.
|
// TODO: Implement a generate function.
|
||||||
@ -19,7 +18,7 @@ data class CashState(
|
|||||||
val issuingInstitution: Institution,
|
val issuingInstitution: Institution,
|
||||||
|
|
||||||
/** Whatever internal ID the bank needs in order to locate that deposit, may be encrypted (propagated) */
|
/** Whatever internal ID the bank needs in order to locate that deposit, may be encrypted (propagated) */
|
||||||
val depositReference: ByteArray,
|
val depositReference: OpaqueBytes,
|
||||||
|
|
||||||
val amount: Amount,
|
val amount: Amount,
|
||||||
|
|
||||||
@ -69,8 +68,7 @@ class CashContract : Contract {
|
|||||||
|
|
||||||
requireThat {
|
requireThat {
|
||||||
"for issuer ${issuer.name} the amounts balance" by (inputAmount == outputAmount + amountExitingLedger)
|
"for issuer ${issuer.name} the amounts balance" by (inputAmount == outputAmount + amountExitingLedger)
|
||||||
// TODO: Introduce a byte array wrapper that makes == do what we expect (Kotlin does not do this for us)
|
"for issuer ${issuer.name} the deposit references are the same" by outputs.all { it.depositReference == depositReference }
|
||||||
"for issuer ${issuer.name} the deposit references are the same" by outputs.all { Arrays.equals(it.depositReference, depositReference) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import com.google.common.io.BaseEncoding
|
import com.google.common.io.BaseEncoding
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
// "sealed" here means there can't be any subclasses other than the ones defined here.
|
// "sealed" here means there can't be any subclasses other than the ones defined here.
|
||||||
sealed class SecureHash(val bits: ByteArray) {
|
sealed class SecureHash(bits: ByteArray) : OpaqueBytes(bits) {
|
||||||
class SHA256(bits: ByteArray) : SecureHash(bits) {
|
class SHA256(bits: ByteArray) : SecureHash(bits) {
|
||||||
init { require(bits.size == 32) }
|
init { require(bits.size == 32) }
|
||||||
}
|
}
|
||||||
@ -28,7 +29,7 @@ sealed class SecureHash(val bits: ByteArray) {
|
|||||||
* A wrapper around a digital signature. The covering field is a generic tag usable by whatever is interpreting the
|
* A wrapper around a digital signature. The covering field is a generic tag usable by whatever is interpreting the
|
||||||
* signature.
|
* signature.
|
||||||
*/
|
*/
|
||||||
sealed class DigitalSignature(val bits: ByteArray, val covering: Int) {
|
sealed class DigitalSignature(bits: ByteArray, val covering: Int) : OpaqueBytes(bits) {
|
||||||
/** A digital signature that identifies who the public key is owned by */
|
/** A digital signature that identifies who the public key is owned by */
|
||||||
open class WithKey(val by: PublicKey, bits: ByteArray, covering: Int) : DigitalSignature(bits, covering)
|
open class WithKey(val by: PublicKey, bits: ByteArray, covering: Int) : DigitalSignature(bits, covering)
|
||||||
class LegallyIdentifiable(val signer: Institution, bits: ByteArray, covering: Int) : WithKey(signer.owningKey, bits, covering)
|
class LegallyIdentifiable(val signer: Institution, bits: ByteArray, covering: Int) : WithKey(signer.owningKey, bits, covering)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.Timestamp
|
import java.security.Timestamp
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk
|
* A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk
|
||||||
@ -67,7 +66,7 @@ data class SignedCommand(
|
|||||||
val commandDataSignature: DigitalSignature.WithKey,
|
val commandDataSignature: DigitalSignature.WithKey,
|
||||||
|
|
||||||
/** Command data, deserialized to an implementation of [Command] */
|
/** Command data, deserialized to an implementation of [Command] */
|
||||||
val serialized: ByteArray,
|
val serialized: OpaqueBytes,
|
||||||
/** Identifies what command the serialized data contains (should maybe be a hash too) */
|
/** Identifies what command the serialized data contains (should maybe be a hash too) */
|
||||||
val classID: String,
|
val classID: String,
|
||||||
/** Hash of a derivative of the transaction data, so this command can only ever apply to one transaction */
|
/** Hash of a derivative of the transaction data, so this command can only ever apply to one transaction */
|
||||||
|
21
src/Utils.kt
21
src/Utils.kt
@ -1,9 +1,22 @@
|
|||||||
import java.math.BigInteger
|
import com.google.common.io.BaseEncoding
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertTrue
|
|
||||||
import kotlin.test.fail
|
|
||||||
|
|
||||||
|
/** A simple class that wraps a byte array and makes the equals/hashCode/toString methods work as you actually expect */
|
||||||
|
open class OpaqueBytes(val bits: ByteArray) {
|
||||||
|
companion object {
|
||||||
|
fun of(vararg b: Byte) = OpaqueBytes(byteArrayOf(*b))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean{
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is OpaqueBytes) return false
|
||||||
|
return Arrays.equals(bits, other.bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode() = Arrays.hashCode(bits)
|
||||||
|
|
||||||
|
override fun toString() = "[" + BaseEncoding.base16().encode(bits) + "]"
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -4,11 +4,10 @@ import org.junit.Test
|
|||||||
// 1. No duplicate input states
|
// 1. No duplicate input states
|
||||||
// 2. There must be at least one input state (note: not "one of the type the contract wants")
|
// 2. There must be at least one input state (note: not "one of the type the contract wants")
|
||||||
|
|
||||||
|
|
||||||
class CashTests {
|
class CashTests {
|
||||||
val inState = CashState(
|
val inState = CashState(
|
||||||
issuingInstitution = MEGA_CORP,
|
issuingInstitution = MEGA_CORP,
|
||||||
depositReference = byteArrayOf(1),
|
depositReference = OpaqueBytes.of(1),
|
||||||
amount = 1000.DOLLARS,
|
amount = 1000.DOLLARS,
|
||||||
owner = DUMMY_PUBKEY_1
|
owner = DUMMY_PUBKEY_1
|
||||||
)
|
)
|
||||||
@ -100,8 +99,8 @@ class CashTests {
|
|||||||
// Can't change deposit reference when splitting.
|
// Can't change deposit reference when splitting.
|
||||||
transaction {
|
transaction {
|
||||||
input { inState }
|
input { inState }
|
||||||
output { outState.copy(depositReference = byteArrayOf(0), amount = inState.amount / 2) }
|
output { outState.copy(depositReference = OpaqueBytes.of(0), amount = inState.amount / 2) }
|
||||||
output { outState.copy(depositReference = byteArrayOf(1), amount = inState.amount / 2) }
|
output { outState.copy(depositReference = OpaqueBytes.of(1), amount = inState.amount / 2) }
|
||||||
contract `fails requirement` "the deposit references are the same"
|
contract `fails requirement` "the deposit references are the same"
|
||||||
}
|
}
|
||||||
// Can't mix currencies.
|
// Can't mix currencies.
|
||||||
|
Loading…
Reference in New Issue
Block a user