Forging Algorithm
Features of the Algorithm
There are two conditions that an account needs to satisfy before it can start forging:
- The account needs to have an effective balance of at least 1000 ARDR. That is, it needs to have had a balance of 1000 ARDR(or more) over the last 1440 blocks / 24 hours.
- This is needed because the forging algorithm depends on the stake of the user.
- The account needs to have at least one outgoing transaction, also confirmed 1440 times.
- The passphrase is the the account's private key, a public key is obtained only after at least one outgoing transaction is executed and confirmed. This can be done in several ways, notably by sending 1 ARDR to your own account (or someone else's), by sending a message using the arbitrary message feature or by registering an alias
When these two conditions are met, a 1000 ARDR effective balance and public key published, the account is eligible to forge. Note that forging will stop if the effective balance drops below 1000 ARDR.
The forging algorithm employed by ARDR revolves around two variables: Hit and Target.
Hit
Each forger generates one 'hit' value between 0 and 2^64 - 1 for each new block. While hit is deterministic, it's produced through hashing inputs, so it can be regarded as a uniform random variable.
From the code, hit is defined as:
BigInteger hit = getHit(publicKey, lastBlock);
Two arguments are given to the getHit
function: the forger's public key and the previous block. The implementation of this function can be seen below.
private static BigInteger getHit(byte[] publicKey, Block block){ [...] MessageDigest digest = Crypto.sha256(); digest.update(block.getGenerationSignature()); byte[] generationSignatureHash = digest.digest(publicKey); return new BigInteger(1, new byte[] {generationSignatureHash[7], generationSignatureHash[6], generationSignatureHash[5], generationSignatureHash[4], generationSignatureHash[3], generationSignatureHash[2], generationSignatureHash[1], generationSignatureHash[0]}); }
Taken from ardor/ src / java / nxt / Generator.java
Each block has a 64 bytes generationSignature
parameter. A forger signs the previous block using its own public key. This same generationSignature
is successively hashed using SHA256. The hit value is obtained by taking the first 8 bytes of the resulting hash.
Target
The target is a ever increasing valor. This is its implementation
BigInteger target = BigInteger.valueOf(lastBlock.getBaseTarget()) .multiply(BigInteger.valueOf(effectiveBalance)) .multiply(BigInteger.valueOf(elapsedTime));
Taken from nxt / src / java / nxt / Generator.java
In other words, we have that
Target = BaseTarget * EffectiveBalance * TimeSinceLastBlock
Where:
- BaseTarget
- This parameter is the same for everybody. It gets adjusted from block to block to obtain, roughly, a generation time between blocks of 60s. If two successive blocks get generated too quickly, the new BaseTarget gets reduced. If instead they take too long, the value gets increased. This adjustment is capped, it can go up at most twice as much (2 * currentBaseTarget) and down at most by half (0.5 * currentBaseTarget).
- EffectiveBalance
- This is your stake in ARDR. Only the amount of ARDR that has been confirmed at least 1440 times counts towards this balance. The reason the effective balance is used is to avoid shuffling attacks.
- TimeSinceLastBlock
- How long, in seconds, since the last block has been generated. This parameter is also the same for everybody.
The block forger
The way who gets to generate the next block is chosen, is done by using the condition:
if (hits.get(accountId).compareTo(target) < 0){ BlockchainProcessorImpl.getInstance().generateBlock(secretPhrase); }
Taken from ardor / src / java / nxt / Generator.java
This says, essentially, that if the condition Hit < Target
is satisfied, you get to generate the next block.
Now that we know the forging algorithm, there are some considerations we can make:
- All the parameters except the effective balance are the same for everybody.
- The hit parameter is static, what changes during the forging process is the target. Every forger has its own target (even though it depends on parameters equal for everybody) and it reflects the forger's stake. Every second that no block gets generated, the target increases until someone's hit, on the network, manages to be smaller than their own target. The consequence is clear: bigger is your stake, higher will be your target (and more rapidly it will grow) making it easier for you to satisfy the condition. On a practical sense, this entails you will get to generate a block more often.
- TimeSinceLastBlock is the parameter that gives target its increasing nature. The longer it takes to generate a block, higher becomes the target.
- The situation where multiple forgers satisfy the forging condition at the same time is tangible, the network handles this situation accordingly.