Observers participant node is down

If an observer ’s participant node in a submitted command is down, would Canton still process and accept that command, will it pass validation?

1 Like

It depends on your confirmation policy. By default, you get the signatory confirmation policy which requires that signatories and actors confirm. So unless the observer is also an actor, they do not have to confirm and the command will go through even if the respective node is down.

Thanks for confirming ( :wink: ) my intuition.

How does one set confirmation policies in the domain, I could not find anything with a search for “confirmation policy”?

The confirmation policy is derived from the “TrustLevel” given to a participant via the participant domain trust certificates: Console Commands — Canton 2.6.0-SNAPSHOT documentation

However, this is hidden behind a feature flag and not supported in production right now and we haven’t yet decided whether we really need this.

1 Like

I think of signatory as having a strict consistency model and observer as eventually consistent.

Or in CAP theorem terms, we always want Availability, so signatory gives you Consistency but isn’t Partition-tolerant, and observer is Partition-tolerant but not Consistent.

Even though there are no strict consistency guarantees on observer you will get confirmation from their node eventually via the ACS commitments (see my forum question on this topic)

That raises an interesting question; should the issuer of a currency be a signatory or an observer?

From a language and rights perspective they have to be a signatory as their authorization of the creation is what gives it value. But from a distributed operations perspective it would be nicer if they were an observer so that they are not a bottleneck on transactions.

The availability requirement on the issuer in Daml’s default way of modelling assets is a tradeoff we made consciously.

Let’s say we have this chain of events where the #s are UTXO/Contract ids.

  1. Bank issues asset #0 to Alice
  2. Alice transfers #0 to Eve who now holds #1
  3. Eve transfers #1 to Bob who now holds #2
  4. Eve transfers #1 to Carol
  5. Eve makes up a #3 and transfers #3 to Carol
  6. Eve transfers #2 to Carol.

4, 5, and 6 should not be possible so Carol needs a mechanism each to detect and prevent these.

  1. Proof of Authenticity: Ascertain whether an asset #N was indeed at some point created.
  2. Double Spend Detection: Ascertain whether a UTXO #N that’s being used in a transaction was already spent.

Different systems solve these two in different ways:

Blockchains typically solve the proof of authenticity by making everything public and delegating the verification to the miners. Anyone can verify that a given UTXO/contract does actually exist. Miners and full nodes do so as part of validating blocks.
Corda is an exception in that is solves this through backchains. Ie if you receive an asset, you receive its entire history back to issuance and can verify that. So in our 5. above, Carol would not be able to verify authenticity because there’s no backchain leading back to an issuer signature.
Another approach that’s “up and coming” is to use Zero Knowledge Proofs to prove authenticity. There are some systems that do this for simple token use-cases. But ZKPs for general purpose smart contracts are some way away.

Blockchains typically solve the double spend protection by making everything public and delegating the verification to the miners. Anyone can verify that a given UTXO/contract was not already spent by going through the entire history or using a state snapshot. Miners and full nodes do so as part of validating blocks.
Corda is an exception in that it solves this through its Notary. You send the UTXO Ids of all “States” you want to consume to the notary, which checks that they did not already get spent in a previous transaction, and then gives you its signature which conveys “no double spend here”. If the notary doesn’t get to see the transaction itself, it does this double spend checking blindly, which means it will also “spend” UTXO Ids that get spent in an invalid transaction. So in our example 6 above, Carol wouldn’t receive #2, but Bob would lose it. This is called a “Denial of State” attack in Corda. If the Notary does see the whole transaction, you have given up a fair amount of privacy.

The tradeoff should be clear from the above. There’s a triangle made out of Privacy, Security and Availability here. Most public blockchain has gone without Privacy. Corda has chosen limited privacy, but depending on setup lost security and introduced a global single point of failure with the Notary. We made this tradeoff available per-contract: You choose the signatories. They are the set that sees the contract, but you have to trust at least one signatory, and they all have to be available. By default, it means that the issuer is a single point of failure for an asset.

But this per-contract trade-off allows us to do better. On our roadmap we have a feature for “shared” parties, where a party might be co-owned between 7 Participants, with a 5-out-of-7 signature policy. If you used that kind of party as an issuer, you still get high privacy and importantly privacy per contract, but you also get fault tolerance for the issuer participants.

1 Like

Thanks for the reply.

I think we should have a behavior specified at the language level so that the abstraction is captured there and one would not have different executions based upon where one is creating a template.

Can’t we already have something along those lines via

wellKnownIssuers : [Party]
wellKnownIssuers = mapOptional partyFromText ["UST", "SNB", "Bernhard"]

template TheMoney with
    issuer : Party
    owner  : Party
    amount : Decimal
  where
    signatory issuer
    observer owner
    ensure  issuer `elem` wellKnownIssuers

Similarly, isn’t the intent for Party’s to be hosted on more than one participant node? That would allow an issuer of lots of assets, (ex. a government treasury/central-bank/money-printer), have a Party that is shared and distributed amongst many small domains so that they are able to authorize transactions?

Another thing to consider is that there might be assets (ex. art) or instances (Partys want to transact without access to the issuer because they are stranded on a desert island with great wifi) where we do want to have something like backchains. And you can almost do that with Daml

template Asset with
    issuer : Party
    owner  : Party
    name   : Text
  where
    signatory issuer
    observer owner
    choice Give : ContractId Asset
      with
        newOwner : Party
      controller owner
      do create this with
           owner = newOwner

    choice Transfer : ContractId TransferedAsset
      with
        newOwner : Party
      controller owner, newOwner    -- to avoid propose & accept.
      do
        create TransferedAsset with
          owner = newOwner
          previousOwner = owner
          assetId = self
          history = []
          ..

template TransferedAsset with
    owner  : Party
    previousOwner : Party
    name   : Text
    assetId : ContractId Asset
    history : [ContractId TransferedAsset]
  where
    signatory owner, previousOwner

    choice TransferAgain : ContractId TransferedAsset
      with
        newOwner : Party
      controller owner, newOwner
      do
        create TransferedAsset with
          owner = newOwner
          previousOwner = owner
          history = self :: history
          ..

    choice Convert : ContractId Asset
      with
        issuer : Party
      controller owner
      do
        -- need issuer rights
        -- create Asset with ..
        undefined

setup : Script ()
setup =  do
  alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
  bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")

  aliceTV <- submit alice do
    createCmd Asset with
      issuer = alice
      owner = alice
      name = "TV"

  bobTV <- submitMulti [alice, bob] [] do
    exerciseCmd aliceTV Transfer with newOwner = bob

  -- alice goes away
  carl <- allocatePartyWithHint "Carl" (PartyIdHint "Carl")

  carlTv <- submitMulti [bob, carl] [] do
    exerciseCmd bobTV TransferAgain with
       newOwner = carl

  doug <- allocatePartyWithHint "Doug" (PartyIdHint "Doug")

  dougTv <- submitMulti [carl, doug] [] do
    exerciseCmd carlTv TransferAgain with
      newOwner = doug

the question though is how can Alice, the original issuer verify the transaction history.

As soon as I wrote this I realized what feature would solve this, we need something like a FutureParty; a Party to be specified later.

data FutureParty = FutureParty Text   -- Should be Opaque
  deriving (Eq, Show)

instance IsParties FutureParty where
  toParties _ = undefined

assigned : FutureParty -> Optional Party
assigned = undefined

template Asset with
    issuer : Party
    owner  : Party
    name   : Text
  where
    signatory issuer
    observer owner
    choice Give : ContractId Asset
      with
        newOwner : Party
      controller owner
      do create this with
           owner = newOwner

    choice Transfer : ContractId TransferedAsset
      with
        newOwner : Party
        validator : FutureParty
      controller owner, newOwner  
      do
        create TransferedAsset with
          owner = newOwner
          previousOwner = owner
          assetId = self
          history = []
          ..

template TransferedAsset with
    owner  : Party
    previousOwner : Party
    name   : Text
    assetId : ContractId Asset
    history : [ContractId TransferedAsset]
    validator : FutureParty
  where
    signatory owner, previousOwner
    observer validator

    choice TransferAgain : ContractId TransferedAsset
      with
        newOwner : Party
      controller owner, newOwner
      do
        create TransferedAsset with
          owner = newOwner
          previousOwner = owner
          history = self :: history
          ..

    choice Convert : ContractId Asset
      with
        issuer : Party
      controller issuer
      do
        -- Not that this is superfluous; once the Party API assigns to the validator
        -- FutureParty, that real party gets to see all the previous transactions and
        -- is now an observer here.
        assertMsg "Not validated by the right party" $
          assigned validator == Some issuer
        create Asset with ..

For a given contract you’re fixing the issuer here so if that issuer is down you haven’t actually gained anything. The feature Bernhard is alluding to is closer to your FutureParty. You have one fixed party at the Daml level but the confirmers of that can be multiple parties and those can change over time. So you can have rules like 2/3 of a consortium must confirm for the issuer party.

It isn’t necessarily the instance of the contract that matters but the ability to execute the choice?

template TheMoney with
    issuer : Party
    owner  : Party
    amount : Decimal
  where
    signatory issuer
    observer owner
    ensure  issuer `elem` wellKnownIssuers

    choice Give : ContractId TheMoney
      with
        issuerToUseToValidateTransaction : Party
        newOwner : Party
      controller owner
      do
        create this with
          owner = newOwner
          issuer = issuerToUseToValidateTransaction

One could try issuers to Give who are available until the transaction works.

Signatories have to validate the execution of choices. So in your example, issuer still has to validate the transaction.