Skip to content

Commit

Permalink
feat: implement IncreaseBalanceTx
Browse files Browse the repository at this point in the history
Implement IncreaseBalanceTx (#888)

* feat: add test transactions and complexity constants

* feat: add IncreaseBalanceTx

* chore: update ts-jest and jest config

* fix: circular dependencies

Closes #873.

* test: add newIncreaseBalanceTx builder tests

* docs: add example for increaseBalanceTx

* chore: adjust example envs
  • Loading branch information
erictaylor authored Oct 25, 2024
1 parent 0097d86 commit 683b56f
Show file tree
Hide file tree
Showing 31 changed files with 737 additions and 215 deletions.
6 changes: 3 additions & 3 deletions examples/p-chain/etna/convertSubnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ const BALANCE_AVAX: number = 1;
* @param subnetId the ID of the subnet that is created via `createSubnetTx`.
* @returns The resulting transaction's ID.
*/
const convertSubnetTxExmaple = async () => {
const convertSubnetTxExample = async () => {
const {
AVAX_PUBLIC_URL,
P_CHAIN_ADDRESS,
PRIVATE_KEY,
NODE_ID,
BLS_PUBLIC_KEY,
BLS_SIGNATURE,
} = getEnvVars();
} = getEnvVars(['BLS_PUBLIC_KEY', 'BLS_SIGNATURE', 'NODE_ID']);
const { context, feeState, pvmApi } = await setupEtnaExample(AVAX_PUBLIC_URL);

const { utxos } = await pvmApi.getUTXOs({ addresses: [P_CHAIN_ADDRESS] });
Expand Down Expand Up @@ -74,4 +74,4 @@ const convertSubnetTxExmaple = async () => {
return pvmApi.issueSignedTx(tx.getSignedTx());
};

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

const BALANCE_AVAX: number = 1;
const VALIDATION_ID: string = '';

const increaseBalanceTx = 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 unsignedTx = pvm.e.newIncreaseBalanceTx(
{
balance: BigInt(BALANCE_AVAX * 1e9),
feeState,
fromAddressesBytes: [testPAddr],
utxos,
validationId: VALIDATION_ID,
},
context,
);

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

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

await increaseBalanceTx().then(console.log);
45 changes: 33 additions & 12 deletions examples/utils/getEnvVars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,58 @@ const P_CHAIN_ADDRESS = process.env['P_CHAIN_ADDRESS'];
const PRIVATE_KEY = process.env['PRIVATE_KEY'];
const X_CHAIN_ADDRESS = process.env['X_CHAIN_ADDRESS'];
const C_CHAIN_ADDRESS = process.env['C_CHAIN_ADDRESS'];
const NODE_ID = process.env['NODE_ID'];
const BLS_PUBLIC_KEY = process.env['BLS_PUBLIC_KEY'];
const BLS_SIGNATURE = process.env['BLS_SIGNATURE'];
const CORETH_ADDRESS = process.env['CORETH_ADDRESS'];

export const getEnvVars = () => {
type PrimaryEnvKeys =
| 'AVAX_PUBLIC_URL'
| 'P_CHAIN_ADDRESS'
| 'PRIVATE_KEY'
| 'X_CHAIN_ADDRESS'
| 'C_CHAIN_ADDRESS'
| 'CORETH_ADDRESS';

type ExampleEnvs<T extends string> = Record<PrimaryEnvKeys | T, string>;

export const getEnvVars = <T extends string>(
additionalEnvsKeys: T[] = [],
): ExampleEnvs<T> => {
if (
!(
AVAX_PUBLIC_URL &&
P_CHAIN_ADDRESS &&
PRIVATE_KEY &&
X_CHAIN_ADDRESS &&
NODE_ID &&
BLS_PUBLIC_KEY &&
BLS_SIGNATURE &&
C_CHAIN_ADDRESS &&
CORETH_ADDRESS
)
) {
throw new Error('Missing environment variable(s).');
throw new Error(
'Missing required environment variable(s). Please check your .env file.',
);
}

return {
const additionalEnvs = additionalEnvsKeys.reduce((acc, key) => {
const env = process.env[key];

if (!env) {
throw new Error(`Missing environment variable: ${key}`);
}

return {
...acc,
[key]: env,
};
}, {} as Record<T, string>);

const envs: ExampleEnvs<T> = {
...additionalEnvs,
AVAX_PUBLIC_URL,
P_CHAIN_ADDRESS,
PRIVATE_KEY,
X_CHAIN_ADDRESS,
NODE_ID,
BLS_PUBLIC_KEY,
BLS_SIGNATURE,
C_CHAIN_ADDRESS,
CORETH_ADDRESS,
};

return envs;
};
20 changes: 9 additions & 11 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
import { createDefaultEsmPreset, type JestConfigWithTsJest } from 'ts-jest';

const preset = createDefaultEsmPreset();

const jestConfig: JestConfigWithTsJest = {
...preset,
moduleFileExtensions: ['js', 'json', 'ts'],
rootDir: 'src',
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
useESM: true,
},
],
},
collectCoverageFrom: ['**/*.(t|j)s'],
coverageDirectory: '../coverage',
testEnvironment: 'node',
coverageProvider: 'v8',
extensionsToTreatAsEsm: ['.ts'],
// Experimental to fix issues with BigInt serialization
// See: https://jestjs.io/docs/configuration#workerthreads
// @ts-expect-error - workerThreads is not in the type definition
workerThreads: true,
};

export default jestConfig;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"rollup": "4.9.6",
"rollup-plugin-filesize": "10.0.0",
"semantic-release": "21.0.1",
"ts-jest": "29.1.2",
"ts-jest": "29.2.5",
"ts-node": "10.9.2",
"typescript": "5.3.3"
},
Expand Down
7 changes: 7 additions & 0 deletions src/fixtures/pvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
RemoveSubnetValidatorTx,
TransferSubnetOwnershipTx,
TransformSubnetTx,
IncreaseBalanceTx,
} from '../serializable/pvm';
import {
baseTx,
Expand Down Expand Up @@ -346,6 +347,12 @@ export const convertSubnetTxBytes = () =>
inputBytes(),
);

export const increaseBalanceTx = () =>
new IncreaseBalanceTx(baseTx(), id(), bigIntPr());

export const increaseBalanceTxBytes = () =>
concatBytes(baseTxbytes(), idBytes(), bigIntPrBytes());

export const pChainOwner = () => new PChainOwner(int(), addresses()());

export const pChainOwnerBytes = () =>
Expand Down
1 change: 1 addition & 0 deletions src/serializable/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export enum TypeSymbols {

ConvertSubnetTx = 'pvm.ConvertSubnetTx',
ConvertSubnetValidator = 'pvm.ConvertSubnetValidator',
IncreaseBalanceTx = 'pvm.IncreaseBalanceTx',

PChainOwner = 'pvm.PChainOwner',

Expand Down
2 changes: 2 additions & 0 deletions src/serializable/fxs/pvm/convertSubnetValidator.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { testPVMCodec } from '../../../fixtures/codec';
import {
convertSubnetValidator,
convertSubnetValidatorBytes,
Expand All @@ -10,4 +11,5 @@ testSerialization(
ConvertSubnetValidator,
convertSubnetValidator,
convertSubnetValidatorBytes,
testPVMCodec,
);
2 changes: 1 addition & 1 deletion src/serializable/fxs/pvm/convertSubnetValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Codec } from '../../codec';
import { serializable } from '../../common/types';
import { BigIntPr, Bytes } from '../../primitives';
import { TypeSymbols } from '../../constants';
import { ProofOfPossession } from '../../pvm';
import { ProofOfPossession } from '../../pvm/proofOfPossession';
import { NodeId } from '../common';
import { PChainOwner } from './pChainOwner';

Expand Down
9 changes: 8 additions & 1 deletion src/serializable/fxs/pvm/pChainOwner.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { testPVMCodec } from '../../../fixtures/codec';
import { pChainOwner, pChainOwnerBytes } from '../../../fixtures/pvm';
import { testSerialization } from '../../../fixtures/utils/serializable';
import { PChainOwner } from './pChainOwner';

testSerialization('PChainOwner', PChainOwner, pChainOwner, pChainOwnerBytes);
testSerialization(
'PChainOwner',
PChainOwner,
pChainOwner,
pChainOwnerBytes,
testPVMCodec,
);
5 changes: 5 additions & 0 deletions src/serializable/pvm/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { TransferSubnetOwnershipTx } from './transferSubnetOwnershipTx';
import { TransformSubnetTx } from './transformSubnetTx';
import { BaseTx } from './baseTx';
import { ConvertSubnetTx } from './convertSubnetTx';
import { IncreaseBalanceTx } from './increaseBalanceTx';

/**
* @see https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/txs/codec.go#L35
Expand Down Expand Up @@ -60,6 +61,10 @@ export const codec = new Codec([
BaseTx, // 34

ConvertSubnetTx, // 35
// Replace these with the actual txs when they are implemented
...new Array(2), // 36-37 RegisterSubnetValidatorTx, SetSubnetValidatorWeightTx
IncreaseBalanceTx, // 38
// DisableSubnetValidatorTx, // 39
]);

let manager: Manager;
Expand Down
12 changes: 12 additions & 0 deletions src/serializable/pvm/increaseBalance.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { testPVMCodec } from '../../fixtures/codec';
import { increaseBalanceTx, increaseBalanceTxBytes } from '../../fixtures/pvm';
import { testSerialization } from '../../fixtures/utils/serializable';
import { IncreaseBalanceTx } from './increaseBalanceTx';

testSerialization(
'IncreaseBalanceTx',
IncreaseBalanceTx,
increaseBalanceTx,
increaseBalanceTxBytes,
testPVMCodec,
);
44 changes: 44 additions & 0 deletions src/serializable/pvm/increaseBalanceTx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { pack, unpack } from '../../utils/struct';
import { BaseTx } from '../avax/baseTx';
import type { Codec } from '../codec';
import { serializable } from '../common/types';
import { TypeSymbols } from '../constants';
import { Id } from '../fxs/common';
import { BigIntPr } from '../primitives';
import { PVMTx } from './abstractTx';

@serializable()
export class IncreaseBalanceTx extends PVMTx {
_type = TypeSymbols.IncreaseBalanceTx;

constructor(
public readonly baseTx: BaseTx,
/**
* The corresponding Validator ID.
*/
public readonly validationId: Id,
/**
* Balance <= sum of AVAX inputs - sum of AVAX outputs - Tx fee
*/
public readonly balance: BigIntPr,
) {
super();
}

static fromBytes(
bytes: Uint8Array,
codec: Codec,
): [increaseBalanceTx: IncreaseBalanceTx, rest: Uint8Array] {
const [baseTx, validationId, balance, rest] = unpack(
bytes,
[BaseTx, Id, BigIntPr],
codec,
);

return [new IncreaseBalanceTx(baseTx, validationId, balance), rest];
}

toBytes(codec: Codec) {
return pack([this.baseTx, this.validationId, this.balance], codec);
}
}
2 changes: 2 additions & 0 deletions src/serializable/pvm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { AbstractSubnetTx } from './abstractSubnetTx';
import { TransferSubnetOwnershipTx } from './transferSubnetOwnershipTx';
import { TransformSubnetTx } from './transformSubnetTx';
import { ConvertSubnetTx } from './convertSubnetTx';
import { IncreaseBalanceTx } from './increaseBalanceTx';

export * from './typeGuards';
export {
Expand All @@ -46,4 +47,5 @@ export {
TransferSubnetOwnershipTx,
TransformSubnetTx,
ConvertSubnetTx,
IncreaseBalanceTx,
};
4 changes: 4 additions & 0 deletions src/serializable/pvm/typeGuards.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
isPvmBaseTx,
isTransferSubnetOwnershipTx,
isTransformSubnetTx,
isConvertSubnetTx,
isIncreaseBalanceTx,
} from './typeGuards';

const cases: [any, TypeSymbols][] = [
Expand All @@ -38,6 +40,8 @@ const cases: [any, TypeSymbols][] = [
[isRewardValidatorTx, TypeSymbols.RewardValidatorTx],
[isTransferSubnetOwnershipTx, TypeSymbols.TransferSubnetOwnershipTx],
[isTransformSubnetTx, TypeSymbols.TransformSubnetTx],
[isConvertSubnetTx, TypeSymbols.ConvertSubnetTx],
[isIncreaseBalanceTx, TypeSymbols.IncreaseBalanceTx],
];

const guards = cases.map((caseItem) => caseItem[0]);
Expand Down
5 changes: 5 additions & 0 deletions src/serializable/pvm/typeGuards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { TransferSubnetOwnershipTx } from './transferSubnetOwnershipTx';
import { TypeSymbols } from '../constants';
import type { TransformSubnetTx } from './transformSubnetTx';
import type { ConvertSubnetTx } from './convertSubnetTx';
import type { IncreaseBalanceTx } from './increaseBalanceTx';

export function isPvmBaseTx(tx: Transaction): tx is BaseTx {
return tx._type === TypeSymbols.PvmBaseTx;
Expand Down Expand Up @@ -92,6 +93,10 @@ export function isConvertSubnetTx(tx: Transaction): tx is ConvertSubnetTx {
return tx._type === TypeSymbols.ConvertSubnetTx;
}

export function isIncreaseBalanceTx(tx: Transaction): tx is IncreaseBalanceTx {
return tx._type === TypeSymbols.IncreaseBalanceTx;
}

export function isEmptySigner(
signer: Signer | SignerEmpty,
): signer is SignerEmpty {
Expand Down
9 changes: 8 additions & 1 deletion src/serializable/pvm/validator.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { testPVMCodec } from '../../fixtures/codec';
import { validator, validatorBytes } from '../../fixtures/pvm';
import { testSerialization } from '../../fixtures/utils/serializable';
import { Validator } from './validator';

testSerialization('Validator', Validator, validator, validatorBytes);
testSerialization(
'Validator',
Validator,
validator,
validatorBytes,
testPVMCodec,
);
Loading

0 comments on commit 683b56f

Please sign in to comment.