Skip to content

Commit

Permalink
feat(weaver): added multiple participants support for data sharing in…
Browse files Browse the repository at this point in the history
… corda

    - refactor(weaver): add pledge verification in interop contract
    - fix(corda-at): typo in ResponderRole for weaver asset transfer
    - fix(ci): rename asset-transfer-fabric job to asset-transfer
    - feat(weaver): add Consume command for external state

Signed-off-by: Sandeep Nishad <sandeep.nishad1@ibm.com>
  • Loading branch information
sandeepnRES committed Jun 15, 2023
1 parent 1e5c349 commit 4e81b92
Show file tree
Hide file tree
Showing 14 changed files with 284 additions and 63 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test_weaver-asset-transfer.yaml
Expand Up @@ -23,7 +23,7 @@ concurrency:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
asset-transfer-fabric:
asset-transfer:
if: ${{ false }}
# The type of runner that the job will run on
runs-on: ubuntu-latest
Expand Down Expand Up @@ -657,7 +657,7 @@ jobs:
if: failure()
run: docker logs driver-corda-Corda_Network2

asset-transfer-fabric-local:
asset-transfer-local:
# if: ${{ false }}
# The type of runner that the job will run on
runs-on: ubuntu-latest
Expand Down
Expand Up @@ -9,6 +9,7 @@ package org.hyperledger.cacti.weaver.imodule.corda.contracts
import org.hyperledger.cacti.weaver.imodule.corda.states.AssetPledgeState
import org.hyperledger.cacti.weaver.imodule.corda.states.AssetClaimStatusState
import org.hyperledger.cacti.weaver.imodule.corda.states.NetworkIdState
import org.hyperledger.cacti.weaver.imodule.corda.states.ExternalState
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.requireSingleCommand
Expand All @@ -17,6 +18,7 @@ import net.corda.core.contracts.StaticPointer
import net.corda.core.transactions.LedgerTransaction
import java.time.Instant
import java.util.*
import co.paralleluniverse.fibers.Suspendable

/**
* AssetTransferContract defines the rules for managing a [AssetPledgeState].
Expand All @@ -37,14 +39,13 @@ class AssetTransferContract : Contract {
when (command.value) {
is Commands.Pledge -> requireThat {
"There should be one input state." using (tx.inputs.size == 1)
"There should be one output state." using (tx.outputs.size == 1)
"The output state should be of type AssetPledgeState." using (tx.outputs[0].data is AssetPledgeState)
"There should be one output AssetPledgeState." using (tx.outputsOfType<AssetPledgeState>().size == 1)

// Get the Asset Pledge State
val pledgeState = tx.outputs[0].data as AssetPledgeState
val pledgeState = tx.outputsOfType<AssetPledgeState>()[0]

// Check if output belong to this contract
"Output state should belong to this contract" using (tx.outputs[0].contract.equals(ID))
// "Output state should belong to this contract" using (tx.outputsOfType<AssetPledgeState>().contract().equals(ID))

// Check if timeout is beyond current time
val expiryTime = Instant.ofEpochSecond(pledgeState.expiryTimeSecs)
Expand All @@ -69,20 +70,21 @@ class AssetTransferContract : Contract {
"AssetPledgeState.localNetwokId must match with the networkId of current network." using (pledgeState.localNetworkId.equals(validNetworkIdState.networkId))
}
is Commands.ClaimRemoteAsset -> requireThat {
"There should be no input state." using (tx.inputs.size == 0)
"There should be one input External state." using (tx.inputsOfType<ExternalState>().size == 1)
"There should be two output states." using (tx.outputs.size == 2)
"One of the output states should be of type AssetClaimStatusState." using (tx.outputsOfType<AssetClaimStatusState>().size == 1)

// Check if output state [AssetClaimStatusState] belongs to this contract
"Output state should belong to this contract" using (tx.outputs[1].contract.equals(ID))
//"Output state should belong to this contract" using (claimStateAndRefs[0].contract.equals(ID))

// Get the input asset pledge state
val claimState = tx.outputs[1].data as AssetClaimStatusState
// Get the output asset claim state
val claimState = tx.outputsOfType<AssetClaimStatusState>()[0]

val inReferences = tx.referenceInputRefsOfType<NetworkIdState>()
"There should be a single reference input network id." using (inReferences.size == 1)
val validNetworkIdState = inReferences.get(0).state.data

// Claim State checks
"AssetClaimStatusState.localNetwokID must match with the networkId of current network." using (claimState.localNetworkID.equals(validNetworkIdState.networkId))

// Check if timeWindow <= expiryTime
Expand All @@ -100,12 +102,12 @@ class AssetTransferContract : Contract {
"The required signers of the transaction must include the recipient." using (command.signers.containsAll(requiredSigners))
}
is Commands.ReclaimPledgedAsset -> requireThat {
"There should be one input state." using (tx.inputs.size == 1)
"The input state should be of type AssetPledgeState." using (tx.inputs[0].state.data is AssetPledgeState)
"There should be one input AssetPledgeState." using (tx.inputsOfType<AssetPledgeState>().size == 1)
"There should be one input ExternalState." using (tx.inputsOfType<ExternalState>().size == 1)
"There should be one output state." using (tx.outputs.size == 1)

// Get the input asset pledge state
val pledgeState = tx.inputs[0].state.data as AssetPledgeState
val pledgeState = tx.inputsOfType<AssetPledgeState>()[0]

// Check if timeWindow > expiryTime
val fromTime = tx.timeWindow!!.fromTime!!
Expand Down Expand Up @@ -140,4 +142,4 @@ class AssetTransferContract : Contract {
class ReclaimPledgedAsset : Commands
class ClaimRemoteAsset : Commands
}
}
}
Expand Up @@ -34,22 +34,30 @@ class ExternalStateContract : Contract {
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands>()
when(command.value) {
is Commands.Issue -> requireThat {
is Commands.Create -> requireThat {
"There should be no input states" using (tx.inputs.isEmpty())
"There should be one output state" using (tx.outputs.size == 1)
"The output state should be of type ExternalState" using (tx.outputs[0].data is ExternalState)
val participantKeys = tx.outputs[0].data.participants.map { it.owningKey }
"The required signers of the transaction must include all participants" using (command.signers.containsAll(participantKeys))
}
is Commands.Consume -> requireThat {
"There should be one ExternalState input states" using (tx.inputsOfType<ExternalState>().size == 1)
"There should be no ExternalState output states" using (tx.outputsOfType<ExternalState>().size == 0)
val participantKeys = tx.inputsOfType<ExternalState>()[0].participants.map { it.owningKey }
"The required signers of the transaction must include all participants" using (command.signers.containsAll(participantKeys))
}
}
}

/**
* Commands are used to indicate the intent of a transaction.
* Commands for [ExternalStateContract] are:
* - Issue
* - Create
* - Consume
*/
interface Commands : CommandData {
class Issue : Commands
class Create : Commands
class Consume : Commands
}
}
Expand Up @@ -50,6 +50,10 @@ data class AssetPledgeState(
override val participants: List<AbstractParty> get() = listOf(locker)
}

/*
* Since there is a limit on the number of parameters to the workflow
* This data class is used as parameter.
*/
@CordaSerializable
data class AssetPledgeParameters(
var assetType: String,
Expand Down
Expand Up @@ -47,7 +47,7 @@ import java.util.Base64

@CordaSerializable
enum class ResponderRole {
LOCKER, RECIPIENT, ISSUER, OBSERVER
LOCKER, RECIPIENT, SIGNER, OBSERVER
}

/**
Expand Down Expand Up @@ -121,7 +121,7 @@ object LockAssetHTLC {
/// Add issuer session if recipient or locker (i.e. me) is not issuer
if (!recipient.equals(issuer) && !ourIdentity.equals(issuer)) {
val issuerSession = initiateFlow(issuer)
issuerSession.send(ResponderRole.ISSUER)
issuerSession.send(ResponderRole.SIGNER)
sessions += issuerSession
}
val fullySignedTx = subFlow(CollectSignaturesFlow(partSignedTx, sessions))
Expand Down Expand Up @@ -150,7 +150,7 @@ object LockAssetHTLC {
@Suspendable
override fun call(): SignedTransaction {
val role = session.receive<ResponderRole>().unwrap { it }
if (role == ResponderRole.ISSUER) {
if (role == ResponderRole.SIGNER) {
val signTransactionFlow = object : SignTransactionFlow(session) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
}
Expand Down Expand Up @@ -407,7 +407,7 @@ object ClaimAssetHTLC {
var sessions = listOf<FlowSession>()
if (!assetExchangeHTLCState.recipient.equals(issuer)) {
val issuerSession = initiateFlow(issuer)
issuerSession.send(ResponderRole.ISSUER)
issuerSession.send(ResponderRole.SIGNER)
sessions += issuerSession
}
val fullySignedTx = subFlow(CollectSignaturesFlow(partSignedTx, sessions))
Expand Down Expand Up @@ -437,7 +437,7 @@ object ClaimAssetHTLC {
@Suspendable
override fun call(): SignedTransaction {
val role = session.receive<ResponderRole>().unwrap { it }
if (role == ResponderRole.ISSUER) {
if (role == ResponderRole.SIGNER) {
val signTransactionFlow = object : SignTransactionFlow(session) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
}
Expand Down Expand Up @@ -542,7 +542,7 @@ object UnlockAssetHTLC {

if (!ourIdentity.equals(issuer)) {
val issuerSession = initiateFlow(issuer)
issuerSession.send(ResponderRole.ISSUER)
issuerSession.send(ResponderRole.SIGNER)
sessions += issuerSession
}
if (!ourIdentity.equals(assetExchangeHTLCState.locker)) {
Expand Down Expand Up @@ -576,7 +576,7 @@ object UnlockAssetHTLC {
@Suspendable
override fun call(): SignedTransaction {
val role = session.receive<ResponderRole>().unwrap { it }
if (role == ResponderRole.ISSUER) {
if (role == ResponderRole.SIGNER) {
val signTransactionFlow = object : SignTransactionFlow(session) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
}
Expand Down

0 comments on commit 4e81b92

Please sign in to comment.