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);