Skip to content

Commit

Permalink
version 0.0.4
Browse files Browse the repository at this point in the history
Dynamic delegated proof of stake consensus added with Round Robin procedure
see more in CHANGELOG.md
  • Loading branch information
alex committed Mar 6, 2020
1 parent eaf2038 commit e0d4a1d
Show file tree
Hide file tree
Showing 13 changed files with 917 additions and 13 deletions.
56 changes: 56 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,59 @@
## 0.0.4
### Description
New algorithm dynamic delegate Proof of stake (dPoS)

### Consensus
`app.CONSENSUS.DynamicDelegateProofOfStakeConsensus`
Essence 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.

At 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.

If 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.

On 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.

If 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.

### Config
Added default consensus config ddpos, for extending.
Added parameters for ddpos:
`section.validatorCount` - count of used validators in round
`section.staticDelegatesLimit` - use static version of dpos if count of validators less than this value (static version use `section.delegates` array of public keys).
`section.timeout` - if cursor validator (active validator in round) not sent block at `prevblock.time + section.timeout` - change cursor for next validator and send decrementPriority for previous validator.
`section.pause` - minimum pause between blocks.

### Objects
Added new object `app.roundManager` - create and update round of dpos. Managing validators and cursor (roundRobit procedure).
Added new object `app.VALIDATOR` - validator instance

### Methods
`app.defineRoundManagerClass(cls)` - define roundManager Class
`app.setRoundManager(obj)` - set roundManager class
`app.defineValidatorClass(cls)` - define validator Class
`app.setRoundManager(obj)` - set roundManager class
`app.merkle(list)` - create merkle tree (btc style) for list
`consensus.addValidator(publickey, priority, volume)` - add validator to list, this list sorting to active validators for each round.

`validator.getId()`
`validator.getVolume()`
`validator.getPriority()`
`validator.updateVolume(volume)`
`validator.updatePriority(priotiry)`

`roundManager.getRoundValidators()` - get validators for round (sort by volume and priority)
`roundManager.check(peer, data)` - check data for consensus
`roundManager.checkBlockTime(peer, data)` - check time of block for consensus
`roundManager.addValidator(publickey, priority, volume)` - add validator to list
`roundManager.removeValidator(publickey)` - remove validator from list
`roundManager.validatorCount()` - count of validators
`roundManager.getValidator(key)` - validator instance (`app.VALIDATOR`)
`roundManager.getCursorId()` - index of cursor from `0` to `count of active validators`
`roundManager.isValidator(key)` - key is validator
`roundManager.isActiveValidator(key)` - key is active validator

### Events
`app.roundmanager` emit when roundManager inited.

## 0.0.3
### Description
Hard and soft forks implementation. Use version of block to check fork. Can not check orphan and side chains.
Expand Down
64 changes: 61 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ Have four modules, based on app.MODULE:

- Peer
- Data
- Validator
- PeerManager
- DataManager
- RoundManager
- Consensus

This modules is native and make consensus algorithm. Must be reimplemented, they are *abstract*.
Expand All @@ -32,6 +34,20 @@ class Peer extends app.MODULE {
}
```
### Validator
Define validator node entry
```js
class Validator extends app.MODULE {
constructor(data);//create new peer
getId(); //get validator public key
getVolume(); //must implement validator volume
getPriority(); //must implement priority of validator
updateVolume(volume); //update validator volume
updatePriority(priority); //update validator priority
}
```
### PeerManager
Define Mapper of Peer entries. Storing, sorting, searching, etc...
Expand All @@ -44,6 +60,28 @@ class PeerMapper extends app.MODULE {
}
```
### roundManager
Define Mapper of proof of stake round
```js
class RoundMapper extends app.MODULE {
getValidatorsList();//sorting active validator list for round
check(peer, data);//check data for round consensus
checkBlockTime(peer, data);//check block time for round
update();//update round info, or start new round
decrementPriority();//decrement priority for cursor
incrementPriority();//increment priority for cursor
getRoundValidators();//get active validators
addValidator(publickey, priority, volume);//add validators to list
removeValidator(publickey);//remove validators from list
validatorCount();//get validator count
getValidator(key);//get validator instance `app.VALIDATOR`
getCursorId();//get active cursor for round
isValidator(key);//check public key for validator
isActiveValidator(key);//check public key for active validator
}
```
### Data
Define blockchain data message (blocks)
Expand Down Expand Up @@ -137,14 +175,19 @@ defineDataClass(man);
defineDataManagerClass(man);
setDataManager(man);
defineConsensusClass(man);
defineValidatorClass(man);
defineRoundManagerClass(man);
setRoundManager(man);
```
To redefine class you need create you own class and extends one from default modules:
- app.PEER
- app.DATA
- app.VALIDATOR
- app.PEERMANAGER
- app.DATAMANAGER
- app.ROUNDMANAGER
- app.CONSENSUS
For example we can create new consensus algorithm with another target-hash verify:
Expand Down Expand Up @@ -303,6 +346,15 @@ getDefaultConfig() {
'delegateMode': true,
'delegates': []//if this param is empty - we can make dynamic delegates
},
'ddpos': {
'extends': 'dpos',
'validatorCount': 60,
'timeout': 60,//timeout in seconds for sending block for validator. If timedout - decrement priority and set cursor to next
'staticDelegatesLimit': 5,//enable static delegates from config if connected validator count less then this parameter
'delegates': [],//if this param is empty - we can make dynamic delegates
"timeout": 60, //max block time after prev block
"pause": 20,//min block time after prev block
},
"genesis": { //need to be rediclared on yours config
"id": 'genesis',
"prev": -1,
Expand All @@ -323,13 +375,15 @@ ConsensusJs have 5 default consensus algoritm:
- ProofOfStakeConsensus
- DelegatedProofOfWorkConsensus
- DelegatedProofOfStakeConsensus
- DynamicDelegateProofOfStakeConsensus
and config sections:
- centralized
- pow
- pow
- pos (actually pow+pos)
- dpow
- dpos
- dpos (actually dpos+pos)
- ddpos
You can extend you own consensus algoritm any default algorithm, for example:
Expand Down Expand Up @@ -365,7 +419,7 @@ class ProofOfStakeConsensus extends app.CONSENSUS.ProofOfWorkConsensus {
getStakeToTargetTransform(publicKey, stake, target) {
//make some algo, to change target, if stakeValue is not null
//if user have make coins - target must be lower
//by default kust decrease diff on 10%, nut we can calculate usercoins/allcoins value, and use this param instead shareStake
//by default just decrease diff on 10%, nut we can calculate usercoins/allcoins value, and use this param instead shareStake
return (stake > 0) ? target * (1 - this.getConfig('shareStake', 0.1)) : target;
}
...
Expand All @@ -387,6 +441,7 @@ List of events:
* app.config
* app.peermanager
* app.datamanager
* app.roundmanager
* app.selected_consensus
* app.consensus.create
* app.consensus.init
Expand All @@ -407,6 +462,9 @@ List of events:
### app.datamanager
DataManager loaded
### app.roundmanager
RoundManager loaded
### app.selected_consensus
Consensus selected (before creation), with 1 parameter `consensus_name`
Expand Down
75 changes: 75 additions & 0 deletions algo/dynamic-dpos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const BN = require('bn.js');

module.exports = function (app) {

class dynamicDelegateProofOfStakeConsensus extends app.CONSENSUS {

constructor(consensus_name, consensus_config_field) {
if (!consensus_config_field)
super('Delegated Proof of Stake', 'ddpos');
else
super(consensus_name, consensus_config_field);
}
/**
* In this method we can check ability of peer to send data in network
* return true is yes, else false.
*/
isPeerCanSendData(peer) {
//one of validator list
//this peer have cursor
if (app.dataManager.getHeight() == -1)
return true;//genesis
return this.isDelegateMode() ? this.getConfig('delegates').indexOf(peer.getId()) != -1 : app.roundManager.isActiveValidator(peer.getId());
}
/**
*Check that data is match to consensus.
For centralized: match all data, sended from mainNode.
For delegate proof: match all data, sended from delegate
For PoW: match data, difficulty > avgDifficultyNetwork
etc
* return true if yes, else false.
*/
isDataMatch(data, peer) {
//check data for consensus
if (app.dataManager.getHeight() == -1 && data.getId() == app.config.genesis.id)
return true;
if (this.isDelegateMode()) {
return this.getConfig('delegates').indexOf(data.getKey()) != -1
} else {
return app.roundManager.isActiveValidator(data.getKey());
}
}
/**
* In this mode only delegate node can send data to network
*/
isDelegateMode() {
return app.roundManager.validatorCount() < this.getConfig('staticDelegatesLimit');//use delegates from config if havent N nodes
}

applyData(peer, data) {
let round = true;
if (!this.isDelegateMode())
round = app.roundManager.check(peer, data);

if (!round)
throw new Error('Key of this validator havent cursor');

let res = false;
res = super.applyData(peer, data);

if (res && !this.isDelegateMode())
app.roundManager.update();

return res;
}

addValidator(publickey, priority, volume) {
this.debug('debug', 'add validator ' + publickey + ' with priority ' + priority + ' and volume ' + volume);
app.roundManager.addValidator(publickey, priority, volume)
}

}

return dynamicDelegateProofOfStakeConsensus;
}

Loading

0 comments on commit e0d4a1d

Please sign in to comment.