mirror of
https://github.com/corda/corda.git
synced 2024-12-25 07:31:10 +00:00
Merged in rnicoll-timestamping-notary-take2 (pull request #206)
Replace named timestamping authority with notary
This commit is contained in:
commit
2cd5014941
@ -131,6 +131,12 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Redeem extends Commands {
|
public static class Redeem extends Commands {
|
||||||
|
private final Party notary;
|
||||||
|
|
||||||
|
public Redeem(Party setNotary) {
|
||||||
|
this.notary = setNotary;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return obj instanceof Redeem;
|
return obj instanceof Redeem;
|
||||||
@ -138,6 +144,12 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Issue extends Commands {
|
public static class Issue extends Commands {
|
||||||
|
private final Party notary;
|
||||||
|
|
||||||
|
public Issue(Party setNotary) {
|
||||||
|
this.notary = setNotary;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
return obj instanceof Issue;
|
return obj instanceof Issue;
|
||||||
@ -163,6 +175,7 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
|
|
||||||
// For now do not allow multiple pieces of CP to trade in a single transaction.
|
// For now do not allow multiple pieces of CP to trade in a single transaction.
|
||||||
if (cmd.getValue() instanceof JavaCommercialPaper.Commands.Issue) {
|
if (cmd.getValue() instanceof JavaCommercialPaper.Commands.Issue) {
|
||||||
|
Commands.Issue issueCommand = (Commands.Issue) cmd.getValue();
|
||||||
State output = single(outputs);
|
State output = single(outputs);
|
||||||
if (!inputs.isEmpty()) {
|
if (!inputs.isEmpty()) {
|
||||||
throw new IllegalStateException("Failed Requirement: output values sum to more than the inputs");
|
throw new IllegalStateException("Failed Requirement: output values sum to more than the inputs");
|
||||||
@ -171,7 +184,7 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
throw new IllegalStateException("Failed Requirement: output values sum to more than the inputs");
|
throw new IllegalStateException("Failed Requirement: output values sum to more than the inputs");
|
||||||
}
|
}
|
||||||
|
|
||||||
TimestampCommand timestampCommand = tx.getTimestampByName("Notary Service");
|
TimestampCommand timestampCommand = tx.getTimestampBy(issueCommand.notary);
|
||||||
if (timestampCommand == null)
|
if (timestampCommand == null)
|
||||||
throw new IllegalArgumentException("Failed Requirement: must be timestamped");
|
throw new IllegalArgumentException("Failed Requirement: must be timestamped");
|
||||||
|
|
||||||
@ -201,7 +214,7 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
!output.getMaturityDate().equals(input.getMaturityDate()))
|
!output.getMaturityDate().equals(input.getMaturityDate()))
|
||||||
throw new IllegalStateException("Failed requirement: the output state is the same as the input state except for owner");
|
throw new IllegalStateException("Failed requirement: the output state is the same as the input state except for owner");
|
||||||
} else if (cmd.getValue() instanceof JavaCommercialPaper.Commands.Redeem) {
|
} else if (cmd.getValue() instanceof JavaCommercialPaper.Commands.Redeem) {
|
||||||
TimestampCommand timestampCommand = tx.getTimestampByName("Notary Service");
|
TimestampCommand timestampCommand = tx.getTimestampBy(((Commands.Redeem) cmd.getValue()).notary);
|
||||||
if (timestampCommand == null)
|
if (timestampCommand == null)
|
||||||
throw new IllegalArgumentException("Failed Requirement: must be timestamped");
|
throw new IllegalArgumentException("Failed Requirement: must be timestamped");
|
||||||
Instant time = timestampCommand.getBefore();
|
Instant time = timestampCommand.getBefore();
|
||||||
@ -232,13 +245,13 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount<Issued<Currency>> faceValue, @Nullable Instant maturityDate, @NotNull Party notary) {
|
public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount<Issued<Currency>> faceValue, @Nullable Instant maturityDate, @NotNull Party notary) {
|
||||||
State state = new State(issuance, issuance.getParty().getOwningKey(), faceValue, maturityDate);
|
State state = new State(issuance, issuance.getParty().getOwningKey(), faceValue, maturityDate);
|
||||||
TransactionState output = new TransactionState<>(state, notary);
|
TransactionState output = new TransactionState<>(state, notary);
|
||||||
return new TransactionType.General.Builder().withItems(output, new Command(new Commands.Issue(), issuance.getParty().getOwningKey()));
|
return new TransactionType.General.Builder().withItems(output, new Command(new Commands.Issue(notary), issuance.getParty().getOwningKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateRedeem(TransactionBuilder tx, StateAndRef<State> paper, List<StateAndRef<Cash.State>> wallet) throws InsufficientBalanceException {
|
public void generateRedeem(TransactionBuilder tx, StateAndRef<State> paper, List<StateAndRef<Cash.State>> wallet) throws InsufficientBalanceException {
|
||||||
new Cash().generateSpend(tx, paper.getState().getData().getFaceValue(), paper.getState().getData().getOwner(), wallet);
|
new Cash().generateSpend(tx, paper.getState().getData().getFaceValue(), paper.getState().getData().getOwner(), wallet);
|
||||||
tx.addInputState(paper);
|
tx.addInputState(paper);
|
||||||
tx.addCommand(new Command(new Commands.Redeem(), paper.getState().getData().getOwner()));
|
tx.addCommand(new Command(new Commands.Redeem(paper.getState().getNotary()), paper.getState().getData().getOwner()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, PublicKey newOwner) {
|
public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, PublicKey newOwner) {
|
||||||
|
@ -65,11 +65,11 @@ class CommercialPaper : Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Commands : CommandData {
|
interface Commands : CommandData {
|
||||||
class Move : TypeOnlyCommandData(), Commands
|
class Move: TypeOnlyCommandData(), Commands
|
||||||
class Redeem : TypeOnlyCommandData(), Commands
|
data class Redeem(val notary: Party) : Commands
|
||||||
// We don't need a nonce in the issue command, because the issuance.reference field should already be unique per CP.
|
// We don't need a nonce in the issue command, because the issuance.reference field should already be unique per CP.
|
||||||
// However, nothing in the platform enforces that uniqueness: it's up to the issuer.
|
// However, nothing in the platform enforces that uniqueness: it's up to the issuer.
|
||||||
class Issue : TypeOnlyCommandData(), Commands
|
data class Issue(val notary: Party) : Commands
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun verify(tx: TransactionForContract) {
|
override fun verify(tx: TransactionForContract) {
|
||||||
@ -79,11 +79,13 @@ class CommercialPaper : Contract {
|
|||||||
// There are two possible things that can be done with this CP. The first is trading it. The second is redeeming
|
// There are two possible things that can be done with this CP. The first is trading it. The second is redeeming
|
||||||
// it for cash on or after the maturity date.
|
// it for cash on or after the maturity date.
|
||||||
val command = tx.commands.requireSingleCommand<CommercialPaper.Commands>()
|
val command = tx.commands.requireSingleCommand<CommercialPaper.Commands>()
|
||||||
|
// If it's an issue, we can't take notary from inputs, so it must be specified in the command
|
||||||
// Here, we match acceptable timestamp authorities by name. The list of acceptable TSAs (oracles) must be
|
val timestamp: TimestampCommand? = if (command.value is Commands.Issue)
|
||||||
// hard coded into the contract because otherwise we could fail to gain consensus, if nodes disagree about
|
tx.getTimestampBy((command.value as Commands.Issue).notary)
|
||||||
// who or what is a trusted authority.
|
else if (command.value is Commands.Redeem)
|
||||||
val timestamp: TimestampCommand? = tx.commands.getTimestampByName("Mock Company 0", "Notary Service", "Bank A")
|
tx.getTimestampBy((command.value as Commands.Redeem).notary)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
for ((inputs, outputs, key) in groups) {
|
for ((inputs, outputs, key) in groups) {
|
||||||
when (command.value) {
|
when (command.value) {
|
||||||
@ -139,7 +141,7 @@ class CommercialPaper : Contract {
|
|||||||
fun generateIssue(faceValue: Amount<Issued<Currency>>, maturityDate: Instant, notary: Party): TransactionBuilder {
|
fun generateIssue(faceValue: Amount<Issued<Currency>>, maturityDate: Instant, notary: Party): TransactionBuilder {
|
||||||
val issuance = faceValue.token.issuer
|
val issuance = faceValue.token.issuer
|
||||||
val state = TransactionState(State(issuance, issuance.party.owningKey, faceValue, maturityDate), notary)
|
val state = TransactionState(State(issuance, issuance.party.owningKey, faceValue, maturityDate), notary)
|
||||||
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
|
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Issue(notary), issuance.party.owningKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -164,7 +166,7 @@ class CommercialPaper : Contract {
|
|||||||
val amount = paper.state.data.faceValue.let { amount -> Amount<Currency>(amount.quantity, amount.token.product) }
|
val amount = paper.state.data.faceValue.let { amount -> Amount<Currency>(amount.quantity, amount.token.product) }
|
||||||
Cash().generateSpend(tx, amount, paper.state.data.owner, wallet)
|
Cash().generateSpend(tx, amount, paper.state.data.owner, wallet)
|
||||||
tx.addInputState(paper)
|
tx.addInputState(paper)
|
||||||
tx.addCommand(CommercialPaper.Commands.Redeem(), paper.state.data.owner)
|
tx.addCommand(CommercialPaper.Commands.Redeem(paper.state.notary), paper.state.data.owner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,6 +496,8 @@ class InterestRateSwap() : Contract {
|
|||||||
val groups = tx.groupStates() { state: InterestRateSwap.State -> state.common.tradeID }
|
val groups = tx.groupStates() { state: InterestRateSwap.State -> state.common.tradeID }
|
||||||
|
|
||||||
val command = tx.commands.requireSingleCommand<InterestRateSwap.Commands>()
|
val command = tx.commands.requireSingleCommand<InterestRateSwap.Commands>()
|
||||||
|
// TODO: This needs to either be the notary used for the inputs, or otherwise
|
||||||
|
// derived as the correct notary
|
||||||
val time = tx.commands.getTimestampByName("Mock Company 0", "Notary Service", "Bank A")?.midpoint
|
val time = tx.commands.getTimestampByName("Mock Company 0", "Notary Service", "Bank A")?.midpoint
|
||||||
if (time == null) throw IllegalArgumentException("must be timestamped")
|
if (time == null) throw IllegalArgumentException("must be timestamped")
|
||||||
|
|
||||||
|
@ -390,9 +390,7 @@ class Obligation<P> : Contract {
|
|||||||
for ((stateIdx, input) in inputs.withIndex()) {
|
for ((stateIdx, input) in inputs.withIndex()) {
|
||||||
val actualOutput = outputs[stateIdx]
|
val actualOutput = outputs[stateIdx]
|
||||||
val deadline = input.dueBefore
|
val deadline = input.dueBefore
|
||||||
// TODO: Determining correct timestamp authority needs rework now that timestamping service is part of
|
val timestamp: TimestampCommand? = tx.timestamp
|
||||||
// notary.
|
|
||||||
val timestamp: TimestampCommand? = tx.commands.getTimestampByName("Mock Company 0", "Notary Service", "Bank A")
|
|
||||||
val expectedOutput: State<P> = input.copy(lifecycle = expectedOutputLifecycle)
|
val expectedOutput: State<P> = input.copy(lifecycle = expectedOutputLifecycle)
|
||||||
|
|
||||||
requireThat {
|
requireThat {
|
||||||
|
@ -3,6 +3,7 @@ package com.r3corda.contracts
|
|||||||
import com.r3corda.contracts.asset.Cash
|
import com.r3corda.contracts.asset.Cash
|
||||||
import com.r3corda.contracts.testing.*
|
import com.r3corda.contracts.testing.*
|
||||||
import com.r3corda.core.contracts.*
|
import com.r3corda.core.contracts.*
|
||||||
|
import com.r3corda.core.crypto.Party
|
||||||
import com.r3corda.core.crypto.SecureHash
|
import com.r3corda.core.crypto.SecureHash
|
||||||
import com.r3corda.core.days
|
import com.r3corda.core.days
|
||||||
import com.r3corda.core.node.services.testing.MockStorageService
|
import com.r3corda.core.node.services.testing.MockStorageService
|
||||||
@ -18,8 +19,8 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
interface ICommercialPaperTestTemplate {
|
interface ICommercialPaperTestTemplate {
|
||||||
fun getPaper(): ICommercialPaperState
|
fun getPaper(): ICommercialPaperState
|
||||||
fun getIssueCommand(): CommandData
|
fun getIssueCommand(notary: Party): CommandData
|
||||||
fun getRedeemCommand(): CommandData
|
fun getRedeemCommand(notary: Party): CommandData
|
||||||
fun getMoveCommand(): CommandData
|
fun getMoveCommand(): CommandData
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,8 +32,8 @@ class JavaCommercialPaperTest() : ICommercialPaperTestTemplate {
|
|||||||
TEST_TX_TIME + 7.days
|
TEST_TX_TIME + 7.days
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun getIssueCommand(): CommandData = JavaCommercialPaper.Commands.Issue()
|
override fun getIssueCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Issue(notary)
|
||||||
override fun getRedeemCommand(): CommandData = JavaCommercialPaper.Commands.Redeem()
|
override fun getRedeemCommand(notary: Party): CommandData = JavaCommercialPaper.Commands.Redeem(notary)
|
||||||
override fun getMoveCommand(): CommandData = JavaCommercialPaper.Commands.Move()
|
override fun getMoveCommand(): CommandData = JavaCommercialPaper.Commands.Move()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,8 +45,8 @@ class KotlinCommercialPaperTest() : ICommercialPaperTestTemplate {
|
|||||||
maturityDate = TEST_TX_TIME + 7.days
|
maturityDate = TEST_TX_TIME + 7.days
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun getIssueCommand(): CommandData = CommercialPaper.Commands.Issue()
|
override fun getIssueCommand(notary: Party): CommandData = CommercialPaper.Commands.Issue(notary)
|
||||||
override fun getRedeemCommand(): CommandData = CommercialPaper.Commands.Redeem()
|
override fun getRedeemCommand(notary: Party): CommandData = CommercialPaper.Commands.Redeem(notary)
|
||||||
override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move()
|
override fun getMoveCommand(): CommandData = CommercialPaper.Commands.Move()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ class CommercialPaperTestsGeneric {
|
|||||||
// Some CP is issued onto the ledger by MegaCorp.
|
// Some CP is issued onto the ledger by MegaCorp.
|
||||||
transaction("Issuance") {
|
transaction("Issuance") {
|
||||||
output("paper") { thisTest.getPaper() }
|
output("paper") { thisTest.getPaper() }
|
||||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
@ -103,7 +104,7 @@ class CommercialPaperTestsGeneric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
command(ALICE_PUBKEY) { thisTest.getRedeemCommand() }
|
command(ALICE_PUBKEY) { thisTest.getRedeemCommand(DUMMY_NOTARY) }
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
outputs(700.DOLLARS `issued by` issuer)
|
outputs(700.DOLLARS `issued by` issuer)
|
||||||
@ -133,7 +134,7 @@ class CommercialPaperTestsGeneric {
|
|||||||
fun `key mismatch at issue`() {
|
fun `key mismatch at issue`() {
|
||||||
transaction {
|
transaction {
|
||||||
output { thisTest.getPaper() }
|
output { thisTest.getPaper() }
|
||||||
command(DUMMY_PUBKEY_1) { thisTest.getIssueCommand() }
|
command(DUMMY_PUBKEY_1) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this `fails with` "output states are issued by a command signer"
|
this `fails with` "output states are issued by a command signer"
|
||||||
}
|
}
|
||||||
@ -143,7 +144,7 @@ class CommercialPaperTestsGeneric {
|
|||||||
fun `face value is not zero`() {
|
fun `face value is not zero`() {
|
||||||
transaction {
|
transaction {
|
||||||
output { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) }
|
output { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) }
|
||||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this `fails with` "output values sum to more than the inputs"
|
this `fails with` "output values sum to more than the inputs"
|
||||||
}
|
}
|
||||||
@ -153,7 +154,7 @@ class CommercialPaperTestsGeneric {
|
|||||||
fun `maturity date not in the past`() {
|
fun `maturity date not in the past`() {
|
||||||
transaction {
|
transaction {
|
||||||
output { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) }
|
output { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) }
|
||||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this `fails with` "maturity date is not in the past"
|
this `fails with` "maturity date is not in the past"
|
||||||
}
|
}
|
||||||
@ -164,7 +165,7 @@ class CommercialPaperTestsGeneric {
|
|||||||
transaction {
|
transaction {
|
||||||
input(thisTest.getPaper())
|
input(thisTest.getPaper())
|
||||||
output { thisTest.getPaper() }
|
output { thisTest.getPaper() }
|
||||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand(DUMMY_NOTARY) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this `fails with` "output values sum to more than the inputs"
|
this `fails with` "output values sum to more than the inputs"
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,7 @@ fun List<AuthenticatedObject<CommandData>>.getTimestampBy(timestampingAuthority:
|
|||||||
* Note that matching here is done by (verified, legal) name, not by public key. Any signature by any
|
* Note that matching here is done by (verified, legal) name, not by public key. Any signature by any
|
||||||
* party with a name that matches (case insensitively) any of the given names will yield a match.
|
* party with a name that matches (case insensitively) any of the given names will yield a match.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(message = "Timestamping authority should always be notary for the transaction")
|
||||||
fun List<AuthenticatedObject<CommandData>>.getTimestampByName(vararg names: String): TimestampCommand? {
|
fun List<AuthenticatedObject<CommandData>>.getTimestampByName(vararg names: String): TimestampCommand? {
|
||||||
val timestampCmd = filter { it.value is TimestampCommand }.singleOrNull() ?: return null
|
val timestampCmd = filter { it.value is TimestampCommand }.singleOrNull() ?: return null
|
||||||
val tsaNames = timestampCmd.signingParties.map { it.name.toLowerCase() }
|
val tsaNames = timestampCmd.signingParties.map { it.name.toLowerCase() }
|
||||||
|
@ -316,6 +316,9 @@ data class AuthenticatedObject<out T : Any>(
|
|||||||
* If present in a transaction, contains a time that was verified by the timestamping authority/authorities whose
|
* If present in a transaction, contains a time that was verified by the timestamping authority/authorities whose
|
||||||
* public keys are identified in the containing [Command] object. The true time must be between (after, before)
|
* public keys are identified in the containing [Command] object. The true time must be between (after, before)
|
||||||
*/
|
*/
|
||||||
|
// TODO: Timestamps are now always provided by the consensus service for the transaction, rather than potentially
|
||||||
|
// having multiple timestamps on a transaction. As such, it likely makes more sense for time to be a field on the
|
||||||
|
// transaction, rather than a command
|
||||||
data class TimestampCommand(val after: Instant?, val before: Instant?) : CommandData {
|
data class TimestampCommand(val after: Instant?, val before: Instant?) : CommandData {
|
||||||
init {
|
init {
|
||||||
if (after == null && before == null)
|
if (after == null && before == null)
|
||||||
|
@ -73,7 +73,8 @@ data class TransactionForVerification(val inputs: List<TransactionState<Contract
|
|||||||
@Throws(TransactionVerificationException::class)
|
@Throws(TransactionVerificationException::class)
|
||||||
fun verify() = type.verify(this)
|
fun verify() = type.verify(this)
|
||||||
|
|
||||||
fun toTransactionForContract() = TransactionForContract(inputs.map { it.data }, outputs.map { it.data }, attachments, commands, origHash)
|
fun toTransactionForContract() = TransactionForContract(inputs.map { it.data }, outputs.map { it.data },
|
||||||
|
attachments, commands, origHash, inputs.map { it.notary }.singleOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,7 +85,8 @@ data class TransactionForContract(val inputs: List<ContractState>,
|
|||||||
val outputs: List<ContractState>,
|
val outputs: List<ContractState>,
|
||||||
val attachments: List<Attachment>,
|
val attachments: List<Attachment>,
|
||||||
val commands: List<AuthenticatedObject<CommandData>>,
|
val commands: List<AuthenticatedObject<CommandData>>,
|
||||||
val origHash: SecureHash) {
|
val origHash: SecureHash,
|
||||||
|
val inputNotary: Party? = null) {
|
||||||
override fun hashCode() = origHash.hashCode()
|
override fun hashCode() = origHash.hashCode()
|
||||||
override fun equals(other: Any?) = other is TransactionForContract && other.origHash == origHash
|
override fun equals(other: Any?) = other is TransactionForContract && other.origHash == origHash
|
||||||
|
|
||||||
@ -158,10 +160,15 @@ data class TransactionForContract(val inputs: List<ContractState>,
|
|||||||
*/
|
*/
|
||||||
data class InOutGroup<T : ContractState, K : Any>(val inputs: List<T>, val outputs: List<T>, val groupingKey: K)
|
data class InOutGroup<T : ContractState, K : Any>(val inputs: List<T>, val outputs: List<T>, val groupingKey: K)
|
||||||
|
|
||||||
|
/** Get the timestamp command for this transaction, using the notary from the input states. */
|
||||||
|
val timestamp: TimestampCommand?
|
||||||
|
get() = if (inputNotary == null) null else commands.getTimestampBy(inputNotary)
|
||||||
|
|
||||||
/** Simply calls [commands.getTimestampBy] as a shortcut to make code completion more intuitive. */
|
/** Simply calls [commands.getTimestampBy] as a shortcut to make code completion more intuitive. */
|
||||||
fun getTimestampBy(timestampingAuthority: Party): TimestampCommand? = commands.getTimestampBy(timestampingAuthority)
|
fun getTimestampBy(timestampingAuthority: Party): TimestampCommand? = commands.getTimestampBy(timestampingAuthority)
|
||||||
|
|
||||||
/** Simply calls [commands.getTimestampByName] as a shortcut to make code completion more intuitive. */
|
/** Simply calls [commands.getTimestampByName] as a shortcut to make code completion more intuitive. */
|
||||||
|
@Deprecated(message = "Timestamping authority should always be notary for the transaction")
|
||||||
fun getTimestampByName(vararg authorityName: String): TimestampCommand? = commands.getTimestampByName(*authorityName)
|
fun getTimestampByName(vararg authorityName: String): TimestampCommand? = commands.getTimestampByName(*authorityName)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -481,7 +481,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
output("alice's paper") {
|
output("alice's paper") {
|
||||||
CommercialPaper.State(MEGA_CORP.ref(1, 2, 3), owner, amount, TEST_TX_TIME + 7.days)
|
CommercialPaper.State(MEGA_CORP.ref(1, 2, 3), owner, amount, TEST_TX_TIME + 7.days)
|
||||||
}
|
}
|
||||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue(notary) }
|
||||||
if (!withError)
|
if (!withError)
|
||||||
timestamp(time = TEST_TX_TIME, notary = notary.owningKey)
|
timestamp(time = TEST_TX_TIME, notary = notary.owningKey)
|
||||||
if (attachmentID != null)
|
if (attachmentID != null)
|
||||||
|
Loading…
Reference in New Issue
Block a user