Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Transaction Builder] Adding distinct interfaces for entry function and script transaction payloads #8

Merged
merged 11 commits into from
Oct 11, 2023
2 changes: 1 addition & 1 deletion src/bcs/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

export * from "./serializer";
export * from "./deserializer";
export * from "./serializer";
61 changes: 61 additions & 0 deletions src/bcs/serializable/entry-function-bytes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

import { Serializer, Serializable } from "../serializer";
import { Deserializer } from "../deserializer";
import { FixedBytes } from "./fixed-bytes";
import { EntryFunctionArgument } from "../../transactions/instances/transactionArgument";
import { HexInput } from "../../types";

/**
* This class exists solely to represent a sequence of fixed bytes as a serialized entry function, because
* serializing an entry function appends a prefix that's *only* used for entry function arguments.
*
* NOTE: Attempting to use this class for a serialized script function will result in erroneous
* and unexpected behavior.
*
* If you wish to convert this class back to a TransactionArgument, you must know the type
* of the argument beforehand, and use the appropriate class to deserialize the bytes within
* an instance of this class.
*/
export class EntryFunctionBytes extends Serializable implements EntryFunctionArgument {
public readonly value: FixedBytes;

private constructor(value: HexInput) {
super();
this.value = new FixedBytes(value);
}

// Note that to see the Move, BCS-serialized representation of the underlying fixed byte vector,
// we must not serialize the length prefix.
//
// In other words, this class is only used to represent a sequence of bytes that are already
// BCS-serialized as a type. To represent those bytes accurately, the BCS-serialized form is the same exact
// representation.
serialize(serializer: Serializer): void {
serializer.serialize(this.value);
}

// When we serialize these bytes as an entry function argument, we need to
// serialize the length prefix. This essentially converts the underlying fixed byte vector to a type-agnostic
// byte vector to an `any` type.
// NOTE: This, and the lack of a `serializeForScriptFunction`, is the only meaningful difference between this
// class and FixedBytes.
serializeForEntryFunction(serializer: Serializer): void {
serializer.serializeU32AsUleb128(this.value.value.length);
serializer.serialize(this);
}

/**
* The only way to create an instance of this class is to use this static method.
*
* This function should only be used when deserializing a sequence of EntryFunctionPayload arguments.
* @param deserializer the deserializer instance with the buffered bytes
* @param length the length of the bytes to deserialize
* @returns an instance of this class, which will now only be usable as an EntryFunctionArgument
*/
static deserialize(deserializer: Deserializer, length: number): EntryFunctionBytes {
const fixedBytes = FixedBytes.deserialize(deserializer, length);
return new EntryFunctionBytes(fixedBytes.value);
}
}
49 changes: 33 additions & 16 deletions src/bcs/serializable/fixed-bytes.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

import { Serializable, Serializer } from "../serializer";
import { Serializer, Serializable } from "../serializer";
import { Deserializer } from "../deserializer";
import { HexInput } from "../../types";
import { Hex } from "../../core";
import { Hex } from "../../core/hex";
import { TransactionArgument } from "../../transactions/instances/transactionArgument";

/**
* This class exists to represent a contiguous sequence of BCS bytes that when serialized
* do *not* prepend the length of the byte sequence at the beginning.
* This class exists to represent a contiguous sequence of already serialized BCS-bytes.
*
* The main time to use this class is when you are passing around already BCS-serialized bytes
* that do not need to undergo another round of BCS serialization.
* It differs from most other Serializable classes in that its internal byte buffer is serialized to BCS
* bytes exactly as-is, without prepending the length of the bytes.
*
* If you want to write your own serialization function and pass the bytes as a transaction argument,
* you should use this class.
*
* This class is also more generally used to represent type-agnostic BCS bytes as a vector<u8>.
*
* An example of this is the bytes resulting from entry function arguments that have been serialized
* for an entry function.
*
* @example
* const yourCustomSerializedBytes = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
* const fixedBytes = new FixedBytes(yourCustomSerializedBytes);
* const payload = generateTransactionPayload({
* function: "0xbeefcafe::your_module::your_function_that_requires_custom_serialization",
* type_arguments: [],
* arguments: [yourCustomBytes],
* });
*
* For example, if you store each of the 32 bytes for an address as a U8 in a MoveVector<U8>, when you
* serialize that MoveVector<U8>, it will be serialized to 33 bytes. If you solely want to pass around
Expand All @@ -20,17 +37,9 @@ import { Hex } from "../../core";
*
* @params value: HexInput representing a sequence of Uint8 bytes
* @returns a Serializable FixedBytes instance, which when serialized, does not prepend the length of the bytes
* @example
* const address = AccountAddress.ONE;
* const bytes = address.bcsToBytes();
* // bytes is the Move serialized version of an address
* // it has a fixed length, meaning it doesn't have a length at the beginning.
* const fixedBytes = new FixedBytes(bytes);
* // or, say, deserializing it from a sequence of bytes and you *do* know the length
* const fixedBytes = FixedBytes.deserialize(deserializer, 32);
* @see EntryFunction
* @see EntryFunctionBytes
*/
export class FixedBytes extends Serializable {
export class FixedBytes extends Serializable implements TransactionArgument {
public value: Uint8Array;

constructor(value: HexInput) {
Expand All @@ -42,6 +51,14 @@ export class FixedBytes extends Serializable {
serializer.serializeFixedBytes(this.value);
}

serializeForEntryFunction(serializer: Serializer): void {
serializer.serialize(this);
}

serializeForScriptFunction(serializer: Serializer): void {
serializer.serialize(this);
}

static deserialize(deserializer: Deserializer, length: number): FixedBytes {
const bytes = deserializer.deserializeFixedBytes(length);
return new FixedBytes(bytes);
Expand Down
87 changes: 79 additions & 8 deletions src/bcs/serializable/move-primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import {
MAX_U8_NUMBER,
MAX_U256_BIG_INT,
} from "../consts";
import { AnyNumber, Uint16, Uint32, Uint8 } from "../../types";
import { Deserializer } from "../deserializer";
import { Serializable, Serializer, ensureBoolean, validateNumberInRange } from "../serializer";
import { TransactionArgument } from "../../transactions/instances/transactionArgument";
import { AnyNumber, Uint16, Uint32, Uint8, ScriptTransactionArgumentVariants } from "../../types";

export class Bool extends Serializable {
export class Bool extends Serializable implements TransactionArgument {
public readonly value: boolean;

constructor(value: boolean) {
Expand All @@ -26,12 +27,22 @@ export class Bool extends Serializable {
serializer.serializeBool(this.value);
}

serializeForEntryFunction(serializer: Serializer): void {
const bcsBytes = this.bcsToBytes();
serializer.serializeBytes(bcsBytes);
}

serializeForScriptFunction(serializer: Serializer): void {
serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.Bool);
serializer.serialize(this);
}

static deserialize(deserializer: Deserializer): Bool {
return new Bool(deserializer.deserializeBool());
}
}

export class U8 extends Serializable {
export class U8 extends Serializable implements TransactionArgument {
public readonly value: Uint8;

constructor(value: Uint8) {
Expand All @@ -44,12 +55,22 @@ export class U8 extends Serializable {
serializer.serializeU8(this.value);
}

serializeForEntryFunction(serializer: Serializer): void {
const bcsBytes = this.bcsToBytes();
serializer.serializeBytes(bcsBytes);
}

serializeForScriptFunction(serializer: Serializer): void {
serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U8);
serializer.serialize(this);
}

static deserialize(deserializer: Deserializer): U8 {
return new U8(deserializer.deserializeU8());
}
}

export class U16 extends Serializable {
export class U16 extends Serializable implements TransactionArgument {
public readonly value: Uint16;

constructor(value: Uint16) {
Expand All @@ -62,12 +83,22 @@ export class U16 extends Serializable {
serializer.serializeU16(this.value);
}

serializeForEntryFunction(serializer: Serializer): void {
const bcsBytes = this.bcsToBytes();
serializer.serializeBytes(bcsBytes);
}

serializeForScriptFunction(serializer: Serializer): void {
serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U16);
serializer.serialize(this);
}

static deserialize(deserializer: Deserializer): U16 {
return new U16(deserializer.deserializeU16());
}
}

export class U32 extends Serializable {
export class U32 extends Serializable implements TransactionArgument {
public readonly value: Uint32;

constructor(value: Uint32) {
Expand All @@ -80,12 +111,22 @@ export class U32 extends Serializable {
serializer.serializeU32(this.value);
}

serializeForEntryFunction(serializer: Serializer): void {
const bcsBytes = this.bcsToBytes();
serializer.serializeBytes(bcsBytes);
}

serializeForScriptFunction(serializer: Serializer): void {
serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U32);
serializer.serialize(this);
}

static deserialize(deserializer: Deserializer): U32 {
return new U32(deserializer.deserializeU32());
}
}

export class U64 extends Serializable {
export class U64 extends Serializable implements TransactionArgument {
public readonly value: bigint;

constructor(value: AnyNumber) {
Expand All @@ -98,12 +139,22 @@ export class U64 extends Serializable {
serializer.serializeU64(this.value);
}

serializeForEntryFunction(serializer: Serializer): void {
const bcsBytes = this.bcsToBytes();
serializer.serializeBytes(bcsBytes);
}

serializeForScriptFunction(serializer: Serializer): void {
serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U64);
serializer.serialize(this);
}

static deserialize(deserializer: Deserializer): U64 {
return new U64(deserializer.deserializeU64());
}
}

export class U128 extends Serializable {
export class U128 extends Serializable implements TransactionArgument {
public readonly value: bigint;

constructor(value: AnyNumber) {
Expand All @@ -116,12 +167,22 @@ export class U128 extends Serializable {
serializer.serializeU128(this.value);
}

serializeForEntryFunction(serializer: Serializer): void {
const bcsBytes = this.bcsToBytes();
serializer.serializeBytes(bcsBytes);
}

serializeForScriptFunction(serializer: Serializer): void {
serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U128);
serializer.serialize(this);
}

static deserialize(deserializer: Deserializer): U128 {
return new U128(deserializer.deserializeU128());
}
}

export class U256 extends Serializable {
export class U256 extends Serializable implements TransactionArgument {
public readonly value: bigint;

constructor(value: AnyNumber) {
Expand All @@ -134,6 +195,16 @@ export class U256 extends Serializable {
serializer.serializeU256(this.value);
}

serializeForEntryFunction(serializer: Serializer): void {
const bcsBytes = this.bcsToBytes();
serializer.serializeBytes(bcsBytes);
}

serializeForScriptFunction(serializer: Serializer): void {
serializer.serializeU32AsUleb128(ScriptTransactionArgumentVariants.U256);
serializer.serialize(this);
}

static deserialize(deserializer: Deserializer): U256 {
return new U256(deserializer.deserializeU256());
}
Expand Down
Loading
Loading