DAML LookupByKey and FetchByKey require different permission to run - daml

I have a contract with multiple signatories. Is there an example on how to do LookupBykey the contract? I am having trouble working out how to get all parties' authorization for LookupBykey to work.
Also is there anyone can explain to me that why LookupByKey requires more permission to run than FetchByKey?
Put up some codes to get the same contract using LookupByKey and FetchByKey. For the same party, FetchByKey works but LookupByKey doesn't.
LookupByKey got
Scenario execution failed on commit at Main:38:3:
#1: lookup by key of Sample:Sample at DA.Internal.Prelude:365:26
failed due to a missing authorization from 'partyA'
run = scenario do
a <- getParty "partyA"
b <- getParty "partyB"
sample <- submit a do create Sample with sig = a, obs = b, content = "some text here"
caller <-submit b do create Caller with sig = b, obs = a
submit b do exercise caller FetchByKey with company="test", text = "fetch by key sample"
pure()
run2 = scenario do
a <- getParty "partyA"
b <- getParty "partyB"
sample <- submit a do create Sample with sig = a, obs = b, content = "some text here"
caller <-submit b do create Caller with sig = b, obs = a
submit b do exercise caller LookupByKey with company="test", text = "look up by key sample"
pure()
-- choices
controller sig can
FetchByKey : Bool
with
company : Text
text : Text
do
re <- fetchByKey #Sample (company, obs)
cid_tr <- exercise (fst re) Operate with text = text
return True
controller sig can
LookupByKey : Bool
with
company : Text
text : Text
do
re <- lookupByKey #Sample (company, obs)
cid <- fetch (fromSome re)
res <- exercise (fromSome re) Operate with text = text
return True

Let's take a look at both fetchByKey and lookupByKey in the context of a generic Sample template:
template Sample
with
maints : [Party]
extraSigs : [Party]
obs : [Party]
where
signatory maints ++ extraSigs
observer obs
key this : Sample
maintainer key.maints
The key is the whole contract, maintained by a specified subset of the signatories. A simple proposal workflow can be used to instantiate such a contract:
template SampleProposal
with
sample : Sample
sigs : [Party]
where
signatory sigs
observer (signatory sample)
choice Sign
: ContractId SampleProposal
with
sig : Party
controller sig
do
assert (sig `elem` signatory sample)
assert (sig `notElem` sigs)
create this with sigs = sig :: sigs
choice Accept
: ContractId Sample
with
sig : Party
controller sig
do
assert (sig `elem` signatory sample)
create sample
makeSample = scenario do
maints <- mapA getParty ["main1", "main2", "maint3"]
extraSigs <- mapA getParty ["sig1", "sig2", "sig3"]
obs <- mapA getParty ["obs1", "obs2", "obs3"]
let sample = Sample with ..
prop <- submit (head maints) do
create SampleProposal with
sample
sigs = [head maints]
signedProp <- foldlA
(\p sig -> submit sig do exercise p Sign with sig)
prop
(tail maints ++ tail extraSigs)
submit (head extraSigs) do
exercise signedProp Accept with sig = head extraSigs
Now, who can fetchByKey and who can lookupByKey this sample?
The first condition for either is that the submitting party has to know of the contract. Ie they must be an stakeholder (ie signatory or observer), or the contract must have been divulged to them.
Secondly, the operation has to be authorised correctly. fetchByKey is authorised like fetch, meaning you have to have the authority of at least one stakeholder. Thus, an observer of Sample can divulge the contract and delegate a fetchByKey.
template FetchByKeyDelegation
with
sampleObserver : Party
agent : Party
where
signatory sampleObserver, agent
nonconsuming choice FetchSampleByKey
: (ContractId Sample, Sample)
with
ctl : Party
sample : Sample
controller ctl
do
fetchByKey #Sample sample
template FetchByKeyDelegationInvite
with
fbkd : FetchByKeyDelegation
where
signatory fbkd.sampleObserver
controller fbkd.agent can
AcceptFBKDI
: ContractId FetchByKeyDelegation
do
create fbkd
delegateFetch = scenario do
sample <- makeSample
let obs = head sample.obs
agent <- getParty "Agent"
prop <- submit obs do
create FetchByKeyDelegationInvite with
fbkd = FetchByKeyDelegation with
sampleObserver = obs
agent
fbkd <- submit agent do
exercise prop AcceptFBKDI
-- By calling FetchSampleByKey, `obs` divulges the contract to `agent`
submit obs do
exercise fbkd FetchSampleByKey with
ctl = obs
sample
-- Now `agent` can use the authority of `obs` to `fetchByKey`
submit agent do
exercise fbkd FetchSampleByKey with
ctl = agent
sample
lookupByKey has stricter authorization rules. Rather than one stakeholder, you need the authority of all maintainers. Other than that, delegation works the same:
template LookupByKeyDelegation
with
maints : [Party]
agent : Party
where
signatory maints, agent
nonconsuming choice LookupSampleByKey
: Optional (ContractId Sample)
with
ctl : Party
sample : Sample
controller ctl
do
lookupByKey #Sample sample
template LookupByKeyDelegationInvite
with
lbkd : LookupByKeyDelegation
sigs : [Party]
where
signatory sigs
observer (signatory lbkd)
choice SignLBKDI
: ContractId LookupByKeyDelegationInvite
with
sig : Party
controller sig
do
assert (sig `elem` signatory lbkd)
assert (sig `notElem` sigs)
create this with sigs = sig :: sigs
controller lbkd.agent can
AcceptLBKDI
: ContractId LookupByKeyDelegation
do
create lbkd
delegateLookup = scenario do
sample <- makeSample
let maints = sample.maints
agent <- getParty "agent"
lbkdi <- submit (head maints) do
create LookupByKeyDelegationInvite with
lbkd = LookupByKeyDelegation with
maints
agent
sigs = [head maints]
signedlbkdi <- foldlA
(\p sig -> submit sig do exercise p SignLBKDI with sig)
lbkdi
(tail maints)
lbkd <- submit agent do
exercise signedlbkdi AcceptLBKDI
-- By calling LookupSampleByKey, a maintainer divulges the contract to `agent`
submit (head maints) do
exercise lbkd LookupSampleByKey with
ctl = head maints
sample
-- Now `agent` can use the authority of `obs` to `lookupByKey`
submit agent do
exercise lbkd LookupSampleByKey with
ctl = agent
sample
In your specific model, it seems that partyB is an observer, but not a maintainer. Thus, they know the contract (the first conditions), and their actions are authorised by a stakeholder (the second condition for fetchByKey). However, the lookupByKey is not authorised by the maintainers so it fails.
The reason the for the difference in authorisation is the behaviour in case of negative lookups. fetchByKey fails on the submitter node so negative lookups never hit the network. lookupByKey allows negative lookups so they do hit the network.
DAML is designed around the principle that no party should ever have to do work unless they signed something. Validating key lookups is work so you should never have to do it unless you signed a contract. With fetchByKey this is true. Unless you signed a contract on which you are maintainer, no honest node will ever submit a fetchByKey on which you are a maintainer.
However, with lookupByKey this is not true. First of all, if you have a negative lookup, the only information you have is who the maintainers are, as only those are part of the key. Thus to run an authorisation check on the submitter node, the authorisation rule has to be about maintainers, not stakeholders.
Now suppose the authority of one maintainer, rather than all of them, was enough. Then the following would be a perfectly legal thing to do:
badLookup = scenario do
frankie <- getParty "Frankie"
spammer <- getParty "spammer"
let
sample = Sample with
maints = [frankie, spammer]
extraSigs = []
obs = []
forA [1..1000] (\_ -> do
submit spammer do
lookupByKey #Sample sample
)
Ie a malicious party could legitimately spam you with expensive operations. This goes against DAMLs core principles so the authorisation rule has to be that all maintainers are needed.
The key take away is that one should consider very carefully whether lookupByKey is actually needed. It's often advisable to design workflows so that all key lookups are positive.

Related

Exercising multiple updates within DAML choice

Is it possible in DAML to use a map-like function to iterate over of list of contractids, retrieve them and execute a choice on each? This appears to be limited in DAML by the way everything needs to be wrapped in a single Update when executing a choice.
Here's an example of what I've attempted (noting the issues):
exerciseChoice: ContractId ContractB -> Update (ContractId ContractB)
exerciseChoice contractB = do (exercise contractB UpdateB with newText = "test")
template ContractA
with
party : Party
contracts: [ContractId ContractB]
where
signatory party
controller party can
nonconsuming UpdateA : [Update (ContractId ContractB)]
with newText : Text
do
-- a <- create ContractB with party = party; text = newText
-- a2 <- exerciseChoice a
-- return [a2] #these lines work fine
return map exerciseChoice contracts
-- #this doesn't work due to DAML implicitly adding Update before return definition
-- i.e. DAML expects type 'Update [Update (ContractId ContractB)]' based on this signature
-- we need a function which converts something like:
-- '[Update (ContractId ContractB)] -> Update [ContractId ContractB]'
template ContractB
with
party : Party
text: Text
where
signatory party
controller party can
UpdateB: ContractId ContractB
with newText: Text
do create this with text = newText
If this can be solved, can you also explain why when returning multiple tuples in DAML they seem to implicitly cast from (Update (ContractId A), Update (ContractId B)) to Update (ContractId A, ContractId B)?
The function map has type (a -> b) -> [a] -> [b] so map exerciseChoice contracts will have type [Update (ContractId ContractB)]. Turning a list of actions into a single action is conceptually simple. The resulting action is the action of performing each action in the list in sequence. And indeed, there is a function for that: sequence : (Applicative m) => [m a] -> m [a]. Update is an applicative so you could write sequence (map exerciseChoice contracts). However, this is such a common task that there is a specialised function mapA : (Applicative m) => (a -> m b) -> [a] -> m [b].
mapA exerciseChoice contracts should do the trick.

Compare value of a nested java bean

I am trying my hands with JESS wherein I want to write a rule as following.
When order amount is greater than 1000 and customer is preferred and customer name matches to order name then do something.
My Order.java has following properties
int amount, Customer cust
And Customer.java is a plain bean class holding following properties.
string name, string address
I am not able to find a way wherein I can get the value of Order.cust.name and compare with Customer.name in JESS.
Can anyone help me here please?
I tried using following but not working out for me.
(defrule HelloCustomer "When customer is preferred and amount is greater than 1001"
?person1 <- (Customer)
?cust <- (Customer {isPreferred == true})
?o <- (Order{amount > (+ 1000 1)})
?person2 <- (Order(customerA))
?person2Name <- (Customer{name == (Order{customerA.name})})
=>
(modify ?o (totalAmount 1000))
(printout t "Found two different " (call ?person2.customerA getName) crlf))
(printout t "Found two different*** " ?person1.name crlf))
You have many of the details right but the fundamentals are mostly wrong. First, note that each “Customer” and “Order” pattern matches a new object; this might match as many as five different objects. Secondly, you’ll need to bind variables to slot values so you can test them in other slots. Lastly, you’ll need to make use of the “OBJECT” slot to retrieve the Java object represented by each of this patterns. Roughly, I think you want something like
(defrule HelloCustomer
(Customer {isPreferred == true} (name ?name) (OBJECT ?customer))
(Order {amount > 1001} (name ?name) (OBJECT ?order)) ;; Repeating variable binds these together
=>
;; do something with ?customer and ?order
The Jess manual covers all of this, but you do have to read the whole thing. After all, you’re learning a whole new programming language.

How do I assert a contract is inactive (archived) in a scenario?

If I wanted to validate that a contract is active, I could simply fetch it in a Scenario:
template Something
with
party : Party
where
signatory party
nonconsuming choice DoStuff : ()
controller party
do
return ()
myTest = scenario do
someone <- getParty "Someone"
submit someone do
cid <- create Something with party = someone
exercise cid DoStuff
fetch cid -- would fail if the DoStuff choice was consuming
How do I assert the opposite?
template Something
with
party : Party
where
signatory party
choice DoStuff : ()
controller party
do
return ()
myTest = scenario do
someone <- getParty "Someone"
submit someone do
cid <- create Something with party = someone
exercise cid DoStuff
fetch cid -- fails the scenario, as it should, but that's what I want to check for
This code shows that you can chain the cid into an appropriate scope to allow a submitMustFail to operate in the way intended:
myTest = scenario do
someone <- getParty "Someone"
cid <- submit someone do
create Something with party = someone
submit someone do
exercise cid DoStuff
submitMustFail someone do
fetch cid -- would fail if the DoStuff choice was consuming

How do I execute some code in DAML N times?

Say I have a choice that takes an integer that represents a count and I want to create a contract that number of times, i.e. execute some block of code that many times.
In Ruby for example this might look like this:
n.times do
#run some code here
end
How do I achieve this in DAML?
TLDR
To apply a ledger operation N times the easiest way is to use the replicateA function from DA.Action.
Example
daml 1.2
module ReplicateDaml
where
import DA.Action
template Demo
with
sig: Party
total: Int
where
signatory sig
testReplicate = scenario do
p <- getParty "party"
let
total = 10
p `submit` replicateA total $ create Demo with sig=p; total
Discussion
The type signature for replicateA is:
-- | `replicateA n act` performs the action n times, gathering the results.
replicateA : (Applicative m) => Int -> m a -> m [a]
You can read this as:
This function supports any type m that has an instance (implementation) for the Applicative typeclass (api or interface).
Its first parameter is an Int
Its second is an 'effect' of the type m that provides a value of type a
It returns the result of repeating the effect N times, collecting the results in a list
The create you describe is of type: Update (ContractId a); and as Update instantiates (has an implementation for) the Applicative typeclass you can use any function that works on Applicative's on Update's — which naturally includes replicateA.
When used this way, substitute Update for m and (ContractId t) for a in the type signature, so:
replicateA : Int -> Update (ContractId t) -> Update [ContractId t]

Elm Json request not working?

In Elm, you can use the Json.decode and Http package to request json data. My attempt was to work out a periodic lookup for emails ( from this url ). The timer operation does work (i tried it with with a simple counter).
I have used this example and this SO question as reference.
Now the types:
type alias Email = { title: String, ... }
type Action =
NoAction
| TickCounter -- TODO rem
| AddEmails (List Email)
Then the main + state + actions ...
main: Signal Html
main = Signal.map (view actions.address) state
state: Signal Model
state = Signal.foldp update makeEmptyModel input
-- handle inputs (merging signals)
input : Signal Action
input =
Signal.mergeMany
[ actions.signal
-- , other actions
, Signal.map checkForNewMails (Time.every (Time.minute / 6.0) ) -- TODO precise timer (quick test)
]
actions: Signal.Mailbox Action
actions = Signal.mailbox NoAction
update: Action -> Model -> Model
update action model =
case action of
NoAction -> model
TickCounter -> { model | count = model.count + 1 }
AddEmails newMails -> { model | emails = newMails }
checkForNewMails: Time -> Action
checkForNewMails t =
let mails = startGettingEmailData
in TickCounter -- TODO replace with AddEmails using mails
The TickCounter is an Action, which i have used to test my timing operation. But the problem is startGettingEmailData. It uses the next snippet, but it doesn't fire any JSON request (i have checked it through the console). Once that has been resolved, i can convert mails to an action so that i can add the emails in the model.
-- url to json
jsonUrl = "https://dl.dropboxusercontent.com/u/14070433/temp.json"
-- get emails
startGettingEmailData: Task Http.Error (List Email)
startGettingEmailData = Http.get emailJsonDecoder jsonUrl
emailJsonDecoder: Json.Decoder (List Email)
emailJsonDecoder =
let makeEmail = Json.object4
(\ti fr da bo -> makeNewEmail -1 ti fr da bo )
("title" := Json.string)
("from" := Json.string)
("date" := Json.string)
("body" := Json.string)
in
"emails" := Json.list makeEmail
Is there a problem with my code ? If not, then is there a way to check the Http.Error content ? (Maybe the problem lies not in my code, but in the network, but i can access the dropbox file by browser...)
Your code for checkForNewMails doesn't actually do anything with mails, so it never gets invoked. The let statement doesn't make any calls, it only lets you define one-off functions within the body of a larger function. Since the in portion merely returns TickCounter, then it means this function only ever returns TickCounter and does nothing else.
Furthermore, startGettingEmailData is returning a Task, which means it only gets invoked when in a port. You can't use it in a function that only returns an Action, because it would never get run.
You'll instead want to write a port that triggers on a timer, then creates a Task which polls your url, then maps the response of that GET request to an Action, calling the actions mailbox. You can use Task.onError to write a simple error handler, which could forward an error message to your view by creating an Error String constructor on your Action type.
Here's an example:
getEmailData _ =
let
request =
Http.get emailJsonDecoder jsonUrl
|> Task.map AddEmails
in
request
`Task.onError` (\err -> Task.succeed (Error (toString err)))
`Task.andThen` (\action -> Signal.send actions.address action)
port getEmails : Signal (Task a ())
port getEmails =
Signal.map getEmailData (Time.every (Time.minute / 6.0) )
The above code will cause the URL to be retrieved, parsed, then, on success, it will trigger you actions mailbox and cause an update of the view. If there is an error, it will send the new action of Error with a message, which you should handle in the view. (You'll have to add Error String to the Action union type and handle it in the model, update, and view functions).