Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class BridgingContract : Contract {
require(moveCommands.size == 1) { "Bridging must have one move command to lock token" }

val lockedSum = tx.outputsOfType<FungibleToken>()
.filter { it.holder == bridgingCommand.bridgeAuthority } // TODO this is mute point for now, change to != bridgeAuthority, to filter only states owned by CI ...
.filter { it.holder != bridgingCommand.bridgeAuthority }
// ... currently can't distinguish between locked and a change, both are for same holder
.sumOf {
it.amount.toDecimal().toLong()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import net.corda.solana.sdk.instruction.Pubkey
class BridgeFungibleTokenFlow(
val holder: AbstractParty,
val observers: List<Party> = emptyList(),
val token: StateAndRef<FungibleToken>, //TODO should be FungibleToken, TODO change to any TokenType would need amendments to UUID retrieval below
val token: StateAndRef<FungibleToken>,
val bridgeAuthority: Party
) : FlowLogic<SignedTransaction>() {

Expand All @@ -46,7 +46,7 @@ class BridgeFungibleTokenFlow(

val cordaTokenId = (token.state.data.amount.token.tokenType as TokenPointer<*>).pointer.pointer.id

val owners = previousOwnersOf(token).map { serviceHub.identityService.wellKnownPartyFromAnonymous(it) ?: it }
val owners = previousOwnersOf(serviceHub, token).map { serviceHub.identityService.wellKnownPartyFromAnonymous(it) ?: it }
val singlePreviousOwner = owners.singleOrNull { it is Party } as Party?
require(singlePreviousOwner != null) {
"Cannot find previous owner of the token to bridge, or multiple found: $owners"
Expand All @@ -70,21 +70,11 @@ class BridgeFungibleTokenFlow(
additionalCommand = additionalCommand,
destination = destination,
mint = mint,
mintAuthority = mintAuthority
mintAuthority = mintAuthority,
holder
)
)
}

fun previousOwnersOf(output: StateAndRef<FungibleToken>): Set<AbstractParty> {
val txHash = output.ref.txhash
val stx = serviceHub.validatedTransactions.getTransaction(txHash)
?: error("Producing transaction $txHash not found")

val inputTokens: List<FungibleToken> =
stx.toLedgerTransaction(serviceHub).inputsOfType<FungibleToken>()

return inputTokens.map { it.holder }.toSet()
}
}

/**
Expand All @@ -106,14 +96,14 @@ constructor(
val additionalCommand: BridgingContract.BridgingCommand,
val destination: Pubkey,
val mint: Pubkey,
val mintAuthority: Pubkey
val mintAuthority: Pubkey,
val holder: AbstractParty
) : AbstractMoveTokensFlow() { //TODO move away from this abstract class, it's progress tracker mention only token move

@Suspendable
override fun addMove(transactionBuilder: TransactionBuilder) {

val amount = token.state.data.amount
val holder = ourIdentity //TODO confidential identity
val output = FungibleToken(amount, holder)
addMoveTokens(transactionBuilder = transactionBuilder, inputs = listOf(token), outputs = listOf(output))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package com.r3.corda.lib.tokens.bridging.flows
import co.paralleluniverse.fibers.Suspendable
import com.r3.corda.lib.tokens.bridging.contracts.BridgingContract
import com.r3.corda.lib.tokens.contracts.states.AbstractToken
import com.r3.corda.lib.tokens.contracts.states.FungibleToken
import com.r3.corda.lib.tokens.contracts.types.IssuedTokenType
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.identity.AbstractParty
import net.corda.core.node.ServiceHub
import net.corda.core.transactions.TransactionBuilder
import net.corda.solana.sdk.instruction.Pubkey
Expand Down Expand Up @@ -80,4 +82,16 @@ fun bridgeToken(
serviceHub, transactionBuilder, listOf(additionalOutput),
additionalCommand, destination, mint, mintAuthority, quantity
)
}

@Suspendable
fun previousOwnersOf(serviceHub: ServiceHub, output: StateAndRef<FungibleToken>): Set<AbstractParty> {
val txHash = output.ref.txhash
val stx = serviceHub.validatedTransactions.getTransaction(txHash)
?: error("Producing transaction $txHash not found")

val inputTokens: List<FungibleToken> =
stx.toLedgerTransaction(serviceHub).inputsOfType<FungibleToken>()

return inputTokens.map { it.holder }.toSet()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.r3.corda.lib.tokens.bridging.flows

import com.r3.corda.lib.tokens.contracts.states.FungibleToken
import net.corda.core.contracts.StateAndRef
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.AppServiceHub
import net.corda.core.node.services.CordaService
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.debug
import org.slf4j.LoggerFactory
import java.util.*
import java.util.concurrent.Executors

@CordaService
class BridgingAuthorityBootstrapService(appServiceHub: AppServiceHub) : SingletonSerializeAsToken() {
private val holdingIdentityPartyAndCertificate: PartyAndCertificate
private val bridgeAuthorityParty = appServiceHub.myInfo.legalIdentities.first()
private val logger = LoggerFactory.getLogger(BridgingAuthorityBootstrapService::class.java)

private val executor = Executors.newSingleThreadExecutor()

init {
val cfg = appServiceHub.getAppContext().config
val holdingIdentityLabel = UUID.fromString(cfg.getString("holdingIdentityLabel"))
val holdingIdentityPublicKey = appServiceHub
.identityService
.publicKeysForExternalId(holdingIdentityLabel)
.singleOrNull()
holdingIdentityPartyAndCertificate = if (holdingIdentityPublicKey == null) {
// Generate a new key pair and self-signed certificate for the holding identity
appServiceHub.keyManagementService.freshKeyAndCert(
identity = requireNotNull(appServiceHub.identityService.certificateFromKey(bridgeAuthorityParty.owningKey)) {
"Could not find certificate for key ${bridgeAuthorityParty.owningKey}"
},
revocationEnabled = false,
externalId = holdingIdentityLabel
)
} else {
// Reuse the existing key pair and certificate for the holding identity
checkNotNull(appServiceHub.identityService.certificateFromKey(holdingIdentityPublicKey)) {
"Could not find certificate for key $holdingIdentityPublicKey"
}
}

appServiceHub.registerUnloadHandler { onStop() }
onStartup(appServiceHub)
}

private fun onStop() {
executor.shutdown()
}

private fun onStartup(appServiceHub: AppServiceHub) {
//Retrieve states from receiver
val receivedStates = appServiceHub.vaultService.queryBy(FungibleToken::class.java).states


callFlow(receivedStates, appServiceHub)
addVaultListener(appServiceHub)
}

private fun addVaultListener(appServiceHub: AppServiceHub) {
appServiceHub.vaultService.trackBy(FungibleToken::class.java).updates.subscribe {
val producedStockStates = it.produced
callFlow(producedStockStates, appServiceHub)
}
}

private fun callFlow(fungibleTokens: Collection<StateAndRef<FungibleToken>>, appServiceHub: AppServiceHub) {
fungibleTokens.forEach { token ->
if (bridgeAuthorityParty !in previousOwnersOf(appServiceHub, token)) {
logger.debug { "Starting flow to bridge ${token.state.data.amount} to Solana" }
executor.submit {
appServiceHub.startFlow(
BridgeFungibleTokenFlow(
holdingIdentityPartyAndCertificate.party,
emptyList(),
token,
bridgeAuthorityParty
)
)
}
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package net.corda.samples.stockpaydividend

import com.lmax.solana4j.api.PublicKey
import com.r3.corda.lib.tokens.bridging.states.BridgedAssetLockState
import com.r3.corda.lib.tokens.bridging.flows.rpc.BridgeToken
import com.r3.corda.lib.tokens.contracts.states.FungibleToken
import com.r3.corda.lib.tokens.contracts.types.TokenPointer
import com.r3.corda.lib.tokens.workflows.utilities.tokenBalance
Expand All @@ -11,34 +10,25 @@ import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.samples.stockpaydividend.flows.*
import net.corda.samples.stockpaydividend.flows.CreateAndIssueStock
import net.corda.samples.stockpaydividend.flows.GetTokenToBridge
import net.corda.samples.stockpaydividend.flows.MoveStock
import net.corda.samples.stockpaydividend.states.StockState
import net.corda.solana.aggregator.common.RpcParams
import net.corda.solana.aggregator.common.Signer
import net.corda.solana.aggregator.common.checkResponse
import net.corda.solana.sdk.internal.Token2022
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.TestIdentity
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.StartedMockNode
import net.corda.testing.node.TestCordapp
import net.corda.testing.node.*
import net.corda.testing.solana.SolanaTestValidator
import org.junit.After
import org.junit.AfterClass
import org.junit.Assert
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import java.math.BigDecimal
import java.util.*
import java.util.concurrent.ExecutionException
import net.corda.testing.node.MockNetworkNotarySpec
import net.corda.testing.node.MockNodeParameters
import net.corda.testing.solana.randomKeypairFile
import org.junit.ClassRule
import org.junit.*
import org.junit.rules.TemporaryFolder
import java.math.BigDecimal
import java.nio.file.Path
import java.util.*
import java.util.concurrent.ExecutionException

class FlowTests {
private var network: MockNetwork? = null
Expand Down Expand Up @@ -126,7 +116,8 @@ class FlowTests {
val baConfig = mapOf(
"participants" to mapOf(COMPANY.name.toString() to tokenAccount.base58()),
"mints" to mapOf(LINEAR_ID.toString() to tokenMint.base58()),
"mintAuthorities" to mapOf(LINEAR_ID.toString() to mintAuthority.account.base58())
"mintAuthorities" to mapOf(LINEAR_ID.toString() to mintAuthority.account.base58()),
"holdingIdentityLabel" to UUID.randomUUID().toString()
)
network = MockNetwork(
MockNetworkParameters(
Expand All @@ -142,7 +133,8 @@ class FlowTests {
notaryConfig = createNotaryConfig()
)
), //TODO start separately notary to provide specific set of cordapps without bridging ones
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
networkParameters = testNetworkParameters(minimumPlatformVersion = 4),
threadPerNode = true
)
)

Expand Down Expand Up @@ -200,7 +192,6 @@ class FlowTests {
notaryParty!!
)
)
network!!.runNetwork()
val stx = future.get()
val stxID = stx.substring(stx.lastIndexOf(" ") + 1)
val stxIDHash: SecureHash = SecureHash.parse(stxID)
Expand Down Expand Up @@ -228,12 +219,10 @@ class FlowTests {
notaryParty!!
)
)
network!!.runNetwork()
future.get()

// Move Stock
future = company!!.startFlow(MoveStock(STOCK_SYMBOL, BUYING_STOCK, shareholder!!.info.legalIdentities[0]))
network!!.runNetwork()
future.get()

//Retrieve states from receiver
Expand All @@ -260,7 +249,6 @@ class FlowTests {
@Test
@Throws(ExecutionException::class, InterruptedException::class)
fun bridgeTest() {

// Issue 1st Stock on Company node
var future = company!!.startFlow<String?>(
CreateAndIssueStock(
Expand All @@ -273,9 +261,7 @@ class FlowTests {
LINEAR_ID
)
)
network!!.runNetwork()
future.get()

// Issue 2nd Stock on Bridge Authority node to verify it remains unaffected
future = bridgingAuthority!!.startFlow(
CreateAndIssueStock(
Expand All @@ -288,9 +274,7 @@ class FlowTests {
LINEAR_ID_2
)
)
network!!.runNetwork()
future.get()

// First stock to be bridged - moving from Company to Bridge Authority
var stockStatePointer = getTokensPointer(company!!, STOCK_SYMBOL)
val (startCordaQuantity) = company!!.services.vaultService.tokenBalance(stockStatePointer)
Expand All @@ -305,7 +289,6 @@ class FlowTests {
var stock2StatePointer = getTokensPointer(bridgingAuthority!!, STOCK_SYMBOL_2)
var (start2CordaQuantity) = bridgingAuthority!!.services.vaultService.tokenBalance(stock2StatePointer)
Assert.assertEquals(ISSUING_STOCK_QUANTITY.toLong(), start2CordaQuantity)

// Move Stock
future =
company!!.startFlow(
Expand All @@ -315,7 +298,6 @@ class FlowTests {
bridgingAuthority!!.info.legalIdentities[0]
)
)
network!!.runNetwork()
future.get()

// Company has no longer the amount of stocks
Expand All @@ -329,25 +311,16 @@ class FlowTests {
)
Assert.assertEquals(ISSUING_STOCK_QUANTITY.toLong(), startBridgingAuthorityCordaQuantity)


val future2 = bridgingAuthority!!.startFlow(
GetTokenToBridge(
STOCK_SYMBOL
)
)
network!!.runNetwork()
val statesToBridge = future2.get()
Assert.assertEquals(1, statesToBridge.size)

future = bridgingAuthority!!.startFlow(
BridgeToken(
statesToBridge.first(),
bridgingAuthority!!.info.legalIdentities[0] //TODO remove this as will be internally moved to own CI
)
)

network!!.runNetwork()
future.get()
// We need to wait for the vault listener to process the newly received token
Thread.sleep(1000)

stockStatePointer = getTokensPointer(bridgingAuthority!!, STOCK_SYMBOL)
val (finalCordaQuantity) = bridgingAuthority!!.services.vaultService.tokenBalance(stockStatePointer)
Expand Down