Skip to content
This repository has been archived by the owner on Feb 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #226 from radixdlt/feature/validator-api
Browse files Browse the repository at this point in the history
Validator api
  • Loading branch information
talekhinezh authored Apr 25, 2021
2 parents 031ddb2 + dc80981 commit e1f846e
Show file tree
Hide file tree
Showing 17 changed files with 225 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ public void start(SimulationNodes.RunningNetwork network) {
List<BFTNode> nodes = network.getNodes();
this.disposable = Observable.interval(1, 1, TimeUnit.SECONDS)
.map(i -> nodes.get(random.nextInt(nodes.size())))
.map(node -> network.getDispatcher(NodeApplicationRequest.class, node))
.subscribe(d -> {
.subscribe(node -> {
var d = network.getDispatcher(NodeApplicationRequest.class, node);
var builder = TxActionListBuilder.create();
if (random.nextBoolean()) {
builder.registerAsValidator();
builder.registerAsValidator(node.getKey());
} else {
builder.unregisterAsValidator();
builder.unregisterAsValidator(node.getKey());
}

var request = NodeApplicationRequest.create(builder.build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ public void start(SimulationNodes.RunningNetwork network) {
this.disposable = Observable.fromIterable(nodes)
.concatMap(i -> Observable.timer(3, TimeUnit.SECONDS).map(l -> i))
.doOnNext(validationRegistrations::onNext)
.map(node -> network.getDispatcher(NodeApplicationRequest.class, node))
.subscribe(d -> d.dispatch(NodeApplicationRequest.create(new RegisterValidator())));
.subscribe(node -> {
var d = network.getDispatcher(NodeApplicationRequest.class, node);
d.dispatch(NodeApplicationRequest.create(new RegisterValidator(node.getKey())));
});
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@

package com.radixdlt.statecomputer;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.radixdlt.atommodel.validators.ValidatorParticle;
import com.radixdlt.crypto.ECPublicKey;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
Expand All @@ -32,69 +31,49 @@
* Wrapper class for registered validators
*/
public final class RegisteredValidators {
private final Map<ECPublicKey, ValidatorDetails> validators;
private final Set<ValidatorParticle> validatorParticles;

private RegisteredValidators(Map<ECPublicKey, ValidatorDetails> validators) {
this.validators = validators;
private RegisteredValidators(Set<ValidatorParticle> validatorParticles) {
this.validatorParticles = validatorParticles;
}

public static RegisteredValidators create() {
return new RegisteredValidators(Map.of());
}

public RegisteredValidators combine(RegisteredValidators v) {
var map = ImmutableMap.<ECPublicKey, ValidatorDetails>builder()
.putAll(validators)
.putAll(v.validators)
.build();

return new RegisteredValidators(map);
return new RegisteredValidators(Set.of());
}

public RegisteredValidators add(ValidatorParticle particle) {
var validator = particle.getKey();

if (validators.containsKey(validator)) {
//TODO: should we merge details???
return this;
}

var map = ImmutableMap.<ECPublicKey, ValidatorDetails>builder()
.putAll(validators)
.put(particle.getKey(), ValidatorDetails.fromParticle(particle))
var map = ImmutableSet.<ValidatorParticle>builder()
.addAll(validatorParticles)
.add(particle)
.build();

return new RegisteredValidators(map);
}

public RegisteredValidators remove(ValidatorParticle particle) {
var validator = particle.getKey();

if (!validators.containsKey(validator)) {
return this;
}

return new RegisteredValidators(
validators.entrySet().stream()
.filter(e -> !e.getKey().equals(validator))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
validatorParticles.stream()
.filter(e -> !e.equals(particle))
.collect(Collectors.toSet())
);
}

public Set<ECPublicKey> toSet() {
return Set.copyOf(validators.keySet());
return validatorParticles.stream()
.map(ValidatorParticle::getKey)
.collect(Collectors.toSet());
}

public <T> List<T> map(BiFunction<ECPublicKey, ValidatorDetails, T> mapper) {
return validators.entrySet()
return validatorParticles
.stream()
.map(entry -> mapper.apply(entry.getKey(), entry.getValue()))
.map(p -> mapper.apply(p.getKey(), ValidatorDetails.fromParticle(p)))
.collect(Collectors.toList());
}

@Override
public int hashCode() {
return Objects.hash(validators);
return Objects.hash(validatorParticles);
}

@Override
Expand All @@ -104,6 +83,6 @@ public boolean equals(Object o) {
}

var other = (RegisteredValidators) o;
return Objects.equals(this.validators, other.validators);
return Objects.equals(this.validatorParticles, other.validatorParticles);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,18 @@ public BiFunction<RegisteredValidators, ValidatorParticle, RegisteredValidators>
return (prev, p) -> {
if (p.isRegisteredForNextEpoch()) {
return prev.add(p);
} else {
return prev.remove(p);
}
return prev;
};
}

@Override
public BiFunction<RegisteredValidators, ValidatorParticle, RegisteredValidators> inputReducer() {
return (prev, p) -> prev;
return (prev, p) -> {
if (p.isRegisteredForNextEpoch()) {
return prev.remove(p);
}
return prev;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public VerifiedTxnsAndProof get() {

// Initial validator registration
for (var validatorKey : validatorKeys) {
var validatorTxn = branch.construct(validatorKey.getPublicKey(), new RegisterValidator())
var validatorTxn = branch.construct(new RegisterValidator(validatorKey.getPublicKey()))
.signAndBuild(validatorKey::sign);
branch.execute(List.of(validatorTxn), PermissionLevel.SYSTEM);
genesisTxns.add(validatorTxn);
Expand All @@ -128,14 +128,13 @@ public VerifiedTxnsAndProof get() {
for (var stakeDelegation : stakeDelegations) {
var delegateAddr = REAddr.ofPubKeyAccount(stakeDelegation.staker().getPublicKey());
var stakerTxn = branch.construct(
stakeDelegation.staker().getPublicKey(),
new StakeTokens(delegateAddr, stakeDelegation.delegate(), stakeDelegation.amount())
).signAndBuild(stakeDelegation.staker()::sign);
branch.execute(List.of(stakerTxn), PermissionLevel.SYSTEM);
genesisTxns.add(stakerTxn);
}

var systemTxn = branch.construct(new SystemNextEpoch(0, 0))
var systemTxn = branch.construct(new SystemNextEpoch(timestamp, 0))
.buildWithoutSignature();
branch.execute(List.of(systemTxn), PermissionLevel.SYSTEM);
genesisTxns.add(systemTxn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Security;
Expand All @@ -97,7 +98,6 @@ private GenerateUniverses() { }
= new BigDecimal(UInt256s.toBigInteger(TokenDefinitionUtils.SUB_UNITS));
private static final String DEFAULT_UNIVERSE = UniverseType.DEVELOPMENT.toString().toLowerCase();
private static final String DEFAULT_TIMESTAMP = String.valueOf(Instant.parse("2020-01-01T00:00:00.00Z").toEpochMilli());
private static final String DEFAULT_KEYSTORE = "universe.ks";
private static final String DEFAULT_STAKE = "5000000";
private static final String VALIDATOR_TEMPLATE = "validator%s.ks";
private static final String STAKER_TEMPLATE = "staker%s.ks";
Expand All @@ -115,9 +115,10 @@ public static void main(String[] args) {
options.addOption("h", "help", false, "Show usage information (this message)");
options.addOption("c", "no-cbor-output", false, "Suppress DSON output");
options.addOption("i", "issue-default-tokens", false, "Issue tokens to default keys 1, 2, 3, 4 and 5 (dev universe only)");
options.addOption("pk", "pubkey-issuance", true, "Pub key (hex) to issue tokens to");
options.addOption("m", "amount-issuance", true, "Amount to issue pub key");
options.addOption("j", "no-json-output", false, "Suppress JSON output");
options.addOption("k", "keystore", true, "Specify universe keystore (default: " + DEFAULT_KEYSTORE + ")");
options.addOption("p", "include-private-keys", false, "Include universe, validator and staking private keys in output");
options.addOption("p", "include-private-keys", false, "Include validator and staking private keys in output");
options.addOption("S", "stake-amounts", true, "Amount of stake for each staked node (default: " + DEFAULT_STAKE + ")");
options.addOption("t", "universe-type", true, "Specify universe type (default: " + DEFAULT_UNIVERSE + ")");
options.addOption("T", "universe-timestamp", true, "Specify universe timestamp (default: " + DEFAULT_TIMESTAMP + ")");
Expand Down Expand Up @@ -212,6 +213,24 @@ public static void main(String[] args) {
);
}

if (cmd.hasOption("pk")) {
try {
var hexPubKey = cmd.getOptionValue("pk");
var pubKey = ECPublicKey.fromHex(hexPubKey);
final UInt256 tokenAmt;
if (cmd.hasOption("a")) {
var amountStr = cmd.getOptionValue("a");
var amt = new BigInteger(amountStr);
tokenAmt = unitsToSubunits(new BigDecimal(amt));
} else {
tokenAmt = unitsToSubunits(DEFAULT_ISSUANCE);
}
tokenIssuancesBuilder.add(TokenIssuance.of(pubKey, tokenAmt));
} catch (PublicKeyException e) {
throw new IllegalStateException("Invalid pub key", e);
}
}

if (universeType == UniverseType.DEVELOPMENT) {
// Issue tokens to initial validators for now to support application services
// FIXME: Remove this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import com.radixdlt.atom.actions.TransferToken;
import com.radixdlt.atom.actions.UnregisterValidator;
import com.radixdlt.atom.actions.UnstakeTokens;
import com.radixdlt.atom.actions.UpdateValidator;
import com.radixdlt.client.AccountAddress;
import com.radixdlt.client.Rri;
import com.radixdlt.client.ValidatorAddress;
Expand Down Expand Up @@ -222,12 +223,23 @@ private TxAction parseAction(JSONObject actionObject) throws IllegalArgumentExce
var toString = paramsObject.getString("to");
var toDelegate = ValidatorAddress.parse(toString);
var amt = parseAmount(paramsObject, "amount");
return new MoveStake(fromDelegate, toDelegate, amt);
return new MoveStake(account, fromDelegate, toDelegate, amt);
}
case "RegisterValidator": {
var name = paramsObject.has("name") ? paramsObject.getString("name") : null;
var url = paramsObject.has("url") ? paramsObject.getString("url") : null;
return new RegisterValidator(bftKey, name, url);
}
case "UnregisterValidator": {
var name = paramsObject.has("name") ? paramsObject.getString("name") : null;
var url = paramsObject.has("url") ? paramsObject.getString("url") : null;
return new UnregisterValidator(bftKey, name, url);
}
case "UpdateValidator": {
var name = paramsObject.has("name") ? paramsObject.getString("name") : null;
var url = paramsObject.has("url") ? paramsObject.getString("url") : null;
return new UpdateValidator(bftKey, name, url);
}
case "RegisterAsValidator":
return new RegisterValidator();
case "UnregisterAsValidator":
return new UnregisterValidator();
default:
throw new IllegalArgumentException("Bad action object: " + actionObject);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ private Txn systemUpdateCommand(long nextView, long nextEpoch) throws TxBuilderE
}

private Txn registerCommand(ECKeyPair keyPair) throws TxBuilderException {
return radixEngine.construct(keyPair.getPublicKey(), new RegisterValidator())
return radixEngine.construct(new RegisterValidator(keyPair.getPublicKey()))
.signAndBuild(keyPair::sign);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public void setup() throws Exception {
.mint(this.tokenRri, this.tokenOwnerAcct, UInt256.TEN)
.build()
).buildWithoutSignature();
var validatorBuilder = this.engine.construct(this.validatorKeyPair.getPublicKey(), new RegisterValidator());
var validatorBuilder = this.engine.construct(new RegisterValidator(this.validatorKeyPair.getPublicKey()));
var txn1 = validatorBuilder.signAndBuild(this.validatorKeyPair::sign);

engine.execute(List.of(txn0, txn1), null, PermissionLevel.SYSTEM);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.radixdlt.atom.actions.SplitToken;
import com.radixdlt.atom.actions.TransferToken;
import com.radixdlt.atom.actions.UnregisterValidator;
import com.radixdlt.crypto.ECPublicKey;
import com.radixdlt.identifiers.REAddr;
import com.radixdlt.utils.UInt256;

Expand Down Expand Up @@ -53,14 +54,14 @@ public TxActionListBuilder createMutableToken(MutableTokenDefinition def) {
return this;
}

public TxActionListBuilder registerAsValidator() {
var action = new RegisterValidator();
public TxActionListBuilder registerAsValidator(ECPublicKey validatorKey) {
var action = new RegisterValidator(validatorKey, null, null);
actions.add(action);
return this;
}

public TxActionListBuilder unregisterAsValidator() {
var action = new UnregisterValidator();
public TxActionListBuilder unregisterAsValidator(ECPublicKey validatorKey) {
var action = new UnregisterValidator(validatorKey, null, null);
actions.add(action);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,30 @@
import com.radixdlt.identifiers.REAddr;
import com.radixdlt.utils.UInt256;

import java.util.Objects;

public final class MoveStake implements TxAction {
private final ECPublicKey from;
private final ECPublicKey to;
private final UInt256 amount;
private final REAddr accountAddr;

public MoveStake(ECPublicKey from, ECPublicKey to, UInt256 amount) {
public MoveStake(REAddr accountAddr, ECPublicKey from, ECPublicKey to, UInt256 amount) {
this.accountAddr = Objects.requireNonNull(accountAddr);
this.from = from;
this.to = to;
this.amount = amount;
}

@Override
public void execute(TxBuilder txBuilder) throws TxBuilderException {
var user = txBuilder.getUserOrFail("Must have an address.");
var userAcct = REAddr.ofPubKeyAccount(user);

txBuilder.swapFungible(
StakedTokensParticle.class,
p -> p.getOwner().equals(userAcct) && p.getDelegateKey().equals(from),
p -> p.getOwner().equals(accountAddr) && p.getDelegateKey().equals(from),
StakedTokensParticle::getAmount,
amt -> new StakedTokensParticle(from, userAcct, amt),
amt -> new StakedTokensParticle(from, accountAddr, amt),
amount,
"Not enough staked."
).with(amt -> new StakedTokensParticle(to, userAcct, amt));
).with(amt -> new StakedTokensParticle(to, accountAddr, amt));
}
}
Loading

0 comments on commit e1f846e

Please sign in to comment.