mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +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
|
||||
|
||||
|
||||
// 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: Implement a generate function.
|
||||
@ -19,7 +18,7 @@ data class CashState(
|
||||
val issuingInstitution: Institution,
|
||||
|
||||
/** 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,
|
||||
|
||||
@ -69,8 +68,7 @@ class CashContract : Contract {
|
||||
|
||||
requireThat {
|
||||
"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 { Arrays.equals(it.depositReference, depositReference) }
|
||||
"for issuer ${issuer.name} the deposit references are the same" by outputs.all { it.depositReference == depositReference }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import com.google.common.io.BaseEncoding
|
||||
import java.security.MessageDigest
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
|
||||
// "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) {
|
||||
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
|
||||
* 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 */
|
||||
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)
|
||||
|
@ -1,6 +1,5 @@
|
||||
import java.security.PublicKey
|
||||
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
|
||||
@ -67,7 +66,7 @@ data class SignedCommand(
|
||||
val commandDataSignature: DigitalSignature.WithKey,
|
||||
|
||||
/** 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) */
|
||||
val classID: String,
|
||||
/** 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 java.security.PublicKey
|
||||
import com.google.common.io.BaseEncoding
|
||||
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
|
||||
// 2. There must be at least one input state (note: not "one of the type the contract wants")
|
||||
|
||||
|
||||
class CashTests {
|
||||
val inState = CashState(
|
||||
issuingInstitution = MEGA_CORP,
|
||||
depositReference = byteArrayOf(1),
|
||||
depositReference = OpaqueBytes.of(1),
|
||||
amount = 1000.DOLLARS,
|
||||
owner = DUMMY_PUBKEY_1
|
||||
)
|
||||
@ -100,8 +99,8 @@ class CashTests {
|
||||
// Can't change deposit reference when splitting.
|
||||
transaction {
|
||||
input { inState }
|
||||
output { outState.copy(depositReference = byteArrayOf(0), amount = inState.amount / 2) }
|
||||
output { outState.copy(depositReference = byteArrayOf(1), amount = inState.amount / 2) }
|
||||
output { outState.copy(depositReference = OpaqueBytes.of(0), amount = inState.amount / 2) }
|
||||
output { outState.copy(depositReference = OpaqueBytes.of(1), amount = inState.amount / 2) }
|
||||
contract `fails requirement` "the deposit references are the same"
|
||||
}
|
||||
// Can't mix currencies.
|
||||
|
Loading…
Reference in New Issue
Block a user