├── .gitignore ├── docs ├── fig3a_first_minerid_key_rolling.png ├── fig3b_second_minerid_key_rolling.png ├── fig5a_minerid_partial_revocation.png ├── fig4a_first_revocation_key_rolling.png ├── fig4b_second_revocation_key_rolling.png ├── mid_generator │ ├── midgen_with_datarefs.png │ ├── midgen_without_datarefs.png │ ├── midri_support_with_extensions.png │ ├── midri_support_without_extensions.png │ └── minerid-generator.md ├── fig5b_minerid_key_complete_revocation.png ├── fig2_minerid_and_revocation_key_reused.png ├── fig1_minerid_and_revocation_key_initial_setup.png ├── block_binding_technique.md ├── integration_and_deployment.md ├── open-miner-sample-integration.js └── open-miner-sample-integration-with-datarefs-support.js ├── package.json ├── extensions ├── blockinfo │ └── README.md ├── minerparams │ └── README.md └── feespec │ └── README.md ├── LICENSE ├── minerId-generator.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .history 2 | .idea 3 | node_modules 4 | -------------------------------------------------------------------------------- /docs/fig3a_first_minerid_key_rolling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/fig3a_first_minerid_key_rolling.png -------------------------------------------------------------------------------- /docs/fig3b_second_minerid_key_rolling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/fig3b_second_minerid_key_rolling.png -------------------------------------------------------------------------------- /docs/fig5a_minerid_partial_revocation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/fig5a_minerid_partial_revocation.png -------------------------------------------------------------------------------- /docs/fig4a_first_revocation_key_rolling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/fig4a_first_revocation_key_rolling.png -------------------------------------------------------------------------------- /docs/fig4b_second_revocation_key_rolling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/fig4b_second_revocation_key_rolling.png -------------------------------------------------------------------------------- /docs/mid_generator/midgen_with_datarefs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/mid_generator/midgen_with_datarefs.png -------------------------------------------------------------------------------- /docs/fig5b_minerid_key_complete_revocation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/fig5b_minerid_key_complete_revocation.png -------------------------------------------------------------------------------- /docs/mid_generator/midgen_without_datarefs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/mid_generator/midgen_without_datarefs.png -------------------------------------------------------------------------------- /docs/fig2_minerid_and_revocation_key_reused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/fig2_minerid_and_revocation_key_reused.png -------------------------------------------------------------------------------- /docs/fig1_minerid_and_revocation_key_initial_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/fig1_minerid_and_revocation_key_initial_setup.png -------------------------------------------------------------------------------- /docs/mid_generator/midri_support_with_extensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/mid_generator/midri_support_with_extensions.png -------------------------------------------------------------------------------- /docs/mid_generator/midri_support_without_extensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitcoin-sv-specs/brfc-minerid/HEAD/docs/mid_generator/midri_support_without_extensions.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "brfc-minerid", 3 | "version": "1.0.0", 4 | "description": "This draft spec is released as an RFC (request for comment) as part of the public review process. Any comments, criticisms or suggestions should be directed toward the [issues page](https://github.com/bitcoin-sv-specs/brfc-minerid/issues) on this github repository.", 5 | "main": "minerId-generator.js", 6 | "scripts": { 7 | "start": "node minerId-generator.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/bitcoin-sv-specs/brfc-minerid.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/bitcoin-sv-specs/brfc-minerid/issues" 18 | }, 19 | "homepage": "https://github.com/bitcoin-sv-specs/brfc-minerid#readme", 20 | "dependencies": { 21 | "bsv": "^1.5.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /extensions/blockinfo/README.md: -------------------------------------------------------------------------------- 1 | # MinerId extension fields 2 | 3 | | BRFC | title | authors | version | 4 | |:------------: |:-------: |:--------: |:-------: | 5 | | a224052ad433 | minerIdExt-blockInfo | nchain | 0.1 | 6 | 7 | ## Block info 8 | 9 | This extension enables the miner to give further information about the block not contained in the block header. 10 | 11 | ## Location 12 | 13 | The blockInfo object should be located under the `extensions` field of the `dynamic-CD` 14 | 15 | 16 | ```json 17 | { 18 | // basic MinerId fields 19 | 20 | "extensions": { 21 | "blockInfo": { 22 | "txCount": number, 23 | "sizeWithoutCoinbase": number 24 | } 25 | } 26 | } 27 | ``` 28 | 29 | | field | function | 30 | |------------ |------- | 31 | | `txCount` | number of transactions in current block | 32 | | `sizeWithoutCoinbase` | size of current block excluding the coinbase transaction (in `bytes`) | 33 | 34 | >**Note**: Since the data in this extension will go into the coinbase document before being signed, it is impossible to definitively know what the total size of the block will be (since Bitcoin/DER signatures are not fixed length and vary between 71 and 73 bytes). For this reason, the `sizeWithoutCoinbase` is provided instead of the full block size. Also anyone who has access to this data will already likely have access to the coinbase tx and thus can add its length to the sizeWithoutCoinbase to calculate the exact block size (as shown below). 35 | ``` 36 | blockSize = size(coinbase_tx) + sizeWithoutCoinbase 37 | ``` 38 | 39 | ## Example 40 | 41 | ```json 42 | { 43 | // basic MinerId fields 44 | 45 | "extensions": { 46 | "blockInfo": { 47 | "txCount": 1517, 48 | "sizeWithoutCoinbase": 1008368 49 | } 50 | } 51 | } 52 | ``` -------------------------------------------------------------------------------- /extensions/minerparams/README.md: -------------------------------------------------------------------------------- 1 | # MinerId extension fields 2 | 3 | | BRFC | title | authors | version | 4 | |:------------: |:-------: |:--------: |:-------: | 5 | | 1b1d980b5b72 | minerIdExt-minerParams | nChain | 0.1 | 6 | 7 | ## Miner policy and consensus configuration 8 | 9 | This extension enables the miner to broadcast their current miner policy and consensus parameters. All fields are optional and any commonly known configuration paramter can be included. By convention the name of the parameter used in the Bitcoin SV reference implementation of bitcoind is used. 10 | 11 | ## Location 12 | 13 | The minerParams object should be located under the `extensions` field of the `static-CD` 14 | 15 | 16 | ```json 17 | { 18 | // basic MinerId fields 19 | 20 | "extensions": { 21 | "minerParams": { 22 | "policy": { 23 | "blockmaxsize": number, 24 | "maxstackmemoryusagepolicy": number, 25 | }, 26 | "consensus": { 27 | "excessiveblocksize": number, 28 | "maxstackmemoryusageconsensus": number 29 | } 30 | } 31 | } 32 | } 33 | ``` 34 | 35 | | field | function | 36 | |------------ |------- | 37 | | `blockmaxsize` | current miner soft cap (in `bytes`) | 38 | | `excessiveblocksize` | current miner hard cap (in `bytes`) | 39 | | `maxstackmemoryusagepolicy` | current miner policy max_stack_memory usage (in `bytes`) | 40 | | `maxstackmemoryusageconsensus` | current miner consensus max_stack_memory usage (in `bytes`) | 41 | 42 | 43 | ## Example 44 | 45 | ```json 46 | { 47 | // basic MinerId fields 48 | 49 | "extensions": { 50 | "minerParams": { 51 | "policy": { 52 | "blockmaxsize": 512000000, 53 | "maxstackmemoryusagepolicy": 10000000, 54 | }, 55 | "consensus": { 56 | "excessiveblocksize": 1000000000, 57 | "maxstackmemoryusageconsensus": 100000000 58 | } 59 | } 60 | } 61 | } 62 | ``` -------------------------------------------------------------------------------- /extensions/feespec/README.md: -------------------------------------------------------------------------------- 1 | # MinerId extension fields 2 | 3 | | BRFC | title | authors | version | 4 | |:------------: |:-------: |:--------: |:-------: | 5 | | 62b21572ca46 | minerIdExt-feeSpec | nchain | 0.1 | 6 | 7 | ## Block info 8 | 9 | This extension enables the miner to publish default fee rates. This is informational only as the Merchant API is the mechanism for real-time querying of miners for current fee rates. 10 | 11 | >**Note**: this fee refers to the miner's default, publicly available fee rates (i.e. calling [MAPI](https://github.com/bitcoin-sv-specs/brfc-merchantapi) *without* a token or user authentication). 12 | 13 | ## Location 14 | 15 | The blockInfo object should be located under the `extensions` field of the `dynamic-CD` 16 | 17 | 18 | ```json 19 | { 20 | // basic MinerId fields 21 | 22 | "extensions": { 23 | "feeSpec": { 24 | "defaultFee": array 25 | } 26 | } 27 | } 28 | ``` 29 | 30 | | field | function | 31 | |------------ |------- | 32 | | `defaultFee` | default fee rate as specified in [brfc-misc/feespec](../../../brfc-misc/feespec/README.md) | 33 | 34 | 35 | ## Example 36 | 37 | ```json 38 | { 39 | // basic MinerId fields 40 | 41 | "extensions": { 42 | "feeSpec": { 43 | "fees": [ 44 | { 45 | "feeType": "standard", 46 | "miningFee": { 47 | "satoshis": 1, 48 | "bytes": 1 49 | }, 50 | "relayFee": { 51 | "satoshis": 1, 52 | "bytes": 10 53 | } 54 | }, 55 | { 56 | "feeType": "data", 57 | "miningFee": { 58 | "satoshis": 2, 59 | "bytes": 1000 60 | }, 61 | "relayFee": { 62 | "satoshis": 1, 63 | "bytes": 10000 64 | } 65 | } 66 | ] 67 | } 68 | } 69 | } 70 | } 71 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Open BSV License 2 | Copyright (c) 2019 Bitcoin Association 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | 1 - The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 2 - The Software, and any software that is derived from the Software or parts thereof, 14 | can only be used on the Bitcoin SV blockchains. The Bitcoin SV blockchains are defined, 15 | for purposes of this license, as the Bitcoin blockchain containing block height #556767 16 | with the hash "000000000000000001d956714215d96ffc00e0afda4cd0a96c96f8d802b1662b" and 17 | the test blockchains that are supported by the un-modified Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | 28 | 29 | Version 0.1.1 of the Bitcoin SV software, and prior versions of software upon which it was based, 30 | were licensed under the MIT License, which is included below. 31 | 32 | The MIT License (MIT) 33 | 34 | Copyright (c) 2009-2010 Satoshi Nakamoto 35 | Copyright (c) 2009-2015 Bitcoin Developers 36 | Copyright (c) 2009-2017 The Bitcoin Core developers 37 | Copyright (c) 2017 The Bitcoin ABC developers 38 | Copyright (c) 2018 Bitcoin Association 39 | 40 | Permission is hereby granted, free of charge, to any person obtaining a copy 41 | of this software and associated documentation files (the "Software"), to deal 42 | in the Software without restriction, including without limitation the rights 43 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 44 | copies of the Software, and to permit persons to whom the Software is 45 | furnished to do so, subject to the following conditions: 46 | 47 | The above copyright notice and this permission notice shall be included in 48 | all copies or substantial portions of the Software. 49 | 50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 51 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 52 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 53 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 54 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 55 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 56 | THE SOFTWARE. 57 | -------------------------------------------------------------------------------- /docs/block_binding_technique.md: -------------------------------------------------------------------------------- 1 | # Block binding security hardening feature 2 | 3 | Blockbind technique describes a robust way of making the MinerId specific to the block it's in by adding block specific data into the final MinerInfo coinbase output. However, doing this creates a causality dilemma _(chicken/egg scenario)_ since the header cannot be finalised and signed by the MinerId key without the hash of the coinbase transaction which, in turn, cannot be created until the MinerId key is finalised. The solution to that is detailed below: 4 | 5 | To get around the chicken/egg scenario described above we recalculate the Merkle tree but replace the coinbase transaction with a modified coinbase transaction _(shown below)_ which ensures that _blockBind_ signature can only be valid if contained within the block that the miner intended. 6 | 7 | ## 1. Modified MinerInfo coinbase transaction 8 | 9 | Steps to create the modified miner-info coinbase transaction: 10 | 11 | 1. Begin with a copy of the original coinbase transaction _(with coinbase1, extranonce1, extranonce2 and coinbase2 parts - see [Mining Protocol v1](https://braiins.com/stratum-v1/docs) definition)_ 12 | 2. Replace the coinbase input _scriptSig_ with an 8 byte array of zeros and ensuring that the length field proceeding the _scriptSig_ is also set to 8. This zeros _extranonce1_ and _extranonce2_ fields. 13 | 1. scriptlen: `0x08` 14 | 2. scriptSig: `0x0000000000000000` 15 | 3. Set the remaining fields of _coinbase1_ to the following values: 16 | 1. version:  `0x01000000` 17 | 2. input count: `0x01` 18 | 3. previous hash: `0x0000000000000000000000000000000000000000000000000000000000000000` 19 | 4. index: `0xffffffff` 20 | 4. Add a miner-info output script with a _minerInfoTxId_ data field: `OP_0 OP_RETURN 0x601dface 0x00 minerInfoTxId`. 21 | 22 | > Note: The choice to use an 8 byte array of zeroes is simply for compatibility with existing bitcoin libraries. Many libraries will check to ensure the coinbase input is valid and one of those rules requires the coinbase to be at least 4 bytes. 23 | 24 | ## 2. Modified merkle root 25 | 26 | Double hash the modified MinerInfo coinbase transaction to get a new transaction ID and modify the Merkle proof by replacing the original coinbase transaction ID with the ID generated. After that calculate `modifiedMerkleRoot` from Merkle branch. 27 | 28 | ## 3. blockBind 29 | 30 | It's a hash over the modified Merkle root and previous block hash concatenated field: 31 | 32 | `Hash256(concat(modifiedMerkleRoot, prevhash))` 33 | 34 | > Note: The concatenation is done on the hex encoded bytes. 35 | 36 | ## 4. blockBindSig 37 | 38 | It's a signature over the _blockBind_ field using the private key associated with the _minerId_ public key. 39 | 40 | ## 5. Location 41 | 42 | The _blockBind_ and _blockBindSig_ fields are added to the final MinerInfo coinbase output which is defined as below: 43 | 44 | `OP_0 OP_RETURN 0x601dface 0x00 minerInfoTxId blockBind blockBindSig` 45 | 46 | # Summary 47 | 48 | Thanks to this technique, the worst an attacker can do is to mine exactly the same block paying to the same _(authentic miner)_ outputs. They can modify the coinbase text but nothing else without invalidating the _blockBind_ and _blockBindSig_ fields, further disincentivising an attack as the block reward is added to the cost of the attack. 49 | -------------------------------------------------------------------------------- /docs/integration_and_deployment.md: -------------------------------------------------------------------------------- 1 | Integration and Deployment 2 | ========================== 3 | 4 | To enable the Miner ID 1.0 Protocol support it is required to integrate the Miner ID Generator and Node Software with the Mining Pool Software _(e.g., S-NOMP Software)_. 5 | 6 | ## 1. Components 7 | 8 | ### 1.1 Miner ID Generator (a.k.a MID Generator) 9 | 10 | The Generator's Web API integration with S-NOMP requires to adopt the following compulsory methods: 11 | 12 | * `GET /opreturn/:alias/:blockHeight([0-9]+)` 13 | * `GET /opreturn/:alias/isvalid` 14 | * `POST /coinbase2` 15 | 16 | #### 1.1.1 DataRefs 17 | 18 | DataRefs support is an optional miner ID feature which additionally requires to integrate: 19 | 20 | * `GET /datarefs/:alias/opreturns` 21 | * `GET /opreturn/:alias/:blockHeight([0-9]+)/:dataRefsTxId` 22 | 23 | ### 1.2 Node 24 | 25 | The Node exposes compulsory RPC methods to be used by S-NOMP: 26 | 27 | * `createminerinfotx` 28 | * `replaceminerinfotx` 29 | * `getminerinfotxid` 30 | 31 | #### 1.2.1 DataRefs 32 | 33 | To support this optional feature the Node exposes the below RPC methods to be used by S-NOMP: 34 | 35 | * `createdatareftx` 36 | * `getdatareftxid` 37 | 38 | ### 1.3 S-NOMP 39 | 40 | stn-open-miner: An example how to update the main method which directly uses the interface to configure the mining candidate job. 41 | 42 | * [open-miner-sample-integration](open-miner-sample-integration.js) 43 | * [open-miner-sample-integration-with-datarefs-support](open-miner-sample-integration-with-datarefs-support.js) 44 | 45 | ## 2. Deployment 46 | 47 | ### 2.1 Miner ID Generator 48 | 49 | Use the Generator's docker image from the Docker Hub repository or install the Generator's server from a source code _(see References point 1.b)_. 50 | 51 | ### 2.2 Node 52 | 53 | Compile and build binaries from the source code or use an official build. 54 | 55 | ## 3. Configuration 56 | 57 | ### 3.1 Miner ID Generator 58 | 59 | Create a new Miner ID reputation chain from scratch or upgrade an existing one using CLI commands _(see References point 1.a)_. 60 | 61 | ### 3.2 Node Software 62 | 63 | To allow the node to sign a miner info transaction an operator must perform an initial configuration. 64 | 65 | 1. Create a BIP-32 signing key to sign a miner info tx. 66 | 1. `bitcoin-cli makeminerinfotxsigningkey` 67 | 68 | 2. Get the miner info funding address. 69 | 1. `bitcoin-cli getminerinfotxfundingaddress` 70 | 71 | 3. Send some minimal BSV amount to the miner info funding address, e.g., using: 72 | 1. `bitcoin-cli sendtoaddress "address" "amount"` 73 | 74 | > Note: 1 Satoshi is enough because miner-info and datarefs transactions pay no fee. 75 | 76 | 4. Configure the node to use the miner info funding outpoint. 77 | 1. `bitcoin-cli setminerinfotxfundingoutpoint "txid" "n"` 78 | 79 | > Note: The correct value for the `"n"` parameter can be checked by executing the `bitcoin-cli getrawtransaction "txid"` command and then decoding the returned raw transaction using the `decoderawtransaction` RPC command - to see at which index the funding output is defined. 80 | 81 | In result, the node creates and configures _.minerinfotxsigningkey.dat_ and _minerinfotxfunding.dat_ files available under `~/.bitcoin/network_name/miner_id/Funding/` directory. 82 | 83 | Make an independent funding configuration, on each of the mining nodes, to allow the mining pool to work on the same Miner ID reputation chain. 84 | 85 | ## 4. References 86 | 87 | 1. Miner ID Generator 88 | 1. [spec](mid_generator/minerid-generator.md) 89 | 2. [source code](https://github.com/bitcoin-sv/minerid-reference) 90 | 2. Miner ID 1.0 Protocol 91 | 1. [spec](../README.md) 92 | -------------------------------------------------------------------------------- /minerId-generator.js: -------------------------------------------------------------------------------- 1 | // Distributed under the Open BSV software license, see the accompanying file LICENSE. 2 | 3 | const bsv = require('bsv') 4 | 5 | const protocolName = '601dface' 6 | const cbdVersion = '0.3' 7 | const protocolIdVersion = '00' 8 | 9 | function createMinerInfoDocument (height, minerIdPublicKey, prevMinerIdPrivKey, revocationKeyPublicKey, prevRevocationKeyPrivKey, optionalData) { 10 | let prevMinerIdPublicKey = prevMinerIdPrivKey.toPublicKey().toString() 11 | 12 | prevMinerIdPublicKey = prevMinerIdPublicKey || minerIdPublicKey 13 | const minerIdSigPayload = Buffer.concat([ 14 | Buffer.from(prevMinerIdPublicKey, 'hex'), 15 | Buffer.from(minerIdPublicKey, 'hex') 16 | ]) 17 | const hash = bsv.crypto.Hash.sha256(minerIdSigPayload) 18 | const prevMinerIdSig = bsv.crypto.ECDSA.sign(hash, prevMinerIdPrivKey).toString() 19 | 20 | let prevRevocationKeyPublicKey = prevRevocationKeyPrivKey.toPublicKey().toString() 21 | prevRevocationKeyPublicKey = prevRevocationKeyPublicKey || revocationKeyPublicKey 22 | const prevRevocationKeySigPayload = Buffer.concat([ 23 | Buffer.from(prevRevocationKeyPublicKey, 'hex'), 24 | Buffer.from(revocationKeyPublicKey, 'hex') 25 | ]) 26 | const hash2 = bsv.crypto.Hash.sha256(prevRevocationKeySigPayload) 27 | const prevRevocationKeySig = bsv.crypto.ECDSA.sign(hash2, prevRevocationKeyPrivKey).toString() 28 | 29 | const doc = { 30 | version: cbdVersion, 31 | height: height, 32 | 33 | prevMinerId: prevMinerIdPublicKey, 34 | prevMinerIdSig: prevMinerIdSig, 35 | 36 | minerId: minerIdPublicKey, 37 | 38 | prevRevocationKey: prevRevocationKeyPublicKey, 39 | prevRevocationKeySig: prevRevocationKeySig, 40 | 41 | revocationKey: revocationKeyPublicKey, 42 | } 43 | if (optionalData) { 44 | doc.minerContact = optionalData 45 | } 46 | return doc 47 | } 48 | 49 | function createMinerInfoOpReturn (height, minerIdPrivKey, prevMinerIdPrivKey, revocationKeyPrivKey, prevRevocationKeyPrivKey, mc) { 50 | const minerIdPublicKey = minerIdPrivKey.toPublicKey().toString() 51 | const revocationKeyPublicKey = revocationKeyPrivKey.toPublicKey().toString() 52 | const doc = createMinerInfoDocument(height, minerIdPublicKey, prevMinerIdPrivKey, revocationKeyPublicKey, prevRevocationKeyPrivKey, mc) 53 | 54 | const payload = JSON.stringify(doc) 55 | 56 | const hash = bsv.crypto.Hash.sha256(Buffer.from(payload)) 57 | const signature = bsv.crypto.ECDSA.sign(hash, minerIdPrivKey).toString() 58 | 59 | const opReturnScript = bsv.Script.buildSafeDataOut([protocolName, protocolIdVersion, payload, signature]).toHex() 60 | return opReturnScript 61 | } 62 | 63 | function createMinerInfoCoinbaseTxOpReturn (minerInfoTxId, blockBind, blockBindSig) { 64 | const minerInfoTxIdInLittleEndianRep = Buffer.from(minerInfoTxId, 'hex').reverse() // swap endianness before adding into the script 65 | return bsv.Script.buildSafeDataOut([protocolName, protocolIdVersion, minerInfoTxIdInLittleEndianRep, blockBind, blockBindSig]).toHex() 66 | } 67 | 68 | const height = 123 69 | const mc = { 70 | name: 'demo', 71 | email: 'demo@demo.com', 72 | merchantAPIEndPoint: 'api.demo.com' 73 | } 74 | const prevMinerIdPrivKey = new bsv.PrivateKey() 75 | const minerIdPrivKey = new bsv.PrivateKey() 76 | 77 | const prevRevocationKeyPrivKey = new bsv.PrivateKey() 78 | const revocationKeyPrivKey = new bsv.PrivateKey() 79 | 80 | const minerInfoOpReturn = createMinerInfoOpReturn(height, minerIdPrivKey, prevMinerIdPrivKey, revocationKeyPrivKey, prevRevocationKeyPrivKey, mc) 81 | console.log(`Miner-info op_return Output Script: ${minerInfoOpReturn}`) 82 | 83 | const minerInfoTxId = "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16" 84 | const blockBind = "0028944a3a436201521bafa5cf82f873c04d212d62d5811324a1fd14095a6ea2" 85 | const blockBindSig = "304402206ea641c5a1568d06572629ab46deef74b351d65e5d3112c9c24cecd896a1108c0220337ba129162c26e6aa996d1f88164566c03ee395d75a63033cb421fc432f1e7a" 86 | const midCoinbaseOpReturn = createMinerInfoCoinbaseTxOpReturn(minerInfoTxId, blockBind, blockBindSig) 87 | console.log(`Miner ID Coinbase Tx op_return Output Script: ${midCoinbaseOpReturn}`) 88 | -------------------------------------------------------------------------------- /docs/open-miner-sample-integration.js: -------------------------------------------------------------------------------- 1 | // Distributed under the Open BSV software license, see the accompanying file LICENSE. 2 | 3 | // The main function which does create a mining candidate job - with or without miner ID. 4 | 5 | // This sample implementation aims to show how to integrate the Miner ID support into mining scripts. 6 | 7 | // Note: 8 | // This function handles two miningCandidate.heigh conditions: 9 | // (a) this.currentJob.height === miningCandidate.height, and 10 | // (b) this.currentJob.height < miningCandidate.height 11 | // A situation where this.currentJob.height > miningCandidate.height is supported by the caller. 12 | 13 | // Data types defined externally and used by this function: 14 | // 15 | // export interface GetOPReturnDto { 16 | // blockHeight: string 17 | // alias: string 18 | // } 19 | // export interface PostCoinbase2Dto { 20 | // prevhash: string 21 | // merkleProof: string[] 22 | // alias: string 23 | // coinbase2: string 24 | // minerInfoTxId: string 25 | // } 26 | // class MiningCandidateJob { 27 | // private readonly coinbase: Coinbase 28 | // private readonly candidateId: string 29 | // private readonly submits: string[] = [] 30 | // private _jobId: string 31 | // private _coinbaseParts!: Buffer[] 32 | // private readonly _daemonId: string 33 | // private readonly _height!: number 34 | // private readonly _prevHash!: string 35 | // private readonly _merkleBranch!: string[] 36 | // private readonly _version!: number 37 | // private readonly _time!: number 38 | // private readonly _bits!: string 39 | // private readonly _merkleTree!: MerkleTree 40 | // private readonly _difficulty!: number 41 | // private readonly _target!: BigNum 42 | // private readonly _coinbaseValue!: number 43 | // private readonly _numOfTx: number 44 | // ... 45 | //} 46 | 47 | private async createNewJobAsync(template: MiningTemplate): Promise { 48 | const jobId = this.getNewJobId() 49 | 50 | if (this.poolConfig.coinbase.minerId.enabled && this.providerConfig.minerIdConfig !== undefined) { 51 | 52 | const miningCandidate = template.result 53 | // Request the Node to return its in-mempool miner-info transaction id 54 | // (the getminerinfotxid RPC interface is called). 55 | const getMinerInfoTxId = async (): Promise => { 56 | const minerInfoTxId = await this.nodeService.getMinerInfoTxId({ daemonId: template.instance.id })).firstOrDefault()?.result 57 | if (minerInfoTxId.length < 1) { 58 | throw new Error("getminerinfotxid rpc result, returned by the Node, is empty!") 59 | } 60 | return minerInfoTxId 61 | } 62 | // Get a miner-info op_return script from the MID Generator 63 | // (the 'GET /opreturn/:alias/:blockHeight([0-9]+)' method is called). 64 | const getMinerInfoOpReturnScript = async (height): Promise => { 65 | const opReturn: GetOPReturnDto = { 66 | blockHeight: height, 67 | alias: this.providerConfig.minerIdConfig.alias 68 | } 69 | const minerInfoOpReturnScript = await this.minerId.getOpReturnAsync(opReturn) 70 | if (minerInfoOpReturnScript.length < 1) { 71 | throw new Error("minerInfoOpReturnScript result, returned by the Generator, is empty!") 72 | } 73 | return minerInfoOpReturnScript 74 | } 75 | // Request the Node to create a miner-info transaction containing the specified miner-info script 76 | // (the createminerinfotx RPC interface is called). 77 | const createMinerInfoTx = async (minerInfoOpReturnScript) : Promise => { 78 | const minerInfoTxId = (await this.daemonManager.createMinerInfoTx({ hexdata: minerInfoOpReturnScript }, {})).first().result 79 | if (minerInfoTxId.length < 1) { 80 | throw new Error("createminerinfotx rpc result, returned by the Node, is empty!") 81 | } 82 | return minerInfoTxId 83 | } 84 | // Request the Node to replace its miner-info transaction by a new one containing the specified miner-info script 85 | // (the replaceminerinfotx RPC interface is called). 86 | const replaceMinerInfoTx = async (minerInfoOpReturnScript) : Promise => { 87 | minerInfoTxId = (await this.daemonManager.replaceMinerInfoTx({ hexdata: minerInfoOpReturnScript }, {})).first().result 88 | if (minerInfoTxId.length < 1) { 89 | throw new Error("recreateminerinfotx rpc result, returned by the Node, is empty!") 90 | } 91 | } 92 | 93 | let minerInfoTxId = {} 94 | 95 | // If the height hasn't been changed, then the correct miner-info tx should be included in the miningCandidate. 96 | if (this.currentJob.height === miningCandidate.height) { 97 | // Check if the last miner-info opreturn script has not been invalidated 98 | // (the 'GET /opreturn/:alias/isvalid' method is called). 99 | if (!(await this.minerId.getIsOpReturnValidAsync(this.providerConfig.minerIdConfig.alias))) { 100 | // Get a miner-info op_return script from the MID Generator. 101 | const minerInfoOpReturnScript = await getMinerInfoOpReturnScript(miningCandidate.height) 102 | // Request the Node to replace its miner-info transaction by a new one containing the specified miner-info script. 103 | minerInfoTxId = await replaceMinerInfoTx(minerInfoOpReturnScript) 104 | // Request the Node to return a new mining candidate with the miner-info transaction included. 105 | template = (await this.getMiningTemplate(this.jobManager.currentJob.daemonId)).first() 106 | } 107 | else { 108 | // Request the Node to return its in-mempool miner-info transaction id. 109 | minerInfoTxId = await getMinerInfoTxId() 110 | } 111 | } 112 | // The node's activeChainTip has been extended, and thus create a new miner-info tx. 113 | else 114 | { 115 | // Get a miner-info op_return script from the MID Generator. 116 | const minerInfoOpReturnScript = await getMinerInfoOpReturnScript(miningCandidate.height) 117 | // Request the Node to create a miner-info transaction containing the specified miner-info script. 118 | minerInfoTxId = await createMinerInfoTx(minerInfoOpReturnScript) 119 | // Request the Node to return a new mining candidate with the miner-info transaction included. 120 | template = (await this.getMiningTemplate(this.jobManager.currentJob.daemonId)).first() 121 | } 122 | 123 | const miningCandidateJob = new MiningCandidateJob( 124 | jobId, 125 | template.instance.id, 126 | template.result, 127 | this.minerId, 128 | this.merchantApi, 129 | this.poolAddressScript, 130 | this.poolConfig.coinbase, 131 | this.recipientManager.getRecipients(), 132 | this.extraNonce.extraNoncePlaceholder, 133 | this.diff1 134 | ) 135 | 136 | const [coinbase1, coinbase2] = miningCandidateJob.coinbaseParts 137 | 138 | // Request the Generator to update the coinbase2 part 139 | // (to add the miner ID coinbase op_return script using the 'POST /coinbase2' method). 140 | const args1: PostCoinbase2Dto = { 141 | alias: this.providerConfig.minerIdConfig.alias, 142 | minerInfoTxId: minerInfoTxId, 143 | prevhash: template.prevhash, 144 | merkleProof: template.merkleProof, 145 | coinbase2: coinbase2.toString("hex") 146 | } 147 | const res1 = await this.minerId.postCoinbase2Async(args1) 148 | if (res1.length < 1) { 149 | throw new Error("Coinbase2 result, returned by the Generator, is empty!") 150 | } 151 | const modifiedCoinbase2 = Buffer.from(res1, "hex") 152 | miningCandidateJob.coinbaseParts = [coinbase1, modifiedCoinbase2] 153 | 154 | // Return a mining candidate job with miner ID. 155 | return miningCandidateJob 156 | } 157 | // Return a mining candidate job without miner ID. 158 | return new MiningCandidateJob ( 159 | jobId, 160 | template.instance.id, 161 | template.result, 162 | this.minerId, 163 | this.merchantApi, 164 | this.poolAddressScript, 165 | this.poolConfig.coinbase, 166 | this.recipientManager.getRecipients(), 167 | this.extraNonce.extraNoncePlaceholder, 168 | this.diff1 169 | ) 170 | } 171 | -------------------------------------------------------------------------------- /docs/mid_generator/minerid-generator.md: -------------------------------------------------------------------------------- 1 | Miner ID Generator 2 | ================== 3 | 4 | The Miner ID Generator _(a.k.a MID Generator)_ is a service built around private keys specified in the Miner ID Protocol. The service creates the keys and uses them to generate signatures required by the protocol, although some signatures are expected in the miner info document infrequently. This property allows the service to adopt different built-in polices for storing private keys. 5 | 6 | 7 | **ECDSA (secp256k1) private keys created and used by the MID Generator** 8 | 9 | | Name | Availability | Storage policy | 10 | | :------------------ | :--------: | :----------------------------------- | 11 | | `minerId` | online | **Locally secure private key store**\* | 12 | | `prevMinerId` | online | **Locally secure private key store**\* | 13 | | `revocationKey` | offline | **Offline secure private key store**\*

_(e.g., HW module)_ | 14 | | `prevRevocationKey` | offline | **Offline secure private key store**\*

_(e.g., HW module)_ | 15 | 16 | > \* Used to store the key and sign messages. 17 | 18 | A reference implementation of the Miner ID document server is available at [https://github.com/bitcoin-sv/minerid-reference](https://github.com/bitcoin-sv/minerid-reference). 19 | 20 | ## 1. Operations 21 | 22 | Key operations supported by the service: 23 | 24 | 1. Create a new Miner ID reputation chain. 25 | 2. Generate private keys (both online and offline). 26 | 3. Create a miner info doc and its signature. 27 | 4. Rotate _minerId_ key. 28 | 5. Rotate _revocationKey_ key. 29 | 6. Revoke _minerId_ reputation. 30 | 7. Sign a revocation message _(used by a miner info doc and the P2P _revokemid_ network message)_. 31 | 32 | > Out-of-scope:
33 | 1. Control of lifespan of the keys _(e.g. scheduled key expiry)_.
34 | 2. Detection of compromised keys. 35 | 36 | ## 2. Interfaces 37 | 38 | ### 2.1 CLI 39 | 40 | CLI interface is expected to be used by an administrator to perform configuration, initial set up, service management and manual interactions specific to the Miner ID protocol. 41 | 42 | Administrative tasks involve: 43 | 44 | 1. Create a Miner ID reputation chain. 45 | 2. Generate private keys. 46 | 1. an online _minerId_ key. 47 | 2. an offline _revocationKey_ key. 48 | 3. Rotate a compromised _minerId_ key. 49 | 4. Rotate a compromised _revocationKey_ key. 50 | 5. Sign a revocation message and notify the node. 51 | 1. Admin specifies _minerId_ to be revoked. 52 | 53 | > Note:
54 | 1. A reference implementation will use template file _(e.g., MinerID.json)_.
55 | 2. A callback to the node is required because the node must send out the _revokemid_ network message _(the outcome of the point 5)_. 56 | 57 | #### 2.1.1 Commands 58 | 59 | | Name | Parameter | Description | 60 | | :------------------------ | :------------------------------- | :---------------------------------------------------- | 61 | | `generateminerid` | `--name foo` | Creates a new Miner ID chain with the given _foo_ name.
Generates the _minerId_ and _prevMinerId_ private keys _(minerId == prevMinerId)_.
Generates the _revocationKey_ and _prevRevocationKey_ private keys _(revocationKey == prevRevocationKey)_. | 62 | | `getcurrentminerid` | `--name foo` | Returns the current _minerId_ public key for the specified _foo_ chain. | 63 | | `rotatemineridkey` | `--name foo` | Rotates the _minerId_ private key used by the existing Miner ID _foo_ chain. | 64 | | `rotaterevocationkey` | `--name foo` | Rotates the _revocationKey_ private key used by the existing Miner ID _foo_ chain. | 65 | | `revokemineridpartially` | `--name foo --minerid minerId` | Revokes rotated Miner ID reputation chains. It cannot revoke the initial chain.
Requires to specify the _minerId_ public key to be revoked in the compromised Miner ID _foo_ chain using the [revocation message](../../README.md#revocationmessage).
Signs the revocation message for the compromised Miner ID _foo_ chain.
Notifies the node to trigger [P2P revokemid](https://github.com/bitcoin-sv-specs/protocol/blob/master/p2p/miner_id.md) network message. | 66 | | `revokemineridcompletely` | `--name foo` | Revokes all Miner ID reputation chains starting from the initial chain.
Uses the _minerId_ public key from the initial _foo_ chain to create the [revocation message](../../README.md#revocationmessage).
Signs the revocation message for the _foo_ chain.
Notifies the node to trigger [P2P revokemid](https://github.com/bitcoin-sv-specs/protocol/blob/master/p2p/miner_id.md) network message. | 67 | | `upgrademinerid` | `--name foo` | Upgrade Miner ID v0.1/v0.2 protocol data to v1.0. It updates an old _foo_ chain configuration to be compliant with the newest protocol version. | 68 | 69 | ### 2.2 RESTful Web API 70 | 71 | Adopt an existing API to interact with the mining software. 72 | 73 | ### 2.3 DataRefs 74 | 75 | DataRefs support is explained in the MID Generator [README](https://github.com/bitcoin-sv/minerid-reference/) document. 76 | 77 | ## 3. Interactions in the Block Mining Process 78 | 79 | This section specifies basic interactions in the block mining process between the Mining Software, the Miner ID Generator and the Node. 80 | 81 | **:Mining Software -> :Miner ID Generator** 82 | 83 | The Mining Software communicates with the Miner ID Generator through Web API interface to: 84 | 85 | 1. create dataRefs outpoint scripts if dataRefs configuration was enabled by an operator. 86 | 2. create a miner info document _(including the specified block height in the request)_ and its signature. 87 | 3. update coinbase2 with `miner-info-txid` and its signature. 88 | 89 | **:Mining Software -> :Full Node** 90 | 91 | The Mining Software communicates with the Full Node through RPC interface to: 92 | 93 | 1. create a dataRefs tx if it was configured to be created. 94 | 2. create a miner info tx containing a miner info document and its signature. 95 | 3. get a mining candidate. 96 | 4. submit a mining solution. 97 | 98 | The proposed solution introduces a new RPC interface required to create a miner info transaction. It also changes the existing - in the v0.2 version - Web API interface to fulfil v1.0 requirements. 99 | 100 | **Pros** 101 | 102 | 1. In this way the existing _getminingcandidate_ and _getblocktemplate_ RPCs don't have to be changed because the Miner ID Protocol is an optional feature. 103 | 2. The _createminerinfotx_ RPC should be invoked only once for the current block height _(the height of a block to be mined)_. The Mining Software doesn't have to call it again _(prior to the next getminingcandidate call)_ if the block height returned in the next mining candidate is the same. 104 | 1. The Mining Software periodically calls _getminingcandidate_ on all the nodes _(from the mining pool)_ and picks the one which contains the highest number of transactions. If the current block height has not been mined yet, then the next returned mining candidate contains the same block height value and most likely a higher number of transactions. 105 | 106 | **Cons** 107 | 108 | 1. Introducing _createminerinfotx_ adds a new extra call in the communication process which could be avoided by adding a new input argument for _getminingcandidate_ and _getblocktemplate_. 109 | 110 | The diagrams below visualise basic interactions between the components. They only show the recommended _getminingcandidate_ RPC _(which returns block header + merkleProof)_ as it is a simplified version of _getblocktemplate_ RPC. 111 | 112 | * The Fig. 1 is a simplified integration without DataRefs support 113 | * The Fig. 2 includes an optional DataRefs feature 114 | 115 | ![](midgen_without_datarefs.png) 116 | 117 | ![](midgen_with_datarefs.png) 118 | 119 | **A special case to cover by the Mining Software** 120 | 121 | If a reorg didn't occur and _getminingcandidate_ returns the next mining candidate with a new block height _(which is equal to the prev block height + 1)_, then it means that: 122 | 123 | 1. The active chain tip has been extended by a block mined by someone else _(not by the miner's mining pool)_. 124 | 2. The Mining Software must interact with other components to create a new: 125 | 1. DataRefs transaction (if it is configured to be created). 126 | 2. Miner info document and its signature. 127 | 3. Miner info transaction. 128 | 4. Miner ID Coinbase Tx. 129 | 3. It skips the last result of _getminingcandidate_ _(which doesn't contain the miner info tx)_ and calls _getminingcandidate_ again. 130 | 1. This will occur _(the fact of skipping one result which is only treated as an indicator of the tip's change)_ if the last block was mined by someone else. 131 | 132 | ## 4. Appendix (Historical Data) 133 | 134 | ### MinerId Builder RI v0.2 135 | 136 | This section describes an existing implementation compliant with the MinerID Protocol v0.2. 137 | 138 | #### Interactions in the Block Mining Process 139 | 140 | ##### Basic support without extensions 141 | 142 | * :Mining Software → :MinerID RI  143 | * :Mining Software → :Full Node 144 | 145 | The Mining Software uses two _GET_ REST API methods to get the _static-CD and sig(static-CD)_ _(GET /opreturn/:alias/:blockHeight([0-9]+) and GET /minerid/:alias/sign/:hash respectively)_. 146 | 147 | ![](midri_support_without_extensions.png) 148 | 149 | ##### Basic support with extensions 150 | 151 | * :Mining Software → :MinerID RI  152 | * :Mining Software → :Full Node 153 | 154 | The Mining Software uses the _POST /coinbase2_ REST API method to add the Miner ID output to _coinbase2_. 155 | 156 | The production script should handle the following: 157 | 158 | 1. `getInfo` _(a result of getinfo RPC)_. 159 | 2. `miningCandidate` _(a result of getminingcandidate RPC)_. 160 | 3. Gets _mAPI fees_ and includes them in the request. 161 | 162 | 1-3 data are included in the _jobData_ field and sent to the MinerID RI through the _POST /coinbase2_ Web API request. 163 | 164 | ![](midri_support_with_extensions.png) 165 | 166 | ## 5. References 167 | 168 | 1. [MinerId Generator - Reference Implementation](https://github.com/bitcoin-sv/minerid-reference) 169 | -------------------------------------------------------------------------------- /docs/open-miner-sample-integration-with-datarefs-support.js: -------------------------------------------------------------------------------- 1 | // Distributed under the Open BSV software license, see the accompanying file LICENSE. 2 | 3 | // The main function which does create a mining candidate job - with or without miner ID. 4 | 5 | // This sample implementation aims to show how to integrate the Miner ID support into mining scripts. 6 | 7 | // Note: 8 | // This function handles two miningCandidate.heigh conditions: 9 | // (a) this.currentJob.height === miningCandidate.height, and 10 | // (b) this.currentJob.height < miningCandidate.height 11 | // A situation where this.currentJob.height > miningCandidate.height is supported by the caller. 12 | 13 | // Data types defined externally and used by this function: 14 | // 15 | // export interface GetOPReturnDto { 16 | // blockHeight: string 17 | // alias: string 18 | // } 19 | // export interface PostCoinbase2Dto { 20 | // prevhash: string 21 | // merkleProof: string[] 22 | // alias: string 23 | // coinbase2: string 24 | // minerInfoTxId: string 25 | // } 26 | // export interface GetDataRefsOpReturnDto { 27 | // alias: string 28 | // } 29 | // export class MiningCandidateJob { 30 | // private readonly coinbase: Coinbase 31 | // private readonly candidateId: string 32 | // private readonly submits: string[] = [] 33 | // private _jobId: string 34 | // private _coinbaseParts!: Buffer[] 35 | // private readonly _daemonId: string 36 | // private readonly _height!: number 37 | // private readonly _prevHash!: string 38 | // private readonly _merkleBranch!: string[] 39 | // private readonly _version!: number 40 | // private readonly _time!: number 41 | // private readonly _bits!: string 42 | // private readonly _merkleTree!: MerkleTree 43 | // private readonly _difficulty!: number 44 | // private readonly _target!: BigNum 45 | // private readonly _coinbaseValue!: number 46 | // private readonly _numOfTx: number 47 | // ... 48 | // } 49 | 50 | private async createNewJobAsync(template: MiningTemplate): Promise { 51 | const jobId = this.getNewJobId() 52 | 53 | if (this.poolConfig.coinbase.minerId.enabled && this.providerConfig.minerIdConfig !== undefined) { 54 | 55 | const miningCandidate = template.result 56 | 57 | // Request the Node to return its in-mempool miner-info transaction id 58 | // (the getminerinfotxid RPC interface is called). 59 | const getMinerInfoTxId = async (): Promise => { 60 | const minerInfoTxId = await this.nodeService.getMinerInfoTxId({ daemonId: template.instance.id })).firstOrDefault()?.result 61 | if (minerInfoTxId.length < 1) { 62 | throw new Error("getminerinfotxid rpc result, returned by the Node, is empty!") 63 | } 64 | return minerInfoTxId 65 | } 66 | // Request the Node to return its in-mempool dataRefs transaction id 67 | // (the getdatareftxid RPC interface is called). 68 | const getDataRefsTxId = async (): Promise => { 69 | return await getDataRefTxId() 70 | } 71 | // Get datarefs op_return script(s) from the MID Generator 72 | // (the 'GET /datarefs/:alias/opreturns' method is called). 73 | const getDataRefsOpReturnScripts = async (): Promise => { 74 | const dataRefsOpReturns: GetDataRefsOpReturnDto = { 75 | alias: this.providerConfig.minerIdConfig.alias 76 | } 77 | return await this.minerId.getDataRefsOpReturnsAsync(dataRefsOpReturns) 78 | } 79 | // Get a miner-info op_return script from the MID Generator 80 | // (the 'GET /opreturn/:alias/:blockHeight([0-9]+)' or 'GET /opreturn/:alias/:blockHeight([0-9]+)/:dataRefsTxId' method is called). 81 | const getMinerInfoOpReturnScript = async (height, dataRefsTxId): Promise => { 82 | const opReturn: GetOPReturnDto = { 83 | blockHeight: height, 84 | alias: this.providerConfig.minerIdConfig.alias 85 | } 86 | if (dataRefsTxId !== undefined) { 87 | opReturn.dataRefsTxId = dataRefsTxId 88 | } 89 | const minerInfoOpReturnScript = await this.minerId.getOpReturnAsync(opReturn) 90 | if (minerInfoOpReturnScript.length < 1) { 91 | throw new Error("minerInfoOpReturnScript result, returned by the Generator, is empty!") 92 | } 93 | return minerInfoOpReturnScript 94 | } 95 | // Request the Node to create a datarefs transaction containing the specified datarefs script(s) 96 | // (the createdatareftx RPC interface is called). 97 | const createDataRefsTx = async (dataRefsOpReturnScripts) : Promise => { 98 | if (!(Array.isArray(dataRefsOpReturnScripts) && dataRefsOpReturnScripts.length)) { 99 | return 100 | } 101 | return (await this.daemonManager.createDataRefTx({ scriptPubKeys: dataRefsOpReturnScripts }, {})).first().result 102 | } 103 | // Request the Node to create a miner-info transaction containing the specified miner-info script 104 | // (the createminerinfotx RPC interface is called). 105 | const createMinerInfoTx = async (minerInfoOpReturnScript) : Promise => { 106 | const minerInfoTxId = (await this.daemonManager.createMinerInfoTx({ hexdata: minerInfoOpReturnScript }, {})).first().result 107 | if (minerInfoTxId.length < 1) { 108 | throw new Error("createminerinfotx rpc result, returned by the Node, is empty!") 109 | } 110 | return minerInfoTxId 111 | } 112 | // Request the Node to replace its miner-info transaction by a new one containing the specified miner-info script 113 | // (the replaceminerinfotx RPC interface is called). 114 | const replaceMinerInfoTx = async (minerInfoOpReturnScript) : Promise => { 115 | minerInfoTxId = (await this.daemonManager.replaceMinerInfoTx({ hexdata: minerInfoOpReturnScript }, {})).first().result 116 | if (minerInfoTxId.length < 1) { 117 | throw new Error("recreateminerinfotx rpc result, returned by the Node, is empty!") 118 | } 119 | } 120 | 121 | let minerInfoTxId = {} 122 | 123 | // If the height hasn't been changed, then the correct miner-info tx should be included in the miningCandidate. 124 | if (this.currentJob.height === miningCandidate.height) { 125 | // Check if the last miner-info opreturn script has not been invalidated 126 | // (the 'GET /opreturn/:alias/isvalid' method is called). 127 | if (!(await this.minerId.getIsOpReturnValidAsync(this.providerConfig.minerIdConfig.alias))) { 128 | // Request the Node to return its in-mempool datarefs transaction id. 129 | const dataRefsTxId = await getDataRefsTxId() 130 | // Get a miner-info op_return script from the MID Generator. 131 | const minerInfoOpReturnScript = await getMinerInfoOpReturnScript(miningCandidate.height, dataRefsTxId) 132 | // Request the Node to replace its miner-info transaction by a new one containing the specified miner-info script. 133 | minerInfoTxId = await replaceMinerInfoTx(minerInfoOpReturnScript) 134 | // Request the Node to return a new mining candidate with the miner-info dataRefs transactions included. 135 | template = (await this.getMiningTemplate(this.jobManager.currentJob.daemonId)).first() 136 | } 137 | else { 138 | // Request the Node to return its in-mempool miner-info transaction id. 139 | minerInfoTxId = await getMinerInfoTxId() 140 | } 141 | } 142 | // The node's activeChainTip has been extended, and thus create a new miner-info tx. 143 | else 144 | { 145 | // Get datarefs op_return script(s) from the MID Generator. 146 | const dataRefsOpReturnScripts = await getDataRefsOpReturnScripts() 147 | // Request the Node to create dataRefs transaction containing the specified script(s). 148 | const dataRefsTxId = await createDataRefsTx(dataRefsOpReturnScripts) 149 | // Get a miner-info op_return script from the MID Generator. 150 | const minerInfoOpReturnScript = await getMinerInfoOpReturnScript(miningCandidate.height, dataRefsTxId) 151 | // Request the Node to create a miner-info transaction containing the specified miner-info script. 152 | minerInfoTxId = await createMinerInfoTx(minerInfoOpReturnScript) 153 | // Request the Node to return a new mining candidate with the miner-info and dataRefs transactions included. 154 | template = (await this.getMiningTemplate(this.jobManager.currentJob.daemonId)).first() 155 | } 156 | 157 | const miningCandidateJob = new MiningCandidateJob( 158 | jobId, 159 | template.instance.id, 160 | template.result, 161 | this.minerId, 162 | this.merchantApi, 163 | this.poolAddressScript, 164 | this.poolConfig.coinbase, 165 | this.recipientManager.getRecipients(), 166 | this.extraNonce.extraNoncePlaceholder, 167 | this.diff1 168 | ) 169 | 170 | const [coinbase1, coinbase2] = miningCandidateJob.coinbaseParts 171 | 172 | // Request the Generator to update the coinbase2 part 173 | // (to add the miner ID coinbase op_return script using the 'POST /coinbase2' method). 174 | const args1: PostCoinbase2Dto = { 175 | alias: this.providerConfig.minerIdConfig.alias, 176 | minerInfoTxId: minerInfoTxId, 177 | prevhash: template.prevhash, 178 | merkleProof: template.merkleProof, 179 | coinbase2: coinbase2.toString("hex") 180 | } 181 | const res1 = await this.minerId.postCoinbase2Async(args1) 182 | if (res1.length < 1) { 183 | throw new Error("Coinbase2 result, returned by the Generator, is empty!") 184 | } 185 | const modifiedCoinbase2 = Buffer.from(res1, "hex") 186 | miningCandidateJob.coinbaseParts = [coinbase1, modifiedCoinbase2] 187 | 188 | // Return a mining candidate job with miner ID. 189 | return miningCandidateJob 190 | } 191 | // Return a mining candidate job without miner ID. 192 | return new MiningCandidateJob ( 193 | jobId, 194 | template.instance.id, 195 | template.result, 196 | this.minerId, 197 | this.merchantApi, 198 | this.poolAddressScript, 199 | this.poolConfig.coinbase, 200 | this.recipientManager.getRecipients(), 201 | this.extraNonce.extraNoncePlaceholder, 202 | this.diff1 203 | ) 204 | } 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## RFC Notice 2 | 3 | This draft specification is released as an RFC _(request for comment)_ as part of the public review process. Any comments, criticisms or suggestions should be directed toward the [issues page](https://github.com/bitcoin-sv-specs/brfc-minerid/issues) on this github repository. 4 | 5 | A reference implementation of the Miner ID document server is available at [https://github.com/bitcoin-sv/minerid-reference](https://github.com/bitcoin-sv/minerid-reference) 6 | 7 | # MinerId Specification 8 | 9 | | **BRFC** | **title** | **authors** | **version** | 10 | | ------------ | ------- | ------ | --- | 11 | | 6a2e939f8d19 | minerId | nChain | 1.0 | 12 | 13 | ## 1. Introduction 14 | 15 | Miners can identify themselves by including Miner information in transactions within the block. The relevant transactions are pointed to _(directly and indirectly)_ by the coinbase transaction of the block. 16 | 17 | The Miner ID is recorded on the BSV main chain _(a.k.a active chain)_, consequently, the Miner ID protocol data present on a forked-branch will be ignored by the processing nodes. Moreover, the Miner ID reputation is built up as a sequence of Miner ID chains recorded on the BSV main chain. The initial miner info document defines the beginning of the first Miner ID chain. The beginning of the following chains is defined by the Miner ID key rotation. The protocol does not support parallel Miner ID chains. Any such occurrence indicates a malicious activity. 18 | 19 | The protocol is cryptographically secured by two independent keys: the Miner ID and Revocation Key. The compromised miner can recover his identity if not more than one key is compromised. 20 | 21 | Miner ID is a voluntary extra service that miners can offer and is not mandatory. 22 | 23 | ### 1.1 Benefits 24 | 25 | Miner ID enables miners to build up a reputation over time that increases public confidence in their services, while at the same time protecting miners from manipulation and spoofing. A miner can also include additional information in the miner info document such as services offered, contact information, etc. Third parties can be confident that any message signed using the miner ID private key comes from that miner. It can be further used for chain analysis _(e.g. determining the number of blocks mined by a specific miner)_. 26 | 27 | ### 1.2 How to enable Miner ID 28 | 29 | The Miner ID Generator is used to generate the Miner ID output _(Miner ID public key and signature)_; the pool operator can append that output to the coinbase transaction before mining for a new block. 30 | 31 | [Miner ID Generator](docs/mid_generator/minerid-generator.md) 32 | 33 | ## 2. Changes to coinbase transaction to incorporate Miner ID 34 | 35 | ### 2.1 Coinbase transaction without Miner ID 36 | 37 | **Coinbase Transaction** 38 | 39 | | **inputs** | **outputs** | **value** | 40 | | ---------- | ---------------------- | -------- | 41 | | `coinbase` | `miner locking script` | `reward` | 42 | 43 | ### 2.2  Coinbase transaction with Miner ID 44 | 45 | #### 2.2.1 Miner Info document 46 | 47 | The table below shows the structure of the coinbase transaction, which refers to the transaction containing the miner info document and signature. 48 | 49 | **Miner ID Coinbase Transaction** 50 | 51 | | **inputs** | **outputs** | **value** | 52 | | ---------- | :------------------------------------------------------------------------------------------ | -------- | 53 | | `coinbase` | `miner locking script` | `reward` | 54 | | | `OP_0 OP_RETURN 0x601dface protocol-id-version miner-info-txid block-bind sig(block-bind)` | `0` | 55 | 56 | The table below shows the structure of the miner info transaction containing the miner info document and signature. 57 | 58 | **Miner Info Transaction** 59 | 60 | | **inputs** | **outputs** | **value** | 61 | | --- | :-------------------------------------------------------------------------- | --- | 62 | | ... | `OP_0 OP_RETURN 0x601dface protocol-id-version miner-info sig(miner-info)` | `0` | 63 | | ... | ... | ... | 64 | 65 | The block height is included in the miner info document. The miner info transaction must be included in the same block as the Miner ID coinbase transaction referencing it. 66 | 67 | Note that different data elements are encapsulated in Bitcoin PUSHDATA operation as described in this [data element framing standard](https://github.com/bitcoin-sv-specs/op_return/blob/master/01-PUSHDATA-data-element-framing.md) 68 | 69 | | **attribute** | **description** | 70 | | :-------------------- | :---------------------------------------------------------------------------------------- | 71 | | `0x601dface` | 4 byte miner ID [protocol prefix](https://github.com/bitcoin-sv-specs/op_return/blob/master/02-OP_RETURN-protocol-prefix-spec.md). | 72 | | `protocol-id-version` | 1 byte protocol prefix version, must be 0. | 73 | | `miner-info` | The miner info document. It must be included in the first output of the miner info transaction _(at the index 0)_. | 74 | | `sig(miner-info)` | The signature on `miner-info` using the private key associated with the miner ID public key defined in `miner-info`. It must be included in the first output of the miner info transaction _(at the index 0)_.
_Note: Only the data itself and NOT the PUSHDATA operations in the script are included in the signature message_. | 75 | | `miner-info-txid` | 32 byte transaction ID of the transaction containing the miner info document. | 76 | | `block-bind` | The hash over the modified Merkle root and previous block hash concatenated field, see [block binding technique](docs/block_binding_technique.md). | 77 | | `sig(block-bind)` | The signature on `block-bind` using the private key associated with the miner ID in the miner info transaction. | 78 | 79 | #### 2.2.2 Miner Info document template 80 | 81 | The miner info document is a stateless JSON document where a miner includes Miner ID information at a specified block height. 82 | 83 | ``` 84 | { 85 | "version": string, 86 | "height": number, 87 | 88 | "prevMinerId": string, 89 | "prevMinerIdSig": string, 90 | 91 | "minerId": string, 92 | 93 | "prevRevocationKey": string, 94 | "prevRevocationKeySig": string, 95 | 96 | "revocationKey": string, 97 | 98 | "revocationMessage": object, 99 | "revocationMessageSig": object, 100 | 101 | "minerContact": { 102 | "name": string, 103 | "merchantAPIEndPoint": string, 104 | "": 105 | }, 106 | 107 | "extensions": { 108 | "authconn": { 109 | "PublicIP": string, 110 | "PublicPort": number 111 | } 112 | } 113 | } 114 | ``` 115 | 116 | | **field** | **status** | **description** | 117 | | :---------------------- | :----------------------: | :---------------------------------------------------------------------------------------- | 118 | | `version` | required | Miner ID implementation version number. | 119 | | `height` | required | Block height in which Miner ID document is included. | 120 | | `prevMinerId` | required | Previous minerId public key, a 33 byte hex string. | 121 | | `prevMinerIdSig` | required | Signature on message = `HASH256(concat(prevMinerId, minerId))` using the private key associated with the prevMinerId public key, 70-72 byte hex _(the concatenation is done on the hex encoded bytes)_. | 122 | | `minerId` | required | Current Miner ID ECDSA _(secp256k1)_ public key represented in compressed form as a 33 byte hex string. | 123 | | `prevRevocationKey` | required | Previous revocationKey public key, a 33 byte hex string. | 124 | | `prevRevocationKeySig` | required | Signature on message = `HASH256(concat(prevRevocationKey, revocationKey))` using the private key associated with the prevRevocationKey public key, a 70-72 byte hex string _(the concatenation is done on the hex encoded bytes)_. | 125 | | `revocationKey` | required | Current revocation key ECDSA _(secp256k1)_ public key represented in compressed form as a 33 byte hex string. | 126 | | `revocationMessage` | conditionally required | [revocationMessage definition](#revocationmessage) | 127 | | `revocationMessageSig` | conditionally required | [revocationMessageSig definition](#revocationmessagesig) | 128 | | `minerContact` | optional | Extra miner details _(with arbitrary number of subfields, none of which are required)_. | 129 | | `extensions` | optional | Additional data defined by MinerId extension BRFCs. This field may be omitted if no extensions are in use. | 130 | 131 | ##### revocationMessage 132 | 133 | The revocation message field to be signed to certify the legitimate miner who wants to revoke past reputation. 134 | 135 | The `revocationMessage` is a JSON object containing the compromised minerId public key - in the case of complete revocation it is the _minerId_ key defined by the initial Miner ID document: 136 | ``` 137 | "revocationMessage": { 138 |     "compromised_minerId": string 139 | } 140 | ``` 141 | The `revocationMessage` field is required in the miner info document only if one of the following occurs: [Rolling the Miner ID using Revocation Key](#441-rolling-the-miner-id-using-revocation-key) or [Complete Miner ID Revocation](#442-complete-miner-id-revocation). 142 | 143 | ##### revocationMessageSig 144 | 145 | The field defines two different signatures on the `msg_hash := Hash256(compromised_minerId)` message digest. 146 | 147 | The `revocationMessageSig` is a JSON object containing two signatures: 148 | ``` 149 | "revocationMessageSig": { 150 |     "sig1": string, 151 |     "sig2": string 152 | } 153 | ``` 154 | > Note: `sig1` and `sig2` are 70-72 byte hex strings. 155 | 156 | `sig1 := Sign(msg_hash, priv_revocationKey)`, where _priv\_revocationKey_ is the private key associated with the _revocationKey_ public key. 157 | 158 | `sig2 := Sign(msg_hash, priv_minerId)`, where _priv\_minerId_ is the private key associated with the miner's: 159 | - current miner ID public key defined in the complete revocation document by the _minerId_ data field, or 160 | - previous miner ID public key defined in the partial revocation document by the _prevMinerId_ data field 161 | 162 | The revocation document is a miner info document which does define a partial or complete miner ID key revocation _(see 4.4.1 and 4.4.2 sections)_. 163 | 164 | The `revocationMessageSig` field is only required if the `revocationMessage` field is defined. 165 | 166 | ##### Extensions object 167 | 168 | | **field** | **type** | **status** | **description** | 169 | | ---------- | ------ | -------- | ------------------------------------------ | 170 | | `dataRefs` | object | optional | See section 7 | 171 | | `authconn` | object | optional | `"PublicIP": string, "PublicPort": number` | 172 | 173 | #### 2.2.4 Miner info signatures. 174 | 175 | The main signature which certifies each and every miner info document is _sig(miner-info)_. All other signatures defined by the protocol are included in the document itself, and thus coupled with the main signature. As a result, the miner ID key is always used to authorise any change in the document. 176 | 177 | #### 2.2.5 Miner ID Extensions 178 | 179 | The base MinerId protocol is intended to be extended with additional information as required. Extensions are out of scope for the base protocol and are described in additional BRFC documents. Extensions along with their BRFCs can be found in the `extensions` directory. For more info please check: 180 | 181 | * [blockinfo](extensions/blockinfo/README.md) 182 | * [feespec](extensions/feespec/README.md) 183 | * [minerparams](extensions/minerparams/README.md) 184 | 185 | Internal extension data can be added in the `extensions` field of the miner info document. Each BRFC should encapsulate its additional data within its own JSON document inside the `extension` object of the relevant miner info document. 186 | 187 | ## 3. Two-key solution to Impersonation 188 | 189 | Miners can identify themselves by including some recognisable data _(e.g. their name)_ in the coinbase transaction whenever they mine a block. However, this data can be forged and is not standardised. 190 | 191 | The miner ID provides a way of cryptographically identifying miners that cannot be forged unless the Miner ID private key is compromised _(known to a 3rd party)_. 192 | 193 | If the miner ID is compromised, a second "revocation" key can be used by the miner to force the roll of the miner ID to a new value chosen by the miner. 194 | 195 | It is vital that both keys are not compromised. If both keys are known to a 3rd party then that party can take over the miner ID chain and the legitimate owner cannot do anything about it. 196 | 197 | **ECDSA (secp256k1) keys used by the MinerID Protocol** 198 | 199 | | **Name** | **Private key**\* | **Public key** | **Description** | 200 | | :------------------ | :------------------ | :----------------- | :--------------------------------------------------------- | 201 | | `minerId` | Used to sign:
a. the miner info document
b. the `block-bind` data
c. the revocation message | Used in the miner info document. | Current MinerID key which identifies a miner. | 202 | | `prevMinerId` | Certifies _minerId_ key rotation. | Used in the miner info document. | Previous MinerID key. | 203 | | `revocationKey` | Signs the revocation message. | Used in the miner info document. | Current revocation key. | 204 | | `prevRevocationKey` | Certifies _revocationKey_ key rotation. | Used in the miner info document. | Previous revocation key. | 205 | 206 | > \* Must be secretly kept. 207 | 208 | > Note: 209 |
1. All the keys are unique. If the private key from the private/public key pair becomes compromised, then the whole key is considered compromised. 210 |
2. A Miner ID is the public key of an ECDSA keypair. The Miner ID private key is used to sign a miner info document that is included as an OP\_RETURN output of a transaction in a block; the coinbase transaction of the same block points to that transaction. 211 | 212 | ## 4. Key Management 213 | 214 | Keys can be compromised. Many security protocols require keys to be changed at regular intervals to improve security. It is a requirement that the specification supports: 215 | 216 | * planned rolling of the miner ID key or revocation key. 217 | * rolling of the miner ID key in response to a spoofed miner ID on the blockchain. 218 | * rolling of the revocation key in response to a data breach. 219 | * complete revocation of a miner ID. 220 | 221 | ### 4.1 Initial Key Setup 222 | 223 | _Use case: Create a new Miner ID reputation chain._ 224 | 225 | The first Miner ID miner info document assigns: 226 | 227 | * the same miner ID public key to _prevMinerId_ and _minerId_ fields defined in the protocol _(which marks the beginning of the MinerID reputation chain)_. 228 | * the same revocation public key to _prevRevocationKey_ and _revocationKey_ fields; it also contains _prevRevocationKeySig_ signature to confirm the possession of the corresponding revocation private key. 229 | 230 | ![](docs/fig1_minerid_and_revocation_key_initial_setup.png) 231 | 232 | > Note: The _prevMinerIdSig_ field is not defined because _prevMinerId == minerId_ and the miner info document itself is signed by _minerId_ private key. 233 | 234 | The protocol assumes that the keys initialised in the first miner info document can be reused by consecutive miner info documents until the key rolling or key revocation occurs. 235 | 236 | ![](docs/fig2_minerid_and_revocation_key_reused.png) 237 | 238 | ### 4.2 Miner ID Key Rolling 239 | 240 | _Use Case: Typically this protocol would be invoked to satisfy security protocols that require that keys be changed at regular intervals, or if it is suspected that someone has knowledge of the miner ID private key who should not have._ 241 | 242 | The protocol replaces the currently active private/public Miner ID key pair with a new one, and thus the former _minerId_ public key becomes _prevMinerId_ public key in the new miner info document. To authorise the Miner ID Key Rolling procedure the _prevMinerIdSig_ signature must be created and included in the new miner info document. This will prove that the miner possesses the corresponding private key of the public key which is being rotated. 243 | 244 | ![](docs/fig3a_first_minerid_key_rolling.png) 245 | 246 | ![](docs/fig3b_second_minerid_key_rolling.png) 247 | 248 | If the _prevMinerIdSig_ is invalid, then the Miner ID Key attached to the block or notification is regarded as invalid and processing proceeds as if the Miner ID document was not present. 249 | 250 | > Note: 251 |
1. The currently active Miner ID key is always the one indicated by the last miner info document recorded on the active chain by the genuine miner. 252 |
2. The Miner ID key can be rotated if either its lifespan has elapsed or the key has been classified as compromised. 253 |
3. The final signature over the miner info document - _sig(miner-info-doc)_ using the current _minerId_ private key - certifies the MinerID Key Rolling procedure. 254 | 255 | ### 4.3 Revocation Key Rolling 256 | 257 | _Use Case: Typically this protocol would be invoked to satisfy security protocols that require that keys be changed at regular intervals, or if it is suspected that someone has knowledge of the revocation private key who should not have._ 258 | 259 | The section describes the protocol used to roll the revocation key if there is no active attempt to impersonate the revocation key.  260 | 261 | The protocol replaces the currently active private/public revocation key pair with a new one, and thus the former _revocationKey_ public key becomes _prevRevocationKey_ public key in the new miner info document. To authorise the Revocation Key Rolling procedure the _prevRevocationKeySig_ signature must be created and included in the new miner info document. This will prove that the corresponding private key _(of the public key which is being rotated)_ is accessible by the miner. 262 | 263 | ![](docs/fig4a_first_revocation_key_rolling.png) 264 | 265 | ![](docs/fig4b_second_revocation_key_rolling.png) 266 | 267 | If the _prevRevocationKeySig_ signature is invalid, then the whole miner info document is considered as invalid. 268 | 269 | > Note: 270 |
1. The currently active revocation key is always the one indicated by the last miner info document recorded on the active chain by the genuine miner. 271 |
2. The revocation key can be rotated if either its lifespan has elapsed or the key has been classified as compromised. 272 |
3. The final signature over the miner info document - _sig(miner-info-doc)_ using the current _minerId_ private key - certifies the Revocation Key Rolling procedure. 273 | 274 | ### 4.4 Key Revocation Protocol 275 | 276 | _The Miner ID reputation chain is required to be a part of the BSV main chain, and thus any change in the Miner ID protocol data must also be recorded on the BSV main chain. This approach guarantees that new nodes will interpret the Miner ID reputation chain correctly during the IBD process._ 277 | 278 | Key Revocation is an optional 2-key solution to security issues. The _minerId_ private key is used in the day-to-day operation of miner ID. A separate revocation key-pair is kept in reserve _(possibly off-site)_ to restore compromised or potentially compromised _minerIds_. 279 | 280 | If the revocation key also becomes compromised, then the miner has effectively lost control of his on-chain identity and he will need to rebuild his identity from scratch. 281 | 282 | The revocation private key should remain as secure as possible and not be distributed unnecessarily - It is therefore not expected to be available for automated signing. The revocation key can only be defined at the same time as the first _minerId_ in the chain of _minerIds_, although it can be rotated in the future. 283 | 284 | #### 4.4.1 Rolling the Miner ID using Revocation Key 285 | 286 | _Use Case: Typically this protocol would be invoked if an attacker has generated a block successfully marked with the miner ID belonging to someone else. I.e. The attacker has successfully impersonated the miner. The miner knows exactly which minerId private key has been compromised. The revocation key has NOT been compromised._ 287 | 288 | The section describes the protocol used to roll the miner ID key using the revocation key. 289 | 290 | This type of attack requires the attacker to know the miner ID private key. 291 | 292 | The compromised miner can decide to partially revoke its reputation if he knows exactly which _minerId_ private key is compromised in his reputation chain. The partial revocation requires to define the _revocationMessage_ and _revocationMessageSig_ fields as well as to rotate the compromised _minerId_ public key in the next miner info document _(the revocation document)_. 293 | 294 | The following example illustrates how to revoke the compromised _minerId=mid4_ public key first detected in the recent _Bn+k_ block. The _Bn+k+1_ block contains the miner info revocation document which defines _minerId_ key rotation of the compromised key, as well as revokes miner's compromised reputation starting from the _Bn+j_ block by using _revocationMessage_ and _revocationMessageSig_. 295 | 296 | ![](docs/fig5a_minerid_partial_revocation.png) 297 | 298 | > Note: 299 |
1. The partial revocation only applies to the Miner ID reputation recorded on the active chain. 300 |
2. Only the current _revocationKey_ private key can be used to sign _revocationMessage_. The old rotated revocation keys are invalid. In Fig. 5a the current revocation key is _rkey1_. 301 |
3. The revocation message is also being signed by the miner ID private key. In Fig. 5a the private key associated with the current compromised _mid4_ public key is the right one to use _(the mid5 key will become the current uncompromised miner ID key once the revocation document is successfully recorded on the active chain)_. 302 |
4. The final signature over the miner info revocation document - _sig(miner-info-doc)_ - is created using the new _minerId=mid5_ private key and included in the miner info transaction belonging to the _Bn+k+1_ block. 303 | 304 | #### 4.4.2 Complete Miner ID Revocation 305 | 306 | _Use case: Typically this protocol is invoked when the miner ID is compromised and the policy is to revoke the entire miner ID chain and start a new chain. The miner is uncertain which minerId private key has been compromised in the chain, and thus wants to invalidate the entire chain to prevent the miner's data to be reused by an attacker. The revocation key has NOT been compromised._ 307 | 308 | The section describes the protocol used to completely revoke the entire Miner ID reputation. 309 | 310 | In order to completely revoke the past Miner ID reputation, the miner needs to include the first minerId public key (defined by the initial Miner ID document) in the _revocationMessage_ field and create _revocationMessageSig_ signature. The Miner ID or Revocation Key Rolling doesn't occur in the revocation document. The _prevMinerId_ field is set to the same value as the _minerId_ field. The revocation process is considered as completed when the revocation document is recorded on the active chain. After that, the legitimate miner must setup a new identity from scratch. 311 | 312 | The Fig. 5b example shows _Bn+k+1_ block containing miner info document where complete revocation is defined. After that the miner must to recreate its reputation chain from scratch - what is shown by _Bn+k+2_ block. 313 | 314 | ![](docs/fig5b_minerid_key_complete_revocation.png) 315 | 316 | > Note: 317 |
1. The complete revocation only applies to the Miner ID reputation recorded on the active chain. 318 |
2. Only the current _revocationKey_ private key can be used to sign _revocationMessage_. The old rotated revocation keys are invalid. In Fig. 5b the current revocation key is _rkey1_. 319 |
3. The current revocation keys can be reused by the miner info document defined in the _Bn+k+2_ block _(the first block of the new identity)_. 320 |
4. The revocation message is also being signed by the miner ID private key. In Fig. 5b the private key associated with the current compromised _mid4_ public key is the right one to use. 321 |
5. The final signature over the miner info revocation document - _sig(miner-info-doc)_ - is created using the current _minerId=mid4_ private key and included in the miner info transaction belonging to the _Bn+k+1_ block. 322 | 323 | ## 5. P2P announcements 324 | 325 | _Use case: A PoW-independent notification sent by a miner to inform its connection peers about the compromised minerId key. The announcement refers to the Miner ID blocks recorded on the active chain._ 326 | 327 | To allow instant revocation of the compromised _minerId_ public key the protocol introduces a new P2P _revokemid_ message. It is not expect that the whole network is reachable via P2P messages, and thus the P2P message should be interpreted as an early announcement of the compromised _minerId_ public key. If the given node gets a valid revocation message, then it should invalidate the compromised _minerId_ public key in its own Miner ID database. 328 | 329 | [P2P revokemid message](https://github.com/bitcoin-sv-specs/protocol/blob/master/p2p/miner_id.md) 330 | 331 | ## 6. DataRefs 332 | 333 | The `dataRefs` field enables miner info documents to include data contained in other transactions. The miner info document will likely be widely requested by users along with block headers in the future. As such it is in the miner's interests to keep the miner info relatively small. A data reference allows data to be stored in other transactions and consolidated into a miner info document. 334 | 335 | The reference points to another transaction output that is expected to contain a JSON object. The contents of that JSON object should be merged into the `extensions` field. Note that the signatures are over the original miner info document containing the dataRef object and do not include the data itself. A hash of the data is included through the `txid`. 336 | 337 | The referenced transaction need not necessarily be included in the same block, allowing old data to be reused. 338 | 339 | > Note: We do not sign the additional data as the transaction id serves as a binding in the signature. The data cannot be changed without rendering the transaction ID invalid. 340 | 341 | ### Template 342 | 343 | The `dataRefs` object should be located in the `extensions` field of the miner info document. 344 | 345 | ``` 346 | "extensions": { 347 | "dataRefs": { 348 | "refs": [ 349 | { 350 | "brfcIds": string[], 351 | "txid": string, 352 | "vout": number, 353 | "compress": string 354 | }, 355 | { 356 | "brfcIds": string[], 357 | "txid": string, 358 | "vout": number, 359 | "compress": string 360 | } 361 | ] 362 | } 363 | } 364 | ``` 365 | 366 | #### `dataRefs` object: 367 | 368 | | **field** | **description** | 369 | | -------- | :---------------------------------------------------------------------------------------- | 370 | | `dataRefs` | Extension name | 371 | | `refs` | Array of reference objects with fields: `brfcIds`, `txid`, `vout`, and `compress` _(optional)_ that each refer to a specific transaction output _(TXO)_ | 372 | 373 | #### `refs` object: 374 | 375 | | **field** | **description** | 376 | | -------------------- | :---------------------------------------------------------------------------------------- | 377 | | `brfcIds` | An array of BRFC ID strings indicating which extensions have data in the remote reference. This can be used to decide if the data is interesting to the user or not. | 378 | | `txid` | Transaction ID of the transaction containing additional miner info document data. | 379 | | `vout` | Vout index of the output containing additional miner info document data. | 380 | | `compress` _(optional)_ | If present, specifies a compression algorithm used to uncompress the target data blob. `gzip` is specified initially. | 381 | 382 | ### Example 383 | 384 | #### `dataRefs` fields: 385 | 386 | ``` 387 | "extensions": { 388 | "dataRefs": { 389 | "refs": [ 390 | { 391 | "brfcIds": ["62b21572ca46", "a224052ad433"], 392 | "txid": "c6e68a930db53b804b6cbc51d4582856079ce075cc305975f7d8f95755068267", 393 | "vout": 2, 394 | "compress": "gzip" 395 | }, 396 | { 397 | "brfcIds": ["b8930c2bbf5d", "1b1d980b5b72"], 398 | "txid": "1bc783a5f7a0e5ef2016caf52cfeabb4b1f43039d57bc07144a5de12382deaf5", 399 | "vout": 0, 400 | "compress": "gzip" 401 | } 402 | ] 403 | } 404 | } 405 | ``` 406 | 407 | ### Referenced output script: 408 | 409 | The transaction output referenced by the `txid` and `vout` fields of each `refs` object in the miner info documents should contain an output script at index 0. If there is more than one referenced output script to include, then each of them must be placed separately in the next consecutive output. 410 | 411 | The format of the referenced output script is: 412 | 413 | > `OP_0 OP_RETURN 0x601dface protocol-id-version OP_PUSHDATA json` 414 | 415 | | **field** | **description** | 416 | | ----- | :---------------------------------------------------------------------------------------- | 417 | | `json` | The brfcId field name should conform to [BRFC ID Assignment](https://bsvalias.org/01-02-brfc-id-assignment.html).
If the dataRef specifies a `compress` field this data may be compressed.

Example:
```{ "brfcId1": { ... }, ... }``` | 418 | 419 | ### Consolidated miner info document: 420 | 421 | ``` 422 | "extensions": { 423 | "dataRefs": { 424 | "refs": [{ 425 | "brfcIds": ["62b21572ca46", "a224052ad433"], 426 | "txid": "c6e68a930db53b804b6cbc51d4582856079ce075cc305975f7d8f95755068267", 427 | "vout": 2, 428 | "compress": "gzip" 429 | }, 430 | { 431 | "brfcIds": ["b8930c2bbf5d", "1b1d980b5b72"], 432 | "txid": "1bc783a5f7a0e5ef2016caf52cfeabb4b1f43039d57bc07144a5de12382deaf5", 433 | "vout": 0, 434 | "compress": "gzip" 435 | } 436 | ], 437 | 438 | "data": { 439 | "62b21572ca46": { 440 | // data for BRFC: 62b21572ca46 441 | }, 442 | "a224052ad433": { 443 | // data for BRFC: a224052ad433 444 | }, 445 | "b8930c2bbf5d": { 446 | // data for BRFC: b8930c2bbf5d 447 | }, 448 | "1b1d980b5b72": { 449 | // data for BRFC: 1b1d980b5b72 450 | } 451 | } 452 | } 453 | } 454 | ``` 455 | > Note: The `"data"` section contains extension data that are defined in the referred to TXOs. 456 | 457 | ### 6.1 Dataref transaction retrieval 458 | 459 | [P2P getdata message extension](https://github.com/bitcoin-sv-specs/protocol/blob/master/p2p/miner_id.md) 460 | 461 | ## 7. Enriched header changes 462 | 463 | [P2P messages sendhdrsen / gethdrsen / hdrsen](https://github.com/bitcoin-sv-specs/protocol/blob/master/p2p/miner_id_headers.md) 464 | 465 | ## 8. Appendix 466 | 467 | ### 8.1 Sample Miner ID implementation 468 | 469 | |[JavaScript](minerId-generator.js)| 470 | 471 | ``` 472 | npm install 473 | npm start 474 | ``` 475 | 476 | ### 8.2 Miner ID Integration & Deployment Instructions 477 | 478 | [Integration and Deployment](docs/integration_and_deployment.md) 479 | 480 | ## 9. Changelog 481 | 482 | | **version** | **description** | 483 | | ------- | :---------------------------------------------------------------------------------------- | 484 | | 0.1 | Initial specification. | 485 | | 0.2 | Change encoding of `prevMinerIdSig` _(use hex encoding instead of default utf8)_. | 486 | | 1.0 | Replace the VCTX transaction by the Revocation Keys. Static and dynamic coinbase documents replaced with miner info document. Coinbase transaction now contains a reference to the miner info document rather than containing the miner info document. Protocol specifier updated.  | 487 | --------------------------------------------------------------------------------