diff --git a/src/fixtures/codec.ts b/src/fixtures/codec.ts index f60a39064..3473cdafe 100644 --- a/src/fixtures/codec.ts +++ b/src/fixtures/codec.ts @@ -2,6 +2,7 @@ import { getAVMManager } from '../serializable/avm/codec'; import { codec as EVMCodec } from '../serializable/evm/codec'; import { Short } from '../serializable/primitives'; import { codec } from '../serializable/pvm/codec'; +import { codec as WarpCodec } from '../serializable/pvm/warp/codec'; // Check for circular imports in the fx type // registries if tests are throwing errors @@ -13,3 +14,5 @@ export const testCodec = () => testManager().getCodecForVersion(new Short(0)); export const testPVMCodec = () => codec; export const testEVMCodec = () => EVMCodec; + +export const testWarpCodec = () => WarpCodec; diff --git a/src/fixtures/primitives.ts b/src/fixtures/primitives.ts index e33c125f2..4a16986b3 100644 --- a/src/fixtures/primitives.ts +++ b/src/fixtures/primitives.ts @@ -65,25 +65,41 @@ export const blsSignatureBytes = () => export const blsSignature = () => BlsSignature.fromBytes(blsSignatureBytes())[0]; +/** + * Example WarpMessage bytes. + * + * WarpUnsignedMessage: + * - networkId: 12345 + * - sourceChainId: rVPmsy4tQrQhQpRE6embdYnmYtwQi8kQAg1ZdaZGzeVdjc4qT + * + * One "signer" on the WarpSignature. + */ export const warpMessageBytes = () => new Uint8Array([ - 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x70, 0x5f, - 0x3d, 0x44, 0x15, 0xf9, 0x90, 0x22, 0x5d, 0x3d, 0xf5, 0xce, 0x43, 0x7d, - 0x7a, 0xf2, 0xaa, 0x32, 0x4b, 0x1b, 0xbc, 0xe8, 0x54, 0xee, 0x34, 0xab, - 0x6f, 0x39, 0x88, 0x22, 0x50, 0xd2, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x38, 0xe6, 0xe9, 0xfe, 0x31, 0xc6, - 0xd0, 0x70, 0xa8, 0xc7, 0x92, 0xdb, 0xac, 0xf6, 0xd0, 0xae, 0xfb, 0x8e, - 0xac, 0x2a, 0xde, 0xd4, 0x9c, 0xc0, 0xaa, 0x9f, 0x42, 0x2d, 0x1f, 0xdd, - 0x9e, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x01, 0x87, 0xf4, 0xbb, 0x2c, 0x42, 0x86, 0x9c, 0x56, 0xf0, - 0x23, 0xa1, 0xca, 0x81, 0x04, 0x5a, 0xff, 0x03, 0x4a, 0xcd, 0x49, 0x0b, - 0x8f, 0x15, 0xb5, 0x06, 0x90, 0x25, 0xf9, 0x82, 0xe6, 0x05, 0xe0, 0x77, - 0x00, 0x7f, 0xc5, 0x88, 0xf7, 0xd5, 0x63, 0x69, 0xa6, 0x5d, 0xf7, 0x57, - 0x4d, 0xf3, 0xb7, 0x0f, 0xf0, 0x28, 0xea, 0x17, 0x37, 0x39, 0xc7, 0x89, - 0x52, 0x5a, 0xb7, 0xee, 0xbf, 0xcb, 0x5c, 0x11, 0x5b, 0x13, 0xcc, 0xa8, - 0xf0, 0x2b, 0x36, 0x21, 0x04, 0xb7, 0x00, 0xc7, 0x5b, 0xc9, 0x52, 0x34, - 0x10, 0x9f, 0x3f, 0x13, 0x60, 0xdd, 0xcb, 0x4e, 0xc3, 0xca, 0xf6, 0xb0, - 0xe8, 0x21, 0xcb, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x70, 0x5f, 0x3d, 0x44, 0x15, 0xf9, + 0x90, 0x22, 0x5d, 0x3d, 0xf5, 0xce, 0x43, 0x7d, 0x7a, 0xf2, 0xaa, 0x32, + 0x4b, 0x1b, 0xbc, 0xe8, 0x54, 0xee, 0x34, 0xab, 0x6f, 0x39, 0x88, 0x22, + 0x50, 0xd2, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xa0, 0x67, 0x3b, 0x4e, 0xe5, 0xec, 0x44, 0xe5, 0x7c, 0x8a, + 0xb2, 0x50, 0xdd, 0x7c, 0xd7, 0xb6, 0x8d, 0x04, 0x42, 0x1f, 0x64, 0xbd, + 0x65, 0x59, 0xa4, 0x28, 0x4a, 0x3e, 0xe3, 0x58, 0xff, 0x2b, 0x00, 0x00, + 0x00, 0x14, 0x5e, 0xfc, 0x86, 0xa1, 0x1c, 0x5b, 0x12, 0xcc, 0x95, 0xb2, + 0xcf, 0x52, 0x7c, 0x02, 0x3f, 0x9c, 0xf6, 0xe0, 0xe8, 0xf6, 0xb6, 0x20, + 0x34, 0x31, 0x5c, 0x5d, 0x11, 0xce, 0xa4, 0x19, 0x0f, 0x6e, 0xa8, 0x99, + 0x78, 0x21, 0xc0, 0x24, 0x83, 0xd2, 0x9a, 0xdb, 0x5e, 0x45, 0x67, 0x84, + 0x3f, 0x7a, 0x44, 0xc3, 0x9b, 0x2f, 0xfa, 0x20, 0xc8, 0x52, 0x0d, 0xc3, + 0x58, 0x70, 0x2f, 0xb1, 0xec, 0x29, 0xf2, 0x74, 0x6d, 0xcc, 0x00, 0x00, + 0x00, 0x00, 0x67, 0x05, 0xaf, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x8e, 0x99, 0xdc, 0x6e, 0xd7, 0x36, 0x08, 0x9c, 0x03, + 0xb9, 0xa1, 0x27, 0x5e, 0x0c, 0xf8, 0x01, 0x52, 0x4e, 0xd3, 0x41, 0xfb, + 0x10, 0x11, 0x1f, 0x29, 0xc0, 0x39, 0x0f, 0xa2, 0xf9, 0x6c, 0xf6, 0xaa, + 0x78, 0x53, 0x9e, 0xc7, 0x67, 0xe5, 0xcd, 0x52, 0x3c, 0x60, 0x6c, 0x7e, + 0xde, 0x50, 0xe6, 0x0b, 0xa6, 0x06, 0x5a, 0x36, 0x85, 0xe7, 0x70, 0xd9, + 0x79, 0xb0, 0xdf, 0x74, 0xe3, 0x54, 0x1b, 0x61, 0xed, 0x63, 0xf0, 0x37, + 0x46, 0x37, 0x76, 0x09, 0x85, 0x76, 0xe3, 0x85, 0x76, 0x7a, 0x69, 0x5d, + 0xe5, 0x93, 0x52, 0xb4, 0x4e, 0x51, 0x58, 0x31, 0xc5, 0xee, 0x7a, 0x8c, + 0xc7, 0x28, 0xf9, ]); diff --git a/src/fixtures/warp.ts b/src/fixtures/warp.ts new file mode 100644 index 000000000..85f9eda32 --- /dev/null +++ b/src/fixtures/warp.ts @@ -0,0 +1,33 @@ +import { + WarpMessage, + WarpSignature, + WarpUnsignedMessage, +} from '../serializable/pvm/warp'; +import { concatBytes } from '../utils'; +import { id, idBytes } from './common'; +import { + blsSignature, + blsSignatureBytes, + bytes, + bytesBytes, + int, + intBytes, +} from './primitives'; +import { bytesForInt } from './utils/bytesFor'; + +export const warpUnsignedMessage = () => + new WarpUnsignedMessage(int(), id(), bytes()); + +export const warpUnsignedMessageBytes = () => + concatBytes(intBytes(), idBytes(), bytesBytes()); + +export const warpSignature = () => new WarpSignature(bytes(), blsSignature()); + +export const warpSignatureBytes = () => + concatBytes(bytesBytes(), blsSignatureBytes()); + +export const warpMessage = () => + new WarpMessage(warpUnsignedMessage(), warpSignature()); + +export const warpMessageBytes = () => + concatBytes(warpUnsignedMessageBytes(), bytesForInt(0), warpSignatureBytes()); diff --git a/src/serializable/constants.ts b/src/serializable/constants.ts index 377d44ed4..27680f869 100644 --- a/src/serializable/constants.ts +++ b/src/serializable/constants.ts @@ -95,4 +95,9 @@ export enum TypeSymbols { EvmInput = 'evm.Input', EvmOutput = 'evm.Output', EvmImportTx = 'evm.ImportTx', + + // Warp + WarpMessage = 'warp.Message', + WarpUnsignedMessage = 'warp.UnsignedMessage', + WarpSignature = 'warp.Signature', } diff --git a/src/serializable/primitives/bytes.ts b/src/serializable/primitives/bytes.ts index f528acdd9..4fcceb618 100644 --- a/src/serializable/primitives/bytes.ts +++ b/src/serializable/primitives/bytes.ts @@ -9,7 +9,7 @@ import { TypeSymbols } from '../constants'; @serializable() export class Bytes extends Primitives { _type = TypeSymbols.Bytes; - constructor(private readonly bytes: Uint8Array) { + constructor(public readonly bytes: Uint8Array) { super(); } diff --git a/src/serializable/pvm/registerL1ValidatorTx.ts b/src/serializable/pvm/registerL1ValidatorTx.ts index 954680e2c..c4b9b7447 100644 --- a/src/serializable/pvm/registerL1ValidatorTx.ts +++ b/src/serializable/pvm/registerL1ValidatorTx.ts @@ -29,6 +29,7 @@ export class RegisterL1ValidatorTx extends PVMTx { [BaseTx, BigIntPr, BlsSignature, Bytes], codec, ); + return [ new RegisterL1ValidatorTx(baseTx, balance, blsSignature, message), rest, diff --git a/src/serializable/pvm/warp/codec.ts b/src/serializable/pvm/warp/codec.ts new file mode 100644 index 000000000..39a2a92cf --- /dev/null +++ b/src/serializable/pvm/warp/codec.ts @@ -0,0 +1,15 @@ +import { Codec, Manager } from '../../codec'; +import { WarpSignature } from './signature'; + +/** + * @see https://github.com/ava-labs/avalanchego/blob/master/vms/platformvm/warp/codec.go + */ +export const codec = new Codec([WarpSignature]); + +let manager: Manager; +export const getWarpManager = () => { + if (manager) return manager; + manager = new Manager(); + manager.RegisterCodec(0, codec); + return manager; +}; diff --git a/src/serializable/pvm/warp/index.ts b/src/serializable/pvm/warp/index.ts new file mode 100644 index 000000000..2ccd8278f --- /dev/null +++ b/src/serializable/pvm/warp/index.ts @@ -0,0 +1,4 @@ +export { codec, getWarpManager } from './codec'; +export { WarpMessage } from './message'; +export { WarpSignature } from './signature'; +export { WarpUnsignedMessage } from './unsignedMessage'; diff --git a/src/serializable/pvm/warp/message.test.ts b/src/serializable/pvm/warp/message.test.ts new file mode 100644 index 000000000..dec4153f6 --- /dev/null +++ b/src/serializable/pvm/warp/message.test.ts @@ -0,0 +1,12 @@ +import { testWarpCodec } from '../../../fixtures/codec'; +import { testSerialization } from '../../../fixtures/utils/serializable'; +import { warpMessage, warpMessageBytes } from '../../../fixtures/warp'; +import { WarpMessage } from './message'; + +testSerialization( + 'WarpMessage', + WarpMessage, + warpMessage, + warpMessageBytes, + testWarpCodec, +); diff --git a/src/serializable/pvm/warp/message.ts b/src/serializable/pvm/warp/message.ts new file mode 100644 index 000000000..4d6e82e9b --- /dev/null +++ b/src/serializable/pvm/warp/message.ts @@ -0,0 +1,36 @@ +import { concatBytes } from '../../../utils/buffer'; +import { unpack } from '../../../utils/struct'; +import type { Codec } from '../../codec'; +import { serializable } from '../../common/types'; +import { TypeSymbols } from '../../constants'; +import type { WarpSignature } from './signature'; +import { WarpUnsignedMessage } from './unsignedMessage'; + +@serializable() +export class WarpMessage { + _type = TypeSymbols.WarpMessage; + + constructor( + public readonly unsignedMessage: WarpUnsignedMessage, + public readonly signature: WarpSignature, + ) {} + + static fromBytes(bytes: Uint8Array, codec: Codec): [WarpMessage, Uint8Array] { + const [unsignedMessage, signatureBytes] = unpack( + bytes, + [WarpUnsignedMessage], + codec, + ); + + const [signature, rest] = codec.UnpackPrefix(signatureBytes); + + return [new WarpMessage(unsignedMessage, signature), rest]; + } + + toBytes(codec: Codec) { + return concatBytes( + this.unsignedMessage.toBytes(codec), + codec.PackPrefix(this.signature), + ); + } +} diff --git a/src/serializable/pvm/warp/signature.test.ts b/src/serializable/pvm/warp/signature.test.ts new file mode 100644 index 000000000..68b98011d --- /dev/null +++ b/src/serializable/pvm/warp/signature.test.ts @@ -0,0 +1,12 @@ +import { testWarpCodec } from '../../../fixtures/codec'; +import { testSerialization } from '../../../fixtures/utils/serializable'; +import { warpSignature, warpSignatureBytes } from '../../../fixtures/warp'; +import { WarpSignature } from './signature'; + +testSerialization( + 'WarpSignature', + WarpSignature, + warpSignature, + warpSignatureBytes, + testWarpCodec, +); diff --git a/src/serializable/pvm/warp/signature.ts b/src/serializable/pvm/warp/signature.ts new file mode 100644 index 000000000..baa2392fe --- /dev/null +++ b/src/serializable/pvm/warp/signature.ts @@ -0,0 +1,45 @@ +import { hammingWeight } from '../../../utils/buffer'; +import { pack, unpack } from '../../../utils/struct'; +import type { Codec } from '../../codec'; +import { serializable } from '../../common/types'; +import { TypeSymbols } from '../../constants'; +import { BlsSignature } from '../../fxs/common'; +import { Bytes } from '../../primitives'; + +@serializable() +export class WarpSignature { + _type = TypeSymbols.WarpSignature; + + constructor( + public readonly signers: Bytes, + public readonly signature: BlsSignature, + ) {} + + static fromBytes( + bytes: Uint8Array, + codec: Codec, + ): [WarpSignature, Uint8Array] { + const [signers, signature, rest] = unpack( + bytes, + [Bytes, BlsSignature], + codec, + ); + + return [new WarpSignature(signers, signature), rest]; + } + + toBytes(codec: Codec) { + return pack([this.signers, this.signature], codec); + } + + /** + * Number of BLS public keys that participated in the + * {@linkcode BlsSignature}. This is exposed because users of the signatures + * typically impose a verification fee that is a function of the number of signers. + * + * This is used to calculate the Warp complexity in transactions. + */ + numOfSigners(): number { + return hammingWeight(this.signers.bytes); + } +} diff --git a/src/serializable/pvm/warp/unsignedMessage.test.ts b/src/serializable/pvm/warp/unsignedMessage.test.ts new file mode 100644 index 000000000..83efed6b2 --- /dev/null +++ b/src/serializable/pvm/warp/unsignedMessage.test.ts @@ -0,0 +1,15 @@ +import { testWarpCodec } from '../../../fixtures/codec'; +import { testSerialization } from '../../../fixtures/utils/serializable'; +import { + warpUnsignedMessage, + warpUnsignedMessageBytes, +} from '../../../fixtures/warp'; +import { WarpUnsignedMessage } from './unsignedMessage'; + +testSerialization( + 'WarpUnsignedMessage', + WarpUnsignedMessage, + warpUnsignedMessage, + warpUnsignedMessageBytes, + testWarpCodec, +); diff --git a/src/serializable/pvm/warp/unsignedMessage.ts b/src/serializable/pvm/warp/unsignedMessage.ts new file mode 100644 index 000000000..cc7c14399 --- /dev/null +++ b/src/serializable/pvm/warp/unsignedMessage.ts @@ -0,0 +1,34 @@ +import { pack, unpack } from '../../../utils/struct'; +import type { Codec } from '../../codec'; +import { serializable } from '../../common/types'; +import { TypeSymbols } from '../../constants'; +import { Id } from '../../fxs/common'; +import { Bytes, Int } from '../../primitives'; + +@serializable() +export class WarpUnsignedMessage { + _type = TypeSymbols.WarpUnsignedMessage; + + constructor( + public readonly networkId: Int, + public readonly sourceChainId: Id, + public readonly payload: Bytes, + ) {} + + static fromBytes( + bytes: Uint8Array, + codec: Codec, + ): [WarpUnsignedMessage, Uint8Array] { + const [networkId, sourceChainId, payload, rest] = unpack( + bytes, + [Int, Id, Bytes], + codec, + ); + + return [new WarpUnsignedMessage(networkId, sourceChainId, payload), rest]; + } + + toBytes(codec: Codec) { + return pack([this.networkId, this.sourceChainId, this.payload], codec); + } +} diff --git a/src/utils/buffer.test.ts b/src/utils/buffer.test.ts index 5b954c7e1..64f225ddc 100644 --- a/src/utils/buffer.test.ts +++ b/src/utils/buffer.test.ts @@ -1,4 +1,9 @@ -import { bufferToBigInt, bufferToNumber, padLeft } from './buffer'; +import { + bufferToBigInt, + bufferToNumber, + hammingWeight, + padLeft, +} from './buffer'; import { describe, it, expect } from 'vitest'; describe('bufferToBigInt', () => { @@ -79,3 +84,31 @@ describe('padLeft', () => { expect(res).toStrictEqual(new Uint8Array([0xaf, 0x72, 0x72])); }); }); + +describe('hammingWeight()', () => { + it('should return expected number of `1` bits from bytes', () => { + expect(hammingWeight(new Uint8Array([0]))).toBe(0); + expect(hammingWeight(new Uint8Array([1]))).toBe(1); + expect(hammingWeight(new Uint8Array([2]))).toBe(1); + expect(hammingWeight(new Uint8Array([3]))).toBe(2); + expect(hammingWeight(new Uint8Array([4]))).toBe(1); + expect(hammingWeight(new Uint8Array([5]))).toBe(2); + expect(hammingWeight(new Uint8Array([6]))).toBe(2); + expect(hammingWeight(new Uint8Array([7]))).toBe(3); + expect(hammingWeight(new Uint8Array([8]))).toBe(1); + expect(hammingWeight(new Uint8Array([9]))).toBe(2); + + expect(hammingWeight(new Uint8Array([0, 0]))).toBe(0); + expect(hammingWeight(new Uint8Array([0, 1]))).toBe(1); + expect(hammingWeight(new Uint8Array([0, 2]))).toBe(1); + expect(hammingWeight(new Uint8Array([0, 3]))).toBe(2); + + expect(hammingWeight(new Uint8Array([1, 1]))).toBe(2); + expect(hammingWeight(new Uint8Array([1, 2]))).toBe(2); + expect(hammingWeight(new Uint8Array([1, 3]))).toBe(3); + + expect(hammingWeight(new Uint8Array([3, 1]))).toBe(3); + expect(hammingWeight(new Uint8Array([3, 2]))).toBe(3); + expect(hammingWeight(new Uint8Array([3, 3]))).toBe(4); + }); +}); diff --git a/src/utils/buffer.ts b/src/utils/buffer.ts index fcaaf1201..c1184a6f6 100644 --- a/src/utils/buffer.ts +++ b/src/utils/buffer.ts @@ -33,4 +33,30 @@ export function padLeft(bytes: Uint8Array, length: number) { return out; } +/** + * Calculates the number of `1`s (set bits) in the binary + * representation a big-endian byte slice. + * + * @param input A Uint8Array + * @returns The number of bits set to 1 in the binary representation of the input + * + * @example + * ```ts + * hammingWeight(new Uint8Array([0, 1, 2, 3, 4, 5])); // 7 + * ``` + */ +export const hammingWeight = (input: Uint8Array): number => { + let count = 0; + + for (let i = 0; i < input.length; i++) { + let num = input[i]; + while (num !== 0) { + count += num & 1; + num >>= 1; + } + } + + return count; +}; + export { concatBytes, strip0x, add0x }; diff --git a/src/utils/getBurnedAmountByTx.test.ts b/src/utils/getBurnedAmountByTx.test.ts index 83a61b0ac..d80978b5e 100644 --- a/src/utils/getBurnedAmountByTx.test.ts +++ b/src/utils/getBurnedAmountByTx.test.ts @@ -39,8 +39,8 @@ import { feeState, l1Validator } from '../fixtures/pvm'; import { bigIntPr, blsSignatureBytes, - bytesBytes, stringPr, + warpMessageBytes, } from '../fixtures/primitives'; const getUtxoMock = ( @@ -677,7 +677,7 @@ describe('getBurnedAmountByTx', () => { const amounts = getBurnedAmountByTx(tx, testContext); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual(749n); + expect(amounts.get(testContext.avaxAssetID)).toEqual(2_200n); }); }); @@ -692,17 +692,17 @@ describe('getBurnedAmountByTx', () => { utxos: [utxo1, utxo2], balance: bigIntPr().value(), blsSignature: blsSignatureBytes(), - message: bytesBytes(), + message: warpMessageBytes(), }, testContext, ).getTx() as AvaxTx; const amounts = getBurnedAmountByTx(tx, testContext); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual(416n); + expect(amounts.get(testContext.avaxAssetID)).toEqual(3_002n); }); - it('calculates the burned amount of RegisterL1Validator tx correctly', () => { + it('calculates the burned amount of IncreaseL1ValidatorBalance tx correctly', () => { const utxo1 = getUtxoMock(testUTXOID1, 5000000n); const utxo2 = getUtxoMock(testUTXOID2, 6000000n); @@ -719,6 +719,6 @@ describe('getBurnedAmountByTx', () => { const amounts = getBurnedAmountByTx(tx, testContext); expect(amounts.size).toEqual(1); - expect(amounts.get(testContext.avaxAssetID)).toEqual(342n); + expect(amounts.get(testContext.avaxAssetID)).toEqual(548n); }); }); diff --git a/src/vms/pvm/etna-builder/builder.ts b/src/vms/pvm/etna-builder/builder.ts index 476e5a01b..8221c2cbf 100644 --- a/src/vms/pvm/etna-builder/builder.ts +++ b/src/vms/pvm/etna-builder/builder.ts @@ -65,29 +65,28 @@ import { INTRINSIC_ADD_PERMISSIONLESS_VALIDATOR_TX_COMPLEXITIES, INTRINSIC_ADD_SUBNET_VALIDATOR_TX_COMPLEXITIES, INTRINSIC_BASE_TX_COMPLEXITIES, + INTRINSIC_CONVERT_SUBNET_TO_L1_TX_COMPLEXITIES, INTRINSIC_CREATE_CHAIN_TX_COMPLEXITIES, INTRINSIC_CREATE_SUBNET_TX_COMPLEXITIES, + INTRINSIC_DISABLE_L1_VALIDATOR_TX_COMPLEXITIES, INTRINSIC_EXPORT_TX_COMPLEXITIES, INTRINSIC_IMPORT_TX_COMPLEXITIES, + INTRINSIC_INCREASE_L1_VALIDATOR_BALANCE_TX_COMPLEXITIES, + INTRINSIC_REGISTER_L1_VALIDATOR_TX_COMPLEXITIES, INTRINSIC_REMOVE_SUBNET_VALIDATOR_TX_COMPLEXITIES, + INTRINSIC_SET_L1_VALIDATOR_WEIGHT_TX_COMPLEXITIES, INTRINSIC_TRANSFER_SUBNET_OWNERSHIP_TX_COMPLEXITIES, - INTRINSIC_CONVERT_SUBNET_TO_L1_TX_COMPLEXITIES, getAuthComplexity, getInputComplexity, getOutputComplexity, getOwnerComplexity, getSignerComplexity, getBytesComplexity, - getConvertSubnetValidatorsComplexity, + getL1ValidatorsComplexity, } from '../txs/fee'; import { spend } from './spend'; import { useSpendableLockedUTXOs, useUnlockedUTXOs } from './spend-reducers'; import type { L1Validator } from '../../../serializable/fxs/pvm/L1Validator'; -import { - INTRINSIC_INCREASE_BALANCE_TX_COMPLEXITIES, - INTRINSIC_REGISTER_L1_VALIDATOR_TX_COMPLEXITIES, - INTRINSIC_SET_L1_VALIDATOR_WEIGHT_TX_COMPLEXITIES, -} from '../txs/fee/constants'; import { getWarpComplexity } from '../txs/fee/complexity'; /** @@ -1395,7 +1394,7 @@ export const newConvertSubnetToL1Tx: TxBuilderFn< const bytesComplexity = getBytesComplexity(memo, address); const authComplexity = getAuthComplexity(Input.fromNative(subnetAuth)); - const validatorComplexity = getConvertSubnetValidatorsComplexity(validators); + const validatorComplexity = getL1ValidatorsComplexity(validators); const sortedValidators = validators.sort((a, b) => bytesCompare(a.nodeId.toBytes(), b.nodeId.toBytes()), @@ -1689,7 +1688,7 @@ export const newIncreaseL1ValidatorBalanceTx: TxBuilderFn< const bytesComplexity = getBytesComplexity(memo); const complexity = addDimensions( - INTRINSIC_INCREASE_BALANCE_TX_COMPLEXITIES, + INTRINSIC_INCREASE_L1_VALIDATOR_BALANCE_TX_COMPLEXITIES, bytesComplexity, ); @@ -1775,7 +1774,7 @@ export const newDisableL1ValidatorTx: TxBuilderFn = ( const authComplexity = getAuthComplexity(disableAuthInput); const complexity = addDimensions( - INTRINSIC_INCREASE_BALANCE_TX_COMPLEXITIES, + INTRINSIC_DISABLE_L1_VALIDATOR_TX_COMPLEXITIES, bytesComplexity, authComplexity, ); diff --git a/src/vms/pvm/etna-builder/spendHelper.test.ts b/src/vms/pvm/etna-builder/spendHelper.test.ts index 6f937f380..b45c6d382 100644 --- a/src/vms/pvm/etna-builder/spendHelper.test.ts +++ b/src/vms/pvm/etna-builder/spendHelper.test.ts @@ -9,12 +9,15 @@ import { id } from '../../../fixtures/common'; import { stakeableLockOut } from '../../../fixtures/pvm'; import { TransferableOutput } from '../../../serializable'; import { isTransferOut } from '../../../utils'; +import type { Dimensions } from '../../common/fees/dimensions'; import { + addDimensions, createDimensions, dimensionsToGas, } from '../../common/fees/dimensions'; import type { SpendHelperProps } from './spendHelper'; import { SpendHelper } from './spendHelper'; +import { getInputComplexity, getOutputComplexity } from '../txs/fee'; const DEFAULT_GAS_PRICE = 3n; @@ -42,6 +45,12 @@ const DEFAULT_PROPS: SpendHelperProps = { weights: DEFAULT_WEIGHTS, }; +const expectedFee = (...dimensions: Dimensions[]): bigint => { + const totalDimensions = addDimensions(...dimensions); + + return dimensionsToGas(totalDimensions, DEFAULT_WEIGHTS) * DEFAULT_GAS_PRICE; +}; + describe('src/vms/pvm/etna-builder/spendHelper', () => { test('initialized with correct values', () => { const spendHelper = new SpendHelper(DEFAULT_PROPS); @@ -65,9 +74,7 @@ describe('src/vms/pvm/etna-builder/spendHelper', () => { expect(spendHelper.getInputsOutputs()).toEqual({ changeOutputs: [], - fee: - dimensionsToGas(DEFAULT_PROPS.initialComplexity, DEFAULT_WEIGHTS) * - DEFAULT_GAS_PRICE, + fee: expectedFee(DEFAULT_PROPS.initialComplexity), inputs: [], inputUTXOs: [], stakeOutputs: [], @@ -80,7 +87,10 @@ describe('src/vms/pvm/etna-builder/spendHelper', () => { expect(spendHelper.getInputsOutputs()).toEqual({ changeOutputs: [], - fee: 942n, + fee: expectedFee( + DEFAULT_PROPS.initialComplexity, + getInputComplexity([inputTransferableInput]), + ), inputs: [inputTransferableInput], inputUTXOs: [inputUtxo], stakeOutputs: [], @@ -92,7 +102,11 @@ describe('src/vms/pvm/etna-builder/spendHelper', () => { expect(spendHelper.getInputsOutputs()).toEqual({ changeOutputs: [changeOutput], - fee: 1251n, + fee: expectedFee( + DEFAULT_PROPS.initialComplexity, + getInputComplexity([inputTransferableInput]), + getOutputComplexity([changeOutput]), + ), inputs: [inputTransferableInput], inputUTXOs: [inputUtxo], stakeOutputs: [], @@ -104,7 +118,11 @@ describe('src/vms/pvm/etna-builder/spendHelper', () => { expect(spendHelper.getInputsOutputs()).toEqual({ changeOutputs: [changeOutput], - fee: 1560n, + fee: expectedFee( + DEFAULT_PROPS.initialComplexity, + getInputComplexity([inputTransferableInput]), + getOutputComplexity([changeOutput, stakeOutput]), + ), inputs: [inputTransferableInput], inputUTXOs: [inputUtxo], stakeOutputs: [stakeOutput], diff --git a/src/vms/pvm/txs/fee/complexity.test.ts b/src/vms/pvm/txs/fee/complexity.test.ts index 2c353087b..024f482ff 100644 --- a/src/vms/pvm/txs/fee/complexity.test.ts +++ b/src/vms/pvm/txs/fee/complexity.test.ts @@ -45,6 +45,11 @@ import { TEST_TRANSACTIONS, TEST_UNSUPPORTED_TRANSACTIONS, } from './fixtures/transactions'; +import { + INTRINSIC_BLS_AGGREGATE_COMPUTE, + INTRINSIC_BLS_VERIFY_COMPUTE, + INTRINSIC_WARP_DB_READS, +} from './constants'; const makeOutputOwners = (numOfAddresses = 0) => new OutputOwners( @@ -133,7 +138,7 @@ describe('Complexity', () => { bandwidth: 92, dbRead: 1, dbWrite: 1, - compute: 0, // TODO: Implement + compute: 0, }), ); }); @@ -146,7 +151,7 @@ describe('Complexity', () => { bandwidth: 161, dbRead: 1, dbWrite: 1, - compute: 0, // TODO: Implement + compute: 200, }), ); }); @@ -159,7 +164,7 @@ describe('Complexity', () => { bandwidth: 299, dbRead: 1, dbWrite: 1, - compute: 0, // TODO: Implement + compute: 600, }), ); }); @@ -181,7 +186,7 @@ describe('Complexity', () => { bandwidth: 311, dbRead: 1, dbWrite: 1, - compute: 0, // TODO: Implement + compute: 600, }), ); }); @@ -230,7 +235,7 @@ describe('Complexity', () => { bandwidth: 144, dbRead: 0, dbWrite: 0, - compute: 0, // TODO: Implement + compute: 1_050, }), ); }); @@ -245,7 +250,7 @@ describe('Complexity', () => { bandwidth: 8, dbRead: 0, dbWrite: 0, - compute: 0, // TODO: Implement + compute: 0, }), ); }); @@ -258,7 +263,7 @@ describe('Complexity', () => { bandwidth: 77, dbRead: 0, dbWrite: 0, - compute: 0, // TODO: Implement + compute: 200, }), ); }); @@ -271,7 +276,7 @@ describe('Complexity', () => { bandwidth: 215, dbRead: 0, dbWrite: 0, - compute: 0, // TODO: Implement + compute: 600, }), ); }); @@ -289,28 +294,24 @@ describe('Complexity', () => { // Example Warp Message const warpMessage = warpMessageBytes(); - test('complexity from empty warp message', () => { - const result = getWarpComplexity(new Bytes(new Uint8Array(0))); - - expect(result).toEqual( - createDimensions({ - bandwidth: 0, - dbRead: 0, - dbWrite: 0, - compute: 0, - }), - ); + test('throws "not enough bytes" error from empty warp message', () => { + expect(() => { + getWarpComplexity(new Bytes(new Uint8Array())); + }).toThrow('not enough bytes'); }); test('complexity from warp message', () => { const result = getWarpComplexity(new Bytes(warpMessage)); + const numOfSigners = 1; expect(result).toEqual( createDimensions({ bandwidth: warpMessage.length, - dbRead: 0, + dbRead: INTRINSIC_WARP_DB_READS, dbWrite: 0, - compute: 0, + compute: + INTRINSIC_BLS_VERIFY_COMPUTE + + INTRINSIC_BLS_AGGREGATE_COMPUTE * numOfSigners, }), ); }); @@ -334,7 +335,7 @@ describe('Complexity', () => { bandwidth: 200, dbRead: 0, dbWrite: 4, - compute: 0, // TODO: Implement + compute: 1_050, }), ); }); @@ -363,7 +364,7 @@ describe('Complexity', () => { bandwidth: 220, dbRead: 0, dbWrite: 4, - compute: 0, // TODO: Implement + compute: 1_050, }), ); }); @@ -392,7 +393,7 @@ describe('Complexity', () => { bandwidth: 220, dbRead: 0, dbWrite: 4, - compute: 0, // TODO: Implement + compute: 1_050, }), ); }); @@ -420,7 +421,7 @@ describe('Complexity', () => { bandwidth: 240, dbRead: 0, dbWrite: 4, - compute: 0, // TODO: Implement + compute: 1_050, }), ); }); diff --git a/src/vms/pvm/txs/fee/complexity.ts b/src/vms/pvm/txs/fee/complexity.ts index 0ee2e6bb2..917ce94e2 100644 --- a/src/vms/pvm/txs/fee/complexity.ts +++ b/src/vms/pvm/txs/fee/complexity.ts @@ -76,7 +76,7 @@ import { INTRINSIC_DISABLE_L1_VALIDATOR_TX_COMPLEXITIES, INTRINSIC_EXPORT_TX_COMPLEXITIES, INTRINSIC_IMPORT_TX_COMPLEXITIES, - INTRINSIC_INCREASE_BALANCE_TX_COMPLEXITIES, + INTRINSIC_INCREASE_L1_VALIDATOR_BALANCE_TX_COMPLEXITIES, INTRINSIC_INPUT_BANDWIDTH, INTRINSIC_INPUT_DB_READ, INTRINSIC_INPUT_DB_WRITE, @@ -94,8 +94,16 @@ import { INTRINSIC_STAKEABLE_LOCKED_INPUT_BANDWIDTH, INTRINSIC_STAKEABLE_LOCKED_OUTPUT_BANDWIDTH, INTRINSIC_TRANSFER_SUBNET_OWNERSHIP_TX_COMPLEXITIES, + INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, + INTRINSIC_BLS_POP_VERIFY_COMPUTE, + INTRINSIC_BLS_AGGREGATE_COMPUTE, + INTRINSIC_BLS_VERIFY_COMPUTE, + INTRINSIC_WARP_DB_READS, } from './constants'; import type { L1Validator } from '../../../../serializable/fxs/pvm/L1Validator'; +import { WarpMessage, getWarpManager } from '../../../../serializable/pvm/warp'; + +const warpManager = getWarpManager(); /** * Returns the complexity outputs add to a transaction. @@ -152,7 +160,7 @@ export const getInputComplexity = ( INTRINSIC_SECP256K1_FX_TRANSFERABLE_INPUT_BANDWIDTH, [FeeDimensions.DBRead]: INTRINSIC_INPUT_DB_READ, [FeeDimensions.DBWrite]: INTRINSIC_INPUT_DB_WRITE, - [FeeDimensions.Compute]: 0, // TODO: Add compute complexity. + [FeeDimensions.Compute]: 0, }; if (isStakeableLockIn(transferableInput.input)) { @@ -167,6 +175,9 @@ export const getInputComplexity = ( inputComplexity[FeeDimensions.Bandwidth] += signatureBandwidth; + inputComplexity[FeeDimensions.Compute] += + numberOfSignatures * INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE; + complexity = addDimensions(complexity, inputComplexity); } @@ -184,7 +195,7 @@ export const getSignerComplexity = ( bandwidth: INTRINSIC_POP_BANDWIDTH, dbRead: 0, dbWrite: 0, - compute: 0, // TODO: Add compute complexity. + compute: INTRINSIC_BLS_POP_VERIFY_COMPUTE, }); }; @@ -218,11 +229,14 @@ export const getAuthComplexity = (input: Serializable): Dimensions => { const bandwidth = signatureBandwidth + INTRINSIC_SECP256K1_FX_INPUT_BANDWIDTH; + const signatureCompute = + numberOfSignatures * INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE; + return createDimensions({ bandwidth, dbRead: 0, dbWrite: 0, - compute: 0, // TODO: Add compute complexity. + compute: signatureCompute, }); }; @@ -237,12 +251,19 @@ export const getBytesComplexity = ( }; export const getWarpComplexity = (message: Bytes): Dimensions => { - // TODO: Finish implementation. + const numberOfSigners = warpManager + .unpack(message.bytes, WarpMessage) + .signature.numOfSigners(); + + const aggregationCompute = numberOfSigners * INTRINSIC_BLS_AGGREGATE_COMPUTE; + + const compute: number = aggregationCompute + INTRINSIC_BLS_VERIFY_COMPUTE; + return createDimensions({ bandwidth: message.length, - dbRead: 0, + dbRead: INTRINSIC_WARP_DB_READS, dbWrite: 0, - compute: 0, + compute, }); }; @@ -422,7 +443,7 @@ const increaseL1ValidatorBalanceTx = ( tx: IncreaseL1ValidatorBalanceTx, ): Dimensions => { return addDimensions( - INTRINSIC_INCREASE_BALANCE_TX_COMPLEXITIES, + INTRINSIC_INCREASE_L1_VALIDATOR_BALANCE_TX_COMPLEXITIES, getBaseTxComplexity(tx.baseTx), ); }; diff --git a/src/vms/pvm/txs/fee/constants.ts b/src/vms/pvm/txs/fee/constants.ts index fd4885075..a99511558 100644 --- a/src/vms/pvm/txs/fee/constants.ts +++ b/src/vms/pvm/txs/fee/constants.ts @@ -69,6 +69,16 @@ export const INTRINSIC_SECP256K1_FX_SIGNATURE_BANDWIDTH = INT_LEN + // Signature index SIGNATURE_LENGTH; // Signature +export const INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE = 200; // secp256k1 signature verification time is around 200us + +export const INTRINSIC_BLS_AGGREGATE_COMPUTE = 5; // BLS public key aggregation time is around 5 us +export const INTRINSIC_BLS_VERIFY_COMPUTE = 1_000; // BLS verification time is around 1000us +export const INTRINSIC_BLS_PUBLIC_KEY_VALIDATION_COMPUTE = 50; // BLS public key validation time is around 50us +export const INTRINSIC_BLS_POP_VERIFY_COMPUTE = + INTRINSIC_BLS_PUBLIC_KEY_VALIDATION_COMPUTE + INTRINSIC_BLS_VERIFY_COMPUTE; + +export const INTRINSIC_WARP_DB_READS = 3 + 20; // chainID -> subnetID mapping + apply weight diffs + apply pk diffs + diff application reads + export const INTRINSIC_POP_BANDWIDTH = PUBLIC_KEY_LENGTH + // Public key BLS_SIGNATURE_LENGTH; // Signature @@ -102,8 +112,8 @@ export const INTRINSIC_CREATE_CHAIN_TX_COMPLEXITIES: Dimensions = { INT_LEN + // genesis length INT_LEN + // subnetAuth typeID INT_LEN, // subnetAuthCredential typeID - [FeeDimensions.DBRead]: 1, - [FeeDimensions.DBWrite]: 1, + [FeeDimensions.DBRead]: 3, // get subnet auth + check for subnet transformation + check for subnet conversion + [FeeDimensions.DBWrite]: 1, // put chain [FeeDimensions.Compute]: 0, }; @@ -111,7 +121,7 @@ export const INTRINSIC_CREATE_SUBNET_TX_COMPLEXITIES: Dimensions = { [FeeDimensions.Bandwidth]: INTRINSIC_BASE_TX_COMPLEXITIES[FeeDimensions.Bandwidth] + INT_LEN, // owner typeID [FeeDimensions.DBRead]: 0, - [FeeDimensions.DBWrite]: 1, + [FeeDimensions.DBWrite]: 1, // write subnet owner [FeeDimensions.Compute]: 0, }; @@ -126,8 +136,8 @@ export const INTRINSIC_ADD_PERMISSIONLESS_VALIDATOR_TX_COMPLEXITIES: Dimensions INT_LEN + // Validator rewards typeID INT_LEN + // Delegator rewards typeID INT_LEN, // Delegation shares - [FeeDimensions.DBRead]: 1, - [FeeDimensions.DBWrite]: 1, + [FeeDimensions.DBRead]: 1, // get staking config + [FeeDimensions.DBWrite]: 3, // put current staker + write weight diff + write pk diff [FeeDimensions.Compute]: 0, }; @@ -139,8 +149,8 @@ export const INTRINSIC_ADD_PERMISSIONLESS_DELEGATOR_TX_COMPLEXITIES: Dimensions ID_LEN + // Subnet ID INT_LEN + // Num stake outs INT_LEN, // Delegator rewards typeID - [FeeDimensions.DBRead]: 1, - [FeeDimensions.DBWrite]: 1, + [FeeDimensions.DBRead]: 1, // get staking config + [FeeDimensions.DBWrite]: 2, // put current staker + write weight diff [FeeDimensions.Compute]: 0, }; @@ -150,8 +160,8 @@ export const INTRINSIC_ADD_SUBNET_VALIDATOR_TX_COMPLEXITIES: Dimensions = { INTRINSIC_SUBNET_VALIDATOR_BANDWIDTH + // Subnet Validator INT_LEN + // Subnet auth typeID INT_LEN, // Subnet auth credential typeID - [FeeDimensions.DBRead]: 2, - [FeeDimensions.DBWrite]: 1, + [FeeDimensions.DBRead]: 3, // get subnet auth + check for subnet transformation + check for subnet conversion + [FeeDimensions.DBWrite]: 3, // put current staker + write weight diff + write pk diff [FeeDimensions.Compute]: 0, }; @@ -182,8 +192,8 @@ export const INTRINSIC_REMOVE_SUBNET_VALIDATOR_TX_COMPLEXITIES: Dimensions = { ID_LEN + // subnetID INT_LEN + // subnetAuth typeId INT_LEN, // subnetAuth credential typeId - [FeeDimensions.DBRead]: 2, - [FeeDimensions.DBWrite]: 1, + [FeeDimensions.DBRead]: 1, // read subnet auth + [FeeDimensions.DBWrite]: 3, // delete validator + write weight diff + write pk diff [FeeDimensions.Compute]: 0, }; @@ -194,8 +204,8 @@ export const INTRINSIC_TRANSFER_SUBNET_OWNERSHIP_TX_COMPLEXITIES: Dimensions = { INT_LEN + // subnetAuth typeID INT_LEN + // owner typeID INT_LEN, // subnetAuth credential typeID - [FeeDimensions.DBRead]: 1, - [FeeDimensions.DBWrite]: 1, + [FeeDimensions.DBRead]: 1, // read subnet auth + [FeeDimensions.DBWrite]: 1, // set subnet owner [FeeDimensions.Compute]: 0, }; @@ -222,8 +232,8 @@ export const INTRINSIC_CONVERT_SUBNET_TO_L1_TX_COMPLEXITIES: Dimensions = { INT_LEN + // validators length INT_LEN + // subnetAuth typeID INT_LEN, // subnetAuthCredential typeID - [FeeDimensions.DBRead]: 2, // subnet auth + manager lookup - [FeeDimensions.DBWrite]: 2, // manager + weight + [FeeDimensions.DBRead]: 3, // subnet auth + transformation lookup + conversion lookup + [FeeDimensions.DBWrite]: 2, // write conversion manager + total weight [FeeDimensions.Compute]: 0, }; @@ -233,28 +243,29 @@ export const INTRINSIC_REGISTER_L1_VALIDATOR_TX_COMPLEXITIES: Dimensions = { LONG_LEN + // balance BLS_SIGNATURE_LENGTH + // proof of possession INT_LEN, // message length - [FeeDimensions.DBRead]: 0, // TODO: Implement - [FeeDimensions.DBWrite]: 0, // TODO: Implement - [FeeDimensions.Compute]: 0, // TODO: Include PoP verification time + [FeeDimensions.DBRead]: 5, // conversion owner + expiry lookup + sov lookup + subnetID/nodeID lookup + weight lookup + [FeeDimensions.DBWrite]: 6, // write current staker + expiry + write weight diff + write pk diff + subnetID/nodeID lookup + weight lookup + [FeeDimensions.Compute]: INTRINSIC_BLS_POP_VERIFY_COMPUTE, }; export const INTRINSIC_SET_L1_VALIDATOR_WEIGHT_TX_COMPLEXITIES: Dimensions = { [FeeDimensions.Bandwidth]: INTRINSIC_BASE_TX_COMPLEXITIES[FeeDimensions.Bandwidth] + INT_LEN, // message length - [FeeDimensions.DBRead]: 0, // TODO: Implement - [FeeDimensions.DBWrite]: 0, // TODO: Implement + [FeeDimensions.DBRead]: 3, // read staker + read conversion + read weight + [FeeDimensions.DBWrite]: 5, // remaining balance utxo + write weight diff + write pk diff + weights lookup + validator write [FeeDimensions.Compute]: 0, }; -export const INTRINSIC_INCREASE_BALANCE_TX_COMPLEXITIES: Dimensions = { - [FeeDimensions.Bandwidth]: - INTRINSIC_BASE_TX_COMPLEXITIES[FeeDimensions.Bandwidth] + - ID_LEN + // validationId - LONG_LEN, // balance - [FeeDimensions.DBRead]: 0, // TODO: Implement - [FeeDimensions.DBWrite]: 0, // TODO: Implement - [FeeDimensions.Compute]: 0, -}; +export const INTRINSIC_INCREASE_L1_VALIDATOR_BALANCE_TX_COMPLEXITIES: Dimensions = + { + [FeeDimensions.Bandwidth]: + INTRINSIC_BASE_TX_COMPLEXITIES[FeeDimensions.Bandwidth] + + ID_LEN + // validationId + LONG_LEN, // balance + [FeeDimensions.DBRead]: 1, // read staker + [FeeDimensions.DBWrite]: 5, // weight diff + deactivated weight diff + public key diff + delete staker + write staker + [FeeDimensions.Compute]: 0, + }; export const INTRINSIC_DISABLE_L1_VALIDATOR_TX_COMPLEXITIES: Dimensions = { [FeeDimensions.Bandwidth]: @@ -262,7 +273,7 @@ export const INTRINSIC_DISABLE_L1_VALIDATOR_TX_COMPLEXITIES: Dimensions = { ID_LEN + // validationId INT_LEN + // auth typeId INT_LEN, // auth credential typeId - [FeeDimensions.DBRead]: 0, // TODO: Implement - [FeeDimensions.DBWrite]: 0, // TODO: Implement + [FeeDimensions.DBRead]: 1, // read staker + [FeeDimensions.DBWrite]: 6, // write remaining balance utxo + weight diff + deactivated weight diff + public key diff + delete staker + write staker [FeeDimensions.Compute]: 0, }; diff --git a/src/vms/pvm/txs/fee/fixtures/transactions.ts b/src/vms/pvm/txs/fee/fixtures/transactions.ts index 4cc38586d..fa2f14231 100644 --- a/src/vms/pvm/txs/fee/fixtures/transactions.ts +++ b/src/vms/pvm/txs/fee/fixtures/transactions.ts @@ -20,7 +20,7 @@ import { INTRINSIC_DISABLE_L1_VALIDATOR_TX_COMPLEXITIES, INTRINSIC_EXPORT_TX_COMPLEXITIES, INTRINSIC_IMPORT_TX_COMPLEXITIES, - INTRINSIC_INCREASE_BALANCE_TX_COMPLEXITIES, + INTRINSIC_INCREASE_L1_VALIDATOR_BALANCE_TX_COMPLEXITIES, INTRINSIC_INPUT_DB_READ, INTRINSIC_INPUT_DB_WRITE, INTRINSIC_OUTPUT_DB_WRITE, @@ -28,15 +28,22 @@ import { INTRINSIC_REMOVE_SUBNET_VALIDATOR_TX_COMPLEXITIES, INTRINSIC_SET_L1_VALIDATOR_WEIGHT_TX_COMPLEXITIES, INTRINSIC_TRANSFER_SUBNET_OWNERSHIP_TX_COMPLEXITIES, + INTRINSIC_BLS_POP_VERIFY_COMPUTE, + INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, + INTRINSIC_WARP_DB_READS, + INTRINSIC_BLS_AGGREGATE_COMPUTE, + INTRINSIC_BLS_VERIFY_COMPUTE, } from '../constants'; -export const TEST_DYNAMIC_PRICE = 100n; +const NANO_AVAX = 1n; + +export const TEST_DYNAMIC_PRICE = NANO_AVAX; export const TEST_DYNAMIC_WEIGHTS: Dimensions = createDimensions({ bandwidth: 1, - dbRead: 200, - dbWrite: 300, - compute: 0, // TODO: Populate + dbRead: 2_000, + dbWrite: 20_000, + compute: 10, }); export const TEST_TRANSACTIONS: ReadonlyArray<{ @@ -58,9 +65,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ INTRINSIC_BASE_TX_COMPLEXITIES[FeeDimensions.DBWrite] + INTRINSIC_INPUT_DB_WRITE + 2 * INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 149_900n, + expectedDynamicFee: 64_399n * NANO_AVAX, }, { @@ -79,9 +86,11 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ ] + INTRINSIC_INPUT_DB_WRITE + 2 * INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: + INTRINSIC_BLS_POP_VERIFY_COMPUTE + + INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 229_100n, + expectedDynamicFee: 137_191n * NANO_AVAX, }, { @@ -101,9 +110,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ ] + 2 * INTRINSIC_INPUT_DB_WRITE + 3 * INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: 2 * INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 314_800n, + expectedDynamicFee: 170_748n * NANO_AVAX, }, { @@ -123,9 +132,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ ] + 1 * INTRINSIC_INPUT_DB_WRITE + 2 * INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 209_900n, + expectedDynamicFee: 106_499n * NANO_AVAX, }, { @@ -145,9 +154,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ ] + 2 * INTRINSIC_INPUT_DB_WRITE + 3 * INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: 2 * INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 312_000n, + expectedDynamicFee: 150_720n * NANO_AVAX, }, { @@ -163,9 +172,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ INTRINSIC_ADD_SUBNET_VALIDATOR_TX_COMPLEXITIES[FeeDimensions.DBWrite] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: 2 * INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 196_000n, + expectedDynamicFee: 112_460n * NANO_AVAX, }, { @@ -181,9 +190,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ INTRINSIC_CREATE_CHAIN_TX_COMPLEXITIES[FeeDimensions.DBWrite] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: 2 * INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 180_900n, + expectedDynamicFee: 72_509n * NANO_AVAX, }, { @@ -199,9 +208,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ INTRINSIC_CREATE_SUBNET_TX_COMPLEXITIES[FeeDimensions.DBWrite] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 143_900n, + expectedDynamicFee: 64_339n * NANO_AVAX, }, { @@ -217,9 +226,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ INTRINSIC_EXPORT_TX_COMPLEXITIES[FeeDimensions.DBWrite] + INTRINSIC_INPUT_DB_WRITE + 2 * INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 153_500n, + expectedDynamicFee: 64_435n * NANO_AVAX, }, { @@ -235,9 +244,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ INTRINSIC_IMPORT_TX_COMPLEXITIES[FeeDimensions.DBWrite] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 113_500n, + expectedDynamicFee: 44_335n * NANO_AVAX, }, { @@ -256,9 +265,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ ] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: 2 * INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 193_600n, + expectedDynamicFee: 108_436n * NANO_AVAX, }, { @@ -277,13 +286,13 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ ] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: 2 * INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 173_600n, + expectedDynamicFee: 68_436n * NANO_AVAX, }, { - name: 'ConvertSubnetTx', + name: 'ConvertSubnetToL1Tx', txHex: '00000000002300003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f234262960000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d200000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f26fc0f94e000000010000000000000000a0673b4ee5ec44e57c8ab250dd7cd7b68d04421f64bd6559a4284a3ee358ff2b705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d2000000000000000100000014c582872c37c81efa2c94ea347af49cdc23a830aa000000000000c137000000003b9aca00a3783a891cb41cadbfcf456da149f30e7af972677a162b984bef0779f254baac51ec042df1781d1295df80fb41c801269731fc6c25e1e5940dc3cb8509e30348fa712742cfdc83678acc9f95908eb98b89b28802fb559b4a2a6ff3216707c07f0ceb0b45a95f4f9a9540bbd3331d8ab4f233bffa4abb97fad9d59a1695f31b92a2b89e365facf7ab8c30de7c4a496d1e000000000000000000000000000000000000000a00000001000000000000000200000009000000011430759900fdf516cdeff6a1390dd7438585568a89c06142c44b3bf1178c4cae4bff44e955b19da08f0359d396a7a738b989bb46377e7465cd858ddd1e8dd3790100000009000000011430759900fdf516cdeff6a1390dd7438585568a89c06142c44b3bf1178c4cae4bff44e955b19da08f0359d396a7a738b989bb46377e7465cd858ddd1e8dd37901', expectedComplexity: createDimensions({ @@ -296,31 +305,38 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE + INTRINSIC_L1_VALIDATOR_COMPLEXITIES[FeeDimensions.DBWrite], - compute: 0, // TODO: Implement + compute: + 2 * INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE + + INTRINSIC_BLS_POP_VERIFY_COMPUTE, }), - expectedDynamicFee: 365_600n, + expectedDynamicFee: 183_156n * NANO_AVAX, }, { - name: 'RegisterSubnetValidatorTx', + name: 'RegisterL1ValidatorTx', txHex: '00000000002400003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b552a000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001ca44ad45a63381b07074be7f82005c41550c989b967f40020f3bedc4b02191f300000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f234262404000000010000000000000000000000003b9aca00ab5cb0516b7afdb13727f766185b2b8da44e2653eef63c85f196701083e649289cce1a23c39eb471b2473bc6872aa3ea190de0fe66296cbdd4132c92c3430ff22f28f0b341b15905a005bbd66cc0f4056bc4be5934e4f3a57151a60060f429190000012f000000003039705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d20000009c000000000001000000000000008e000000000001a0673b4ee5ec44e57c8ab250dd7cd7b68d04421f64bd6559a4284a3ee358ff2b000000145efc86a11c5b12cc95b2cf527c023f9cf6e0e8f6b62034315c5d11cea4190f6ea8997821c02483d29adb5e4567843f7a44c39b2ffa20c8520dc358702fb1ec29f2746dcc000000006705af280000000000000000000000000000000000000000000000010000000000000001018e99dc6ed736089c03b9a1275e0cf801524ed341fb10111f29c0390fa2f96cf6aa78539ec767e5cd523c606c7ede50e60ba6065a3685e770d979b0df74e3541b61ed63f037463776098576e385767a695de59352b44e515831c5ee7a8cc728f9000000010000000900000001a0950b9e6e866130f0d09e2a7bfdd0246513295237258afa942b1850dab79824605c796bbfc9223cf91935fb29c66f8b927690220b9b1c24d6f078054a3e346201', expectedComplexity: createDimensions({ bandwidth: 710, dbRead: INTRINSIC_REGISTER_L1_VALIDATOR_TX_COMPLEXITIES[FeeDimensions.DBRead] + - INTRINSIC_INPUT_DB_READ, + INTRINSIC_INPUT_DB_READ + + INTRINSIC_WARP_DB_READS, dbWrite: INTRINSIC_REGISTER_L1_VALIDATOR_TX_COMPLEXITIES[FeeDimensions.DBWrite] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: + INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE + + INTRINSIC_BLS_POP_VERIFY_COMPUTE + + INTRINSIC_BLS_AGGREGATE_COMPUTE + + INTRINSIC_BLS_VERIFY_COMPUTE, }), - expectedDynamicFee: 151_000n, + expectedDynamicFee: 241_260n * NANO_AVAX, }, { - name: 'SetSubnetValidatorWeightTx', + name: 'SetL1ValidatorWeightTx', txHex: '00000000002500003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b5100000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001389c41b6ed301e4c118bd23673268fd2054b772efcf25685a117b74bab7ae5e400000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b552a000000010000000000000000000000d7000000003039705f3d4415f990225d3df5ce437d7af2aa324b1bbce854ee34ab6f39882250d200000044000000000001000000000000003600000000000338e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000000000001000000000000000500000000000000010187f4bb2c42869c56f023a1ca81045aff034acd490b8f15b5069025f982e605e077007fc588f7d56369a65df7574df3b70ff028ea173739c789525ab7eebfcb5c115b13cca8f02b362104b700c75bc95234109f3f1360ddcb4ec3caf6b0e821cb0000000100000009000000010a29f3c86d52908bf2efbc3f918a363df704c429d66c8d6615712a2a584a2a5f264a9e7b107c07122a06f31cadc2f51285884d36fe8df909a07467417f1d64cf00', expectedComplexity: createDimensions({ @@ -328,38 +344,46 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ dbRead: INTRINSIC_SET_L1_VALIDATOR_WEIGHT_TX_COMPLEXITIES[ FeeDimensions.DBRead - ] + INTRINSIC_INPUT_DB_READ, + ] + + INTRINSIC_INPUT_DB_READ + + INTRINSIC_WARP_DB_READS, dbWrite: INTRINSIC_SET_L1_VALIDATOR_WEIGHT_TX_COMPLEXITIES[ FeeDimensions.DBWrite ] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: + INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE + + INTRINSIC_BLS_AGGREGATE_COMPUTE + + INTRINSIC_BLS_VERIFY_COMPUTE, }), - expectedDynamicFee: 131_800n, + expectedDynamicFee: 206_568n * NANO_AVAX, }, { - name: 'IncreaseBalanceTx', + name: 'IncreaseL1ValidatorBalanceTx', txHex: '00000000002600003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b4e52000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001f61ea7e3bb6d33da9901644f3c623e4537b7d1c276e9ef23bcc8e4150e494d6600000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b510000000001000000000000000038e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000000000002000000010000000900000001cb56b56387be9186d86430fad5418db4d13e991b6805b6ba178b719e3f47ce001da52d6ed3173bfdd8b69940a135432abce493a10332e881f6c34cea3617595e00', expectedComplexity: createDimensions({ bandwidth: 339, dbRead: - INTRINSIC_INCREASE_BALANCE_TX_COMPLEXITIES[FeeDimensions.DBRead] + - INTRINSIC_INPUT_DB_READ, + INTRINSIC_INCREASE_L1_VALIDATOR_BALANCE_TX_COMPLEXITIES[ + FeeDimensions.DBRead + ] + INTRINSIC_INPUT_DB_READ, dbWrite: - INTRINSIC_INCREASE_BALANCE_TX_COMPLEXITIES[FeeDimensions.DBWrite] + + INTRINSIC_INCREASE_L1_VALIDATOR_BALANCE_TX_COMPLEXITIES[ + FeeDimensions.DBWrite + ] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 113_900n, + expectedDynamicFee: 146_339n * NANO_AVAX, }, { - name: 'DisableSubnetValidatorTx', + name: 'DisableL1ValidatorTx', txHex: '00000000002700003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f1f88b4b9e000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001fd91c5c421468b13b09dda413bdbe1316c7c9417f2468b893071d4cb608a01da00000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f1f88b4e5200000001000000000000000038e6e9fe31c6d070a8c792dbacf6d0aefb8eac2aded49cc0aa9f422d1fdd9ecd0000000a00000000000000020000000900000001ff99bb626d898907a660701e2febaa311b4e644fe71add2d1a3f71748102c73f54d73c8370a9ae33e09c984bb8c03da4922bf208af836ec2daaa31cb42788bee010000000900000000', expectedComplexity: createDimensions({ @@ -371,9 +395,9 @@ export const TEST_TRANSACTIONS: ReadonlyArray<{ INTRINSIC_DISABLE_L1_VALIDATOR_TX_COMPLEXITIES[FeeDimensions.DBWrite] + INTRINSIC_INPUT_DB_WRITE + INTRINSIC_OUTPUT_DB_WRITE, - compute: 0, // TODO: Implement + compute: INTRINSIC_SECP256K1_FX_SIGNATURE_COMPUTE, }), - expectedDynamicFee: 114_700n, + expectedDynamicFee: 166_347n * NANO_AVAX, }, ]; diff --git a/src/vms/pvm/txs/fee/index.ts b/src/vms/pvm/txs/fee/index.ts index a962068dc..9a0f23e2b 100644 --- a/src/vms/pvm/txs/fee/index.ts +++ b/src/vms/pvm/txs/fee/index.ts @@ -10,6 +10,10 @@ export { INTRINSIC_REMOVE_SUBNET_VALIDATOR_TX_COMPLEXITIES, INTRINSIC_TRANSFER_SUBNET_OWNERSHIP_TX_COMPLEXITIES, INTRINSIC_CONVERT_SUBNET_TO_L1_TX_COMPLEXITIES, + INTRINSIC_DISABLE_L1_VALIDATOR_TX_COMPLEXITIES, + INTRINSIC_INCREASE_L1_VALIDATOR_BALANCE_TX_COMPLEXITIES, + INTRINSIC_REGISTER_L1_VALIDATOR_TX_COMPLEXITIES, + INTRINSIC_SET_L1_VALIDATOR_WEIGHT_TX_COMPLEXITIES, } from './constants'; export { @@ -20,7 +24,7 @@ export { getSignerComplexity, getTxComplexity, getBytesComplexity, - getL1ValidatorsComplexity as getConvertSubnetValidatorsComplexity, + getL1ValidatorsComplexity, } from './complexity'; export { calculateFee } from './calculator';