Given mystring = "O=PartyB,L=New York,C=US", is there an easy method to convert the string into a real Party object? Have been looking at CordaRPCOps.wellKnownPartyFromX500Name() but cannot get it to work... Have tried variations of the following:
val rpcOps = new CordaRPCOps()
val otherParty: Party = rpcOps.wellKnownPartyFromX500Name(partyString) ?: throw Exception("Party not recognised.")
There are several ways you can do this:
Parsing the string into an X500 name
val x500Name = CordaX500Name.parse("O=PartyB,L=New York,C=US")
val party = rpcOps.wellKnownPartyFromX500Name(x500Name)
Constructing the X500 name directly
val x500Name = CordaX500Name(organisation = "PartyB", locality = "New York", country = "US")
val party = rpcOps.wellKnownPartyFromX500Name(x500Name)
Fuzzy matching
val matchingParties = rpcOps.partiesFromName("PartyB", false)
if (matchingParties.size != 1) {
throw IllegalArgumentException()
}
val party2 = matchingParties.single()
Getting an CordaRPCOps instance
Here's an example of how you'd get a CordaRPCOps instance in the first place:
val rpcAddress = NetworkHostAndPort(host, rpcPort)
val rpcClient = CordaRPCClient(rpcAddress)
val rpcConnection = rpcClient.start(username, password)
val cordaRPCOps = rpcConnection.proxy
You could also get the Party instance without using CordaRPCOps. But this solution requires you to have access to the ServiceHub instance. This means you can access it from a CordaService. Let's say serviceHub is a CordaService instance. Then, try the following:
val x500Name = CordaX500Name.parse("O=PartyB,L=New York,C=US")
val party: Party? = serviceHub.networkMapCache.getPeerByLegalName(x500Name)
This will handy in case you require Party instance from String in a Corda service, where it is unnecessary to create an RPC connection.
Related
I had cloned the cordapp-kotlin-template . I've defined a state called LoadState which looks like this:
#BelongsToContract(LoadContract::class)
data class LoadState(
val loadId: String,
val transporterName: String? = null,
val vehicleModel: String? = null,
val regLicenseNo: String? = null,
val totalWeight: Int? = null,
val notes: String? = null,
val suppliers: MutableList<SupplierDetailsModel> = mutableListOf(),
val recycler: Party,
override val participants: List<AbstractParty> = listOf(recycler)
) : QueryableState {
override fun generateMappedObject(schema: MappedSchema): PersistentState {
if (schema is LoadSchemaV1) {
return PersistentLoadState(this.loadId)
} else {
throw IllegalArgumentException("Unsupported Schema.")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> {
return listOf(LoadSchemaV1())
}
}
When I use the application for the first time and issue a LoadState say LO123 it works fine. The state is issued and recorded in the vault as well.
This is how my the LoadState issue flow looks like:
#Suspendable
override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.getNotary(CordaX500Name.parse("O=Notary,L=London,C=GB"))
progressTracker.currentStep = TX_COMPONENTS
val recyclerName = CordaX500Name(
organisation = "MyRecycler",
locality = "Mumbai",
country = "IN"
)
val recycler = serviceHub.identityService.wellKnownPartyFromX500Name(recyclerName)
?: throw IllegalArgumentException("Could not find party Recycler.")
val outputState = LoadState(
loadId = req.loadId,
loadDate = req.loadDate,
transporterName = req.transporterName,
vehicleModel = req.vehicleModel,
regLicenseNo = req.regLicenseNo,
totalWeight = req.totalWeight,
assets = req.assets,
suppliers = req.suppliers,
recycler = recycler,
notes = req.notes
)
val command = Command(LoadContract.Commands.Create(), listOf(ourIdentity.owningKey))
progressTracker.currentStep = TX_BUILDING
val txBuilder = TransactionBuilder(notary = notary)
.addOutputState(outputState, LoadContract.ID)
.addCommand(command)
// signing the transaction
progressTracker.currentStep = TX_SIGNING
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// verifying the transaction
progressTracker.currentStep = TX_VERIFICATION
txBuilder.verify(serviceHub)
// We finalise the transaction and then send it to the counterparty.
progressTracker.currentStep = FINALIZATION
val recyclerSession = initiateFlow(recycler)
return subFlow(FinalityFlow(signedTx, listOf()))
}
Now there's a requirement to add a new field to our LoadState:
val myNewField: String? = null
After adding this new field to the LoadState I'm running the deployNodes command. Once the build folders are generated I'm copying the contents of the Node/cordapps folder to my old build Node/cordapps folder.
Now, while starting the nodes I'm running the migration commands (core schemas and app schemas). Once the migration process is complete and the nodes are up, I'm calling an api endpoint which invokes a flow which takes L0123 as input, copies it, and modifies some params and create a new output state of type LoadState. The error is thrown at:
txBuilder.verify(serviceHub) in my UpdateLoadFlow. This is how my update flow looks like:
#Suspendable
override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.getNotary(CordaX500Name.parse("O=Notary,L=London,C=GB"))
progressTracker.currentStep = TX_COMPONENTS
val recyclerName = CordaX500Name(
organisation = "MyRecycler",
locality = "Mumbai",
country = "IN"
)
val recycler = serviceHub.identityService.wellKnownPartyFromX500Name(recyclerName)
?: throw IllegalArgumentException("Could not find party Recycler.")
val inputState = QueryVault().queryLoadById(req.loadId, Vault.StateStatus.UNCONSUMED, serviceHub.vaultService)
?: throw Exception("Load ${req.loadId} not found.")
val vaultData = inputState.state.data
var outputState = vaultData.copy(myNewField = "some new value");
val command = Command(LoadContract.Commands.Update(), listOf(ourIdentity.owningKey))
progressTracker.currentStep = TX_BUILDING
val txBuilder = TransactionBuilder(notary = notary)
.addInputState(inputState)
.addOutputState(outputState, LoadContract.ID)
.addCommand(command)
txBuilder.verify(serviceHub)
// signing the transaction
progressTracker.currentStep = TX_SIGNING
val signedTx = serviceHub.signInitialTransaction(txBuilder)
// verifying the transaction
progressTracker.currentStep = TX_VERIFICATION
// We finalise the transaction and then send it to the counterparty.
progressTracker.currentStep = FINALIZATION
val recyclerSession = initiateFlow(recycler)
return subFlow(FinalityFlow(signedTx, listOf()))
}
Please help me out with this.
After our discussion in the above comments section, it appears that your states have been issued using Whitelist zone constraint.
Also looking at the code it is clear that you have not explicitly added Whitelist Zone constraints for your states.
Then there remain two more possibilities by which states are issued using Whitelist Zone constraint.
One is you are using some Corda version before 4 or you have included the necessary configs to include white list zone constraints in network bootstrapper as specified here.
You have two options - either start from scratch and make sure you use Corda 4.
If you cannot start from scratch follow this path to first migrate the whitelist zone constraints to signature constraints, before running your UpdateLoadFlow. You can refer to this blog which talks about constraint migration.
I'm very new to F# and trying to figure out the best way to convert between similar but different data objects while minimising duplication of code. I'm writing an app using the Fabulous F# MVU framework for Xamarin Forms.
I'm using sqlite-net-pcl for data persistence, which calls for an object when creating a database table and interacting with that table, and I have created an AuthorObject:
type AuthorObject() =
[<PrimaryKey>] // sqlite-net-pcl attribute
member val AuthorId = 0 with get, set
member val Username = "" with get, set
member val Token = "" with get, set
Fabulous makes use of record types for models and I have created an Author record type to pass around inside Fabulous:
type Author =
{ AuthorId: int
Username: string
Token: string }
But I also have to interact with a web service API library (written in C#) that returns an AuthorDetailDto object. AuthorDetailDto has the same named properties on it as above, plus more that I don't need to worry about. I cannot change the definition of AuthorDetailDto.
So it seems the constraints are currently that sqlite-net-pcl needs an object with members, Fabulous needs a record type, and I have this AuthorDetailDto object coming across from the service API client library that I can't change. I need to convert between these types and I initially was hoping that I could get away with just:
let convertToObject author = // Was hoping author param here could be generic in some way
let obj = AuthorObject()
obj.AuthorId <- author.AuthorId
obj.Username <- author.Username
obj.Token <- author.Token
obj
let convertToModel (obj: AuthorObject) : Author =
{ AuthorId = obj.AuthorId
Username = obj.Username
Token = obj.Token }
But with the added complication of AuthorDetailDto, I'm finding I now need to do something like this to keep the compiler happy:
let convertAuthorDetailDtoToObject (dto: AuthorDetailDto) =
let obj = AuthorObject()
obj.AuthorId <- dto.AuthorId
obj.Username <- dto.Username
obj.Token <- dto.Token
obj
let convertAuthorToObject (author: Author) =
let obj = AuthorObject()
obj.AuthorId <- author.AuthorId
obj.Username <- author.Username
obj.Token <- author.Token
obj
let convertToModel (obj: AuthorObject) : Author =
{ AuthorId = obj.AuthorId
Username = obj.Username
Token = obj.Token }
Here are some source files:
DomainModels.fs:
namespace MyApp.Mobile
module DomainModels =
type Author =
{ AuthorId: int
Username: string
Token: string }
Author.fs:
namespace MyApp.Mobile.Repository
open SQLite
open Client // Provides AuthorDetailDto.
open MyApp.Mobile.DomainModels
module Author =
type AuthorObject() =
[<PrimaryKey>]
member val AuthorId = 0 with get, set
member val Username = "" with get, set
member val Token = "" with get, set
let convertToObject author =
let obj = AuthorObject()
obj.AuthorId <- author.AuthorId
obj.Username <- author.Username
obj.Token <- author.Token
obj
let convertToModel (obj: AuthorObject) : Author =
{ AuthorId = obj.AuthorId
Username = obj.Username
Token = obj.Token }
let connect dbPath = async {
let db = SQLiteAsyncConnection(SQLiteConnectionString dbPath)
do! db.CreateTableAsync<AuthorObject>() |> Async.AwaitTask |> Async.Ignore
return db
}
let purgeLoggedInAuthor dbPath = async {
let! db = connect dbPath
do! db.ExecuteAsync "DELETE FROM AuthorObject" |> Async.AwaitTask |> Async.Ignore
}
let insertLoggedInAuthor dbPath author = async {
let! db = connect dbPath
do! db.InsertAsync (convertToObject author) |> Async.AwaitTask |> Async.Ignore
}
The initial call looks like this:
insertLoggedInAuthor dbPath loggedInAuthor // loggedInAuthor is an AuthorDetailDto.
The error is FS0001 This expression was expected to have type 'DomainModels.Author' but here has type 'AuthorDetailDto' due to both types being referenced in Author.fs.
I've tried messing around with inline and static member constraints, and attempting to define an interface, but due to Author needing to be a record type and not being able to change AuthorDetailDto, it doesn't seem like this approach is viable. Or perhaps I'm just not well versed enough in F#.
To those more familiar with F# than me, what is the best way to address this kind of situation to minimise code duplication?
I have a unit test using this notation:
ledgerServices.ledger {
transaction {
...
this.verifies()
}
}
I'd like to actually have the LedgerTransaction object so that I can test a helper method that takes in the tx as an argument. Is it possible to do this using the LedgerDSL or do I need to manually use the TransactionBuilder to create the LedgerTransaction instance in my unit test?
I don't think so.
I would suggest you compute a transaction and test your helper method as you test a flow:
#Test
fun flowReturnsCorrectlyFormedPartiallySignedTransaction() {
val lender = a.info.chooseIdentityAndCert().party
val borrower = b.info.chooseIdentityAndCert().party
val stx = issueIou(IOUState(10.POUNDS, lender, borrower))
val inputIou = stx.tx.outputs.single().data as IOUState
val flow = IOUTransferFlow(inputIou.linearId, c.info.chooseIdentityAndCert().party)
val future = a.startFlow(flow)
mockNetwork.runNetwork()
val ptx = future.getOrThrow()
// Check the transaction is well formed...
// One output IOUState, one input state reference and a Transfer command with the right properties.
assert(ptx.tx.inputs.size == 1)
assert(ptx.tx.outputs.size == 1)
assert(ptx.tx.inputs.single() == StateRef(stx.id, 0))
println("Input state ref: ${ptx.tx.inputs.single()} == ${StateRef(stx.id, 0)}")
val outputIou = ptx.tx.outputs.single().data as IOUState
println("Output state: $outputIou")
val command = ptx.tx.commands.single()
assert(command.value == IOUContract.Commands.Transfer())
ptx.verifySignaturesExcept(b.info.chooseIdentityAndCert().party.owningKey, c.info.chooseIdentityAndCert().party.owningKey,
mockNetwork.defaultNotaryNode.info.legalIdentitiesAndCerts.first().owningKey)
}
I am doing some reporting kind of things using customer query. for that, i have to fetch the data from respective node's database.but,no clue how to do that.its normally fetching all data irrespective of node.
Let's do an example based on the IOU CorDapp (https://github.com/corda/cordapp-example/). There are several ways you can do this:
1. Via an API endpoint
This endpoint will return any IOUs stored on the node with a value above minValue:
#GET
#Path("ious-above-value")
#Produces(MediaType.APPLICATION_JSON)
fun getIOUsAboveValue(#QueryParam("minValue") minValue: Int): List<IOUState> {
val results = builder {
val currencyIndex = IOUSchemaV1.PersistentIOU::value.greaterThan(minValue)
val customCriteria = QueryCriteria.VaultCustomQueryCriteria(currencyIndex)
rpcOps.vaultQueryBy<IOUState>(customCriteria)
}
val stateAndRefs = results.states
return stateAndRefs.map { stateAndRef -> stateAndRef.state.data }
}
2. Via a client
This client will return any IOUs stored on the node with a value above minValue:
fun main(args: Array<String>) {
require(args.size == 1) { "Usage: ExampleClientRPC <node address>" }
val nodeAddress = NetworkHostAndPort.parse(args[0])
val client = CordaRPCClient(nodeAddress)
// Can be amended in the com.example.MainKt file.
val proxy = client.start("user1", "test").proxy
val results = builder {
val currencyIndex = IOUSchemaV1.PersistentIOU::value.greaterThan(3)
val customCriteria = QueryCriteria.VaultCustomQueryCriteria(currencyIndex)
proxy.vaultQueryBy<IOUState>(customCriteria)
}
val stateAndRefs = results.states
val states = stateAndRefs.map { stateAndRef -> stateAndRef.state.data }
}
3. Via the node's database directly
You can log into the node's database by following the instructions here: https://docs.corda.net/node-database.html. You will then be able to execute SQL queries against the node's database directly.
Corda 2.0
JDK 1.8.0_162
I'm trying to debug an inconsistent behaviour in FinalityFlow. Inconsistent as in different results in Mock and Real nodes.
The Procedure on Real Nodes
I'm trying to send a transaction to another node through one of the alternative FinalityFlow constructors:
constructor(transaction: SignedTransaction, extraParticipants: Set<Party>) : this(transaction, extraParticipants, tracker())
I communicate with my node through RPC. The procedure starts by retreiving the other node's Party by it's name, eg. O=PartyA,L=London,C=GB:
val extraRecipientParties = myExtraRecipientsStringList.map { rpcOps.wellKnownPartyFromX500Name(CordaX500Name.build(X500Principal(it)))!! }
Then, rpcOps calls the flow responsible for creating a state:
val flow = rpcOps.startFlow(::CreateStateFlow, other, arguments, extraRecipientParties)
val result = flow.returnValue.getOrThrow()
val newState = result.tx.outRef<MyStateClass>(0)
CreateStateFlow is pretty standard:
#StartableByRPC
class CreateStateFlow(
val s: String,
val p: String,
val o: String,
val extraParticipants: List<Party>
) : FlowLogic<SignedTransaction>() {
constructor(s: String, p: String, o: String): this(s, p, o, emptyList())
#Suspendable
override fun call() : SignedTransaction {
val notary = serviceHub.networkMapCache.notaryIdentities.first()
val newState = MyStateClass(ourIdentity, s, p, o, extraRecipients=extraParticipants)
val command = Command(TripleContract.Create(), listOf(ourIdentity.owningKey))
val outputState = StateAndContract(newState, TripleContract.CONTRACT_REF)
val utx = TransactionBuilder(notary=notary).withItems(
command,
outputState
)
val stx = serviceHub.signInitialTransaction(builder=utx, signingPubKeys=listOf(ourIdentity.owningKey))
if (newState.extraRecipients.isEmpty()) {
return subFlow(FinalityFlow(stx))
}
return subFlow(FinalityFlow(stx, newState.extraRecipients.toSet() ))
}
}
What I expect is that now, on any node owned by parties in the extraRecipients variable, I should be able to find newState by querying the vault.
Indeed, this is true when I test it on Mock nodes, but not when rpc calls
rpcOps.vaultQueryBy<MyStateClass>().states --> returns an empty list
Test on Mock Nodes
#Test
fun `FinalityFlow used to federate a transaction`(){
val partyAString = node1.info.legalIdentities.first().name.toString()
val aStringX500Name = CordaX500Name.build(X500Principal(partyAString))
val node2FindPartyA = node2.rpcOps.wellKnownPartyFromX500Name(aStringX500Name)!!
assert(node1.info.legalIdentities.contains(node2FindPartyA))
val executingFlow = node2.start(CreateStateFlow("fo", "boo", "bar", listOf(node2FindPartyA)))
val flowResult = executingFlow.getOrThrow()
val stateInNode2 = flowResult.tx.outRef<MyStateClass>(0)
val stateInNode1 = node1.database.transaction {
node1.services.loadState(stateInNode2.ref)
}
assert(stateInNode1.data == stateInNode2.state.data)
Edit:
MyStateClass.kt
data class MyStateClass(
val owner: Party,
val s: String,
val p: String,
val o: String,
val extraRecipients: List<Party>,
val lastEditor: AbstractParty = owner,
override val participants: List<AbstractParty> = listOf(owner),
override val linearId: UniqueIdentifier = UniqueIdentifier()
) : LinearState, QueryableState {
object MyStateSchemaV1 : MappedSchema(MyStateClass::class.java, 1, listOf(MyStateEntity::class.java)) {
#Entity
#Table(name = "my-state")
class MyStateEntity(state: MyStateClass) : PersistentState() {
#Column #Lob
var owner: ByteArray = state.owner.owningKey.encoded
#Column
var s: String = state.s
#Column
var p: String = state.p
#Column
var o: String = state.o
#Column #ElementCollection
var extra_recipients: Set<ByteArray> = state.extraRecipients.map { it.owningKey.encoded }.toSet()
#Column #ElementCollection
var participants: Set<ByteArray> = state.participants.map { it.owningKey.encoded }.toSet()
#Column #Lob
var last_editor: ByteArray = state.owner.owningKey.encoded
#Column
var linear_id: String = state.linearId.id.toString()
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(MyStateSchemaV1)
override fun generateMappedObject(schema: MappedSchema): PersistentState = MyStateSchemaV1.MyStateEntity(this)
}
Although you introduced a new variable val extraRecipients: List<Party>, your participants is only on the owner, override val participants: List<AbstractParty> = listOf(owner) Therefore only the owner party should have the state in the vault.
The extraRecipients in FinalityFlow do not store the states in the vault (states storage), but they store the copy of the notarised transaction in the transaction storage.
The definition of loadState function is Given a [StateRef] loads the referenced transaction and looks up the specified output [ContractState]. Because node 1 was added as a extra recipient of the transaction in finality flow (think of it as the cc-ed recipient of an email), when asked to loadState, it was able to deduce the state from the transaction storage since it consists of inputs, commands, outputs, etc. So here you've proven that the transaction was sent to the other parties during FinalityFlow.
While on the rpcOps.vaultQueryBy<MyStateClass>().states, it was actually querying states from the node states vault - not transaction storage, therefore returned an empty list.
If you want the extraRecipients to store the state, you'll need to add them in the participants field of the state or use observable-states concept here.