{"id":15789855,"url":"https://github.com/gettocat/consensusjs","last_synced_at":"2025-07-18T17:33:33.634Z","repository":{"id":57158356,"uuid":"192401477","full_name":"gettocat/consensusjs","owner":"gettocat","description":"Consensus module for nodejs. Centralized, PoW, PoS, dPoS, dPoW.","archived":false,"fork":false,"pushed_at":"2020-05-21T09:09:42.000Z","size":178,"stargazers_count":6,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-11T08:04:41.340Z","etag":null,"topics":["bitcoin","blockchain","consensus","ethereum","gettocat","javascript","nanocat","orwell","seeyouoncoinmarketcap"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gettocat.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-06-17T18:51:21.000Z","updated_at":"2025-03-05T17:43:43.000Z","dependencies_parsed_at":"2022-09-07T01:00:58.489Z","dependency_job_id":null,"html_url":"https://github.com/gettocat/consensusjs","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/gettocat/consensusjs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gettocat%2Fconsensusjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gettocat%2Fconsensusjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gettocat%2Fconsensusjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gettocat%2Fconsensusjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gettocat","download_url":"https://codeload.github.com/gettocat/consensusjs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gettocat%2Fconsensusjs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265801965,"owners_count":23830506,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitcoin","blockchain","consensus","ethereum","gettocat","javascript","nanocat","orwell","seeyouoncoinmarketcap"],"created_at":"2024-10-04T22:03:59.385Z","updated_at":"2025-07-18T17:33:33.610Z","avatar_url":"https://github.com/gettocat.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# consensusjs\nConsensus module for nodejs. Centralized, PoW, PoS, dPoS, dPoW.\n\n## example \nLook example in folder \"example\".\n\n## App\nThis application implements consensus algorithm work. You can reimplement classess, objects, methods for your application.\n\nHave four modules, based on app.MODULE:\n\n- Peer\n- Data\n- Validator\n- PeerManager\n- DataManager\n- RoundManager\n- Consensus\n\nThis modules is native and make consensus algorithm. Must be reimplemented, they are *abstract*. \n\n### Peer\nDefine connected node entry\n\n```js\nclass Peer extends app.MODULE {\n    constructor(data);//create new peer\n    getId(); //get peer id\n    send(peerId, msg); //must implement peer sending data algorithm to another peerId\n    relay(msg); //sending message to All connected peers\n    newMessage(message); //invoke this method when new message recived from peer\n    close(code); //invoke this method when peer disconnected or error\n    error(code, message); //invoke this method when have some error\n}\n```\n\n### Validator\nDefine validator node entry\n\n```js\nclass Validator extends app.MODULE {\n    constructor(data);//create new peer\n    getId(); //get validator public key\n    getVolume(); //must implement validator volume \n    getPriority(); //must implement priority of validator\n    updateVolume(volume); //update validator volume \n    updatePriority(priority); //update validator priority \n}\n```\n\n### PeerManager\nDefine Mapper of Peer entries. Storing, sorting, searching, etc...\n\n```js\nclass PeerMapper extends app.MODULE {\n    getPeersList();//get list of all peers\n    addPeer(peer);//add peer to list\n    removePeerById(peerId);//remove peer by id\n    removePeer(peer);//remove Peer\n}\n```\n\n### roundManager\nDefine Mapper of proof of stake round\n\n```js\nclass RoundMapper extends app.MODULE {\n    getValidatorsList();//sorting active validator list for round\n    check(peer, data);//check data for round consensus\n    checkBlockTime(peer, data);//check block time for round\n    update();//update round info, or start new round\n    decrementPriority();//decrement priority for cursor\n    incrementPriority();//increment priority for cursor\n    getRoundValidators();//get active validators\n    addValidator(publickey, priority, volume);//add validators to list\n    removeValidator(publickey);//remove validators from list\n    validatorCount();//get validator count\n    getValidator(key);//get validator instance `app.VALIDATOR`\n    getCursorId();//get active cursor for round\n    isValidator(key);//check public key for validator\n    isActiveValidator(key);//check public key for active validator\n}\n```\n\n### Data\nDefine blockchain data message (blocks)\n\n```js\nclass Data extends app.MODULE {\n    constructor(data);//create new Data\n    static IsDataMessage(message);//check message for data-message fields\n    static getIdFieldName();//get field id name from message\n    static getPrevIdFieldName();//get field name prev block id from message\n    static getTimeFieldName();//get block time field name\n    static getBitsFieldName()//get bits filed name\n    static getVersionFieldName()//get version filed name\n    getId();//get data id value (hash)\n    getVersion();//get version of block\n    getBits();//get bits value\n    getPrevId();//get prevblock hash value\n    getKey();//get public key of block (used for pos consensus, need sign coinbase block too)\n    getTime();//get timme value\n    isValid();//verify block for syntax and value validity\n    getStakeValue(height);//used for PoS. Get stake value of block-sender (public key of block)\n    isDelegateMessage();//used for delegates. Check block-sender for delegate.\n}\n```\n### DataMapper\nDefine Mapper of Data entries. Storing, sorting, searching, etc...\n\n```js\nclass DataMapper extends app.MODULE {\n\n    getDataList();//get all data\n    _addDataToBlockChain(data);//add data to list, without verify (init from local version of blockchain)\n    addData(data);//add data to list with verify (recive from network)\n    getData(id);//get data by id\n    getDataFromHeight(h);//get Data by height\n    getDataHeight(dataId);//get height by data id\n    getHeight();//get blockchain info\n    getDataSlice(a, b)//Get slice of data. From a to b, if b is not defined b = 0. b\u003ca. Top block at list[0]\n    replaceDataById(dataId, newData);//replace data by hash\n    replaceData(oldData, newData);//replace data by obj\n    getTopInfo();//get blockchain top info\n    isDataLinked(data);//Verify data-linking to chain. Check previd and search it in blockchain\n    getGenesis();//get genesis section from config\n    getSideList();//get side chain list\n    getSideData(id);//get data from sidechain\n    getOrphanList();//get orphan chain list\n    getOrphanData(id);//get data from orphan chain\n    seekBlockNetwork(id);//request block from network (for orphan block parent), MUST be implemented in your code\n    inMainChainData(id);//check block for contains in main chain\n}\n```\n\n### Consensus\nDescribe consensus algorithm. \n\n```js\nclass AbstractConsensus extends app.MODULE {\n\n    constructor(consensus_name, consensus_config_field);//consensus_name - is name for logs,consensus_config_field - config section, with params for this consensus algorithm. See more: config sections \n    getConfig(field, defaultValue);//get config section param (or all if first param is null);\n    init()//init consensus\n    applyData(peer, data);//add data from peer to consensus\n    isPeerCanSendData(peer);//In this method we can check ability of peer to send data in network\n    /**\n     *Check that data is match to consensus.\n        For centralized: match all data, sended from mainNode.\n        For delegate proof: match all data, sended from delegate\n        For PoW: match data, difficulty \u003e avgDifficultyNetwork\n        etc\n        * return true if yes, else false.\n        */\n    isDataMatch(data, peer);\n    /**\n     * In this mode enabled: only delegate node can send data to network, used for dPos and dPoW consensus.\n     */\n    isDelegateMode();\n    checkHash(hash, target);//Check that hash is valid for this target\n    inConsensusVersionRange(data);//check validity of block for version and forks\n    getForks();//gets forks config for consensus\n}\n```\n\n## App.Extending\n\nAny class in application can be redefined before start, for example we have app methods:\n\n```js\ndefinePeerClass(man);\ndefinePeerManagerClass(man);\nsetPeerManager(man);\ndefineDataClass(man);\ndefineDataManagerClass(man);\nsetDataManager(man);\ndefineConsensusClass(man);\ndefineValidatorClass(man);\ndefineRoundManagerClass(man);\nsetRoundManager(man);\n```\n\nTo redefine class you need create you own class and extends one from default modules:\n\n- app.PEER\n- app.DATA\n- app.VALIDATOR\n- app.PEERMANAGER\n- app.DATAMANAGER\n- app.ROUNDMANAGER\n- app.CONSENSUS\n\nFor example we can create new consensus algorithm with another target-hash verify:\n\n```js\nmodule.exports = function(app) {\n    class NewConsensus extends app.CONSENSUS {\n        constructor(){\n            super('New consensus', 'newconsensus_config_field')\n        }\n        checkHash(hash, target) {\n            return hash * target \u003c 5;\n        }   \n    }\n\n    return NewConsensus\n}\n```\n\nPut this code to file newcons.js, and require it:\n\n```js\n\nconst newconst = require('newcons.js');\nconst ConsensusJS = require(\"consensusjs\");\nlet config = {\n    \"newconsensus_config_field\":{\n        \"extends\":\"pow\"//read about config extending in config section\n    }\n    \"genesis\":{ //required section\n        ///...\n    }\n};\nlet app = new ConsensusJS(config);\napp.defineConsensusClass(newconst(app));\n//now you can start app with new consensus\napp.start();\n```\n\n\n## Config sections\n\nYou can create many config sections:\n```js\n\"section1\": {\n    \"param1\": \"value1\",\n    \"param2\": \"value2\"\n    //etc\n}\n```\n\nand use it in application: `app.config.section1.param1`\nAny consensus have default config section, defined in second param of constructor, for example:\n```js\nconstructor(){\n    super('New consensus', 'newconsensus_config_field')\n}\n```\n\nin this example we must have section `newconsensus_config_field` in config!\n\n### genesis\nGenesis section is required for application. Its describe data of default first block in chain.\n```js\n\"genesis\": {\n    \"id\": 'genesis hash',\n    \"prev\": -1,\n    \"bits\": 1,\n    \"time\": 0,\n    \"nonce\": 0,\n},\n```\n\n### extending sections\n\nConfig sections can be extending by existing sections:\n```js\n\"section1\": {\n    \"param1\":'value1',\n    \"param2\":'value2',\n},\n\"section2\":{\n    \"extends\":\"section1\",\n    \"param3\":\"value3\"\n}\n```\n\nIn this example section2 have next params: \n```js\n\"param1\":'value1',\n\"param2\":'value2',\n\"param3\":\"value3\"\n```\n\nExtending can be nested:\n\n```js\n\"section1\": {\n    \"param1\":'value1',\n    \"param2\":'value2',\n},\n\"section2\":{\n    \"extends\":\"section1\",\n    \"param3\":\"value3\"\n},\n\"section3\": {\n    \"extends\": \"section2\",\n    \"params5\": 1,\n}\n```\n\nit will be: \n\n```js\n\"param1\":'value1',\n\"param2\":'value2',\n\"param3\":\"value3\",\n\"params5\": 1,\n```\n\nExtending config sections is used for default consensus algorithms, see below.\n\n### default config parameters:\n\nConfig default parameters, that can be extend and rewrite defined in `app.getDefaultConfig()`:\n```js\ngetDefaultConfig() {\n    return {\n        'centralized': {\n            'mainNode': '',//public key, only this node can emit new blocks, if you need make centralized with more then 1 node - use delegateMode\n            'ignorePrevChilds': true,//ignore childs of prev block when add new block. If this param is false - add new block to side if childs of parent is more then 1\n        },\n        'pow': {\n            \"premine\": 24,//number of height - when premine will stop\n            \"blockcount\": 12, ///number of blocks in target calculation\n            \"blocktime\": 300, //time of one block in seconds\n            \"maxtarget\": 1, //min difficulty\n            \"excludeFirst\": 1, //dont use this numbers blocks in calculation of new target \n            \"diffWindow\": 120, //window of data, used for target\n            \"diffCut\": 6,\n            \"changeBranchDelay\": 0,//The number of blocks that we ignore when sidechain length is bigger then main chain,\n            \"removeOrphanCount\": 100,// the number of blocks after which we remove the old blocks from the lost ones\n            'ignorePrevChilds': true,//ignore childs of prev block when add new block. If this param is false - add new block to side if childs of parent is more then 1\n        },\n        'pos': {\n            'extends': 'pow',//extending config params from pow section\n            'shareStake': 0.1,//max share of stake value that we can decrease from target\n        },\n        'dpos': {\n            'extends': 'pos',\n            'delegateMode': true,//new block can emit only public keys from this array\n            'delegates': []//if this param is empty - we can make dynamic delegates\n        },\n        'dpow': {\n            'extends': 'pow',\n            'delegateMode': true,\n            'delegates': []//if this param is empty - we can make dynamic delegates\n        },\n        'ddpos': {\n            'extends': 'dpos',\n            'validatorCount': 60,\n            'timeout': 60,//timeout in seconds for sending block for validator. If timedout - decrement priority and set cursor to next\n            'staticDelegatesLimit': 5,//enable static delegates from config if connected validator count less then this parameter\n            'delegates': [],//if this param is empty - we can make dynamic delegates\n            \"timeout\": 60, //max block time after prev block\n            \"pause\": 20,//min block time after prev block\n        },\n        \"genesis\": { //need to be rediclared on yours config\n            \"id\": 'genesis',\n            \"prev\": -1,\n            \"bits\": 1,\n            \"time\": 0,\n            \"nonce\": 0,\n        }\n    };\n}\n```\n\n## App default consensus algorithm\n\nConsensusJs have 5 default consensus algoritm:\n\n- Centralized\n- ProofOfWorkConsensus\n- ProofOfStakeConsensus\n- DelegatedProofOfWorkConsensus\n- DelegatedProofOfStakeConsensus\n- DynamicDelegateProofOfStakeConsensus\n\nand config sections:\n- centralized\n- pow\n- pos (actually pow+pos)\n- dpow\n- dpos (actually dpos+pos)\n- ddpos\n\nYou can extend you own consensus algoritm any default algorithm, for example:\n\n```js\nclass myConsensus extends app.CONSENSUS.ProofOfStakeConsensus {\n    constructor () {\n        super(\"My first consensus\", \"myConsensus\");\n    }\n}\n```\n\nand config sections:\n```js\n\"myConsensus\":{\n    \"extends\": \"pos\"\n}\n```\n\n## Default Consensuses description\n\n### centralized\nOnly one node, defined in config - can send new blocks to chain. If `config.delegateMode` is enabled, public keys from `config.delegates` can send data too.\n\n### pow \nBlock to network can send everyone, who have hashrate and can mine blocks\n\n### pos\nExtending pow config and use mining for search new blocks, but if user have stakeValue (bigger percent of coins), he can reduse up to 10% from target and make faster hash searching. Have new method in implementation:\n\n```js\nclass ProofOfStakeConsensus extends app.CONSENSUS.ProofOfWorkConsensus {\n    ...\n    getStakeToTargetTransform(publicKey, stake, target) {\n        //make some algo, to change target, if stakeValue is not null\n        //if user have make coins - target must be lower\n        //by default just decrease diff on 10%, nut we can calculate usercoins/allcoins value, and use this param instead shareStake\n        return (stake \u003e 0) ? target * (1 - this.getConfig('shareStake', 0.1)) : target;\n    }\n    ...\n}\n```\n\n### dpow\nSame as pow, but only defined in `config.delegates` publicKeys can send new blocks to network, or messages for `data.isDelegateMessage` method returns `true`.\n\n### dpos\nSame pos and dpow implementation.\n\n### ddpos\nEssence of this consensus comes down to the Round Robin procedure. The network goes rounds of adding blocks. A general list of validators is set, and at the beginning of each round, a list of active validators for a given round is selected by sorting by the parameters of the total number of coins in delegation and the priority described below. The number of validators active for a round is set by the validatorCount parameter.\n\nAt the beginning of the round, a cursor is formed that defines the active validator, which, and only that has the right to generate a block and add it to the network at the moment. After successfully adding a block, the cursor moves to the next validator in the list of active ones until the round ends. After this, the procedure of creating a new round is repeated, by selecting new active validators and moving the cursor to position 0. In the network, you can set a pause for blocks, using the pause parameter (in seconds), at which the block will not be added to the network if the time is between the new and previous block less than that number of seconds.\n\nIf the active validator with the cursor does not manage to place the block on the network at the timeout specified by the parameter (in seconds), the cursor moves to the next validator, and it must change the information about the previous validator and add it to the network on its behalf, decreasing the priority parameter by 1.\n\nOn the contrary, if the previous validator published the block to the network at the right time and without errors, the next validator should increase the priority parameter by 1 if the parameter is less than zero.\n\nIf the total number of validators in the network is less than the staticDelegatesLimit parameter, the network switches to staticDelegates mode, i.e. only validators can publish blocks with public keys described in the delegates array parameter.\n\n## app.Events\n\nList of events: \n\n* app.config\n* app.peermanager\n* app.datamanager\n* app.roundmanager\n* app.selected_consensus\n* app.consensus.create\n* app.consensus.init\n* app.data.seek\n* app.data.chainchain\n* app.data.new\n* app.data{someDataId}\n* app.data.tx{someTxId}\n* app.data.removeOld\n  \n\n ###  app.config\n Config inited, with 1 parameter - `config`\n\n ### app.peermanager\n PeerManager loaded\n\n ### app.datamanager\n DataManager loaded\n\n ### app.roundmanager\n RoundManager loaded\n\n ### app.selected_consensus\n Consensus selected (before creation), with 1 parameter `consensus_name`\n\n ### app.consensus.create\n After creation of consensus, params:\n `data.config` config of consensus (with extended fields)\n `data.name` - name of consensus \n `data.field` - config field of consensus\n\n ### app.consensus.init\nAfter initialization of consensus, params:\n`data.id` - hash of top block after init\n`data.height` - height of storage\n\n### app.data.seek\nTry to search data in network, local storages dont have dataId (childs in orphan)\nParam: `dataId`\n\n### app.data.chainchain\nchange slice of side chain to main chain, params: \n`data.items` count of tree items\n`data.data` (app.DATA object), change from tree \n`data.oldheight` old height\n`data.newheight` new height\n`data.nowInMain` now in main (arr with dataId items, replaced to main chain)\n\n### app.data.new\n`data.dataId` id of data\n`data.chain` (main|side|orphan)\n`data.height` added only if chain = main\n\n### app.data{someDataId}\nemit this event when dataId added in main chain\n\n### app.data.tx{someTxId}\nemit this event when data with txId added in main chain\n\n### app.data.removeOld\nemited when remove old data from side/orphan \n`data.chain`  side|orphan\n`data.dataId`  data id\n`data.delta`  seconds timeout of data","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgettocat%2Fconsensusjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgettocat%2Fconsensusjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgettocat%2Fconsensusjs/lists"}