Intent/Audience
After five iterations of the mutual_credit module for Drupal over 12 years, I developed the Credit Commons API and reference implementation for recursive mutual credit accounting, and last year also implemented it in version 6 of the mutual credit module. I recommend anyone implementing a mutual credit accounting to read this if they are interested in a flexible architecture which could point the way to eventual interoperability.
Previous versions supported multiple currencies, but I finally decided that one currency per community provided the most utility for the least complexity.
I'm documenting the Drupal module and the reference implementation together for now, because they really slightly different expressions of the same architecture. This blog should be considered an introduction, not a full description.
Introduction
The burgeoning security architecture of blockchains tends to generate rigid software, poor user experiences, irreversible transactions, and no notion of workflow. The ledger is held up to be some kind of objective immutable and important 'source of truth'. In the local and community contexts however, this is the wrong set of priorities. The ledger is not supposed to be the arbiter of truth, but a shared memory. Security is not about preventing theft of money, because money does not exist, only goods and services, which are not on the ledger. If the ledger could be tampered with, the beneficiary could only be in the community, the money cannot leave. The workflow of transactions need to be configurable because each community has different expectations about signing and erasing transactions, as well as perhaps having other needs. Critically the mutual credit module and a back end web service like the Credit Commons are not supposed to provide the full user experience, but be configurable to work as part of a full application, integrated with other things, especially the marketplace of goods and services.
The accounting methodology is mutual credit. That means that balances are never transferred on or off the ledger, but only between all accounts on the ledger. The sum of all account balances is always zero. When an account's balance is zero that means they neither owe nor are owed by other accounts.
This architecture consists of several components, which make up the structure of this document.
- Wallets, with min & max limits
- Transactions comprising header data and entries.
- Business logic
- Workflows including states and types and transitions.
Building an accounting system in this way will make it easier to connect using the credit commons protocol, which requires all of those things. The Drupal module goes further with these extra features.
- Transaction form designer
- Balance limit plugin
- Mass transactions
- Delegation of wallets.
Finally this article will finish by talking about the interoperability this architecture enables.
Wallets
In most accounting systems as with the Credit Commons, a wallet is really just a unique ID used in the ledger. In the Credit Commons reference implementation, the wallet IDs are strings, while in Drupal a wallet is a full entity with a numeric ID, name, a user who owns it, and capacity for other fields to be added. I was able to make several visualisations of wallets, which can be shown in different wallet view modes.
Each wallet must also have stored a min and max balance limit, which is checked during the validation of each transaction. The limits can be looked up or calculated or can revert to a default value.
Transactions
An entry is a transfer of units from the payer account to the payee account. It remains unchanged once written except for the txid. Here is the movement of 10 units from alice to bob.
ID | TxID | Payee | Payer | Description | Quant | Author | Metadata |
---|---|---|---|---|---|---|---|
1 | 2 | alice | bob | blablah | 10 | alice | key/value pairs as needed |
A transaction is one or more entries, grouped together under the same header which stores the UUID, creator ID, version number, workflow type and current state and time it was written. When the transaction advances along its workflow pay, say from pending state to completed state, a new header is written leaving the previous one as history. here you can see that alice created the transaction at 5pm and bob confirmed it at 6pm. Row #2 supercedes row #1 as shown in the txid column of the entries table.
ID | UUID | Version | Workflow ID | Scribe | State | Written |
---|---|---|---|---|---|---|
1 | b9ef08e9-7e7f-4fb0-863d-d3e6f40538c0 | 1 | default_payment | alice | pending | 2024-04-05 17:00:00 |
2 | b9ef08e9-7e7f-4fb0-863d-d3e6f40538c0 | 2 | default_payment | bob | completed | 2024-04-05 18:00:00 |
It can be advantageous to store the transactions which 'count' i.e. those in completed state, in a format,easier to query. The following table is what I worked out for that. Each entry is written twice. When you want to calculate an account balance, you filter the table using the acc1 column. This is how I produce statistics of each wallet's activity.
TxID | EID | UUID | Acc1 | Acc2 | Income | Expenditure | Diff | Volume | Written |
---|---|---|---|---|---|---|---|---|---|
2 | 1 | b9ef08e9-7e7f-4fb0-863d-d3e6f40538c0 | alice | bob |
| 10 | -10 | 10 | 2024-04-05 18:00:00 |
2 | 1 | b9ef08e9-7e7f-4fb0-863d-d3e6f40538c0 | bob | alice | 10 |
| 10 | 10 | 2024-04-05 18:00:00 |
Business logic
During the routine business of building a transaction, the system must have an opportunity, before validation of adding extra entries to the transaction, typically for charging transaction fees. In the credit commons reference implementation, this is a microservice which takes the transaction and returns any new entries it wants to add. In Drupal it is an EventSubscriber.
Workflows
A workflow is a structured object that defines the starting state of the transaction, and which states it can move into from each state. Here is an example, in yml format
id: bill label: Bill summary: The author bills another account, which must agree. direction: bill #allowed bill, credit, 3rdparty creation state: pending label: Create confirm: true states: pending: completed label: Sign by: payer erased label: Erase by: payee completed erased: label: Erase by: '' disabled: false
The 'direction' is either bill, credit or 3rdparty, which means the person creating the transaction is the payee, the payer or someone else (typically an admin). The transaction is created in a pending state, which means version 1 in the transactions table will always be pending. From there the payer is allowed to 'Sign' it, moving it to completed state. The transaction can then be erased, but only by an admin. If the transaction is disabled, that means it cannot be used in new transactions.
This way of doing workflow also encapsulates a lot of the logic of access control at least on existing transactions.
N.B. This workflow can't cope with multi-sig transitions, or named parties.
Access Control
the system needs to know which user can create, see, perform each workflow transaction on each transaction. The workflow encapsulates the trickiest of those. What remains is which users can use which workflow, and also the transaction visibility. In Drupal each workflow has a permission, and users are assumed to be able to see all transactions in their wallets. Then there are permissions to view the stats for other wallets, view all transactions.
Drupal module extras
The following features are all separate modules (or submodules) in Drupal, so are enabled independently of each other.
Transaction form designer gives the user a twig template with which to lay out a transaction form, and a way to configure default and hidden values. Each transaction form implements one and only one workflow. If the user controls multiple wallets, the form will allow present them with radio buttons to select which wallets to use.
Balance limits is a plugin system that allows configurable balance limits. Four plugins are provided, fixed, balanced (same above and below) and calculated, which uses variables like income and volume to calculate the limit for each wallet.
Mass transactions is two transaction forms, many2one and one2many that allows the user to include or exclude payer or payee wallets. It creates one transaction with multiple entries.
Burser allows wallet owners to delegate control of their wallets to other users who can then pay out of them.
Drupal can mail the other party to a transaction when their signature is needed to complete the transaction. Or when it is not.
Interoperability
So far I've written about this architecture as if it were a standalone system, but actually it was largely arrived at by considering how to allow currency and software designers maximum freedom while being able to log transactions between systems. The Credit Commons Protocol routes all payments through shared ledgers, which means transactions are always sent towards the trunk of a tree and back out towards the leaves. The protocol therefore only describes the relationship between a ledger and a 'trunkwards' ledger.
The main concern is that the two ledgers must match. The whole ledger does not have to match, only the one account which is mirrored on both ledgers. This is accomplished using a hashchain. The cc-php-lib provides a function for generating a hash from a transaction and the previous hash.
Another concern of interoperability is exchange rates. The protocol delegates the converting of trunkward values, and hence to management of exchange rates, to the leafward node. Since it is important to include the quantity of units in the hash, it is important to ensure there are no rounding errors.
The most complex aspect though, is that when a transaction spans several nodes, all nodes must agree about the workflow, which is to say, who can do what to each transaction. This is only possible if both end-nodes of the transaction and all the nodes in between can see the workflow. One way therefore is to bundle the workflow with the transaction (this might yet turn out to be a better idea) but what happens at the moment is that workflows are propagated up from the node where it is defined to all the children, or branches of that node. That means if Alice in Austria wants to pay Bob in Britain, she must choose a workflow which is defined at the Europe level or the world level, and the software has to help with that.
Comments