Alias ExpiringValidator

A Height and PublicKey pair to represent expiring Validators

alias ExpiringValidator = std.typecons.Tuple!(agora.common.Types.Height,"enrolled_height",geod24.bitblob.BitBlob!(64L).BitBlob,"utxo",agora.crypto.Key.PublicKey,"pubkey");

Example

test for functions of ValidatorSet

import agora.consensus.data.Transaction;
import agora.consensus.EnrollmentManager;
import std.algorithm;
import std.conv;
import std.range;

const FirstEnrollHeight = Height(1);
scope storage = new MemoryUTXOSet;
auto getPenaltyDeposit = (Hash utxo)
{
    UTXO val;
    return storage.peekUTXO(utxo, val) ? 10_000.coins : 0.coins;
};
auto params = new immutable(ConsensusParams)();
scope set = new ValidatorSet(new ManagedDatabase(":memory:"), params);

Hash[] utxos;
genesisSpendable().take(8).enumerate
    .map!(en => en.value.refund(WK.Keys[en.index].address).sign(OutputType.Freeze))
    .each!((tx) {
        storage.put(tx);
        utxos ~= UTXO.getHash(tx.hashFull(), 0);
    });

// add enrollments
auto enroll = EnrollmentManager.makeEnrollment(utxos[0], WK.Keys[0], FirstEnrollHeight, params.ValidatorCycle);
assert(set.add(FirstEnrollHeight, &storage.peekUTXO, getPenaltyDeposit, enroll, WK.Keys[0].address) is null);
assert(set.countActive(FirstEnrollHeight) == 0);
assert(set.countActive(FirstEnrollHeight + 1) == 1);    // Will be active next block
ExpiringValidator[] ex_validators;
assert(set.hasEnrollment(FirstEnrollHeight, utxos[0]));
assert(set.add(FirstEnrollHeight, &storage.peekUTXO, getPenaltyDeposit, enroll, WK.Keys[0].address) == "Already enrolled at this height");

auto enroll2 = EnrollmentManager.makeEnrollment(utxos[1], WK.Keys[1], FirstEnrollHeight, params.ValidatorCycle);
assert(set.add(FirstEnrollHeight, &storage.peekUTXO, getPenaltyDeposit, enroll2, WK.Keys[1].address) is null);
assert(set.countActive(FirstEnrollHeight + 1) == 2);

const SecondEnrollHeight = Height(9);
auto enroll3 = EnrollmentManager.makeEnrollment(utxos[2], WK.Keys[2], SecondEnrollHeight, params.ValidatorCycle);
assert(set.add(SecondEnrollHeight, &storage.peekUTXO, getPenaltyDeposit, enroll3, WK.Keys[2].address) is null);
assert(set.countActive(SecondEnrollHeight + 1) == 3);

// check if enrolled heights are not set
Hash[] keys;
set.getEnrolledUTXOs(SecondEnrollHeight + 1, keys);
assert(keys.length == 3);
assert(keys.isStrictlyMonotonic!("a < b"));

// slash ValidatorSet
set.slashValidator(utxos[1], SecondEnrollHeight + 1);
assert(set.countActive(SecondEnrollHeight + 1) == 2);
assert(set.hasEnrollment(SecondEnrollHeight + 1,utxos[0]));
set.slashValidator(utxos[0], SecondEnrollHeight + 1);
// The enrollment will remain even though it is slashed
assert(set.hasEnrollment(SecondEnrollHeight + 1, utxos[0]));
set.removeAll();
assert(set.countActive(SecondEnrollHeight + 1) == 0);

Enrollment[] ordered_enrollments;
ordered_enrollments ~= enroll;
ordered_enrollments ~= enroll2;
/// PreImageCache for the first enrollment
auto cache = PreImageCycle(WK.Keys[0].secret, set.params.ValidatorCycle);

// Reverse ordering
ordered_enrollments.sort!("a.utxo_key > b.utxo_key");
foreach (i, ordered_enroll; ordered_enrollments)
    assert(set.add(FirstEnrollHeight, storage.getUTXOFinder(),
        getPenaltyDeposit, ordered_enroll, WK.Keys[i].address) is null);
set.getEnrolledUTXOs(FirstEnrollHeight + 1, keys);
assert(keys.length == 2);
assert(keys.isStrictlyMonotonic!("a < b"));

// test for adding and getting preimage
assert(set.getPreimage(utxos[0]) == PreImageInfo(enroll.utxo_key, enroll.commitment, FirstEnrollHeight));
assert(cache[FirstEnrollHeight] == enroll.commitment);
auto preimage_11 = PreImageInfo(utxos[0], cache[SecondEnrollHeight + 2], SecondEnrollHeight + 2);
assert(set.addPreimage(preimage_11));
assert(set.getPreimage(utxos[0]) == preimage_11);

auto far_height = Height(10000);
auto too_far_preimage = PreImageInfo(utxos[0], cache[far_height], far_height);
assert(!set.addPreimage(too_far_preimage));
assert(set.getPreimage(utxos[0]) == preimage_11);

// test for clear up expired validators
enroll = EnrollmentManager.makeEnrollment(utxos[2], WK.Keys[2], SecondEnrollHeight, params.ValidatorCycle);
assert(set.add(SecondEnrollHeight, &storage.peekUTXO, getPenaltyDeposit, enroll, WK.Keys[2].address) is null);
keys.length = 0;
assert(set.getEnrolledUTXOs(Height(set.params.ValidatorCycle + 8), keys));
assert(keys.length == 1);
assert(keys[0] == utxos[2]);

// add enrollment at the genesis block:
// validates blocks [1 .. ValidatorCycle] inclusively
assert(set.params.ValidatorCycle > 10);
set.removeAll();  // clear all

assert(set.countActive(SecondEnrollHeight + 1) == 0);
enroll = EnrollmentManager.makeEnrollment(utxos[0], WK.Keys[0], FirstEnrollHeight, params.ValidatorCycle);
assert(set.add(FirstEnrollHeight, &storage.peekUTXO, getPenaltyDeposit, enroll, WK.Keys[0].address) is null);

// still active at height 1008
keys.length = 0;

assert(set.countActive(Height(set.params.ValidatorCycle)) == 1);
assert(set.getEnrolledUTXOs(Height(set.params.ValidatorCycle), keys));
assert(keys.length == 1);
assert(keys[0] == utxos[0]);

// cleared after a new cycle was started (which started at height 1 so add 2)
assert(set.countActive(Height(set.params.ValidatorCycle + 2)) == 0);
assert(set.getEnrolledUTXOs(Height(1010), keys));
assert(keys.length == 0);
set.removeAll();  // clear all

enroll = EnrollmentManager.makeEnrollment(utxos[0], WK.Keys[0], FirstEnrollHeight, params.ValidatorCycle);
assert(set.add(FirstEnrollHeight, &storage.peekUTXO, getPenaltyDeposit, enroll, WK.Keys[0].address) is null);
far_height = Height(5 * params.ValidatorCycle);
foreach (idx; 1..7)
    assert(set.addPreimage(PreImageInfo(utxos[0], cache[Height(idx * params.ValidatorCycle)], Height(idx * params.ValidatorCycle))));
// enroll again at a much later height, preimage should not be updated
enroll = EnrollmentManager.makeEnrollment(utxos[0], WK.Keys[0], far_height, params.ValidatorCycle);
assert(set.getPreimage(utxos[0]).height > far_height);

enroll = EnrollmentManager.makeEnrollment(utxos[1], WK.Keys[1], FirstEnrollHeight, params.ValidatorCycle);
assert(set.add(FirstEnrollHeight, &storage.peekUTXO, getPenaltyDeposit, enroll, WK.Keys[1].address) is null);
// enroll again at a much later height, preimage should still be accepted
enroll = EnrollmentManager.makeEnrollment(utxos[1], WK.Keys[1], far_height, params.ValidatorCycle);
assert(set.add(far_height, &storage.peekUTXO, getPenaltyDeposit, enroll, WK.Keys[1].address) is null);
assert(set.getPreimage(utxos[1]).height == far_height);