The account and transaction structures built into Polkadot’s relay chain are extremely flexible and have allowed Unit 410 to maintain a high level of security for accounts we access frequently for day-to-day operations. In particular, layering multi-signature (multisig) and proxy accounts over the standard stash and controller model has been particularly beneficial.
Verifying complicated transactions for these account types is critical to operational security, however it can be difficult with existing tooling. We built the Polkadot Tx Parser to bridge this gap, a tool we’ve recently made publicly available.
Stash & Controller Accounts
Both validators and nominators in Polkadot are encouraged to use a 2-key model referred to as the Stash & Controller keys. While both keys are relevant, they have separate permissions: stash accounts move funds & controller keys perform most day-to-day operations like changing session keys, commission rates, and the payment destination.
In general, we thoroughly appreciate this philosophy and will articulate it as follows:
- A key with higher-levels of permissions should be used less-frequently and stored more securely than a key with fewer permissions.
- A key that is more operationally necessary should be more readily accessible than a key that is less operationally necessary.
Even though we appreciate that a controller key retains fewer permissions than its corresponding stash key, it is still able to perform some sensitive operations. This presents an interesting dilemma: The controller key has enough permissions that accessing it should be well-defined and never by a single individual, but is critical to safely operating a validator and so should be readily accessible.
Thankfully, we’re able to elegantly solve this problem by concurrently using multisig and proxy accounts.
Multisig & Proxy Accounts
Like the name suggests, a multisig account is an account that requires multiple signatures in order to perform any action. Each multi-signature account is composed of a set of N approving accounts and a threshold number M. In order to broadcast a transaction on behalf of the multi-signature account, M of the approving accounts must collectively sign-off on the intended operation.
A proxy account can be delegated permissions by another account. For instance, account A can add account B as a Staking Proxy for account A. Once established, account B can perform operations related to staking on behalf of account A but cannot transfer DOTs on behalf of account A.
In many ways, proxy accounts are a more generalized solution to permission segregation than the original Stash → Controller paradigm.
Multisig & Proxy Accounts In Action
By using multisig & proxy accounts together we’re able to ensure that our controllers are both well-secured and accessible for day-to-day operations.
Each SRE who maintains our fleet of validators has created an account which we call an Approver account. Collectively, these Approver accounts derive a multi-signature account we call the Operation Controller. Each validator stash & controller account then adds the Operation Controller as a proxy. The following image shows a high-level view of our validator account structure:
Even more conveniently, each stash & controller account can grant a different type of permission to the Operation Controller. For instance, the Operation Controller could be an Any proxy for the controller accounts but a Non Transfer proxy for the stash accounts.
When performing an operation by a multisig account the approvals will be included in separate blocks. As a result, some reference to the extrinsic itself must be preserved on-chain. Polkadot utilizes on-chain storage for many aspects of the network operations - storage itself is extremely well-supported - however the transaction structure is incredibly flexible and with this flexibility comes virtually unbounded extrinsic size. In order to effectively defend against chain bloat from malicious and legitimate use, Polkadot has elected to store the hash of the multi-signature extrinsic on-chain.
While this solution effectively mitigates against otherwise inevitable chain bloat, it opens up another problem: How can an approving account sign-off on a transaction if all they observe on-chain is a hash? Afterall, the fundamental reason to use multi-signature accounts is to prevent unilateral access for sensitive operations.
In order to solve this issue, we’ve built and have been using an application called the Polkadot Tx Parser. This newly open-sourced tool allows each participant to independently verify the contents of a call and can be run as both a web app & CLI.
An approval then uses the following workflow:
Initially an individual will generate a new operation on behalf of the Operation Controller. Once generated, the proposer will record the hex-encoded call data and share that with the other approvers.
In the images below we’re using the excellent Polkadot JS Apps to generate & submit this extrinsic:
Each approver parses the contents of the call data using the Polkadot Tx Parser to ensure that the contents of the operation and resulting hash are as expected:
yarn workspace @polkadot-tx-parser/cli run parse \
--tx <TX> \
Note that we’re also passing in an “aliases file” here. This is a simple mapping from Polkadot account to a human-readable name. As part of our initial key-generation ceremony we generated this file and ensured that each participant signed it using their primary GPG key. This step seems trivial but when working with many accounts can help prevent serious mistakes.
Once an approver has gained sufficient confidence in the contents of the proposed operation, they can move forward with approving the operation. Again, in this example, we’re using the Polkadot JS Apps to perform this.
Note that the last approver must include the full hex-encoded contents of the call in order for the extrinsic to be successfully included in the chain.
As discussed previously, Polkadot often preemptively takes action to avoid excessive chain bloat. In addition to storing hashes instead of full objects on-chain, an insignificant quantity (~20) DOTs must be locked to initiate multisig operations. This can be a frustrating thing to debug, so ensure your accounts are properly funded!
Though we use these Operation Controller accounts to perform many actions, we still prefer to store and move funds with a fully air-gapped transaction flow. That said, eventually we’d like to make use of a relatively new feature called Time-Delayed Proxies.
Time-delayed proxies are a type of proxy that ensures that any call initiated by the proxy won’t actually be enacted by the chain for a specified period of time after the final approval. In the time between the final approval and before the operation has been enacted, the proxied account (or even another proxy) can cancel the operation. Eventually, we’d like to build a watchdog application to monitor the chain for unintended or unauthorized use of a time-delayed proxy & trigger an alert if such an operation were detected.
With both time-delayed proxies and this watchdog application in place, we feel confident that we could use proxies & multisignature accounts for every single operation type in Polkadot.
Polkadot offers some tremendous primitives to securely & efficiently interact with sensitive accounts. By using multisignature accounts & proxies, Polkadot Tx Parser and its corresponding workflow we can move more quickly while feeling confident that no individual retains unilateral access to any of our sensitive accounts.
We hope that this can serve as an example of an elegant and effective permission strategy for other cryptocurrency applications.
As usual, we welcome all code review & open-source contributions to the Polkadot Tx Parser and encourage anyone interested in this work to get in touch via our address below.