mirror of
https://github.com/corda/corda.git
synced 2025-02-18 16:40:55 +00:00
Push unit tests that needed some contract functionality into core by embedding the minimal contract support needed.
This commit is contained in:
parent
a7c0296f6b
commit
69cba98ddc
@ -80,5 +80,4 @@ dependencies {
|
|||||||
compile project(':core')
|
compile project(':core')
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile "commons-fileupload:commons-fileupload:1.3.1"
|
|
||||||
}
|
}
|
@ -21,9 +21,15 @@ repositories {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//noinspection GroovyAssignabilityCheck
|
||||||
|
configurations {
|
||||||
|
quasar
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile 'org.assertj:assertj-core:3.4.1'
|
testCompile 'org.assertj:assertj-core:3.4.1'
|
||||||
|
testCompile "commons-fileupload:commons-fileupload:1.3.1"
|
||||||
|
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
@ -57,4 +63,40 @@ dependencies {
|
|||||||
|
|
||||||
// For JSON
|
// For JSON
|
||||||
compile "com.fasterxml.jackson.core:jackson-databind:2.5.5"
|
compile "com.fasterxml.jackson.core:jackson-databind:2.5.5"
|
||||||
|
|
||||||
|
// Quasar: for the bytecode rewriting for state machines.
|
||||||
|
quasar "co.paralleluniverse:quasar-core:${quasar_version}:jdk8@jar"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks.withType(Test) {
|
||||||
|
jvmArgs "-javaagent:${configurations.quasar.singleFile}"
|
||||||
|
jvmArgs "-Dco.paralleluniverse.fibers.verifyInstrumentation"
|
||||||
|
}
|
||||||
|
tasks.withType(JavaExec) {
|
||||||
|
jvmArgs "-javaagent:${configurations.quasar.singleFile}"
|
||||||
|
jvmArgs "-Dco.paralleluniverse.fibers.verifyInstrumentation"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// These lines tell gradle to run the Quasar suspendables scanner to look for unannotated super methods
|
||||||
|
// that have @Suspendable sub implementations. These tend to cause NPEs and are not caught by the verifier
|
||||||
|
// NOTE: need to make sure the output isn't on the classpath or every other run it generates empty results, so
|
||||||
|
// we explicitly delete to avoid that happening. We also need to turn off what seems to be a spurious warning in the IDE
|
||||||
|
//
|
||||||
|
// TODO: Make this task incremental, as it can be quite slow.
|
||||||
|
|
||||||
|
//noinspection GroovyAssignabilityCheck
|
||||||
|
task quasarScan(dependsOn: ['classes']) << {
|
||||||
|
ant.taskdef(name:'scanSuspendables', classname:'co.paralleluniverse.fibers.instrument.SuspendablesScanner',
|
||||||
|
classpath: "${sourceSets.main.output.classesDir}:${sourceSets.main.output.resourcesDir}:${configurations.runtime.asPath}")
|
||||||
|
delete "$sourceSets.main.output.resourcesDir/META-INF/suspendables", "$sourceSets.main.output.resourcesDir/META-INF/suspendable-supers"
|
||||||
|
ant.scanSuspendables(
|
||||||
|
auto:false,
|
||||||
|
suspendablesFile: "$sourceSets.main.output.resourcesDir/META-INF/suspendables",
|
||||||
|
supersFile: "$sourceSets.main.output.resourcesDir/META-INF/suspendable-supers") {
|
||||||
|
fileset(dir: sourceSets.main.output.classesDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jar.dependsOn quasarScan
|
@ -1,16 +1,43 @@
|
|||||||
package core.contracts
|
package core.contracts
|
||||||
|
|
||||||
import contracts.Cash
|
import core.crypto.Party
|
||||||
import contracts.testing.`owned by`
|
import core.crypto.SecureHash
|
||||||
import core.node.services.testing.MockStorageService
|
import core.node.services.testing.MockStorageService
|
||||||
import core.testing.*
|
import core.testing.*
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.security.SecureRandom
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertNotEquals
|
import kotlin.test.assertNotEquals
|
||||||
|
|
||||||
|
val TEST_PROGRAM_ID = TransactionGroupTests.TestCash()
|
||||||
|
|
||||||
class TransactionGroupTests {
|
class TransactionGroupTests {
|
||||||
val A_THOUSAND_POUNDS = Cash.State(MINI_CORP.ref(1, 2, 3), 1000.POUNDS, MINI_CORP_PUBKEY, DUMMY_NOTARY)
|
val A_THOUSAND_POUNDS = TestCash.State(MINI_CORP.ref(1, 2, 3), 1000.POUNDS, MINI_CORP_PUBKEY, DUMMY_NOTARY)
|
||||||
|
|
||||||
|
class TestCash : Contract {
|
||||||
|
override val legalContractReference = SecureHash.sha256("TestCash")
|
||||||
|
|
||||||
|
override fun verify(tx: TransactionForVerification) {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class State(
|
||||||
|
val deposit: PartyAndReference,
|
||||||
|
val amount: Amount,
|
||||||
|
override val owner: PublicKey,
|
||||||
|
override val notary: Party) : OwnableState {
|
||||||
|
override val contract: Contract = TEST_PROGRAM_ID
|
||||||
|
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
|
}
|
||||||
|
interface Commands : CommandData {
|
||||||
|
class Move() : TypeOnlyCommandData(), Commands
|
||||||
|
data class Issue(val nonce: Long = SecureRandom.getInstanceStrong().nextLong()) : Commands
|
||||||
|
data class Exit(val amount: Amount) : Commands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
infix fun TestCash.State.`owned by`(owner: PublicKey) = copy(owner = owner)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun success() {
|
fun success() {
|
||||||
@ -22,13 +49,13 @@ class TransactionGroupTests {
|
|||||||
transaction {
|
transaction {
|
||||||
input("£1000")
|
input("£1000")
|
||||||
output("alice's £1000") { A_THOUSAND_POUNDS `owned by` ALICE_PUBKEY }
|
output("alice's £1000") { A_THOUSAND_POUNDS `owned by` ALICE_PUBKEY }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
input("alice's £1000")
|
input("alice's £1000")
|
||||||
arg(ALICE_PUBKEY) { Cash.Commands.Move() }
|
arg(ALICE_PUBKEY) { TestCash.Commands.Move() }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Exit(1000.POUNDS) }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Exit(1000.POUNDS) }
|
||||||
}
|
}
|
||||||
|
|
||||||
verify()
|
verify()
|
||||||
@ -40,7 +67,7 @@ class TransactionGroupTests {
|
|||||||
transactionGroup {
|
transactionGroup {
|
||||||
val t = transaction {
|
val t = transaction {
|
||||||
output("cash") { A_THOUSAND_POUNDS }
|
output("cash") { A_THOUSAND_POUNDS }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Issue() }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
val conflict1 = transaction {
|
val conflict1 = transaction {
|
||||||
@ -48,7 +75,7 @@ class TransactionGroupTests {
|
|||||||
val HALF = A_THOUSAND_POUNDS.copy(amount = 500.POUNDS) `owned by` BOB_PUBKEY
|
val HALF = A_THOUSAND_POUNDS.copy(amount = 500.POUNDS) `owned by` BOB_PUBKEY
|
||||||
output { HALF }
|
output { HALF }
|
||||||
output { HALF }
|
output { HALF }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
|
||||||
}
|
}
|
||||||
|
|
||||||
verify()
|
verify()
|
||||||
@ -59,7 +86,7 @@ class TransactionGroupTests {
|
|||||||
val HALF = A_THOUSAND_POUNDS.copy(amount = 500.POUNDS) `owned by` ALICE_PUBKEY
|
val HALF = A_THOUSAND_POUNDS.copy(amount = 500.POUNDS) `owned by` ALICE_PUBKEY
|
||||||
output { HALF }
|
output { HALF }
|
||||||
output { HALF }
|
output { HALF }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
|
||||||
}
|
}
|
||||||
|
|
||||||
assertNotEquals(conflict1, conflict2)
|
assertNotEquals(conflict1, conflict2)
|
||||||
@ -78,7 +105,7 @@ class TransactionGroupTests {
|
|||||||
val tg = transactionGroup {
|
val tg = transactionGroup {
|
||||||
transaction {
|
transaction {
|
||||||
output("cash") { A_THOUSAND_POUNDS }
|
output("cash") { A_THOUSAND_POUNDS }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Issue() }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
@ -93,7 +120,7 @@ class TransactionGroupTests {
|
|||||||
tg.txns += TransactionBuilder().apply {
|
tg.txns += TransactionBuilder().apply {
|
||||||
addInputState(input)
|
addInputState(input)
|
||||||
addOutputState(A_THOUSAND_POUNDS)
|
addOutputState(A_THOUSAND_POUNDS)
|
||||||
addCommand(Cash.Commands.Move(), BOB_PUBKEY)
|
addCommand(TestCash.Commands.Move(), BOB_PUBKEY)
|
||||||
}.toWireTransaction()
|
}.toWireTransaction()
|
||||||
|
|
||||||
val e = assertFailsWith(TransactionResolutionException::class) {
|
val e = assertFailsWith(TransactionResolutionException::class) {
|
||||||
@ -114,7 +141,7 @@ class TransactionGroupTests {
|
|||||||
input("£1000")
|
input("£1000")
|
||||||
input("£1000")
|
input("£1000")
|
||||||
output { A_THOUSAND_POUNDS.copy(amount = A_THOUSAND_POUNDS.amount * 2) }
|
output { A_THOUSAND_POUNDS.copy(amount = A_THOUSAND_POUNDS.amount * 2) }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFailsWith(TransactionConflictException::class) {
|
assertFailsWith(TransactionConflictException::class) {
|
||||||
@ -128,19 +155,19 @@ class TransactionGroupTests {
|
|||||||
val signedTxns: List<SignedTransaction> = transactionGroup {
|
val signedTxns: List<SignedTransaction> = transactionGroup {
|
||||||
transaction {
|
transaction {
|
||||||
output("£1000") { A_THOUSAND_POUNDS }
|
output("£1000") { A_THOUSAND_POUNDS }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Issue() }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() }
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
input("£1000")
|
input("£1000")
|
||||||
output("alice's £1000") { A_THOUSAND_POUNDS `owned by` ALICE_PUBKEY }
|
output("alice's £1000") { A_THOUSAND_POUNDS `owned by` ALICE_PUBKEY }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Move() }
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
input("alice's £1000")
|
input("alice's £1000")
|
||||||
arg(ALICE_PUBKEY) { Cash.Commands.Move() }
|
arg(ALICE_PUBKEY) { TestCash.Commands.Move() }
|
||||||
arg(MINI_CORP_PUBKEY) { Cash.Commands.Exit(1000.POUNDS) }
|
arg(MINI_CORP_PUBKEY) { TestCash.Commands.Exit(1000.POUNDS) }
|
||||||
}
|
}
|
||||||
}.signAll()
|
}.signAll()
|
||||||
|
|
@ -1,14 +1,8 @@
|
|||||||
package core.node
|
package core.node
|
||||||
|
|
||||||
import contracts.DUMMY_PROGRAM_ID
|
import core.contracts.*
|
||||||
import contracts.DummyContract
|
|
||||||
import core.contracts.Contract
|
|
||||||
import core.contracts.ContractState
|
|
||||||
import core.contracts.PartyAndReference
|
|
||||||
import core.contracts.TransactionBuilder
|
|
||||||
import core.crypto.Party
|
import core.crypto.Party
|
||||||
import core.crypto.SecureHash
|
import core.crypto.SecureHash
|
||||||
import core.node.AttachmentsClassLoader
|
|
||||||
import core.node.services.AttachmentStorage
|
import core.node.services.AttachmentStorage
|
||||||
import core.node.services.testing.MockAttachmentStorage
|
import core.node.services.testing.MockAttachmentStorage
|
||||||
import core.serialization.*
|
import core.serialization.*
|
||||||
@ -30,11 +24,36 @@ interface DummyContractBackdoor {
|
|||||||
fun inspectState(state: ContractState): Int
|
fun inspectState(state: ContractState): Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val ATTACHMENT_TEST_PROGRAM_ID = AttachmentClassLoaderTests.AttachmentDummyContract()
|
||||||
|
|
||||||
class AttachmentClassLoaderTests {
|
class AttachmentClassLoaderTests {
|
||||||
companion object {
|
companion object {
|
||||||
val ISOLATED_CONTRACTS_JAR_PATH = AttachmentClassLoaderTests::class.java.getResource("isolated.jar")
|
val ISOLATED_CONTRACTS_JAR_PATH = AttachmentClassLoaderTests::class.java.getResource("isolated.jar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AttachmentDummyContract : Contract {
|
||||||
|
class State(val magicNumber: Int = 0,
|
||||||
|
override val notary: Party) : ContractState {
|
||||||
|
override val contract = ATTACHMENT_TEST_PROGRAM_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Commands : CommandData {
|
||||||
|
class Create : TypeOnlyCommandData(), Commands
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun verify(tx: TransactionForVerification) {
|
||||||
|
// Always accepts.
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "empty contract"
|
||||||
|
override val legalContractReference: SecureHash = SecureHash.sha256("")
|
||||||
|
|
||||||
|
fun generateInitial(owner: PartyAndReference, magicNumber: Int, notary: Party): TransactionBuilder {
|
||||||
|
val state = State(magicNumber, notary)
|
||||||
|
return TransactionBuilder().withItems(state, Command(Commands.Create(), owner.party.owningKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun importJar(storage: AttachmentStorage) = ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) }
|
fun importJar(storage: AttachmentStorage) = ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) }
|
||||||
|
|
||||||
// These ClassLoaders work together to load 'AnotherDummyContract' in a disposable way, such that even though
|
// These ClassLoaders work together to load 'AnotherDummyContract' in a disposable way, such that even though
|
||||||
@ -127,7 +146,7 @@ class AttachmentClassLoaderTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `verify that contract DummyContract is in classPath`() {
|
fun `verify that contract DummyContract is in classPath`() {
|
||||||
val contractClass = Class.forName("contracts.DummyContract")
|
val contractClass = Class.forName("core.node.AttachmentClassLoaderTests\$AttachmentDummyContract")
|
||||||
val contract = contractClass.newInstance() as Contract
|
val contract = contractClass.newInstance() as Contract
|
||||||
|
|
||||||
assertNotNull(contract)
|
assertNotNull(contract)
|
||||||
@ -190,13 +209,13 @@ class AttachmentClassLoaderTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test serialization of WireTransaction with statically loaded contract`() {
|
fun `test serialization of WireTransaction with statically loaded contract`() {
|
||||||
val tx = DUMMY_PROGRAM_ID.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
|
val tx = ATTACHMENT_TEST_PROGRAM_ID.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
|
||||||
val wireTransaction = tx.toWireTransaction()
|
val wireTransaction = tx.toWireTransaction()
|
||||||
val bytes = wireTransaction.serialize()
|
val bytes = wireTransaction.serialize()
|
||||||
val copiedWireTransaction = bytes.deserialize()
|
val copiedWireTransaction = bytes.deserialize()
|
||||||
|
|
||||||
assertEquals(1, copiedWireTransaction.outputs.size)
|
assertEquals(1, copiedWireTransaction.outputs.size)
|
||||||
assertEquals(42, (copiedWireTransaction.outputs[0] as DummyContract.State).magicNumber)
|
assertEquals(42, (copiedWireTransaction.outputs[0] as AttachmentDummyContract.State).magicNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
@ -74,6 +74,7 @@ class SerializationTokenTest {
|
|||||||
|
|
||||||
@Test(expected = IllegalStateException::class)
|
@Test(expected = IllegalStateException::class)
|
||||||
fun `unannotated throws`() {
|
fun `unannotated throws`() {
|
||||||
|
@Suppress("UNUSED_VARIABLE")
|
||||||
val tokenizableBefore = UnannotatedSerializeAsSingletonToken()
|
val tokenizableBefore = UnannotatedSerializeAsSingletonToken()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,48 @@
|
|||||||
package core.serialization
|
package core.serialization
|
||||||
|
|
||||||
import contracts.Cash
|
|
||||||
import core.*
|
|
||||||
import core.contracts.*
|
import core.contracts.*
|
||||||
|
import core.crypto.Party
|
||||||
|
import core.crypto.SecureHash
|
||||||
import core.node.services.testing.MockStorageService
|
import core.node.services.testing.MockStorageService
|
||||||
|
import core.seconds
|
||||||
import core.testing.*
|
import core.testing.*
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.security.PublicKey
|
||||||
|
import java.security.SecureRandom
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
|
val TEST_PROGRAM_ID = TransactionSerializationTests.TestCash()
|
||||||
|
|
||||||
class TransactionSerializationTests {
|
class TransactionSerializationTests {
|
||||||
// Simple TX that takes 1000 pounds from me and sends 600 to someone else (with 400 change).
|
class TestCash : Contract {
|
||||||
|
override val legalContractReference = SecureHash.sha256("TestCash")
|
||||||
|
|
||||||
|
override fun verify(tx: TransactionForVerification) {
|
||||||
|
}
|
||||||
|
|
||||||
|
data class State(
|
||||||
|
val deposit: PartyAndReference,
|
||||||
|
val amount: Amount,
|
||||||
|
override val owner: PublicKey,
|
||||||
|
override val notary: Party) : OwnableState {
|
||||||
|
override val contract: Contract = TEST_PROGRAM_ID
|
||||||
|
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
|
}
|
||||||
|
interface Commands : CommandData {
|
||||||
|
class Move() : TypeOnlyCommandData(), Commands
|
||||||
|
data class Issue(val nonce: Long = SecureRandom.getInstanceStrong().nextLong()) : Commands
|
||||||
|
data class Exit(val amount: Amount) : Commands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple TX that takes 1000 pounds from me and sends 600 to someone else (with 400 change).
|
||||||
// It refers to a fake TX/state that we don't bother creating here.
|
// It refers to a fake TX/state that we don't bother creating here.
|
||||||
val depositRef = MINI_CORP.ref(1)
|
val depositRef = MINI_CORP.ref(1)
|
||||||
val outputState = Cash.State(depositRef, 600.POUNDS, DUMMY_PUBKEY_1, DUMMY_NOTARY)
|
val outputState = TestCash.State(depositRef, 600.POUNDS, DUMMY_PUBKEY_1, DUMMY_NOTARY)
|
||||||
val changeState = Cash.State(depositRef, 400.POUNDS, TestUtils.keypair.public, DUMMY_NOTARY)
|
val changeState = TestCash.State(depositRef, 400.POUNDS, TestUtils.keypair.public, DUMMY_NOTARY)
|
||||||
|
|
||||||
val fakeStateRef = generateStateRef()
|
val fakeStateRef = generateStateRef()
|
||||||
lateinit var tx: TransactionBuilder
|
lateinit var tx: TransactionBuilder
|
||||||
@ -24,7 +50,7 @@ class TransactionSerializationTests {
|
|||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
tx = TransactionBuilder().withItems(
|
tx = TransactionBuilder().withItems(
|
||||||
fakeStateRef, outputState, changeState, Command(Cash.Commands.Move(), arrayListOf(TestUtils.keypair.public))
|
fakeStateRef, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(TestUtils.keypair.public))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +87,7 @@ class TransactionSerializationTests {
|
|||||||
// If the signature was replaced in transit, we don't like it.
|
// If the signature was replaced in transit, we don't like it.
|
||||||
assertFailsWith(SignatureException::class) {
|
assertFailsWith(SignatureException::class) {
|
||||||
val tx2 = TransactionBuilder().withItems(fakeStateRef, outputState, changeState,
|
val tx2 = TransactionBuilder().withItems(fakeStateRef, outputState, changeState,
|
||||||
Command(Cash.Commands.Move(), TestUtils.keypair2.public))
|
Command(TestCash.Commands.Move(), TestUtils.keypair2.public))
|
||||||
tx2.signWith(TestUtils.keypair2)
|
tx2.signWith(TestUtils.keypair2)
|
||||||
|
|
||||||
signedTX.copy(sigs = tx2.toSignedTransaction().sigs).verify()
|
signedTX.copy(sigs = tx2.toSignedTransaction().sigs).verify()
|
Loading…
x
Reference in New Issue
Block a user