Skip to content

Commit

Permalink
Merge pull request #887 from ava-labs/erictaylor/convertSubnetTx
Browse files Browse the repository at this point in the history
Tests and fixes for ConvertSubnetTx
  • Loading branch information
ruijialin-avalabs authored Oct 23, 2024
2 parents ce3abbf + 9b82b2d commit 52eb746
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 59 deletions.
36 changes: 21 additions & 15 deletions src/fixtures/pvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import {
transferableOutputBytes,
} from './avax';
import {
address,
addressBytes,
addresses,
addressesBytes,
id,
idBytes,
nodeId,
Expand Down Expand Up @@ -65,6 +65,7 @@ import { makeList, makeListBytes } from './utils/makeList';
import type { FeeState } from '../vms/pvm';
import { ConvertSubnetTx } from '../serializable/pvm/convertSubnetTx';
import { ConvertSubnetValidator } from '../serializable/fxs/pvm/convertSubnetValidator';
import { PChainOwner } from '../serializable/fxs/pvm/pChainOwner';

export const validator = () =>
new Validator(nodeId(), bigIntPr(), bigIntPr(), bigIntPr());
Expand Down Expand Up @@ -306,33 +307,30 @@ export const transformSubnetTxBytes = () =>

export const convertSubnetValidator = () =>
new ConvertSubnetValidator(
nodeId(),
bytes(),
bigIntPr(),
bigIntPr(),
signer(),
outputOwner(),
outputOwner(),
proofOfPossession(),
pChainOwner(),
pChainOwner(),
);

export const convertSubnetValidatorBytes = () =>
concatBytes(
nodeIdBytes(),
bytesBytes(),
bigIntPrBytes(),
bigIntPrBytes(),
bytesForInt(28),
signerBytes(),
bytesForInt(11),
outputOwnerBytes(),
bytesForInt(11),
outputOwnerBytes(),
proofOfPossessionBytes(),
pChainOwnerBytes(),
pChainOwnerBytes(),
);

export const convertSubnetTx = () =>
new ConvertSubnetTx(
baseTx(),
id(),
id(),
address(),
bytes(),
makeList(convertSubnetValidator)(),
input(),
);
Expand All @@ -342,12 +340,20 @@ export const convertSubnetTxBytes = () =>
baseTxbytes(),
idBytes(),
idBytes(),
addressBytes(),
bytesBytes(),
makeListBytes(convertSubnetValidatorBytes)(),
bytesForInt(10),
inputBytes(),
);

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

export const pChainOwnerBytes = () =>
concatBytes(
intBytes(), // threshold
addressesBytes(),
);

export const feeState = (): FeeState => ({
capacity: 1n,
excess: 1n,
Expand Down
13 changes: 13 additions & 0 deletions src/serializable/fxs/pvm/convertSubnetValidator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {
convertSubnetValidator,
convertSubnetValidatorBytes,
} from '../../../fixtures/pvm';
import { testSerialization } from '../../../fixtures/utils/serializable';
import { ConvertSubnetValidator } from './convertSubnetValidator';

testSerialization(
'ConvertSubnetValidator',
ConvertSubnetValidator,
convertSubnetValidator,
convertSubnetValidatorBytes,
);
59 changes: 34 additions & 25 deletions src/serializable/fxs/pvm/convertSubnetValidator.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { pack, unpack } from '../../../utils/struct';
import { Codec } from '../../codec/codec';
import type { Codec } from '../../codec';
import type { Serializable } from '../../common/types';
import { serializable } from '../../common/types';
import { BigIntPr } from '../../primitives';
import { BigIntPr, Bytes } from '../../primitives';
import { TypeSymbols } from '../../constants';
import { emptyNodeId } from '../../../constants/zeroValue';
import { ProofOfPossession } from '../../pvm';
import { NodeId } from '../common';
import { concatBytes } from '@noble/hashes/utils';
import type { SignerEmpty } from '../../pvm';
import type { Signer } from '../../pvm';
import type { OutputOwners } from '../secp256k1';
import { PChainOwner } from './pChainOwner';

/**
* @see https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/txs/convert_subnet_tx.go#86
Expand All @@ -19,36 +16,36 @@ export class ConvertSubnetValidator {
_type = TypeSymbols.ConvertSubnetValidator;

constructor(
public readonly nodeId: NodeId,
public readonly nodeId: Bytes,
public readonly weight: BigIntPr,
public readonly balance: BigIntPr,
public readonly signer: Signer | SignerEmpty,
public readonly signer: ProofOfPossession,
public readonly remainingBalanceOwner: Serializable,
public readonly deactivationOwner: Serializable,
) {}

getBalance() {
return this.balance.value();
return this.balance;
}

getRemainingBalanceOwner() {
return this.remainingBalanceOwner as OutputOwners;
return this.remainingBalanceOwner as PChainOwner;
}

getDeactivationOwner() {
return this.deactivationOwner as OutputOwners;
return this.deactivationOwner as PChainOwner;
}

static fromNative(
nodeId: string,
weight: bigint,
balance: bigint,
signer: Signer | SignerEmpty,
remainingBalanceOwner: OutputOwners,
deactivationOwner: OutputOwners,
signer: ProofOfPossession,
remainingBalanceOwner: PChainOwner,
deactivationOwner: PChainOwner,
) {
return new ConvertSubnetValidator(
NodeId.fromString(nodeId),
new Bytes(NodeId.fromString(nodeId).toBytes()),
new BigIntPr(weight),
new BigIntPr(balance),
signer,
Expand All @@ -69,7 +66,11 @@ export class ConvertSubnetValidator {
remainingBalanceOwner,
deactivationOwner,
rest,
] = unpack(bytes, [NodeId, BigIntPr, BigIntPr, Codec, Codec, Codec], codec);
] = unpack(
bytes,
[Bytes, BigIntPr, BigIntPr, ProofOfPossession, PChainOwner, PChainOwner],
codec,
);

return [
new ConvertSubnetValidator(
Expand All @@ -85,11 +86,16 @@ export class ConvertSubnetValidator {
}

toBytes(codec: Codec) {
return concatBytes(
pack([this.nodeId, this.weight, this.balance], codec),
codec.PackPrefix(this.signer),
codec.PackPrefix(this.remainingBalanceOwner),
codec.PackPrefix(this.deactivationOwner),
return pack(
[
this.nodeId,
this.weight,
this.balance,
this.signer,
this.remainingBalanceOwner,
this.deactivationOwner,
],
codec,
);
}

Expand All @@ -98,9 +104,12 @@ export class ConvertSubnetValidator {
throw new Error('Weight must be greater than 0');
}

if (this.nodeId === emptyNodeId) {
throw new Error('Node ID must be non-empty');
}
// const nodeId = new NodeId(this.nodeId.toBytesWithoutLength());

// TODO: Properly add this logic back with new types.
// if (this.nodeId === emptyNodeId) {
// throw new Error('Node ID must be non-empty');
// }
return true;
}
}
5 changes: 5 additions & 0 deletions src/serializable/fxs/pvm/pChainOwner.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { pChainOwner, pChainOwnerBytes } from '../../../fixtures/pvm';
import { testSerialization } from '../../../fixtures/utils/serializable';
import { PChainOwner } from './pChainOwner';

testSerialization('PChainOwner', PChainOwner, pChainOwner, pChainOwnerBytes);
45 changes: 45 additions & 0 deletions src/serializable/fxs/pvm/pChainOwner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { concatBytes } from '@noble/hashes/utils';
import { toListStruct } from '../../../utils/serializeList';
import { pack, unpack } from '../../../utils/struct';
import { serializable } from '../../common/types';
import { Int } from '../../primitives';
import { Address } from '../common/address';
import { TypeSymbols } from '../../constants';
import type { Codec } from '../../codec';

/**
* @see https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/message/register_subnet_validator.go
*/
@serializable()
export class PChainOwner {
_type = TypeSymbols.PChainOwner;

constructor(
public readonly threshold: Int,
public readonly addresses: Address[],
) {}

getAddresses() {
return this.addresses;
}

static fromBytes(bytes: Uint8Array, codec: Codec): [PChainOwner, Uint8Array] {
const [threshold, addresses, remaining] = unpack(
bytes,
[Int, toListStruct(Address)],
codec,
);
return [new PChainOwner(threshold, addresses), remaining];
}

toBytes(codec: Codec) {
return concatBytes(pack([this.threshold, this.addresses], codec));
}

static fromNative(addresses: readonly Uint8Array[], threshold = 1) {
return new PChainOwner(
new Int(threshold),
addresses.map((addr) => new Address(addr)),
);
}
}
7 changes: 4 additions & 3 deletions src/serializable/pvm/convertSubnetTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { Codec } from '../codec/codec';
import type { Serializable } from '../common/types';
import { serializable } from '../common/types';
import { TypeSymbols } from '../constants';
import { Address, Id } from '../fxs/common';
import { Id } from '../fxs/common';
import { ConvertSubnetValidator } from '../fxs/pvm/convertSubnetValidator';
import { Bytes } from '../primitives';
import { AbstractSubnetTx } from './abstractSubnetTx';

@serializable()
Expand All @@ -18,7 +19,7 @@ export class ConvertSubnetTx extends AbstractSubnetTx {
public readonly baseTx: BaseTx,
public readonly subnetID: Id,
public readonly chainID: Id,
public readonly address: Address,
public readonly address: Bytes,
public readonly validators: ConvertSubnetValidator[],
public readonly subnetAuth: Serializable,
) {
Expand All @@ -36,7 +37,7 @@ export class ConvertSubnetTx extends AbstractSubnetTx {
const [baseTx, subnetID, chainID, address, validators, subnetAuth, rest] =
unpack(
bytes,
[BaseTx, Id, Id, Address, toListStruct(ConvertSubnetValidator), Codec],
[BaseTx, Id, Id, Bytes, toListStruct(ConvertSubnetValidator), Codec],
codec,
);
return [
Expand Down
5 changes: 2 additions & 3 deletions src/vms/pvm/etna-builder/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
} from '../../../constants/networkIDs';
import type { TransferOutput } from '../../../serializable';
import {
Address,
Input,
NodeId,
OutputOwners,
Expand Down Expand Up @@ -1387,7 +1386,7 @@ export const newConvertSubnetTx: TxBuilderFn<NewConvertSubnetTxProps> = (
for (const validator of sortedValidators) {
toBurn.set(
context.avaxAssetID,
(toBurn.get(context.avaxAssetID) ?? 0n) + validator.getBalance(),
(toBurn.get(context.avaxAssetID) ?? 0n) + validator.getBalance().value(),
);
}

Expand Down Expand Up @@ -1434,7 +1433,7 @@ export const newConvertSubnetTx: TxBuilderFn<NewConvertSubnetTxProps> = (
),
Id.fromString(subnetId),
Id.fromString(chainId),
new Address(address),
new Bytes(address),
sortedValidators,
Input.fromNative(subnetAuth),
),
Expand Down
16 changes: 7 additions & 9 deletions src/vms/pvm/txs/fee/complexity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
TransferableOutput,
} from '../../../../serializable';
import { ConvertSubnetValidator } from '../../../../serializable/fxs/pvm/convertSubnetValidator';
import { PChainOwner } from '../../../../serializable/fxs/pvm/pChainOwner';
import {
ProofOfPossession,
SignerEmpty,
Expand Down Expand Up @@ -281,7 +282,7 @@ describe('Complexity', () => {

describe('getConvertSubnetValidatorComplexity', () => {
test('any can spend', () => {
const pChainOwner = OutputOwners.fromNative([], 0n, 1);
const pChainOwner = PChainOwner.fromNative([], 1);
const validator = ConvertSubnetValidator.fromNative(
'NodeID-MqgFXT8JhorbEW2LpTDGePBBhv55SSp3M',
1n,
Expand All @@ -302,16 +303,15 @@ describe('Complexity', () => {
);
});
test('single remaining balance owner', () => {
const remainingBalanceOwner = OutputOwners.fromNative(
const remainingBalanceOwner = PChainOwner.fromNative(
[
utils.bech32ToBytes(
'P-custom1p8ddr5wfmfq0zv3n2wnst0cm2pfccaudm3wsrs',
),
],
0n,
1,
);
const deactivationOwner = OutputOwners.fromNative([], 0n, 1);
const deactivationOwner = PChainOwner.fromNative([], 1);
const validator = ConvertSubnetValidator.fromNative(
'NodeID-MqgFXT8JhorbEW2LpTDGePBBhv55SSp3M',
1n,
Expand All @@ -332,16 +332,15 @@ describe('Complexity', () => {
);
});
test('single deactivation owner', () => {
const deactivationOwner = OutputOwners.fromNative(
const deactivationOwner = PChainOwner.fromNative(
[
utils.bech32ToBytes(
'P-custom1p8ddr5wfmfq0zv3n2wnst0cm2pfccaudm3wsrs',
),
],
0n,
1,
);
const remainingBalanceOwner = OutputOwners.fromNative([], 0n, 1);
const remainingBalanceOwner = PChainOwner.fromNative([], 1);
const validator = ConvertSubnetValidator.fromNative(
'NodeID-MqgFXT8JhorbEW2LpTDGePBBhv55SSp3M',
1n,
Expand All @@ -362,13 +361,12 @@ describe('Complexity', () => {
);
});
test('remaining balance owner and deactivation owner', () => {
const pChainOwner = OutputOwners.fromNative(
const pChainOwner = PChainOwner.fromNative(
[
utils.bech32ToBytes(
'P-custom1p8ddr5wfmfq0zv3n2wnst0cm2pfccaudm3wsrs',
),
],
0n,
1,
);
const validator = ConvertSubnetValidator.fromNative(
Expand Down
Loading

0 comments on commit 52eb746

Please sign in to comment.