pact-lang

How does a keyset get enforced on a module?


The Pact documentation describes governing a module with a keyset. Specifically, given the module (module foo 'foo-keyset ...), then if someone tries to upgrade the contract then the 'foo-keyset will be "enforced on the transaction signature set."

I am trying to understand what it means to enforce 'foo-keyset. How does this string relate to one or more specific signatures in the signature set? Does the string mean anything, or could I name it anything I want? If the string is arbitrary, then what exactly is being enforced?

I tried to resolve this with the Pact documentation, to see whether there was a way to relate this keyset string with one or more signatures in the keyset. The docs describe a keyset as a list of public keys and a keyset predicate, and gives some JSON examples such as these two:

{ "keys": ["abc6bab9b88e08d","fe04ddd404feac2"], "pred": "keys-2" }
{ "keys": ["abc6bab9b88e08d","fe04ddd404feac2"], "pred": "my-module.custom-pred" }

I thought perhaps the keyset predicate could be used to relate 'foo-keyset to a particular key or set of keys in the keyset, like requiring a predicate my-module.foo-keyset, but the documentation for keyset predicates seems to focus on counting a particular number of matches from a keyset.


Solution

  • A keyset reference is a unique keyset defined in the environment. Either repl or the blockchain. In repl you can define a keyset like this:

    (env-data {
      "bob-guard": {"keys": ["bob"], "pred": "keys-all"}
     ,"alice-guard": {"keys": ["alice"], "pred": "keys-all"}
      })
    

    On the blockchain you need to choose a unique name and define it with define-keyset:

        (define-keyset 'my-unique-keyset-name (read-keyset "keyset"))
    

    read-keyset reads the keyset from the variable my-keyset defined in the transaction data.

    If you define a governance keyset for your module the keyset will be validated upon upgrade through deployment of the same module name. The validity of the keyset is checked through the predicate (pred). The predicate can be any function that is fed as arguments the number of keys in the keyset and the count of matching signatures. If the predicate returns true the transaction can continue. You can construct custom predicates matching this function signature:

        (defun keys-majority:bool(count:integer matched:integer)
          (>= matched (+ (/ count 2) 1)))
    

    And then create a keyset using that custom predicate:

    (env-data {
      "my-module-guard": {"keys": ["bob", "alice", "babena" ],
      "pred": "my-module.keys-majority"}
    })