Skip to content

Commit

Permalink
Merge pull request #885 from ava-labs/convertSubnetTx
Browse files Browse the repository at this point in the history
Implement ConvertSubnetTx
  • Loading branch information
ruijialin-avalabs authored Oct 24, 2024
2 parents 7d08ce5 + d09a4e7 commit 0097d86
Show file tree
Hide file tree
Showing 37 changed files with 1,071 additions and 118 deletions.
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ PRIVATE_KEY=...
C_CHAIN_ADDRESS=0x...
X_CHAIN_ADDRESS=X-fuji...
P_CHAIN_ADDRESS=P-fuji...
CORETH_ADDRESS=C-fuji...
CORETH_ADDRESS=C-fuji...
NODE_ID=NodeID-...
BLS_PUBLIC_KEY=0x...
BLS_SIGNATURE=0x...
16 changes: 5 additions & 11 deletions examples/c-chain/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,15 @@ import { bech32ToBytes, hexToBuffer } from '../../src/utils';
import { getContextFromURI } from '../../src/vms/context';
import { newExportTxFromBaseFee } from '../../src/vms/evm';
import { evmapi } from '../chain_apis';

const C_CHAIN_ADDRESS = process.env.C_CHAIN_ADDRESS;
const X_CHAIN_ADDRESS = process.env.X_CHAIN_ADDRESS;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
import { getEnvVars } from '../utils/getEnvVars';

const main = async () => {
if (!C_CHAIN_ADDRESS || !X_CHAIN_ADDRESS || !PRIVATE_KEY) {
throw new Error('Missing environment variable(s).');
}
const { AVAX_PUBLIC_URL, C_CHAIN_ADDRESS, PRIVATE_KEY, X_CHAIN_ADDRESS } =
getEnvVars();

const provider = new JsonRpcProvider(
process.env.AVAX_PUBLIC_URL + '/ext/bc/C/rpc',
);
const provider = new JsonRpcProvider(AVAX_PUBLIC_URL + '/ext/bc/C/rpc');

const context = await getContextFromURI(process.env.AVAX_PUBLIC_URL);
const context = await getContextFromURI(AVAX_PUBLIC_URL);
const txCount = await provider.getTransactionCount(C_CHAIN_ADDRESS);
const baseFee = await evmapi.getBaseFee();
const xAddressBytes = bech32ToBytes(X_CHAIN_ADDRESS);
Expand Down
12 changes: 4 additions & 8 deletions examples/c-chain/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@ import { getContextFromURI } from '../../src/vms/context';
import { newImportTxFromBaseFee } from '../../src/vms/evm';
import { evmapi } from '../chain_apis';
import { getChainIdFromContext } from '../utils/getChainIdFromContext';

const C_CHAIN_ADDRESS = process.env.C_CHAIN_ADDRESS;
const CORETH_ADDRESS = process.env.CORETH_ADDRESS;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
import { getEnvVars } from '../utils/getEnvVars';

const main = async (sourceChain: 'X' | 'P') => {
if (!C_CHAIN_ADDRESS || !CORETH_ADDRESS || !PRIVATE_KEY) {
throw new Error('Missing environment variable(s).');
}
const { AVAX_PUBLIC_URL, C_CHAIN_ADDRESS, PRIVATE_KEY, CORETH_ADDRESS } =
getEnvVars();

const baseFee = await evmapi.getBaseFee();
const context = await getContextFromURI(process.env.AVAX_PUBLIC_URL);
const context = await getContextFromURI(AVAX_PUBLIC_URL);

const { utxos } = await evmapi.getUTXOs({
sourceChain,
Expand Down
22 changes: 20 additions & 2 deletions examples/generate-keys.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import { secp256k1, utils } from '../src';
import { info, secp256k1, utils } from '../src';
import { Address } from 'micro-eth-signer';
import { getEnvVars } from './utils/getEnvVars';

type NodeIdResponse = {
nodeID: string;
nodePOP: { publicKey: string; proofOfPossession: string };
};

/**
* Generate a new private/public key pair and console log out the needed environment variables
* needed to run the examples. Please these values in a `.env` file.
*/
const main = async () => {
const { AVAX_PUBLIC_URL } = getEnvVars();

const privateKey = secp256k1.randomPrivateKey();
const publicKey = secp256k1.getPublicKey(privateKey);

const hrp = 'custom';
const address = utils.formatBech32(
hrp,
secp256k1.publicKeyBytesToAddress(publicKey),
);

const infoApi = new info.InfoApi(AVAX_PUBLIC_URL);

const nodeIdResponse = (await infoApi.getNodeId()) as NodeIdResponse;

console.log('Copy the below values to your `.env` file:');
console.log('------------------------------------------\n');

Expand All @@ -26,6 +37,13 @@ const main = async () => {
console.log('X_CHAIN_ADDRESS=', `"X-${address}"`);
console.log('C_CHAIN_ADDRESS=', `"${Address.fromPublicKey(publicKey)}"`);
console.log('CORETH_ADDRESS=', `"C-${address}"`);

console.log('NodeId=', `"${nodeIdResponse.nodeID}"`);
console.log('BLS_PUBLIC_KEY=', `"${nodeIdResponse.nodePOP.publicKey}"`);
console.log(
'BLS_SIGNATURE=',
`"${nodeIdResponse.nodePOP.proofOfPossession}"`,
);
};

main();
10 changes: 3 additions & 7 deletions examples/p-chain/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@ import { bech32ToBytes, hexToBuffer } from '../../src/utils';
import { getContextFromURI } from '../../src/vms/context';
import { newBaseTx } from '../../src/vms/pvm';
import { pvmapi } from '../chain_apis';

const P_CHAIN_ADDRESS = process.env.P_CHAIN_ADDRESS;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
import { getEnvVars } from '../utils/getEnvVars';

const main = async () => {
if (!P_CHAIN_ADDRESS || !PRIVATE_KEY) {
throw new Error('Missing environment variable(s).');
}
const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars();

const { utxos } = await pvmapi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] });
const context = await getContextFromURI(process.env.AVAX_PUBLIC_URL);
const context = await getContextFromURI(AVAX_PUBLIC_URL);

const tx = newBaseTx(context, [bech32ToBytes(P_CHAIN_ADDRESS)], utxos, [
TransferableOutput.fromNative(context.avaxAssetID, BigInt(0.1 * 1e9), [
Expand Down
10 changes: 3 additions & 7 deletions examples/p-chain/delegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@ import { bech32ToBytes, hexToBuffer } from '../../src/utils';
import { getContextFromURI } from '../../src/vms/context';
import { PVMApi, newAddPermissionlessDelegatorTx } from '../../src/vms/pvm';
import { pvmapi } from '../chain_apis';

const P_CHAIN_ADDRESS = process.env.P_CHAIN_ADDRESS;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
import { getEnvVars } from '../utils/getEnvVars';

const main = async () => {
if (!P_CHAIN_ADDRESS || !PRIVATE_KEY) {
throw new Error('Missing environment variable(s).');
}
const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars();

const { utxos } = await pvmapi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] });
const context = await getContextFromURI(process.env.AVAX_PUBLIC_URL);
const context = await getContextFromURI(AVAX_PUBLIC_URL);
const startTime = await new PVMApi().getTimestamp();
const startDate = new Date(startTime.timestamp);
const start = BigInt(startDate.getTime() / 1000);
Expand Down
77 changes: 77 additions & 0 deletions examples/p-chain/etna/convertSubnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { addTxSignatures, pvm, utils } from '../../../src';
import { setupEtnaExample } from './utils/etna-helper';
import { ConvertSubnetValidator } from '../../../src/serializable/fxs/pvm/convertSubnetValidator';
import { ProofOfPossession } from '../../../src/serializable/pvm';
import { PChainOwner } from '../../../src/serializable/fxs/pvm/pChainOwner';
import { getEnvVars } from '../../utils/getEnvVars';

const AMOUNT_TO_VALIDATE_AVAX: number = 1;
const BALANCE_AVAX: number = 1;

/**
* Converts a subnet to permissionless subnet.
*
* **Note** A subnet must be created (createSubnetTx) and a chain must be created (createChainTx)
* before a subnet can be converted from permissioned to permissionless.
* @param BLS_PUBLIC_KEY BLS key from info.getNodeID on AVAX_PUBLIC_URL
* @param BLS_SIGNATURE BLS signature from info.getNodeID on AVAX_PUBLIC_URL
* @param NODE_ID the ID of the node from info.getNodeID on AVAX_PUBLIC_URL.
* @param chainId the ID of the chain that is created via `createChainTx`.
* @param subnetId the ID of the subnet that is created via `createSubnetTx`.
* @returns The resulting transaction's ID.
*/
const convertSubnetTxExmaple = async () => {
const {
AVAX_PUBLIC_URL,
P_CHAIN_ADDRESS,
PRIVATE_KEY,
NODE_ID,
BLS_PUBLIC_KEY,
BLS_SIGNATURE,
} = getEnvVars();
const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL);

const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] });

const testPAddr = utils.bech32ToBytes(P_CHAIN_ADDRESS);

const pChainOwner = PChainOwner.fromNative([testPAddr], 1);

const publicKey = utils.hexToBuffer(BLS_PUBLIC_KEY);

const signature = utils.hexToBuffer(BLS_SIGNATURE);

const signer = new ProofOfPossession(publicKey, signature);

const validator = ConvertSubnetValidator.fromNative(
NODE_ID,
BigInt(AMOUNT_TO_VALIDATE_AVAX * 1e9),
BigInt(BALANCE_AVAX * 1e9),
signer,
pChainOwner,
pChainOwner,
);

const tx = pvm.e.newConvertSubnetTx(
{
feeState,
fromAddressesBytes: [testPAddr],
subnetId: '', // subnetId from createSubnetTx
utxos,
chainId: '', // chainId from createChainTx
validators: [validator],
subnetAuth: [0],
address: testPAddr,
},
context,
);

await addTxSignatures({
unsignedTx: tx,
privateKeys: [utils.hexToBuffer(PRIVATE_KEY)],
});

return pvmApi.issueSignedTx(tx.getSignedTx());
};

convertSubnetTxExmaple().then(console.log);
49 changes: 49 additions & 0 deletions examples/p-chain/etna/createChain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { addTxSignatures, pvm, utils } from '../../../src';
import { setupEtnaExample } from './utils/etna-helper';
import { testGenesisData } from '../../../src/fixtures/transactions';
import { getEnvVars } from '../../utils/getEnvVars';

/**
* Create a new chain on the P-Chain.
*
* **Note** A subnet must be created (createSubnetTx) before a chain can be created.
* @param vmId the platform chain's vmID can be found in the `InfoApi.getVMs` response.
* @param subnetId the ID of the subnet that is created via `createSubnetTx`.
* @param chainName the name of the chain. Can be any string.
* @returns The resulting transaction's ID.
*/
const createChainTxExample = async () => {
const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars();
const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL);

const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] });

const testPAddr = utils.bech32ToBytes(P_CHAIN_ADDRESS);

const vmId = 'rWhpuQPF1kb72esV2momhMuTYGkEb1oL29pt2EBXWmSy4kxnT'; // platform vmId
const subnetId = ''; // subnetId from createSubnetTx

const tx = pvm.e.newCreateChainTx(
{
feeState,
fromAddressesBytes: [testPAddr],
utxos,
chainName: 'test chain',
subnetAuth: [0],
subnetId,
vmId,
fxIds: [],
genesisData: testGenesisData,
},
context,
);

await addTxSignatures({
unsignedTx: tx,
privateKeys: [utils.hexToBuffer(PRIVATE_KEY)],
});

return pvmApi.issueSignedTx(tx.getSignedTx());
};

createChainTxExample().then(console.log);
32 changes: 32 additions & 0 deletions examples/p-chain/etna/createSubnet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { addTxSignatures, pvm, utils } from '../../../src';
import { getEnvVars } from '../../utils/getEnvVars';
import { setupEtnaExample } from './utils/etna-helper';

const createSubnetTxExample = async () => {
const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars();

const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL);

const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] });

const testPAddr = utils.bech32ToBytes(P_CHAIN_ADDRESS);

const tx = pvm.e.newCreateSubnetTx(
{
feeState,
fromAddressesBytes: [testPAddr],
utxos,
subnetOwners: [testPAddr],
},
context,
);

await addTxSignatures({
unsignedTx: tx,
privateKeys: [utils.hexToBuffer(PRIVATE_KEY)],
});

return pvmApi.issueSignedTx(tx.getSignedTx());
};

createSubnetTxExample().then(console.log);
6 changes: 3 additions & 3 deletions examples/p-chain/etna/utils/etna-helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Context, Info, pvm } from '../../../../src';
import { Context, info, pvm } from '../../../../src';
import type { FeeState } from '../../../../src/vms/pvm';

export const setupEtnaExample = async (
Expand All @@ -12,9 +12,9 @@ export const setupEtnaExample = async (
const pvmApi = new pvm.PVMApi(uri);
const feeState = await pvmApi.getFeeState();

const info = new Info(uri);
const infoApi = new info.InfoApi(uri);

const { etnaTime } = await info.getUpgradesInfo();
const { etnaTime } = await infoApi.getUpgradesInfo();

const etnaDateTime = new Date(etnaTime);
const now = new Date();
Expand Down
16 changes: 9 additions & 7 deletions examples/p-chain/etna/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ const DAYS_TO_VALIDATE: number = 21;
const nodeId = getRandomNodeId();

const main = async () => {
const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY } = getEnvVars();
const {
AVAX_PUBLIC_URL,
P_CHAIN_ADDRESS,
PRIVATE_KEY,
BLS_PUBLIC_KEY,
BLS_SIGNATURE,
} = getEnvVars();

const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL);

Expand All @@ -23,13 +29,9 @@ const main = async () => {
endTime.setDate(endTime.getDate() + DAYS_TO_VALIDATE);
const end: bigint = BigInt(endTime.getTime() / 1_000);

const publicKey = utils.hexToBuffer(
'0x8f95423f7142d00a48e1014a3de8d28907d420dc33b3052a6dee03a3f2941a393c2351e354704ca66a3fc29870282e15',
);
const publicKey = utils.hexToBuffer(BLS_PUBLIC_KEY);

const signature = utils.hexToBuffer(
'0x86a3ab4c45cfe31cae34c1d06f212434ac71b1be6cfe046c80c162e057614a94a5bc9f1ded1a7029deb0ba4ca7c9b71411e293438691be79c2dbf19d1ca7c3eadb9c756246fc5de5b7b89511c7d7302ae051d9e03d7991138299b5ed6a570a98',
);
const signature = utils.hexToBuffer(BLS_SIGNATURE);

const tx = pvm.e.newAddPermissionlessValidatorTx(
{
Expand Down
12 changes: 4 additions & 8 deletions examples/p-chain/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@ import { getContextFromURI } from '../../src/vms/context';
import { newExportTx } from '../../src/vms/pvm';
import { pvmapi } from '../chain_apis';
import { getChainIdFromContext } from '../utils/getChainIdFromContext';

const P_CHAIN_ADDRESS = process.env.P_CHAIN_ADDRESS;
const X_CHAIN_ADDRESS = process.env.X_CHAIN_ADDRESS;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
import { getEnvVars } from '../utils/getEnvVars';

const main = async () => {
if (!P_CHAIN_ADDRESS || !X_CHAIN_ADDRESS || !PRIVATE_KEY) {
throw new Error('Missing environment variable(s).');
}
const { AVAX_PUBLIC_URL, P_CHAIN_ADDRESS, PRIVATE_KEY, X_CHAIN_ADDRESS } =
getEnvVars();

const context = await getContextFromURI(process.env.AVAX_PUBLIC_URL);
const context = await getContextFromURI(AVAX_PUBLIC_URL);

const { utxos } = await pvmapi.getUTXOs({
addresses: [P_CHAIN_ADDRESS],
Expand Down
Loading

0 comments on commit 0097d86

Please sign in to comment.