diff --git a/Tokens/stockpaydividend/README.md b/Tokens/stockpaydividend/README.md index 8809de97..c7c45322 100755 --- a/Tokens/stockpaydividend/README.md +++ b/Tokens/stockpaydividend/README.md @@ -123,3 +123,130 @@ Provides basic understanding from the ground up. [Sample - TokenSDK with Account](https://github.com/corda/accounts/tree/master/examples/tokens-integration-test) An basic sample of how account feature can be integrated with TokenSDK +## Bridging To Solana + +### Running on Solana Dev Net + +Build nodes: + +```bash +./gradlew deployNodes +``` + +Create Solana Accounts and Fund accounts of WayneCo and the BridgingAuthority, create Mint and TokenAccount to be +bridged to: + +```bash +./gradlew setupSolanaAccounts +``` + +Amend `BrigindAuthority` Cordapp's configuration with mappings to newly created accounts: + +```bash +./gradlew expandBACordappConfig +``` + +Run nodes: + +```bash +./build/nodes/runnodes +``` + +### Running with Solana Local Validator + +Start a local solana validator with the notary program deployed following instruction from Corda Enterprise. +(the below commands can be run in one go from `./addCordaNetwork.sh` script). + +Add the new network and Notary key to Solana. This Notary will be used by the Corda nodes. +```bash +ADMIN_CLI="solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" +java -jar $ADMIN_CLI create-network -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +``` + +The command will output confirmation with network ID `1`: + +``` +Creating network ... +✓ Corda network creation successful - network ID: 1 +``` + +Add the Notary key to the network: + +```bash +> java -jar $ADMIN_CLI authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +``` + +This command will output: + +``` +> Authorizing notary Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5... +> ✓ Notary authorized successfully: Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 +``` + +You can list the authorized notaries with: + +```bash +java -jar $ADMIN_CLI list-notaries -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +``` + +The expected output is: +``` +Network ID: 0 + 1. Notary: DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3 +Network ID: 1 + 1. Notary: Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 +``` + +Follow the steps from Running on Solana Dev Net. +The only change is to replace Solana Dev Net url with local validator url `http://localhost:8899` in notary config in +`depolyNodes` task. + +#### Using the Cordapps + +You can check the current balance on Solana Account: + +```bash +spl-token balance --address $TOKEN_ACCOUNT +spl-token display $TOKEN_ACCOUNT +``` + +##### On WayneCo node console: + +Issue Stock `TEST` with linearID `6116560b-c78e-4e13-871d-d666a5d032a3` matching configuration in Bridging Authority. + +```bash +start CreateAndIssueStock \ +symbol: TEST, \ +name: "Test Stock", \ +currency: USD, \ +price: 7.4, \ +issueVol: 2000, \ +notary: "O=Notary Service,L=Zurich,C=CH", \ +linearId: 6116560b-c78e-4e13-871d-d666a5d032a3 +``` + +Move 1000 stock tokens to self to Bridging Authority: + +```bash +start MoveStock symbol: TEST, quantity: 1000, recipient: "O=Bridging Authority,L=New York,C=US" +``` + +##### On BridgingAuthority node console: + +This is currently a manual step to showcase how bridging, normally the bridging flow starts automatically as soon as +a participant moves token(s) to the Bridging Authority: + +```bash +start GetTokenToBridgeFormatted symbol: TEST +``` + +```bash +start BridgeTokenRpc tokenRef: { txhash: , index: 0 } , bridgeAuthority: "O=Bridging Authority,L=New York,C=US" +``` + +You can check the current balance on Solana Account has increased: + +```bash +spl-token balance --address $TOKEN_ACCOUNT +spl-token display $TOKEN_ACCOUNT +``` \ No newline at end of file diff --git a/Tokens/stockpaydividend/addCordaNetwork.sh b/Tokens/stockpaydividend/addCordaNetwork.sh new file mode 100644 index 00000000..c647a017 --- /dev/null +++ b/Tokens/stockpaydividend/addCordaNetwork.sh @@ -0,0 +1,6 @@ +#!/bin/bash +ADMIN_CLI="solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" + +java -jar $ADMIN_CLI create-network -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI authorize --address Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5 --network 1 -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI list-notaries -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json diff --git a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/rpc/BridgeTokens.kt b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/rpc/BridgeTokens.kt index b956034c..80cd19a0 100644 --- a/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/rpc/BridgeTokens.kt +++ b/Tokens/stockpaydividend/bridging-flows/src/main/kotlin/com/r3/corda/lib/tokens/bridging/flows/rpc/BridgeTokens.kt @@ -8,6 +8,7 @@ import net.corda.core.flows.StartableByRPC import net.corda.core.identity.Party import com.r3.corda.lib.tokens.contracts.states.FungibleToken import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.StateRef import net.corda.core.utilities.ProgressTracker @InitiatingFlow @@ -37,3 +38,12 @@ class BridgeToken( } } +@StartableByRPC +class BridgeTokenRpc(private val tokenRef: StateRef, private val bridgeAuthority: Party) : FlowLogic() { + @Suspendable + override fun call(): String { + val stateAndRef: StateAndRef = serviceHub.toStateAndRef(tokenRef) + val result = subFlow(BridgeToken(stateAndRef, bridgeAuthority)) + return result + } +} \ No newline at end of file diff --git a/Tokens/stockpaydividend/build.gradle b/Tokens/stockpaydividend/build.gradle index 0edb0add..9c468be8 100644 --- a/Tokens/stockpaydividend/build.gradle +++ b/Tokens/stockpaydividend/build.gradle @@ -86,6 +86,22 @@ allprojects { releasesOnly() } } + + // For SNAPSHOT version + maven { + url "https://software.r3.com/artifactory/r3-corda-dev" + authentication { + basic(BasicAuthentication) + } + credentials { + username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME') + password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD') + } + content { + includeGroup 'com.r3.libs' + includeModule 'com.r3.corda', 'corda-shell' + } + } } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) { @@ -137,10 +153,13 @@ dependencies { cordapp project(":workflows") cordapp project(":contracts") + cordapp project(":bridging-contracts") // Used in the test + cordapp project(":bridging-flows") // Used in the test + cordaProvided "org.apache.logging.log4j:log4j-slf4j2-impl:${log4j_version}" cordaProvided "org.apache.logging.log4j:log4j-web:${log4j_version}" cordaProvided "org.slf4j:jul-to-slf4j:$slf4j_version" - cordaDriver "net.corda:corda-shell:$corda_release_version" + cordaDriver "com.r3.corda:corda-shell:$corda_release_version" // Token SDK dependencies. cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version" cordapp "$tokens_release_group:tokens-workflows:$tokens_release_version" @@ -151,7 +170,18 @@ cordapp { minimumPlatformVersion corda_platform_version.toInteger() } +def solanaNotaryKeyFileName = 'Dev7chG99tLCAny3PNYmBdyhaKEVcZnSTp3p1mKVb5m5.json' +def solanaNotaryKeyPath = "$buildDir/extracted/dev-keys/$solanaNotaryKeyFileName" +def custodiedKeysDirectory = "${project.buildDir}/nodes/custodied-keys" +def keysDirectory = "${project.buildDir}/nodes/solana-keys" +def bridgeAuthorityWallet = "${custodiedKeysDirectory}/bridge-authority-wallet.json" +def tokenMintFile = "${keysDirectory}/token-mint.pub" +def bigBankFile = "${keysDirectory}/big-corp.pub" + +def linearId = '6116560b-c78e-4e13-871d-d666a5d032a3' + task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { + dependsOn('extractDevKey') nodeDefaults { projectCordapp { deploy = false @@ -164,7 +194,14 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { } node { name "O=Notary,L=London,C=GB" - notary = [validating : false] + notary = [validating : false, + serviceLegalName: "O=Notary Service,L=Zurich,C=CH", + solana : [ + notaryKeypairFile: file(solanaNotaryKeyPath).absolutePath, + custodiedKeysDir : file(custodiedKeysDirectory).absolutePath, + rpcUrl: "https://api.devnet.solana.com" + ] + ] p2pPort 10002 rpcSettings { address("localhost:10003") @@ -207,6 +244,39 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { } rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]] } + node { + name "O=Bridging Authority,L=New York,C=US" + p2pPort 10018 + rpcSettings { + address("localhost:10019") + adminAddress("localhost:10047") + } + cordapp(project(':bridging-contracts')) + cordapp(project(':bridging-flows')) + rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]] + } +} + +tasks.register('expandBACordappConfig') { + doLast { + def pubkey1 = file(bigBankFile).getText('UTF-8').trim() + def pubkey2 = file(tokenMintFile).getText('UTF-8').trim() + def stdout = new ByteArrayOutputStream() + exec { commandLine 'solana-keygen', 'pubkey', "${bridgeAuthorityWallet}"; standardOutput = stdout } + def pubkey3 = stdout.toString('UTF-8').trim() + + copy { + from "${project.projectDir}/workflows/src/main/resources" + include 'bridging-flows-template.conf' + into "$buildDir/nodes/BridgingAuthority/cordapps/config" + rename { String name -> name.replace('template', '1.0') } + expand(bigBankAccount: pubkey1, + tokenMintAccount: pubkey2, + bridgeAuthorityAccount: pubkey3, + cordaTokenTypeLinearId: linearId, + bigBankCordaIdentity: "O=WayneCo,L=SF,C=US") + } + } } task installQuasar(type: Copy) { @@ -216,3 +286,24 @@ task installQuasar(type: Copy) { } } +configurations { + libJar { + transitive = false + } +} + +dependencies { + libJar 'com.r3.corda.lib.solana:token-bridging-workflows:0.1.0-SNAPSHOT@jar' +} + +tasks.register('extractDevKey', Copy) { + from({ zipTree(configurations.libJar.singleFile) }) { + include "dev-keys/$solanaNotaryKeyFileName" + } + into "$buildDir/extracted" +} + +tasks.register('setupSolanaAccounts', Exec) { + dependsOn('extractDevKey') + commandLine 'bash', '-x', "$projectDir/setupSolanaAccounts.sh", "$solanaNotaryKeyPath" +} \ No newline at end of file diff --git a/Tokens/stockpaydividend/runSolana.sh b/Tokens/stockpaydividend/runSolana.sh new file mode 100644 index 00000000..6f410863 --- /dev/null +++ b/Tokens/stockpaydividend/runSolana.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +#build +./gradlew solana-aggregator:admin-cli:build +cd solana-aggregator/notary-program +anchor build + + +#start +solana-test-validator --reset --ledger ../admin-cli/build/test-ledger --bpf-program target/deploy/corda_notary-keypair.json target/deploy/corda_notary.so + +#new terminal +ADMIN_CLI="solana-aggregator/admin-cli/build/libs/admin-cli-4.14-SNAPSHOT.jar" +solana airdrop -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json --commitment confirmed 10 +solana airdrop -k solana-aggregator/notary-program/dev-keys/DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3.json --commitment confirmed 10 +java -jar $ADMIN_CLI initialize -u http://localhost:8899 -v -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI create-network -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json +java -jar $ADMIN_CLI authorize --address DevNMdtQW3Q4ybKQvxgwpJj84h5mb7JE218qTpZQnoA3 --network 0 -u http://localhost:8899 -k solana-aggregator/notary-program/dev-keys/DevAD5S5AFhTTCmrD8Jg58bDhbZabSzth7Bu6rG4HFYo.json diff --git a/Tokens/stockpaydividend/setupSolanaAccounts.sh b/Tokens/stockpaydividend/setupSolanaAccounts.sh new file mode 100755 index 00000000..c1101b02 --- /dev/null +++ b/Tokens/stockpaydividend/setupSolanaAccounts.sh @@ -0,0 +1,41 @@ +#!/bin/bash +solana config set --url localhost + +NOTARY_ACCOUNT=`solana address -k $1` +solana airdrop 10 $NOTARY_ACCOUNT + +bridgeAuthorityWallet=./build/nodes/custodied-keys/bridge-authority-wallet.json +solana-keygen new -o $bridgeAuthorityWallet --no-bip39-passphrase -f + +bigBankWallet=./build/nodes/solana-keys/big-corp-wallet.json +solana-keygen new -o $bigBankWallet --no-bip39-passphrase -f + +bridgeAuthorityAccount=`solana address -k $bridgeAuthorityWallet` +funderKeyFile=$1 +solana transfer $bridgeAuthorityAccount 0.1 --fee-payer $funderKeyFile --from $funderKeyFile --allow-unfunded-recipient + +bigBankAccount=`solana address -k $bigBankWallet` +solana transfer $bigBankAccount 0.1 --fee-payer $funderKeyFile --from $funderKeyFile --allow-unfunded-recipient + +tokenMintFile=./build/nodes/solana-keys/token-mint.json +solana-keygen new -o $tokenMintFile --no-bip39-passphrase + +MINT_ACCOUNT=$(spl-token create-token \ + --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \ + --mint-authority $bridgeAuthorityAccount \ + --fee-payer $bridgeAuthorityWallet \ + --decimals 9 \ + --output json \ + $tokenMintFile | jq -r '.commandOutput.address') + +TOKEN_ACCOUNT=$(spl-token create-account \ + --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \ + --owner $bigBankAccount \ + --fee-payer $funderKeyFile \ + $MINT_ACCOUNT | awk '/^Creating account / {print $3}') + +bigBankPubKeyFile=./build/nodes/solana-keys/big-corp.pub +echo $TOKEN_ACCOUNT >> $bigBankPubKeyFile + +tokenMintPubKeyFile=./build/nodes/solana-keys/token-mint.pub +echo $MINT_ACCOUNT >> $tokenMintPubKeyFile \ No newline at end of file diff --git a/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt b/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt index 7db816f9..818b78ec 100644 --- a/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt +++ b/Tokens/stockpaydividend/workflows/src/main/kotlin/net/corda/samples/stockpaydividend/flows/QueryFlows.kt @@ -65,4 +65,20 @@ class GetTokenToBridge( val tokens: List> = serviceHub.vaultService.tokenAmountsByToken(pointer).states return tokens } +} + +@InitiatingFlow +@StartableByRPC +class GetTokenToBridgeFormatted( + val symbol: String +) : FlowLogic>() { + + override val progressTracker = ProgressTracker() + + @Suspendable + override fun call(): List { + val tokens: List> = subFlow(GetTokenToBridge(symbol)) + val formatted: List = tokens.map { "${it.ref}" } + return formatted + } } \ No newline at end of file diff --git a/Tokens/stockpaydividend/workflows/src/main/resources/bridging-flows-template.conf b/Tokens/stockpaydividend/workflows/src/main/resources/bridging-flows-template.conf new file mode 100644 index 00000000..f694c1ec --- /dev/null +++ b/Tokens/stockpaydividend/workflows/src/main/resources/bridging-flows-template.conf @@ -0,0 +1,3 @@ +participants = {"${bigBankCordaIdentity}" = "${bigBankAccount}"} +mints = {"${cordaTokenTypeLinearId}" = "${tokenMintAccount}"} +mintAuthorities = {"${cordaTokenTypeLinearId}" = "${bridgeAuthorityAccount}"} \ No newline at end of file