Function isInvalidReason
Check the validity of an enrollment.
string isInvalidReason
(
in ref const(Enrollment) enrollment,
scope nothrow @safe bool delegate(in ref geod24 .bitblob .BitBlob!(64L), out UTXO) findUTXO,
in const(Height) height,
scope nothrow @trusted bool delegate(in ref geod24 .bitblob .BitBlob!(64L), out EnrollmentState) findEnrollment,
scope nothrow @safe Amount delegate(geod24 .bitblob .BitBlob!(64L)) getPenaltyDeposit
) nothrow @safe;
string isInvalidReason
(
in ref const(Enrollment) enrollment,
scope nothrow @safe bool delegate(in ref geod24 .bitblob .BitBlob!(64L), out UTXO) findUTXO,
in const(Height) height,
scope nothrow @trusted bool delegate(in ref geod24 .bitblob .BitBlob!(64L), out EnrollmentState) findEnrollment,
scope nothrow @safe Amount delegate(geod24 .bitblob .BitBlob!(64L)) getPenaltyDeposit,
out Amount stake
) nothrow @safe;
A Validator's enrollment is considered valid if: - UTXO is unspent frozen utxo - Signatures are authentic - The frozen amount must be equal to or greater than 40,000 BOA
Parameters
Name | Description |
---|---|
enrollment | The enrollment of the target to be verified |
findUTXO | delegate to find the referenced unspent UTXOs with |
height | The Height that this Enrollment is proposed at |
stake | an output param for the staked Amount for this enroll |
Returns
null
if the validator's UTXO is valid, otherwise a string
explaining the reason it is invalid.
Example
import std .algorithm .searching;
import std .string;
import agora .consensus .state .UTXOSet;
KeyPair[] key_pairs = [KeyPair .random, KeyPair .random, KeyPair .random, KeyPair .random];
Amount delegate(Hash utxo) @safe nothrow getPenaltyDeposit = (utxo) { return 10_000 .coins; };
auto params = new immutable(ConsensusParams)();
auto stateDB = new ManagedDatabase(":memory:");
scope utxo_set = new UTXOSet(stateDB);
scope validator_set = new ValidatorSet(stateDB, params);
scope UTXOFinder utxoFinder = utxo_set .getUTXOFinder();
// normal frozen transaction
Transaction tx1 = Transaction(
[Output(Amount .MinFreezeAmount, key_pairs[0] .address, OutputType .Freeze)]
);
// payment transaction
Transaction tx2 = Transaction(
[Output(Amount .MinFreezeAmount, key_pairs[1] .address)]
);
// Insufficient freeze amount transaction
Transaction tx3 = Transaction(
[Output(Amount(1), key_pairs[2] .address, OutputType .Freeze)]
);
// normal freeze amount transaction
Transaction tx4 = Transaction(
[Output(Amount .MinFreezeAmount, key_pairs[3] .address)]
);
auto utxo_hash1 = UTXO .getHash(hashFull(tx1), 0);
auto utxo_hash2 = UTXO .getHash(hashFull(tx2), 0);
auto utxo_hash3 = UTXO .getHash(hashFull(tx3), 0);
auto utxo_hash4 = UTXO .getHash(hashFull(tx4), 0);
Pair signature_noise = Pair .random;
Pair node_key_pair_1 = Pair .fromScalar(key_pairs[0] .secret);
Enrollment enroll1;
enroll1 .utxo_key = utxo_hash1;
enroll1 .commitment = hashFull(Scalar .random());
enroll1 .enroll_sig = sign(node_key_pair_1, signature_noise, hashMulti(Height(0), enroll1));
Pair node_key_pair_2 = Pair .fromScalar(key_pairs[1] .secret);
Enrollment enroll2;
enroll2 .utxo_key = utxo_hash2;
enroll2 .commitment = hashFull(Scalar .random());
enroll2 .enroll_sig = sign(node_key_pair_2, signature_noise, hashMulti(Height(0), enroll2));
Pair node_key_pair_3 = Pair .fromScalar(key_pairs[2] .secret);
Enrollment enroll3;
enroll3 .utxo_key = utxo_hash3;
enroll3 .commitment = hashFull(Scalar .random());
enroll3 .enroll_sig = sign(node_key_pair_3, signature_noise, hashMulti(Height(0), enroll3));
// Make pair with non matching scalar and point
Pair node_key_pair_invalid = Pair(node_key_pair_2 .v, node_key_pair_3 .V);
Enrollment enroll4;
enroll4 .utxo_key = utxo_hash4;
enroll4 .commitment = hashFull(Scalar .random());
enroll4 .enroll_sig = sign(node_key_pair_invalid, signature_noise, hashMulti(Height(0), enroll4));
assert(!enroll1 .isValid(utxoFinder, Height(0),
&validator_set .findRecentEnrollment,
getPenaltyDeposit));
assert(!enroll2 .isValid(utxoFinder, Height(0),
&validator_set .findRecentEnrollment,
getPenaltyDeposit));
assert(!enroll3 .isValid(utxoFinder, Height(0),
&validator_set .findRecentEnrollment,
getPenaltyDeposit));
assert(!enroll4 .isValid(utxoFinder, Height(0),
&validator_set .findRecentEnrollment,
getPenaltyDeposit));
utxo_set .updateUTXOCache(tx1, Height(0), params .CommonsBudgetAddress);
utxo_set .updateUTXOCache(tx2, Height(0), params .CommonsBudgetAddress);
utxo_set .updateUTXOCache(tx3, Height(0), params .CommonsBudgetAddress);
utxo_set .updateUTXOCache(tx4, Height(0), params .CommonsBudgetAddress);
// Nomal
assert(enroll1 .isValid(utxoFinder, Height(0),
&validator_set .findRecentEnrollment,
getPenaltyDeposit));
// Unspent frozen UTXO not found for the validator.
assert(!enroll1 .isValid( utxoFinder, Height(0),
&validator_set .findRecentEnrollment,
getPenaltyDeposit));
// UTXO is not frozen.
assert(canFind(enroll2 .isInvalidReason(utxoFinder,
Height(0), &validator_set .findRecentEnrollment, getPenaltyDeposit), "UTXO is not frozen"));
// The frozen amount must be equal to or greater than 40,000 BOA.
assert(!enroll3 .isValid(utxoFinder, Height(0),
&validator_set .findRecentEnrollment,
getPenaltyDeposit));
// Enrollment signature verification has an error.
assert(!enroll4 .isValid(utxoFinder, Height(0),
&validator_set .findRecentEnrollment,
getPenaltyDeposit));
const utxoPeek = &utxo_set .peekUTXO;
auto cycle = PreImageCycle(key_pairs[0] .secret, params .ValidatorCycle);
enroll1 .utxo_key = utxo_hash1;
enroll1 .commitment = cycle[Height(0)];
enroll1 .enroll_sig = sign(node_key_pair_1, signature_noise, hashMulti(Height(0), enroll1));
assert(validator_set .add(Height(0), utxoPeek, getPenaltyDeposit, enroll1,
key_pairs[0] .address) is null);
assert(validator_set .countActive(Height(params .ValidatorCycle + 1)) == 0);
// Add initial enrollment first
enroll1 .commitment = cycle[Height(0)];
enroll1 .enroll_sig = sign(node_key_pair_1, signature_noise, hashMulti(Height(0), enroll1));
validator_set .add(Height(0), utxoPeek, getPenaltyDeposit, enroll1, key_pairs[0] .address);
// First 2 iterations should fail because commitment is wrong
foreach (offset; [-1, +1, 0])
{
enroll1 .commitment = cycle[Height(params .ValidatorCycle + offset)];
enroll1 .enroll_sig = sign(node_key_pair_1, signature_noise, hashMulti(Height(params .ValidatorCycle), enroll1));
assert((offset == 0) == (validator_set .add(Height(params .ValidatorCycle),
utxoPeek, getPenaltyDeposit, enroll1, key_pairs[0] .address) is null));
}
assert(validator_set .countActive(Height(params .ValidatorCycle + 1)) == 1);
Enrollment invalid;
assert(isInvalidReason(invalid,
(in Hash, out UTXO utxo) { utxo .output .type = OutputType .Freeze; return true; },
Height(0), null, null)
== "Enrollment: Address is not a valid point on Curve25519");