Skip to content

Commit

Permalink
feat(tx-builder)!: deserialisation of channels.get.offchain_state
Browse files Browse the repository at this point in the history
BREAKING CHANGE: StateTrees fields decoded as objects mapping key to decoded entry instead internals

BREAKING CHANGE: The content of Tag.*Mtree entries decoded and moved to `payload` field
  • Loading branch information
davidyuk committed Jan 25, 2023
1 parent 8af721a commit ed31aff
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/tx/builder/field-types/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default function genArrayField<Input, Output, Binary>(
deserialize: (value: Binary, params: unknown) => Output;
},
): {
serialize: (value: Input[], params: unknown) => Binary[];
serialize: (value: readonly Input[], params: unknown) => Binary[];
deserialize: (value: Binary[], params: unknown) => Output[];
} {
return {
Expand Down
4 changes: 2 additions & 2 deletions src/tx/builder/field-types/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import type { unpackTx as unpackTxType, buildTx as buildTxType } from '../index'

export default function genEntryField<T extends Tag = Tag>(tag?: T): {
serialize: (
// TODO: replace with `Parameters<typeof buildTxType<T>>[0]`,
// TODO: replace with `TxParams & { tag: T }`,
// but fix TS2502 value is referenced directly or indirectly in its own type annotation
value: any,
options: { buildTx: typeof buildTxType },
) => Buffer;
deserialize: (
value: Buffer, options: { unpackTx: typeof unpackTxType },
// TODO: replace with `ReturnType<typeof unpackTxType<T>>`,
// TODO: replace with `TxUnpacked & { tag: T }`,
// TS2577 Return type annotation circularly references itself
) => any;
} {
Expand Down
6 changes: 6 additions & 0 deletions src/tx/builder/field-types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import _fee from './fee';
import _field from './field';
import _gasLimit from './gas-limit';
import _gasPrice from './gas-price';
import _map from './map';
import _mptree from './mptree';
import _name from './name';
import _nameFee from './name-fee';
Expand All @@ -24,6 +25,7 @@ import _shortUIntConst from './short-u-int-const';
import _string from './string';
import _ttl from './ttl';
import _uInt from './u-int';
import _wrapped from './wrapped';

// TODO: remove after fixing https://github.com/Gerrit0/typedoc-plugin-missing-exports/issues/15
const abiVersion = _abiVersion;
Expand All @@ -40,6 +42,7 @@ const fee = _fee;
const field = _field;
const gasLimit = _gasLimit;
const gasPrice = _gasPrice;
const map = _map;
const mptree = _mptree;
const name = _name;
const nameFee = _nameFee;
Expand All @@ -52,6 +55,7 @@ const shortUIntConst = _shortUIntConst;
const string = _string;
const ttl = _ttl;
const uInt = _uInt;
const wrapped = _wrapped;

export type BinaryData = Buffer | Buffer[] | Buffer[][]
| Array<[Buffer, Array<[Buffer, Buffer[]]>]>;
Expand All @@ -77,6 +81,7 @@ export {
field,
gasLimit,
gasPrice,
map,
mptree,
name,
nameFee,
Expand All @@ -89,4 +94,5 @@ export {
string,
ttl,
uInt,
wrapped,
};
45 changes: 45 additions & 0 deletions src/tx/builder/field-types/map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Tag } from '../constants';
import {
encode, Encoding, Encoded, decode,
} from '../../../utils/encoder';
import type { unpackTx as unpackTxType, buildTx as buildTxType } from '../index';

export default function genMapField<E extends Encoding, T extends Tag>(encoding: E, tag: T): {
serialize: (
// TODO: replace with `TxParams & { tag: T }`,
// but fix TS2502 value is referenced directly or indirectly in its own type annotation
value: Record<Encoded.Generic<E>, any>, options: { buildTx: typeof buildTxType }
) => Buffer;
deserialize: (
value: Buffer, options: { unpackTx: typeof unpackTxType },
// TODO: replace with `TxUnpacked & { tag: T }`,
// TS2577 Return type annotation circularly references itself
) => Record<Encoded.Generic<E>, any>;
recursiveType: true;
} {
return {
serialize(object, { buildTx }) {
return decode(buildTx({
tag: Tag.Mtree,
values: Object.entries(object).map(([key, value]) => ({
tag: Tag.MtreeValue,
key: decode(key as Encoded.Generic<E>),
value: decode(buildTx({ ...value as any, tag })),
})),
}));
},

deserialize(buffer, { unpackTx }) {
const { values } = unpackTx(encode(buffer, Encoding.Transaction), Tag.Mtree);
return Object.fromEntries(values
// TODO: remove after resolving https://github.com/aeternity/aeternity/issues/4066
.filter(({ key }) => encoding !== Encoding.ContractAddress || key.length === 32)
.map(({ key, value }) => [
encode(key, encoding),
unpackTx(encode(value, Encoding.Transaction), tag),
])) as Record<Encoded.Generic<E>, any>;
},

recursiveType: true,
};
}
32 changes: 32 additions & 0 deletions src/tx/builder/field-types/wrapped.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Tag } from '../constants';
import { encode, Encoding, decode } from '../../../utils/encoder';
import type { unpackTx as unpackTxType, buildTx as buildTxType } from '../index';

type TagWrapping = Tag.AccountsMtree | Tag.CallsMtree | Tag.ChannelsMtree | Tag.ContractsMtree
| Tag.NameserviceMtree | Tag.OraclesMtree;

export default function genWrappedField<T extends TagWrapping>(tag: T): {
serialize: (
// TODO: replace with `(TxParams & { tag: T })['payload']`,
// but fix TS2502 value is referenced directly or indirectly in its own type annotation
value: any, options: { buildTx: typeof buildTxType }
) => Buffer;
deserialize: (
value: Buffer, options: { unpackTx: typeof unpackTxType },
// TODO: replace with `(TxUnpacked & { tag: T })['payload']`,
// TS2577 Return type annotation circularly references itself
) => any;
recursiveType: true;
} {
return {
serialize(payload, { buildTx }) {
return decode(buildTx({ tag, payload }));
},

deserialize(buffer, { unpackTx }) {
return unpackTx<TagWrapping>(encode(buffer, Encoding.Transaction), tag).payload;
},

recursiveType: true,
};
}
2 changes: 1 addition & 1 deletion src/tx/builder/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export async function buildTxAsync(params: TxParamsAsync): Promise<Encoded.Trans
* @returns Object with transaction param's
*/
export function unpackTx<TxType extends Tag>(
encodedTx: Encoded.Transaction | Encoded.Poi,
encodedTx: Encoded.Transaction | Encoded.Poi | Encoded.StateTrees | Encoded.CallStateTree,
txType?: TxType,
): TxUnpacked & { tag: TxType } {
const binary = rlpDecode(decode(encodedTx));
Expand Down
113 changes: 91 additions & 22 deletions src/tx/builder/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import SchemaTypes from './SchemaTypes';
import {
uInt, shortUInt, coinAmount, name, nameId, nameFee, deposit, gasLimit, gasPrice, fee,
address, pointers, entry, enumeration, mptree, shortUIntConst, string, encoded, raw,
array, boolean, ctVersion, abiVersion, ttl, nonce,
array, boolean, ctVersion, abiVersion, ttl, nonce, map, wrapped,
} from './field-types';
import { Encoded, Encoding } from '../../utils/encoder';
import { idTagToEncoding } from './field-types/address';
Expand Down Expand Up @@ -43,21 +43,88 @@ interface EntryAny {
recursiveType: true;
}

interface EntryAnyArray {
serialize: (value: Array<TxParams | Uint8Array | Encoded.Transaction>) => Buffer[];
deserialize: (value: Buffer[]) => TxUnpacked[];
const entryAny = entry() as unknown as EntryAny;

interface EntryMtreeValueArray {
serialize: (
value: Array<TxParams & { tag: Tag.MtreeValue } | Uint8Array | Encoded.Transaction>,
) => Buffer[];
deserialize: (value: Buffer[]) => Array<TxUnpacked & { tag: Tag.MtreeValue }>;
recursiveType: true;
}

const entryMtreeValueArray = array(entry(Tag.MtreeValue)) as unknown as EntryMtreeValueArray;

interface EntryTreesPoi {
serialize: (value: TxParams & { tag: Tag.TreesPoi } | Uint8Array | Encoded.Transaction) => Buffer;
deserialize: (value: Buffer) => TxUnpacked & { tag: Tag.TreesPoi };
recursiveType: true;
}

const entryAny = entry() as unknown as EntryAny;
const entryTreesPoi = entry(Tag.TreesPoi) as unknown as EntryTreesPoi;

interface MapContracts {
serialize: (
value: Record<Encoded.ContractAddress, TxParams & { tag: Tag.Contract }>,
) => Buffer;
deserialize: (
value: Buffer,
) => Record<Encoded.ContractAddress, TxUnpacked & { tag: Tag.Contract }>;
recursiveType: true;
}

const mapContracts = map(Encoding.ContractAddress, Tag.Contract) as unknown as MapContracts;

interface MapAccounts {
serialize: (
value: Record<Encoded.AccountAddress, TxParams & { tag: Tag.Account }>,
) => Buffer;
deserialize: (value: Buffer) => Record<Encoded.AccountAddress, TxUnpacked & { tag: Tag.Account }>;
recursiveType: true;
}

const mapAccounts = map(Encoding.AccountAddress, Tag.Account) as unknown as MapAccounts;

interface MapCalls {
serialize: (
value: Record<Encoded.Bytearray, TxParams & { tag: Tag.ContractCall }>,
) => Buffer;
deserialize: (value: Buffer) => Record<Encoded.Bytearray, TxUnpacked & { tag: Tag.ContractCall }>;
recursiveType: true;
}

const mapCalls = map(Encoding.Bytearray, Tag.ContractCall) as unknown as MapCalls;

interface MapChannels {
serialize: (
value: Record<Encoded.Channel, TxParams & { tag: Tag.Channel }>,
) => Buffer;
deserialize: (value: Buffer) => Record<Encoded.Channel, TxUnpacked & { tag: Tag.Channel }>;
recursiveType: true;
}

const mapChannels = map(Encoding.Channel, Tag.Channel) as unknown as MapChannels;

interface MapNames {
serialize: (
value: Record<Encoded.Name, TxParams & { tag: Tag.Name }>,
) => Buffer;
deserialize: (value: Buffer) => Record<Encoded.Name, TxUnpacked & { tag: Tag.Name }>;
recursiveType: true;
}

const mapNames = map(Encoding.Name, Tag.Name) as unknown as MapNames;

interface MapOracles {
serialize: (
value: Record<Encoded.OracleAddress, TxParams & { tag: Tag.Oracle }>,
) => Buffer;
deserialize: (value: Buffer) => Record<Encoded.OracleAddress, TxUnpacked & { tag: Tag.Oracle }>;
recursiveType: true;
}

const mapOracles = map(Encoding.OracleAddress, Tag.Oracle) as unknown as MapOracles;

/**
* @see {@link https://github.com/aeternity/protocol/blob/c007deeac4a01e401238412801ac7084ac72d60e/serializations.md#accounts-version-1-basic-accounts}
*/
Expand Down Expand Up @@ -182,18 +249,19 @@ export const txSchema = [{
callData: encoded(Encoding.ContractBytearray),
}, {
tag: shortUIntConst(Tag.ContractCall),
version: shortUIntConst(1, true),
version: shortUIntConst(2, true),
callerId: address(Encoding.AccountAddress),
callerNonce: shortUInt,
height: shortUInt,
contractId: address(Encoding.ContractAddress),
gasPrice,
// TODO: rename after resolving https://github.com/aeternity/protocol/issues/506
gasPrice: uInt,
gasUsed: shortUInt,
returnValue: encoded(Encoding.ContractBytearray),
returnType: enumeration(CallReturnType),
// TODO: add serialization for
// <log> :: [ { <address> :: id, [ <topics> :: binary() }, <data> :: binary() } ]
log: raw,
log: array(raw),
}, {
tag: shortUIntConst(Tag.Oracle),
version: shortUIntConst(1, true),
Expand Down Expand Up @@ -418,6 +486,7 @@ export const txSchema = [{
}, {
tag: shortUIntConst(Tag.TreesPoi),
version: shortUIntConst(1, true),
// TODO: inline an extra wrapping array after resolving https://github.com/aeternity/protocol/issues/505
accounts: array(mptree(Encoding.AccountAddress, Tag.Account)),
calls: array(mptree(Encoding.Bytearray, Tag.ContractCall)),
channels: array(mptree(Encoding.Channel, Tag.Channel)),
Expand All @@ -426,17 +495,17 @@ export const txSchema = [{
oracles: array(mptree(Encoding.OracleAddress, Tag.Oracle)),
}, {
tag: shortUIntConst(Tag.StateTrees),
version: shortUIntConst(1, true),
contracts: entryAny,
calls: entryAny,
channels: entryAny,
ns: entryAny,
oracles: entryAny,
accounts: entryAny,
version: shortUIntConst(0, true),
contracts: wrapped(Tag.ContractsMtree) as unknown as MapContracts,
calls: wrapped(Tag.CallsMtree) as unknown as MapCalls,
channels: wrapped(Tag.ChannelsMtree) as unknown as MapChannels,
ns: wrapped(Tag.NameserviceMtree) as unknown as MapNames,
oracles: wrapped(Tag.OraclesMtree) as unknown as MapOracles,
accounts: wrapped(Tag.AccountsMtree) as unknown as MapAccounts,
}, {
tag: shortUIntConst(Tag.Mtree),
version: shortUIntConst(1, true),
values: array(entry()) as unknown as EntryAnyArray,
values: entryMtreeValueArray,
}, {
tag: shortUIntConst(Tag.MtreeValue),
version: shortUIntConst(1, true),
Expand All @@ -445,27 +514,27 @@ export const txSchema = [{
}, {
tag: shortUIntConst(Tag.ContractsMtree),
version: shortUIntConst(1, true),
contracts: entryAny,
payload: mapContracts,
}, {
tag: shortUIntConst(Tag.CallsMtree),
version: shortUIntConst(1, true),
calls: entryAny,
payload: mapCalls,
}, {
tag: shortUIntConst(Tag.ChannelsMtree),
version: shortUIntConst(1, true),
channels: entryAny,
payload: mapChannels,
}, {
tag: shortUIntConst(Tag.NameserviceMtree),
version: shortUIntConst(1, true),
mtree: entryAny,
payload: mapNames,
}, {
tag: shortUIntConst(Tag.OraclesMtree),
version: shortUIntConst(1, true),
otree: entryAny,
payload: mapOracles,
}, {
tag: shortUIntConst(Tag.AccountsMtree),
version: shortUIntConst(1, true),
accounts: entryAny,
payload: mapAccounts,
}, {
tag: shortUIntConst(Tag.GaAttachTx),
version: shortUIntConst(1, true),
Expand Down
Loading

0 comments on commit ed31aff

Please sign in to comment.