convex.core.init.Init Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of convex-core Show documentation
Show all versions of convex-core Show documentation
Convex core libraries and common utilities
The newest version!
package convex.core.init;
import java.io.IOException;
import java.util.List;
import convex.core.Coin;
import convex.core.Constants;
import convex.core.State;
import convex.core.data.ABlob;
import convex.core.data.ACell;
import convex.core.data.AList;
import convex.core.data.AVector;
import convex.core.data.AccountKey;
import convex.core.data.AccountStatus;
import convex.core.data.Address;
import convex.core.data.Index;
import convex.core.data.PeerStatus;
import convex.core.data.Symbol;
import convex.core.data.Vectors;
import convex.core.lang.Code;
import convex.core.lang.Context;
import convex.core.lang.Core;
import convex.core.lang.RT;
import convex.core.lang.Reader;
import convex.core.util.Utils;
/**
* Static class for generating the initial Convex Genesis State
*
* "The beginning is the most important part of the work." - Plato, The Republic
*/
public class Init {
// Standard accounts numbers
public static final Address NULL_ADDRESS = Address.create(0);
public static final Address INIT_ADDRESS = Address.create(1);
// Governance accounts and funding pools
public static final Address FOUNDATION_ADDRESS = Address.create(2);
public static final Address RESERVE_ADDRESS = Address.create(3);
public static final Address UNRELEASED_ADDRESS = Address.create(4);
public static final Address DISTRIBUTION_ADDRESS = Address.create(5);
public static final Address GOVERNANCE_ADDRESS = Address.create(6);
public static final Address ADMIN_ADDRESS = Address.create(7);
// Built-in special accounts
public static final Address CORE_ADDRESS = Address.create(8);
public static final Address REGISTRY_ADDRESS = Address.create(9);
public static final Address TRUST_ADDRESS = Address.create(10);
// Base for genesis addresses
public static final Address GENESIS_ADDRESS = Address.create(11);
// Controller for genesis peer address
public static final Address GENESIS_PEER_ADDRESS = Address.create(12);
// First user of Protonet, i.e. @mikera
public static final Address FIRST_USER_ADDRESS = Address.create(13);
// Constants
private static final Index EMPTY_PEERS = Index.none();
private static final Index> EMPTY_SCHEDULE = Index.none();
/**
* Creates the base genesis state (before deployment of standard libraries and actors). This is the minimum state required for Convex operation.
*
* @param peerKeys Keys for genesis users and peers
* @return Base genesis state
*/
public static State createBaseState(AccountKey governanceKey, AccountKey genesisKey, List peerKeys) {
// accumulators for initial state maps
Index peers = EMPTY_PEERS;
AVector accts = Vectors.empty();
long supply = Constants.MAX_SUPPLY;
// Null account, cannot ever be used by anyone
{
AccountStatus nullAccount=AccountStatus.create(Coin.ZERO,AccountKey.NULL);
accts=accts.conj(nullAccount);
}
// "init" Account, used only for network setup and update. Doesn't need coins because we run state updates directly
{
AccountStatus initAccount=AccountStatus.create(Coin.ZERO,AccountKey.NULL);
accts = addAccount(accts, INIT_ADDRESS, initAccount);
}
// Foundation fund for initial operations and fallback governance
{
long foundationFund = 1*Coin.EMERALD;
AccountStatus foundationAccount=AccountStatus.create(foundationFund,governanceKey);
accts = addAccount(accts, FOUNDATION_ADDRESS, foundationAccount);
supply-=foundationFund;
}
// Foundation reserve fund (rest of foundation 25%, controlled by foundation account)
{
long reserve = 248*Coin.EMERALD;
AccountStatus reserveAccount=AccountStatus.create(reserve,governanceKey);
reserveAccount=reserveAccount.withController(FOUNDATION_ADDRESS);
accts = addAccount(accts, RESERVE_ADDRESS, reserveAccount); // 24% Foundation
supply -= reserve;
}
// Reserve for distribution via release curve - 74% for coin purchasers
{
long releaseCurveFund = 740 * Coin.EMERALD;
AccountStatus releaseCurveAccount=AccountStatus.create(releaseCurveFund,governanceKey);
releaseCurveAccount=releaseCurveAccount.withController(FOUNDATION_ADDRESS);
accts = addAccount(accts, UNRELEASED_ADDRESS, releaseCurveAccount);
supply -= releaseCurveFund;
}
// Initial account for release curve distribution - 1% for initial coin purchasers
{
long distributionFund = 10 * Coin.EMERALD;
AccountStatus releaseCurveAccount=AccountStatus.create(distributionFund,governanceKey);
releaseCurveAccount=releaseCurveAccount.withController(FOUNDATION_ADDRESS);
accts = addAccount(accts, DISTRIBUTION_ADDRESS, releaseCurveAccount);
supply -= distributionFund;
}
// Governance Address, used to manage key network operations e.g. CNS root changes
{
long governFund = 1 * Coin.DIAMOND;
AccountStatus governanceAccount=AccountStatus.create(governFund,governanceKey);
governanceAccount=governanceAccount.withController(FOUNDATION_ADDRESS);
accts = addAccount(accts, GOVERNANCE_ADDRESS, governanceAccount);
supply -= governFund;
}
// Admin address, used for non-critical operations, accessible with genesis Key
{
long admin = 1 * Coin.DIAMOND;
AccountStatus governanceAccount=AccountStatus.create(admin,genesisKey);
governanceAccount=governanceAccount.withController(FOUNDATION_ADDRESS);
accts = addAccount(accts, ADMIN_ADDRESS, governanceAccount);
supply -= admin;
}
// Core library at static address: CORE_ADDRESS
accts = addCoreLibrary(accts);
// Core Account should now be fully initialised
// BASE_USER_ADDRESS = accts.size();
// Always have at least one user and one peer setup
int keyCount = peerKeys.size();
assert(keyCount > 0);
// Build globals
AVector globals = Constants.INITIAL_GLOBALS;
// Create the initial state with static libraries and memory allowances
State s = State.create(accts, peers, globals, EMPTY_SCHEDULE);
{
supply-=s.getGlobalMemoryValue().longValue();
// There should be at least 100,000 Convex Gold for genesis to succeed, to be distributed to genesis account(s)
assert(supply>100000*Coin.GOLD);
// Add the static defined libraries at addresses: TRUST_ADDRESS, REGISTRY_ADDRESS
s = addStaticLibraries(s);
// Reload accounts with the libraries
accts = s.getAccounts();
}
// Set up initial user accounts, one for each genesis key.
assert(accts.count() == GENESIS_ADDRESS.longValue());
{
long userFunds = (long)(supply*0.8); // 80% to user accounts
supply -= userFunds;
// Genesis user gets half of all user funds
long genFunds = userFunds/2;
accts = addAccount(accts, GENESIS_ADDRESS, peerKeys.get(0), genFunds);
userFunds -= genFunds;
// One Peer account for each specified key (including initial genesis user)
for (int i = 0; i < keyCount; i++) {
Address address = Address.create(accts.count());
assert(address.longValue() == accts.count());
AccountKey key = peerKeys.get(i);
long userBalance = userFunds / (keyCount-i);
accts = addAccount(accts, address, key, userBalance);
userFunds -= userBalance;
}
assert(userFunds == 0L);
}
// Finally add peers
// Set up initial peers
// BASE_PEER_ADDRESS = accts.size();
{
long peerFunds = supply;
supply -= peerFunds;
for (int i = 0; i < keyCount; i++) {
AccountKey peerKey = peerKeys.get(i);
Address peerController = getGenesisPeerAddress(i);
// Divide funds among peers
long peerStake = peerFunds / (keyCount-i);
// Add peer with specified stake
peers = addPeer(peers, peerKey, peerController, peerStake);
peerFunds -= peerStake;
}
assert(peerFunds == 0L);
}
// Add the new accounts to the State
s = s.withAccounts(accts);
// Add peers to the State
s = s.withPeers(peers);
{ // Test total funds after creating user / peer accounts
long total = s.computeTotalFunds();
if (total != Constants.MAX_SUPPLY) throw new Error("Bad total amount: " + total);
}
return s;
}
/**
* Creates the built-in static Libraries (registry, trust)
* @param s State to add core libraries to
* @param trustAddress
* @param registryAddress
* @return Updates state
*/
private static State addStaticLibraries(State s) {
// At this point we have a raw initial State with no user or peer accounts
s = doActorDeploy(s, "/convex/core/registry.cvx");
s = doActorDeploy(s, "/convex/core/trust.cvx");
{ // Register core library now that registry exists
Context ctx = Context.create(s, INIT_ADDRESS);
ctx = ctx.eval(Reader.read("(call *registry* (cns-update 'convex.core " + CORE_ADDRESS + "))"));
s = ctx.getState();
s = register(s, CORE_ADDRESS, "Convex Core Library", "Core utilities accessible by default in any account.");
s = register(s, FOUNDATION_ADDRESS, "Foundation Governance", "Master network governance account.");
s = register(s, RESERVE_ADDRESS, "Foundation Reserve", "Unreleased Foundation coin reserve.");
s = register(s, UNRELEASED_ADDRESS, "Release Curve Reserve", "Release curve unreleased coins.");
s = register(s, DISTRIBUTION_ADDRESS, "Release Curve Pre-Distribution", "Release curve coins for future distribution.");
s = register(s, GOVERNANCE_ADDRESS, "Network Governance", "Network governance account.");
s = register(s, ADMIN_ADDRESS, "Network Admin", "Network admin accouns.");
}
return s;
}
public static State createState(List genesisKeys) {
return createState(genesisKeys.get(0),genesisKeys);
}
public static State createState(AccountKey genesisKey,List peerKeys) {
return createState(genesisKey,genesisKey,peerKeys);
}
public static State createState(AccountKey governanceKey, AccountKey genesisKey,List peerKeys) {
State s=createBaseState(governanceKey, genesisKey, peerKeys);
s = addStandardLibraries(s);
s = addTestingCurrencies(s);
s = addCNSTree(s);
// Final funds check
long finalTotal = s.computeTotalFunds();
if (finalTotal != Constants.MAX_SUPPLY)
throw new Error("Bad total funds in init state amount: " + finalTotal);
return s;
}
private static State addTestingCurrencies(State s) {
try {
@SuppressWarnings("unchecked")
AVector> table = (AVector>) Reader
.readResourceAsData("/convex/torus/genesis-currencies.cvx");
for (AVector row : table) {
s = doCurrencyDeploy(s, row);
}
} catch (IOException e) {
throw new Error("Failure reading source data for currencies",e);
}
return s;
}
private static State addStandardLibraries(State s) {
s = doActorDeploy(s, "/convex/asset/fungible.cvx");
s = doActorDeploy(s, "/convex/lab/trusted-oracle/actor.cvx");
s = doActorDeploy(s, "/convex/lab/oracle.cvx");
s = doActorDeploy(s, "/convex/asset/asset.cvx");
s = doActorDeploy(s, "/convex/torus/exchange.cvx");
s = doActorDeploy(s, "/convex/asset/nft/simple.cvx");
s = doActorDeploy(s, "/convex/asset/nft/basic.cvx");
s = doActorDeploy(s, "/convex/asset/nft/tokens.cvx");
s = doActorDeploy(s, "/convex/asset/box/actor.cvx");
s = doActorDeploy(s, "/convex/asset/box.cvx");
s = doActorDeploy(s, "/convex/asset/multi-token.cvx");
s = doActorDeploy(s, "/convex/asset/share.cvx");
s = doActorDeploy(s, "/convex/asset/market/trade.cvx");
s = doActorDeploy(s, "/convex/asset/wrap/convex.cvx");
s = doActorDeploy(s, "/convex/lab/play.cvx");
s = doActorDeploy(s, "/convex/lab/did.cvx");
s = doActorDeploy(s, "/convex/lab/curation-market.cvx");
s = doActorDeploy(s, "/convex/trust/ownership-monitor.cvx");
s = doActorDeploy(s, "/convex/trust/delegate.cvx");
s = doActorDeploy(s, "/convex/trust/whitelist.cvx");
s = doActorDeploy(s, "/convex/trust/monitors.cvx");
s = doActorDeploy(s, "/convex/trust/governance.cvx");
s = doActorDeploy(s, "/convex/asset/spatial.cvx");
// s = doActorDeploy(s, "convex/user.cvx");
return s;
}
private static State addCNSTree(State s) {
Context ctx=Context.create(s, INIT_ADDRESS);
//ctx=ctx.eval(Reader.read("(do (*registry*/create 'user.init))"));
//ctx.getResult();
// check we can get access to general trust monitors
//ctx=ctx.eval(Reader.read("(import convex.trust.monitors :as mon)"));
//ctx.getResult();
//ctx=ctx.eval(Reader.read("(def tmon (mon/permit-actions :create))"));
//ctx.getResult();
//ctx=ctx.eval(Reader.read("(do ("+TRUST_ADDRESS+"/change-control [*registry* [\"user\"]] tmon))"));
//ctx.getResult();
s=ctx.getState();
return s;
}
public static Address calcPeerAddress(int userCount, int index) {
return Address.create(GENESIS_ADDRESS.longValue() + userCount + index);
}
public static Address calcUserAddress(int index) {
return Address.create(GENESIS_ADDRESS.longValue() + index);
}
// A CVX file contains forms which must be wrapped in a `(do ...)` and deployed as an actor.
// First form is the name that must be used when registering the actor.
//
private static State doActorDeploy(State s, String resource) {
Context ctx = Context.create(s, INIT_ADDRESS);
ACell ADD_NETWORK_GOVERNANCE=Reader.read("(set-controller "+GOVERNANCE_ADDRESS+")");
try {
AList forms = Reader.readAll(Utils.readResourceAsString(resource));
AList code=forms.drop(1);
// Add network governance controller
code=code.cons(ADD_NETWORK_GOVERNANCE);
ctx = ctx.deploy(code.toCellArray());
if (ctx.isExceptional()) throw new Error("Error deploying actor: "+resource+"\n" + ctx.getValue());
Address addr=ctx.getResult();
@SuppressWarnings("unchecked")
AList qsym=(AList) forms.get(0);
Symbol sym=qsym.get(1);
ctx = ctx.eval(Code.cnsUpdate(sym, addr));
if (ctx.isExceptional()) throw new Error("Error while registering actor:" + ctx.getValue());
return ctx.getState();
} catch (IOException e) {
throw new Error(e);
}
}
private static State doCurrencyDeploy(State s, AVector row) {
String symName = row.get(0).toString();
String name = row.get(1).toString();
String desc = row.get(2).toString();
double usdPrice = RT.jvm(row.get(6)); // Value in USD for currency, e.g. USD=1.0, GBP=1.3
long decimals = RT.jvm(row.get(5)); // Decimals for lowest currency unit, e.g. USD = 2
long usdValue=(Long) RT.jvm(row.get(4)); // USD value of liquidity in currency
// number of sub-units in currency
long subDivisions=Math.round(Math.pow(10, decimals));
// Currency liquidity (in lowest currency subdivision)
double liquidity = (usdValue/usdPrice)*subDivisions;
long supply = Math.round(liquidity);
// CVX price for currency
double cvxPrice = usdPrice * 1000000000; // One CVX Gold = 1 USD in genesis
double cvx = cvxPrice * supply / subDivisions;
String metaString="{:name "+RT.print(name)+ ":desc "+RT.print(desc)+"}";
Context ctx = Context.create(s, GENESIS_ADDRESS);
ctx = ctx.eval(Reader
.read("(do "
+ "(import convex.fungible :as fun) "
+ "(deploy "
+ "'(call *registry* (register "+metaString+"))"
+ "(fun/build-token {:supply " + supply + " :decimals "+decimals+"})"
+")"
+ ")"));
Address addr = ctx.getResult();
ctx = ctx.eval(Reader.read("(do (import torus.exchange :as torus) (torus/add-liquidity " + addr + " "
+ (supply / 2) + " " + (cvx / 2) + "))"));
if (ctx.isExceptional()) throw new Error("Error adding market liquidity: " + ctx.getValue());
Symbol sym=Symbol.create("currency."+symName);
ctx = ctx.eval(Code.cnsUpdate(sym, addr));
if (ctx.isExceptional()) throw new Error("Error registering currency in CNS: " + ctx.getValue());
return ctx.getState();
}
private static State register(State state, Address origin, String name, String description) {
Context ctx = Context.create(state, origin);
ctx = ctx.eval(Reader.read("(call *registry* (register {:description \"" + description + "\" :name \"" + name + "\"}))"));
return ctx.getState();
}
public static Address getGenesisAddress() {
return GENESIS_ADDRESS;
}
public static Address getGenesisPeerAddress(int index) {
return GENESIS_ADDRESS.offset(index+1);
}
private static Index addPeer(Index peers, AccountKey peerKey,
Address owner, long initialStake) {
PeerStatus ps = PeerStatus.create(owner, initialStake, null);
if (peers.containsKey(peerKey)) throw new IllegalArgumentException("Duplicate peer key");
return peers.assoc(peerKey, ps);
}
private static AVector addAccount(AVector accts, Address address, AccountStatus account) {
long num=address.longValue();
if (accts.count() != num) throw new Error("Incorrect initialisation address: " + address);
accts = accts.conj(account);
return accts;
}
private static AVector addCoreLibrary(AVector accts) {
if (accts.count() != CORE_ADDRESS.longValue()) throw new Error("Incorrect core library address: " + accts.count());
AccountStatus as = AccountStatus.createActor();
as=as.withEnvironment(Core.ENVIRONMENT);
as=as.withMetadata(Core.METADATA);
accts = accts.conj(as);
return accts;
}
private static AVector addAccount(AVector accts, Address a, AccountKey key,
long balance) {
if (accts.count() != a.longValue()) throw new Error("Incorrect account address: " + a);
AccountStatus as = AccountStatus.create(0L, balance, key);
as = as.withMemory(Constants.INITIAL_ACCOUNT_ALLOWANCE);
accts = accts.conj(as);
return accts;
}
}