Authors | Henry Tsai |
Revision | 2022-04-04 |
The main value-proposition of ION is its ability to batch tens of thousands of DID/DPKI operations in a single Bitcoin transaction, thereby drastically increase throughput capacity of the network.
Every ION DID has a state that is represented as a DID Document when resolved. The owner can perform the following operations to change the DID state:
-
Create
-
Update
Used to update the DID state. e.g. change service endpoints or application-layer keys.
-
Recover
A recovery is used to reset the DID to the new intended state. All state changes due to prior update operations will be overwritten.
-
Deactivate
Deactivation is irreversible. Once a DID is deactivated, the DID will not resolve to a DID Document.
-
An ION transaction references up to 10,000 ION operations.
-
An ION transaction is a Bitcoin transaction with ION specific metadata written as an
OP_RETURN
output. -
The ION specific metadata written in each transaction on bitcoin is of the format:
ion:<operation_count>.<ipfs_cid>
operation_count
: The number of operations batched in the transaction. An ION node will only accept create, recover & deactivate operations as a grouping as long as their total count is less or equal to the declared number. The remaining available count is then used to check against the update operations and be accepted or rejected as a grouping.ipfs_cid
: The IPFS CID of the Core Index File that contains and references the data regarding all the ION operations batched in this transaction.
- An update key is used to perform an update operation.
- A recovery key is used to perform a recover or deactivate operation.
- NEVER handout your recovery key.
- Every time an update or a recover operation is performed, the corresponding update or recovery key is required to be regenerated. More detail can be found in Commit-Reveal Scheme.
- ION currently supports only SECP256K1 for update and recovery key.
Given the permissionless nature of ION, in order for each ION node to filter out invalid operations pertains to a DID during resolution, a hash-based commit-reveal is used:
-
Every ION operation must declare (aka. commit) the hash of a secret value so that only if the right secret value (aka. reveal) is given in the subsequent operation, would the operation be considered.
-
This scheme allows resolution logic to filter out invalid operations with a simple hash check without needing to perform signature validation.
-
Since a new secret value is required to be generated kept for every operation, ION made the design choice of deriving the secret value directly from the update and recovery key, so that wallet implementor do not need to keep yet another two secret strings (one for update operations, the other for recover or deactivate operation).
-
The precise specification on commit-reveal value generation can be found in the Sidetree spec.
-
The computation (resolution) of a DID's state is done by sequentially applying changes in the logical operation chain formed by the commit-reveal scheme .
-
The operations in the logical operation chain does not have to follow chronological order. As in, a succeeding/child operation can be anchored on the blockchain before its preceding/parent operation. This behavior allows for rapid DID updates, albeit expected to be uncommon.
-
If two operations with the same reveal-value and valid signatures are observed, the operation anchored earlier is chosen.
In order to avoid spammers writing the maximum possible batch size for every single ION transaction, a proof-of-work based algorithm was originally considered. Eventually an algorithm that enforces a minimum transaction fee per-operation was chosen, aka proof-of-fee.
-
This per-operation transaction fee is based on the normalized Bitcoin transaction fee at the time of anchoring calculated from a look-back period of
100
blocks (feeLookBackWindowInBlocks
parameter in versioned ION Bitcoin config). -
The Bitcoin transaction fee normalization algorithm can be found in
NormalizedFeeCalculator.calculateNormalizedFee()
in code. -
The per-operation fee is a fraction of the normalized Bitcoin transaction fee. This is determined by the normalized transaction fee to per operation fee multiplier (
normalizedFeeToPerOperationFeeMultiplier
parameter in versioned ION Core config). This value is currently set to0.001
(or1/1000
th), which means that the ION node allows you to batch 1000 operations at the same estimated cost of one bitcoin transaction.
To prevent ION transactions from overloading the capacity of Bitcoin network, an throttling algorithm is implemented such that only a finite number of ION transactions and operations per block will be accepted by ION.
-
The maximum allowed ION transactions per block is currently
300
. -
The maximum allowed ION operations per block is currently
600,000
, this equates to60
ION transactions if all transactions are written with maximum batch size. -
ION will only accept at most
1
ION transaction per writer (Bitcoin address). In the case that multiple transactions were written by the same block, the transaction with lowest index in the block will be kept for selection/throttling, the rest is discarded. -
Transactions are sorted and selected by highest fee paid. If fee paid is the same between multiple transactions, lower indexed transaction is selected first.
-
The algorithm implementation can be found in
TransactionSelector
class in code.
In addition to proof-of-fee, value locking is introduced to further prevent spam. In essence, you need to be willing to set aside some money for about a month in order to write bigger batches or operations.
-
Some bitcoins need to be locked for
4500
blocks using a Bitcoin time locked transaction prior to writing ION transaction containing more than100
operations. -
Bitcoin's relative time lock is used for value locking.
-
The larger the batch, the more bitcoins need to be locked.
-
The writer can write transactions with batch size that the value lock allows during the time lock window.
-
The writer ID is the public key hash of the Bitcoin address that holds the bitcoins being spent.
-
The writer ID must match the lock owner, where the owner is the public key hash of the bitcoin address receiver of the locked amount upon expiry. (See
resolveLockIdentifierAndThrowOnError()
in code). Note that owner of the lock is the receiver of the bitcoins, not the sender, since logically, it is the receiver's bitcoins that are locked once the time lock transaction is written. -
Both transaction hash and the value lock redeem script is written as the "value lock ID" in the IPFS Core Index file for value lock verification. However technically only transaction hash is needed. The value lock redeem script is there only due to the original implementation using absolute time lock, this part of the code was not removed when we switched to relative time lock.
-
The allowed batch size is calculated based on the block the time lock is written in, it remains constant throughout the lock window.
The formula to calculate the required lock amount can be calculated using:
requiredLockedBtc = numberOfOpsPerTransactionDesired x estimatedNormalizedFee x normalizedFeeToPerOperationFeeMultiplier x valueTimeLockAmountMultiplier
where the following are constants:
normalizedFeeToPerOperationFeeMultiplier = 0.001
valueTimeLockAmountMultiplier = 60000
For instance, if you would like to write batches of 1000-operation transactions, substituting constant values and estimated normalized fee of 0.000011
you'll get:
1000 x 0.000011 x 0.001 x 60000 = 0.66 BTC
This means you will need to lock 0.66 BTC in order for transactions of 1000 operations to be accepted by ION.
-
did:ion:<did-suffix>:<initial-state>
-
The
did-suffix
is derived frominitial-state
. -
Long-Form DIDs MUST be resolved by an ION node in case that the DID state has changed from the initial-state.
-
Intended for immediate use upon generation of an ION DID to overcome the delay in anchoring on Bitcoin blockchain.
-
The delay in anchoring could be intentional or technical.
-
Explained in detail in Sidetree spec.
Late publishing is the act of:
- A DID owner (or the valid key holder) anchoring two or more operations with the same reveal-value on the blockchain.
- Intentionally withhold the layer-2 data for the operation(s) with an earlier anchor time.
- Publishes the withheld layer-2 data at a later time.
Observe the fork of the operation chain and the switch from the operation with the later anchor time being chosen by resolution logic to the operation with the earlier anchor time upon the late-publish.
- Transactions with missing layer-2 data are called unresolvable transactions.
- Unresolvable transactions are retried for reprocessing with an exponential back-off on each ION node.
- Late publishing is an uninteresting attack if done by DID owner.
- In a custodial setting, the malicious custodian can prepare and perform a late publish to seize control of a DID.
- As a rule-of-thumb, NEVER hand the recovery key to anyone, because anyone with your recovery key can prepare and perform a late-publish.
- Assuming recovery key is kept private as intended, it is safe to hand the update key to a custodian.
graph TD
subgraph ION Bitcoin Service
ION-Bitcoin-REST-API
Bitcoin-Processor
ION-Bitcoin-MongoDB
Lock-Monitor
Spending-Monitor
end
subgraph ION Core Service
ION-Core-REST-API
Batch-Writer
Observer
Resolver
Request-Queue
ION-Core-MongoDB
end
subgraph IPFS Node
go-IPFS
end
ION-Core-REST-API --> Resolver
ION-Core-REST-API --> Request-Queue
Batch-Writer --> Request-Queue
Batch-Writer --> ION-Bitcoin-REST-API
Observer --> ION-Bitcoin-REST-API
Observer --> ION-Core-MongoDB
Resolver --> ION-Core-MongoDB
Observer --> go-IPFS
Batch-Writer --> go-IPFS
ION-Bitcoin-REST-API --> Bitcoin-Processor
Bitcoin-Processor --> ION-Bitcoin-MongoDB
Bitcoin-Processor --> Bitcoin-Core
Lock-Monitor --> Bitcoin-Core
- Reads Bitcoin transactions and processes ION transactions.
- Deferred processing operations until resolution time.
-
Writes ION transactions to Bitcoin network.
-
Gets fee and lock information from ION Bitcoin service to decide how many operations can be written in a transaction.
-
The Bitcoin miners at any given time may demand more or less transaction fee the the total per-operation fee required by ION. The batch writer will spend the lager amount of the two to ensure reliable anchoring of the ION transaction.
-
Waits for confirmation.
- For DID resolution and posting operation requests.
- Loads versioned components under the
versions
folder based on block height.
- Transforms internal ION DID state to a standardized DID Document.
- Handles concurrent download of IPFS files.
- Part of Observer.
- Main component.
- Handles inbound requests from ION Core Service.
- Reads Bitcoin block files directly for faster parsing of ION transactions.
- Process the block files in reverse block height order.
- Monitors spending, prevents write when specified limit is met.
- Responsible for monitoring the value lock such that the ION Core Batch Writer is able to write ION transactions with the desired batch size.
- An interface that allows plugin of different Bitcoin wallet implementation.
- Default implementation uses Bitcoin Core.
- ION Core interfaces with IPFS through go-IPFS RPC API.