Skip to content

Commit

Permalink
The validator set add an enrollment before the cycle
Browse files Browse the repository at this point in the history
The validator set should allow a node to re-enroll before the validator cycle
ends. It's not for sure that an enrollment will be contained in the next
new block, so the validator set must add the enrollment earlier before the
cycle ends.

I added the constant about the round that a node can re-enroll because
unnecessary re-enrollments should be avoid. The value can be configurable.

Relates bosagora#824
  • Loading branch information
linked0 committed Jun 2, 2020
1 parent 721aba8 commit fd0ced1
Showing 1 changed file with 78 additions and 10 deletions.
88 changes: 78 additions & 10 deletions source/agora/consensus/ValidatorSet.d
Expand Up @@ -41,6 +41,10 @@ public class ValidatorSet
/// SQLite db instance
private Database db;

/// The available round for re-enrollment that is configurable
static private const uint ReEnrollAvailableRound =
Enrollment.ValidatorCycle / 2;

/***************************************************************************
Constructor
Expand Down Expand Up @@ -96,23 +100,43 @@ public class ValidatorSet
if (auto reason = isInvalidReason(enroll, finder))
return reason;

// check if already exists
if (this.hasEnrollment(enroll.utxo_key))
return "This validator is already enrolled";

try
{
const bool exist = this.hasEnrollment(enroll.utxo_key);
if (exist)
{
// this checks that the re-enrollment is requested past the half
// of the validator cycle.
if (block_height - this.getEnrolledHeight(enroll.utxo_key) <
ReEnrollAvailableRound)
{
log.info("Rejected re-enrollment is too early, " ~
"Data was: {}", enroll);
return "The re-enrollment is too early";
}
}

static ubyte[] buffer;
serializeToBuffer(enroll, buffer);
const img = PreImageInfo(enroll.utxo_key, enroll.random_seed, 0);
static ubyte[] pbuffer;
serializeToBuffer(img, pbuffer);

() @trusted {
this.db.execute("INSERT INTO validator_set " ~
"(key, val, enrolled_height, preimage) VALUES (?, ?, ?, ?)",
enroll.utxo_key.toString(), buffer, block_height, pbuffer);
}();
if (exist)
{
() @trusted {
this.db.execute("UPDATE validator_set SET " ~
"val = ?, enrolled_height = ?, preimage = ? WHERE key = ?",
buffer, block_height, pbuffer, enroll.utxo_key.toString());
}();
}
else
{
() @trusted {
this.db.execute("INSERT INTO validator_set " ~
"(key, val, enrolled_height, preimage) VALUES (?, ?, ?, ?)",
enroll.utxo_key.toString(), buffer, block_height, pbuffer);
}();
}
}
catch (Exception ex)
{
Expand Down Expand Up @@ -789,3 +813,47 @@ unittest
assert(set.getEnrolledHeight(enrolls[1].utxo_key) ==
blocks[100].header.height);
}

/// Test for re-enrollment
unittest
{
import agora.common.Amount;
import agora.consensus.data.Transaction;
import agora.consensus.Genesis;

scope storage = new TestUTXOSet;
auto key_pair = getGenesisKeyPair();
foreach (uint idx; 0 .. 8)
{
auto input = Input(hashFull(GenesisTransaction), idx);
Transaction tx =
{
TxType.Freeze,
[input],
[Output(Amount.MinFreezeAmount, key_pair.address)]
};

auto signature = key_pair.secret.sign(hashFull(tx)[]);
tx.inputs[0].signature = signature;
storage.put(tx);
}
Hash[] utxos = storage.keys;

auto set = new ValidatorSet(":memory:");
scope(exit) set.shutdown();

// create enrollments
auto seed = Scalar.random();
auto enroll = createEnrollment(utxos[0], key_pair, seed);

// add the enrollment for the first time
assert(set.add(1, &storage.findUTXO, enroll) is null);

// add the enrollment again before the `ReEnrollAvailableRound`
assert(set.add(ValidatorSet.ReEnrollAvailableRound, &storage.findUTXO,
enroll) !is null);

// add the enrollment again from the `ReEnrollAvailableRound`
assert(set.add(ValidatorSet.ReEnrollAvailableRound + 1,
&storage.findUTXO, enroll) is null);
}

0 comments on commit fd0ced1

Please sign in to comment.