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