I have some troubles implementing a FungibleAsset contract.
The Dapp should consist of a Pool which is tokenized through PoolToken. For the PoolToken I implement the FungibleAsset class. There is a flow called CreatePoolFlow which uses the Create-Command from the Pool and the Issue-Command from the PoolToken. This flow should create a new Pool and Issue a specified number of Tokens the the Creator. When invoking the Flow an Error occurs.
Is there any guide on how to implement FungibleAsset or in general Tokens?
Thanks in advance for any help!
Pool.kt:
package com.company
//imports here
// *****************
// * Contract Code *
// *****************
class Pool : Contract {
// This is used to identify our contract when building a transaction
companion object {
const val POOL_CONTRACT_ID: ContractClassName = "com.company.Pool"
}
// A transaction is considered valid if the verify() function of the contract of each of the transaction's input
// and output states does not throw an exception.
override fun verify(tx: LedgerTransaction) {
//requireSingleCommand
var command = tx.commands.requireSingleCommand<Pool.Commands>()
val timeWindow: TimeWindow? = tx.timeWindow
when (command.value) {
is Commands.Create -> {
}
else -> throw IllegalArgumentException("Unrecognised command!")
}
}
// Used to indicate the transaction's intent.
interface Commands : CommandData {
class Create : TypeOnlyCommandData(), Commands
}
}
// *********
// * State *
// *********
data class PoolState(val issuer: Party,
override val participants: List<Party> = listOf(issuer),
val id: UniqueIdentifier = UniqueIdentifier())
//val id: UniqueIdentifier = UniqueIdentifier()) //val loans: Array<StateRef<Loan>>
: ContractState {
//override val participants = listOf(issuer)
//fun withoutOwner() = copy(issuer = AnonymousParty(NullKeys.NullPublicKey))
}
PoolToken.kt:
package com.company
//imports here
// *****************
// * Contract Code *
// *****************
class PoolToken : Contract {
// This is used to identify our contract when building a transaction
companion object {
const val POOLTOKEN_CONTRACT_ID: ContractClassName = "com.company.PoolToken"
}
// A transaction is considered valid if the verify() function of the contract of each of the transaction's input
// and output states does not throw an exception.
override fun verify(tx: LedgerTransaction) {
//requireSingleCommand
var command = tx.commands.requireSingleCommand<PoolToken.Commands>()
val timeWindow: TimeWindow? = tx.timeWindow
when (command.value) {
is Commands.Issue -> {
}
is Commands.Move -> {
}
is Commands.Exit -> {
}
else -> throw IllegalArgumentException("Unrecognised command!")
}
}
// Used to indicate the transaction's intent.
interface Commands : CommandData {
class Move : TypeOnlyCommandData(), Commands
class Issue : TypeOnlyCommandData(), Commands
class Exit : TypeOnlyCommandData(), Commands
}
}
// *********
// * State *
// *********
data class PoolTokenState(override val owner: AbstractParty,
override val amount: Amount<Issued<UniqueIdentifier>>)
: FungibleAsset<UniqueIdentifier> { //TODO: Change to BigDecimal
override val exitKeys = setOf(owner.owningKey) //, amount.token.issuer.party.owningKey
override val participants = listOf(owner)
override fun withNewOwnerAndAmount(newAmount: Amount<Issued<UniqueIdentifier>>, newOwner: AbstractParty): FungibleAsset<UniqueIdentifier>
= copy(amount = amount.copy(newAmount.quantity), owner = newOwner)
//override fun toString() = "$amount Tokens of Pool ${amount.token.product} from ${amount.token.issuer} owned by $owner"
override fun withNewOwner(newOwner: AbstractParty) = CommandAndState(PoolToken.Commands.Move(), copy(owner = newOwner))
//fun withoutOwner() = copy(issuer = AnonymousParty(NullKeys.NullPublicKey))
}
PoolFlows.kt:
package com.company
//imports here
#InitiatingFlow
#StartableByRPC
class CreatePoolFlow(val tokens: Long) : FlowLogic<SignedTransaction>() { //val auditor: Party
override val progressTracker = ProgressTracker()
#Suspendable
override fun call(): SignedTransaction {
//retrieve the notary identity from the network map
val notary = serviceHub.networkMapCache.notaryIdentities.first()
//create a transaction builder
val txBuilder = TransactionBuilder(notary = notary)
//get all parties in the network
//TODO: Construct as set and convert to list in order to prevent duplicates?
val nodes = serviceHub.networkMapCache.allNodes
val _parties = mutableListOf<Party>()
for (node in nodes) {
for (party in node.legalIdentities) {
_parties.add(party)
}
}
val parties: List<Party> = _parties
//create the Pool transaction components
val outputStatePool = PoolState(ourIdentity,parties)
val outputContractAndStatePool = StateAndContract(outputStatePool, POOL_CONTRACT_ID)
val cmdPool = Command(Pool.Commands.Create(), ourIdentity.owningKey)
//create the PoolToken transaction components
val outputStatePoolToken = PoolTokenState(ourIdentity, Amount(tokens,Issued(ourIdentity.ref(),outputStatePool.id)))
val outputContractAndStatePoolToken = StateAndContract(outputStatePoolToken, POOLTOKEN_CONTRACT_ID)
val cmdPoolToken = Command(PoolToken.Commands.Issue(), ourIdentity.owningKey)
//add the items to the builder
txBuilder.withItems(outputContractAndStatePool, cmdPool, outputContractAndStatePoolToken, cmdPoolToken)
//add time
val currentTime = serviceHub.clock.instant()
txBuilder.setTimeWindow(currentTime, 60.seconds)
//verifying the transaction
txBuilder.verify(serviceHub)
//signing the transaction
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// Finalising the transaction.
subFlow(FinalityFlow(signedTx))
return signedTx
}
}
Error when executing the flow:
java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Failed requirement.
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
at com.company.FlowTests.create pool and tokens(FlowTests.kt:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.IllegalArgumentException: Failed requirement.
at net.corda.core.utilities.OpaqueBytes.<init>(ByteArrays.kt:154)
at net.corda.core.utilities.OpaqueBytes$Companion.of(ByteArrays.kt:150)
at net.corda.core.identity.AbstractParty.ref(AbstractParty.kt:32)
at com.company.CreatePoolFlow.call(PoolFlows.kt:63)
at com.company.CreatePoolFlow.call(PoolFlows.kt:34)
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:96)
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:44)
at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092)
at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788)
at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100)
at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
at java.util.concurrent.FutureTask.run(FutureTask.java)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:62)
I assume the problem is in the line
val outputStatePoolToken = PoolTokenState(ourIdentity, Amount(tokens,Issued(ourIdentity.ref(),outputStatePool.id)))
You are creating ourIdenity.ref() with empty bytes, which will cause the exact error.
Pass something like OpaqueBytes.of(0) to it and see if it helps
Related
i want to use addIssueTokens to add token to TransactionBuilder.
my code
#InitiatingFlow
#StartableByRPC
class Issue(val otherParty: Party) : FlowLogic<SignedTransaction>() {
override val progressTracker = ProgressTracker()
#Suspendable
override fun call() : SignedTransaction{
val jpyToken = createFungibleToken("JPY", 1000, otherParty)
val gbToken = createFungibleToken("GB", 1000, otherParty)
val notary = serviceHub.networkMapCache.notaryIdentities.single()
val txBuilder = TransactionBuilder(notary)
//may be add other output input
//....
//add token to txBuilder
addIssueTokens(txBuilder, listOf(jpyToken,gbToken))
txBuilder.verify(serviceHub)
// Sign the transaction
val ptx = serviceHub.signInitialTransaction(txBuilder, ourIdentity.owningKey)
// Instantiate a network session with the shareholder
val holderSession = initiateFlow(otherParty)
val sessions = listOf(holderSession)
// Ask the shareholder to sign the transaction
val stx = subFlow(CollectSignaturesFlow(ptx, listOf(holderSession)))
return subFlow<SignedTransaction>(FinalityFlow(stx, sessions))
}
fun createFungibleToken(symbol:String,amout:Long,target : AbstractParty) : FungibleToken{
val tokenType = TokenType(symbol, 0)
val issuedTokenType = IssuedTokenType(ourIdentity, tokenType)
val amount = Amount<IssuedTokenType>(amout, issuedTokenType)
return FungibleToken(amount, target)
}
}
#InitiatedBy(Issue::class)
class IssueResponder(val otherPartySession: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val signTransactionFlow = object : SignTransactionFlow(otherPartySession) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
}
}
val txId = subFlow(signTransactionFlow).id
return subFlow(ReceiveFinalityFlow(otherPartySession, expectedTxId = txId))
}
}
shell: start flow
>>start com.template.flows.Issue otherParty: "O=PartyB,L=New York,C=US"
Starting
Collecting signatures from counterparties.
Starting
Broadcasting transaction to participants
Done
Flow completed with result: SignedTransaction(id=547B812BA5574168DA8085C87AADFCAFA2A098CF62F375C21D450C0FE2402547)
it seems that Flow completed. but i check partyB database ,there is no data in vault_states table .
why?
ps.i knew how to use com.r3.corda.lib.tokens.workflows.flows.rpc.IssueTokens flow
The holder is not a required signer when issuing tokens; try removing the CollectSignaturesFlow and SignTransactionFlow calls from the initiator and responder flows; only keep FinalityFlow and ReceiveFinalityFlow.
You can see here that the required signers are:
The issuer on issuing.
The holder on moving.
The issuer and holder on redeeming.
But also the contract says here that there could be other signers on issue; so I'm not 100% sure that's the cause of the problem.
Try removing the signature flows and re-run your test; and let me know if it worked.
Another thing, check the logs of the initiating node (the log is inside logs folder inside your node's folder structure); are there any errors?.
I am trying to make use of Composite Key feature to show how an asset on the ledger could be controlled using a composite key. I get following error in my flow code:
java.lang.IllegalArgumentException: Could not find Party for Anonymous(DLHpGSdYSvv7vLRGJuuZSsTWQpk7ehkB7B1K1bzV68YmY7)
at net.corda.core.identity.IdentityUtils.groupAbstractPartyByWellKnownParty(IdentityUtils.kt:47)
at net.corda.core.identity.IdentityUtils.groupAbstractPartyByWellKnownParty(IdentityUtils.kt:63)
at net.corda.core.flows.FinalityFlow.getPartiesToSend(FinalityFlow.kt:96)
at net.corda.core.flows.FinalityFlow.call(FinalityFlow.kt:54)
at net.corda.core.flows.FinalityFlow.call(FinalityFlow.kt:28)
at net.corda.core.flows.FlowLogic.subFlow(FlowLogic.kt:290)
at com.example.flow.ExampleFlow$Initiator.call(ExampleFlow.kt:102)
at com.example.flow.ExampleFlow$Initiator.call(ExampleFlow.kt:34)
Below is my State definition
data class LandState(val alice: Party, val bob: Party, override val linearId: UniqueIdentifier = UniqueIdentifier()): LinearState{
val owner: AbstractParty = AnonymousParty (CompositeKey.Builder().addKeys(alice.owningKey, bob.owningKey).build())
override val participants: List<AbstractParty> = listOf(owner)}
Below is my contract code
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands.Create>()
requireThat {
"No inputs should be consumed when issuing an IOU." using (tx.inputs.isEmpty())
"Only one output state should be created." using (tx.outputs.size == 1)
val out = tx.outputsOfType<LandState>().single()
"Command must be signed by the Composite key holder" using (command.signers.contains(out.owner.owningKey))
}
}
Below is my Flow
object ExampleFlow {
#InitiatingFlow
#StartableByRPC
class Initiator( val alice: Party,
val bob: Party) : FlowLogic<SignedTransaction>() {
/**
* The progress tracker checkpoints each stage of the flow and outputs the specified messages when each
* checkpoint is reached in the code. See the 'progressTracker.currentStep' expressions within the call() function.
*/
companion object {
object GENERATING_TRANSACTION : Step("Generating transaction based on new IOU.")
object VERIFYING_TRANSACTION : Step("Verifying contract constraints.")
object SIGNING_TRANSACTION : Step("Signing transaction with our private key.")
object GATHERING_SIGS : Step("Gathering the counterparty's signature.") {
override fun childProgressTracker() = CollectSignaturesFlow.tracker()
}
object FINALISING_TRANSACTION : Step("Obtaining notary signature and recording transaction.") {
override fun childProgressTracker() = FinalityFlow.tracker()
}
fun tracker() = ProgressTracker(
GENERATING_TRANSACTION,
VERIFYING_TRANSACTION,
SIGNING_TRANSACTION,
GATHERING_SIGS,
FINALISING_TRANSACTION
)
}
override val progressTracker = tracker()
/**
* The flow logic is encapsulated within the call() method.
*/
#Suspendable
override fun call(): SignedTransaction {
// Obtain a reference to the notary we want to use.
val notary = serviceHub.networkMapCache.notaryIdentities[0]
// Stage 1.
progressTracker.currentStep = GENERATING_TRANSACTION
// Generate an unsigned transaction.
val landState = LandState(alice, bob)
// val iouState = IOUState(iouValue, serviceHub.myInfo.legalIdentities.first(), otherParty)
val txCommand = Command(IOUContract.Commands.Create(), listOf(landState.owner.owningKey))
val txBuilder = TransactionBuilder(notary)
.addOutputState(landState, IOU_CONTRACT_ID)
.addCommand(txCommand)
// Stage 2.
progressTracker.currentStep = VERIFYING_TRANSACTION
// Verify that the transaction is valid.
// Stage 3.
progressTracker.currentStep = SIGNING_TRANSACTION
// Sign the transaction.
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
// Stage 4.
progressTracker.currentStep = GATHERING_SIGS
// Send the state to the counterparty, and receive it back with their signature.
val otherPartyFlow = initiateFlow(bob)
val fullySignedTx = subFlow(CollectSignaturesFlow(partSignedTx, setOf(otherPartyFlow), GATHERING_SIGS.childProgressTracker()))
txBuilder.verify(serviceHub)
// Stage 5.
progressTracker.currentStep = FINALISING_TRANSACTION
// Notarise and record the transaction in both parties' vaults.
return subFlow(FinalityFlow(fullySignedTx))
}
}
#InitiatedBy(Initiator::class)
class Acceptor(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
}
}
return subFlow(signTransactionFlow)
}
}}
Am I missing anything? The owner field in the above sate is of type AnonymousParty since composite key is of type public key and not a well known certificate.
You can't use CollectSignaturesFlow with CompositeKeys. You need to define your own custom flow for collecting the required signatures.
Why? Because CollectSignaturesFlow works out who to request signatures from based on the parties listed in the commands. Since the CompositeKey does not correspond to a specific party, the node does not know who to request a signature from and throws an exception.
i'm having some trouble to implement a simple use case in corda. i want to implement a free of payment transfer. the lifecycle is maintained in corda.
At first i create a bond state that will be saved only on nodeA that works without problem). After that i want to create a new state that represent an FOP proposal, that the receiver(nodeB) can accept. The idea is that the bond will be consumed when the fop proposal is created. after the receiver accepts the proposal(another flow that needs to be implemented) a new bond state will be created an the the receiver is the owner.
The Flow to create the FOP proposal (a state that should be saved on nodeA and nodeB) is this:
object IssueFopFlow {
#InitiatingFlow
#StartableByRPC
class Initiator(private val sender: AbstractParty,
private val receiver: AbstractParty,
private val amount: Int,
private val bondLinearId: UniqueIdentifier) : MyBaseFlow() {
companion object {
object INITIALISING : ProgressTracker.Step("Performing initial steps.")
object BUILDING : ProgressTracker.Step("Building and verifying transaction.")
object SIGNING : ProgressTracker.Step("Signing transaction.")
object COLLECTING : ProgressTracker.Step("Collecting counterparty signature.") {
override fun childProgressTracker() = CollectSignaturesFlow.tracker()
}
object FINALISING : ProgressTracker.Step("Finalising transaction.") {
override fun childProgressTracker() = FinalityFlow.tracker()
}
fun tracker() = ProgressTracker(INITIALISING, BUILDING, SIGNING, COLLECTING, FINALISING)
}
override val progressTracker: ProgressTracker = tracker()
#Suspendable
override fun call(): SignedTransaction {
// Step 1. Initialisation.
progressTracker.currentStep = INITIALISING
val bondForFop = getBondByLinearId(bondLinearId)
val inputBond = bondForFop.state.data
// check that the bond owner is calling this flow
check(inputBond.owner == ourIdentity) {
throw FlowException("Issue fop transfer must be initiated by the bond owner.")
}
// check the amount
check(inputBond.amount >= amount) {
throw FlowException("Not enough of the bond ${inputBond.isin}. Quantity: ${inputBond.amount}. Amount to transfer: ${amount}")
}
// create fop transfer state
val fopTransfer = FopState(sender, receiver, amount, inputBond.isin)
val ourSigningKey = fopTransfer.sender.owningKey
// Step 2. Building.
progressTracker.currentStep = BUILDING
val utx = TransactionBuilder(firstNotary)
.addInputState(bondForFop)
.addOutputState(fopTransfer, FOP_CONTRACT_ID)
.addCommand(FopContract.Commands.Issue(), listOf(sender.owningKey, receiver.owningKey))
.setTimeWindow(serviceHub.clock.instant(), 30.seconds)
// create new bond state
if (amount < inputBond.amount) {
val (command, newState) = inputBond.reduce(amount)
utx
.addOutputState(newState, BOND_CONTRACT_ID)
.addCommand(command, newState.owner.owningKey)
}
utx.verify(serviceHub)
// Step 3. Sign the transaction.
progressTracker.currentStep = SIGNING
val ptx = serviceHub.signInitialTransaction(utx, ourSigningKey)
// Step 4. Get the counter-party signature.
progressTracker.currentStep = COLLECTING
val senderFlow = initiateFlow(ourIdentity)
val stx = subFlow(
CollectSignaturesFlow(
ptx,
setOf(senderFlow),
listOf(sender.owningKey, receiver.owningKey),
COLLECTING.childProgressTracker()
)
)
// Step 5. Finalise the transaction.
progressTracker.currentStep = FINALISING
return subFlow(FinalityFlow(stx, FINALISING.childProgressTracker()))
}
}
#InitiatedBy(Initiator::class)
class Responder(private val otherFlow: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
subFlow(IdentitySyncFlow.Receive(otherFlow))
val stx = subFlow(SignTxFlowNoChecking(otherFlow))
return waitForLedgerCommit(stx.id)
}
}
}
My current problem is, that i get an exception:
net.corda.core.contracts.TransactionVerificationException$ContractConstraintRejection: Contract constraints failed for com.models.BondContract, transaction: 44A52F07B9579C5106321361A6154C1EE5EF5670FA94CEFF24AB487F0B20D733
at net.corda.core.transactions.LedgerTransaction.verifyConstraints(LedgerTransaction.kt:91) ~[corda-core-2.0.0.jar:?]
at net.corda.core.transactions.LedgerTransaction.verify(LedgerTransaction.kt:67) ~[corda-core-2.0.0.jar:?]
at net.corda.core.transactions.TransactionBuilder.verify(TransactionBuilder.kt:113) ~[corda-core-2.0.0.jar:?]
at com.flow.IssueFopFlow$Initiator.call(IssueFopFlow.kt:78) ~[cordapp-0.1.jar:?]
at com.flow.IssueFopFlow$Initiator.call(IssueFopFlow.kt:19) ~[cordapp-0.1.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:96) [corda-node-2.0.0.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:41) [corda-node-2.0.0.jar:?]
at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092) [quasar-core-0.7.9-jdk8.jar:0.7.9]
at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788) [quasar-core-0.7.9-jdk8.jar:0.7.9]
at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100) [quasar-core-0.7.9-jdk8.jar:0.7.9]
at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91) [quasar-core-0.7.9-jdk8.jar:0.7.9]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_162]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_162]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [?:1.8.0_162]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [?:1.8.0_162]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_162]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_162]
I don't get why. the verify implementation of the BondContract should be fine(only from my point of view :D).
Here is also the code of the bond contract.
open class BondContract : Contract {
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands>()
val setOfSigners = command.signers.toSet()
when (command.value) {
is Commands.Issue -> verifyIssue(tx, setOfSigners)
is Commands.Reduce -> verifyReduce(tx, setOfSigners)
else -> throw IllegalArgumentException("Unrecognised command.")
}
}
private fun keysFromParticipants(bond: BondState): Set<PublicKey> {
return bond.participants.map {
it.owningKey
}.toSet()
}
private fun verifyIssue(tx: LedgerTransaction, signers: Set<PublicKey>) = requireThat {
"No inputs should be consumed when issuing an obligation." using (tx.inputStates.isEmpty())
"Only one bond state should be created when issuing an obligation." using (tx.outputStates.size == 1)
val bond = tx.outputsOfType<BondState>().single()
"A newly issued bond must have a positive amount." using (bond.amount > 0)
"Issuer may sign bond issue transaction." using
(signers == keysFromParticipants(bond))
}
private fun verifyReduce(tx: LedgerTransaction, signers: Set<PublicKey>) = requireThat {
val bond = tx.outputsOfType<BondState>().single()
"A bond must have a positive amount." using (bond.amount > 0)
}
interface Commands : CommandData {
class Move : TypeOnlyCommandData(), Commands
class Issue : TypeOnlyCommandData(), Commands
class Reduce : TypeOnlyCommandData(), Commands
}
}
// *********
// * State *
// *********
data class BondState(val isin: String,
val amount: Int,
override val owner: AbstractParty,
override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState, QueryableState, OwnableState {
override val participants: List<AbstractParty> get() = listOf(owner)
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is BondSchemaV1 -> BondSchemaV1.PersistentBond(
this.owner.toString(),
this.isin,
this.amount,
this.linearId.id
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(BondSchemaV1)
override fun withNewOwner(newOwner: AbstractParty) = CommandAndState(BondContract.Commands.Move(), copy(owner = newOwner))
fun reduce(amountToReduce: Int) = CommandAndState(BondContract.Commands.Reduce(), copy(amount = amount - amountToReduce))
}
Can someone help me?
This problem was fixed by performing a clean build, either by:
Using the Gradle clean flag (e.g. gradlew clean deployNodes)
Deleting the build folder manually
I'm trying to create a flow to Issue and Move currency.
The Contract i have use is the CommercialPaper from the tutorial
https://docs.corda.net/tutorial-contract.html. But i can't get it to work. Here is my flow from the code. I use following commands in the CLI after starting up all the nodes (notery/networkmap,PartyA,PartyB)
start CPIssueFlow value: 6
start CPMoveFlow value: 3, otherParty: "O=PartyB,L=New York,C=US"
The error i get is 'Insufficient funds' from the function 'gatherOurInputs'. Can How can i fix this?
UPDATE:
The github repo is : https://github.com/WesleyCap/Corda/
The Code is updated. The CPIssueFlow didnt work correctly. Now i get the next error.
Contract verification failed: Failed requirement: for reference [00] at issuer C=GB,L=London,O=PartyA the amounts balance: 6 - 0 != 0, contract: net.corda.finance.contracts.asset.Cash#58e904ed, transaction: 71F70042CDA3D46E05ABE319DA5F14D3BDBBB1C80A24753AA9AC660DCD830109
package com.template
import co.paralleluniverse.fibers.Suspendable
import com.template.CommercialPaperContract.Companion.CP_PROGRAM_ID
import net.corda.core.contracts.*
import net.corda.core.flows.*
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.VaultService
import net.corda.core.node.services.vault.QueryCriteria
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import net.corda.finance.DOLLARS
import net.corda.finance.EUR
import net.corda.finance.contracts.asset.CASH
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.contracts.asset.PartyAndAmount
import net.corda.finance.issuedBy
import java.time.Instant
import java.util.*
import net.corda.core.node.services.vault.builder
import net.corda.core.utilities.OpaqueBytes
import net.corda.finance.flows.AbstractCashFlow
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.schemas.CashSchemaV1
import net.corda.core.contracts.Amount
import net.corda.core.flows.StartableByRPC
// *********
// * Flows *
// *********
object CPIssueFlow {
#InitiatingFlow
#StartableByRPC
class Initiator(val value: Long) : FlowLogic<Unit>() {
/** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
override val progressTracker = tracker()
companion object {
object PREPARING : ProgressTracker.Step("Gathering the required inputs.")
object CREATECURRENCY : ProgressTracker.Step("Creating cash.")
object SIGNING : ProgressTracker.Step("Sign the transaction.")
object TOVAULT : ProgressTracker.Step("Returning the newly-issued cash state.")
fun tracker() = ProgressTracker(PREPARING, CREATECURRENCY, SIGNING, TOVAULT)
}
/** The flow logic is encapsulated within the call() method. */
#Suspendable
override fun call() {
progressTracker.currentStep = PREPARING
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val builder = TransactionBuilder(notary)
val amount = Amount(value , EUR)
val issuer = ourIdentity.ref(1)
progressTracker.currentStep = CREATECURRENCY
val signers = Cash().generateIssue(builder, amount.issuedBy(issuer), ourIdentity, notary)
progressTracker.currentStep = SIGNING
val tx = serviceHub.signInitialTransaction(builder, signers)
progressTracker.currentStep = TOVAULT
subFlow(FinalityFlow(tx))
}
}
#InitiatedBy(CPIssueFlow.Initiator::class)
class Acceptor(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be an CommercialPaperState transaction." using (output is CommercialPaperState)
}
}
return subFlow(signTransactionFlow)
}
}
}
object CPMoveFlow {
#InitiatingFlow
#StartableByRPC
class Initiator(val value: Long, val otherParty: Party) : FlowLogic<Unit>() {
/** The progress tracker provides checkpoints indicating the progress of the flow to observers. */
override val progressTracker = tracker()
companion object {
object PREPARING : ProgressTracker.Step("Getting the needed information")
object PREPARESTATES : ProgressTracker.Step("Creating inputstates,outputstates and commands")
object ADDSTATESTOTX : ProgressTracker.Step("Add inputstates,outputstates and commands to the transaction")
object VERIFYTX : ProgressTracker.Step("Verify transaction")
object SIGNING : ProgressTracker.Step("Signing the transaction")
object SENDTOVAULT : ProgressTracker.Step("Put the transaction in the vault")
fun tracker() = ProgressTracker(PREPARING, PREPARESTATES, ADDSTATESTOTX, VERIFYTX, SIGNING, SENDTOVAULT)
}
/** The flow logic is encapsulated within the call() method. */
#Suspendable
override fun call() {
progressTracker.currentStep = PREPARING
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val txBuilder = TransactionBuilder(notary = notary)
val issuer = ourIdentity.ref(1)
val amount = Amount(value , EUR)
progressTracker.currentStep = PREPARESTATES
// We create the transaction components.
val (inputStates, residual) = gatherOurInputs(serviceHub,runId.uuid ,amount.issuedBy(issuer) , notary)
val outputState = CommercialPaperState(issuer, otherParty, amount.issuedBy(issuer), Instant.now())
val outputContractAndState = StateAndContract(outputState, CP_PROGRAM_ID)
val cmd = Command(CommercialPaperContract.Commands.Move(), listOf(ourIdentity.owningKey, otherParty.owningKey))
progressTracker.currentStep = ADDSTATESTOTX
txBuilder.withItems(outputContractAndState, cmd)
txBuilder.addInputState(inputStates[0])
progressTracker.currentStep = VERIFYTX
txBuilder.verify(serviceHub)
progressTracker.currentStep = SIGNING
val signedTx = serviceHub.signInitialTransaction(txBuilder)
val otherpartySession = initiateFlow(otherParty)// Creating a session with the other party.
val fullySignedTx = subFlow(CollectSignaturesFlow(signedTx, listOf(otherpartySession), CollectSignaturesFlow.tracker())) // Obtaining the counterparty's signature.
progressTracker.currentStep = SENDTOVAULT
// Finalising the transaction.
subFlow(FinalityFlow(fullySignedTx))
}
}
#InitiatedBy(CPMoveFlow.Initiator::class)
class Acceptor(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val signTransactionFlow = object : SignTransactionFlow(otherPartyFlow) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be an IOU transaction." using (output is CommercialPaperState)
}
}
return subFlow(signTransactionFlow)
}
}
// This is equivalent to the Cash.generateSpend
// Which is brought here to make the filtering logic more visible in the example
private fun gatherOurInputs(serviceHub: ServiceHub,
lockId: UUID,
amountRequired: Amount<Issued<Currency>>,
notary: Party?): Pair<List<StateAndRef<Cash.State>>, Long> {
// extract our identity for convenience
val ourKeys = serviceHub.keyManagementService.keys
val ourParties = ourKeys.map { serviceHub.identityService.partyFromKey(it) ?: throw IllegalStateException("Unable to resolve party from key") }
val fungibleCriteria = QueryCriteria.FungibleAssetQueryCriteria(owner = ourParties)
val notaries = notary ?: serviceHub.networkMapCache.notaryIdentities.first()
val vaultCriteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(notary = listOf(notaries as AbstractParty))
val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.equal(amountRequired.token.product.currencyCode) }
val cashCriteria = QueryCriteria.VaultCustomQueryCriteria(logicalExpression)
val fullCriteria = fungibleCriteria.and(vaultCriteria).and(cashCriteria)
val eligibleStates = serviceHub.vaultService.tryLockFungibleStatesForSpending(lockId, fullCriteria, amountRequired.withoutIssuer(), Cash.State::class.java)
check(eligibleStates.isNotEmpty()) { "Insufficient funds" }
val amount = eligibleStates.fold(0L) { tot, (state) -> tot + state.data.amount.quantity }
val change = amount - amountRequired.quantity
return Pair(eligibleStates, change)
}
}
During the CPMoveFlow, you're attempting to gather cash from your vault. However, you don't have any at this point.
In order for the cash to be used as an input into this transaction, it will need to come from somewhere. In these situations where you're building out a prototype/testing, your best bet is to self issue yourself cash.
Take a look at the code in the flow here.
Edit:
gatherOurInputs is insufficient to correctly spend cash. You will need both inputs and outputs where the inputs is cash you previously self issued and the output will x amount of that cash with the other party now as the owner.
The easiest way forward would be to use the Cash.generateSpend function which will add both the inputs and outputs for you to the transaction e.g.
Cash.generateSpend(serviceHub, txBuilder, amount, otherParty)
You will now see a different error pertaining to contract verification failing for your commercial paper, but I'll leave that to you to debug. The unit test framework within Corda is really good for this.
I have been following the official tutorials on how to implement observer nodes from here and here. I tried testing the flow to broadcast the transaction to observer nodes, however, I am not sure if I implemented the flow correctly. After running the flow, no states showed up in the vault of the observer node. No states (that corresponded to the transaction that was broadcast) showed up when I ran a RPC vault query nor did it show when I accessed the H2 database of the observer node. Debugging showed that the flow code was called. No exception was thrown as well.
Is the flow working correctly? Also how can I view the broadcasted transactions as an observer node - is it stored as a consumed state in its vault?
The flow code:
object BroadcastTransaction {
#InitiatingFlow
class BroadcastTransactionToObservers(private val stx: SignedTransaction, private val observers: List<Party>) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val sessions = observers.map { initiateFlow(it) }
sessions.forEach { subFlow(SendTransactionFlow(it, stx)) }
}
}
#InitiatedBy(BroadcastTransactionToObservers::class)
class RecordTransactionAsObserver(private val otherSession: FlowSession) :FlowLogic<Unit>() {
#Suspendable
override fun call() {
subFlow( ReceiveTransactionFlow(
otherSideSession = otherSession,
checkSufficientSignatures = true,
statesToRecord = StatesToRecord.ALL_VISIBLE
)
)
}
}
}
How I call the flow:
subFlow(BroadcastTransaction.BroadcastTransactionToObservers(fullySignedTx, listOf(observer)))
Prior initiating flow:
#InitiatingFlow
#StartableByRPC
class Initiator (val id: String,
val transferParty : Party,
val observer : Party) : BaseFlow() {
#Suspendable
override fun call() : SignedTransaction {
progressTracker.currentStep = ID_OTHER_NODES
val notary = serviceHub.networkMapCache.notaryIdentities[0]
progressTracker.currentStep = EXTRACTING_VAULT_STATES
val stateAndRef = getItemStateByItemId(id)
val inputState = stateAndRef.state.data
progressTracker.currentStep = TX_BUILDING
val txBuilder = TransactionBuilder(notary = notary)
val outputState = createOutput(inputState)
val signerKeys = listOf(ourIdentity.owningKey, transferParty.owningKey)
val cmd = Command(outputState.command, signerKeys)
txBuilder.addInputState(stateAndRef)
.addOutputState(outputState.ownableState, CONTRACT_ID)
.addCommand(cmd)
progressTracker.currentStep = TX_VERIFICATION
txBuilder.verify(serviceHub)
progressTracker.currentStep = TX_SIGNING
val signedTx = serviceHub.signInitialTransaction(txBuilder)
progressTracker.currentStep = SENDING_AND_RECEIVING_DATA
val sessions = setOf(initiateFlow(transferParty))
progressTracker.currentStep = SIGS_GATHERING
val fullySignedTx: SignedTransaction = subFlow(CollectSignaturesFlow(signedTx, sessions, SIGS_GATHERING.childProgressTracker()))
subFlow(BroadcastTransaction.BroadcastTransactionToObservers(fullySignedTx, listOf(observer)))
progressTracker.currentStep = FINALISATION
return subFlow(FinalityFlow(fullySignedTx, setOf(ourIdentity),FINALISATION.childProgressTracker()))
}
You are broadcasting the transaction to the observer before it has received a signature from the notary as part of FinalityFlow. Nodes will not record a transaction's states in their vault unless it has all the required signatures, including the notary's.
Try moving the call to FinalityFlow above the call to BroadcastTransaction.BroadcastTransactionToObservers.