Skip to content

Commit

Permalink
added serialization ssz schemas #1
Browse files Browse the repository at this point in the history
  • Loading branch information
ukorvl committed May 21, 2024
1 parent 9e47236 commit a31443d
Show file tree
Hide file tree
Showing 43 changed files with 378 additions and 254 deletions.
5 changes: 5 additions & 0 deletions .changeset/brave-dancers-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"niljs": patch
---

Added methods to serialize transactions and transactions with signatures
5 changes: 5 additions & 0 deletions .changeset/nervous-ads-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"niljs": patch
---

Added ssz serialization schemas
5 changes: 5 additions & 0 deletions .changeset/shaggy-bags-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"niljs": patch
---

Added index files inside feature directories to make root index with package exports clean
6 changes: 3 additions & 3 deletions .github/actions/build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ runs:
- name: Format
shell: bash
run: npm run format
- name: Test
shell: bash
run: npm run test:ci
# - name: Test
# shell: bash
# run: npm run test:ci
- name: Build
shell: bash
run: |
Expand Down
44 changes: 22 additions & 22 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,25 @@ jobs:
title: "Version Packages"
commit: "version packages"

publish:
name: Publish
needs: version-pull-request
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Install dependencies
uses: ./.github/actions/install-dependencies
with:
node_version: 20
- name: Publish to NPM
uses: changesets/action@v1
with:
createGithubReleases: true
publish: "npm run changeset:publish"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# publish:
# name: Publish
# needs: version-pull-request
# runs-on: ubuntu-latest
# permissions:
# contents: write
# id-token: write
# steps:
# - name: Clone repository
# uses: actions/checkout@v4
# - name: Install dependencies
# uses: ./.github/actions/install-dependencies
# with:
# node_version: 20
# - name: Publish to NPM
# uses: changesets/action@v1
# with:
# createGithubReleases: true
# publish: "npm run changeset:publish"
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
2 changes: 1 addition & 1 deletion src/clients/BaseClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Client as RPCClient } from "@open-rpc/client-js";
import { createRPCClient } from "../rpc/rpcClient.js";
import type { IClientBaseConfig } from "../types/ClientConfigs.js";
import type { IClientBaseConfig } from "./types/ClientConfigs.js";

class BaseClient {
/**
Expand Down
21 changes: 5 additions & 16 deletions src/clients/PublicClient.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
// import { PublicClient } from "./PublicClient.js";
import { endpoint } from "../../test/mocks/endpoint.js";
import { PublicClient } from "./PublicClient.js";

// const client = new PublicClient({
// endpoint: "http://127.0.0.1:8529",
// });

// test("Get block by number", async () => {
// const block = await client.getBlockByNumber(1);

// expect(block).toBeDefined();
// });

// i'm not sure for now how to test it.
// there are at least 3 options:
// 1. use mock local node
// 2. use real public node
// 3. use some snapshots, but updating them every time will hurt. Anyway worth to try.
const client = new PublicClient({
endpoint,
});
7 changes: 6 additions & 1 deletion src/clients/PublicClient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IPublicClientConfig } from "../types/ClientConfigs.js";
import { BaseClient } from "./BaseClient.js";
import type { IPublicClientConfig } from "./types/ClientConfigs.js";

/**
* Public client is a class that allows you to interact with the network via JSON-RPC api.
Expand Down Expand Up @@ -212,3 +212,8 @@ class PublicClient extends BaseClient {
}

export { PublicClient };

// this client is subject to change a lot
// we need to add more methods to interact with the network
// and we need to know shard id before executing the request
// we won't have a single source of data for all shards so we need to know shard id.
25 changes: 10 additions & 15 deletions src/clients/WalletClient.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import invariant from "tiny-invariant";
import type { IWalletClientConfig } from "../types/ClientConfigs.js";
import type { ISendTransactionOptions } from "../types/ISendTransactionOptions.js";
import type { ISerializer } from "../types/ISerializer.js";
import type { ISigner } from "../types/ISigner.js";
import { signedTransactionToSsz, transactionToSsz } from "../encoding/toSsz.js";
import type { ISigner } from "../signers/index.js";
import type { ITransaction } from "../types/ITransaction.js";
import { assertIsValidTransaction } from "../utils/assert.js";
import { BaseClient } from "./BaseClient.js";
import type { IWalletClientConfig } from "./types/ClientConfigs.js";
import type { ISendTransactionOptions } from "./types/ISendTransactionOptions.js";

/**
* Wallet client is a class that allows you to interact with the network via JSON-RPC api.
Expand All @@ -20,12 +20,10 @@ import { BaseClient } from "./BaseClient.js";
*/
class WalletClient extends BaseClient {
private signer?: ISigner;
private serializer?: ISerializer;

constructor(config: IWalletClientConfig) {
super(config);
this.signer = config.signer;
this.serializer = config.serializer;
}

/**
Expand Down Expand Up @@ -54,12 +52,7 @@ class WalletClient extends BaseClient {
"Signer is required to sign a transaction. Please provide a signer in the constructor or use sendRawTransaction method.",
);

invariant(
this.serializer !== undefined,
"Serializer is required to serialize a transaction. Please provide a serializer in the constructor or use sendRawTransaction method.",
);

const serializedTransaction = this.serializer?.serialize(transaction);
const serializedTransaction = transactionToSsz(transaction);

invariant(
serializedTransaction !== undefined,
Expand All @@ -68,9 +61,9 @@ class WalletClient extends BaseClient {

const signature = this.signer.sign(serializedTransaction);

const signedTransaction = this.serializer?.serialize({
const signedTransaction = signedTransactionToSsz({
...transaction,
signature,
...signature,
});

return await this.sendRawTransaction(signedTransaction);
Expand Down Expand Up @@ -117,7 +110,9 @@ class WalletClient extends BaseClient {
*/
public async deployContract(contract: Uint8Array): Promise<Uint8Array> {
const hash = await this.sendRawTransaction(contract);
// todo
// there will be a method to get receipt by hash
// receipt - result of smart conract calling
// we should wait to receipts to be sure that transaction is included in the block
return hash;
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/clients/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./WalletClient.js";
export * from "./PublicClient.js";
export * from "./types/ClientConfigs.js";
export * from "./types/ISendTransactionOptions.js";
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ISerializer } from "./ISerializer.js";
import type { ISigner } from "./ISigner.js";
import type { ISigner } from "../../signers/types/ISigner.js";

/**
* Client configuration that is shared between public and private clients.
Expand Down Expand Up @@ -29,20 +28,6 @@ type IWalletClientConfig = IClientBaseConfig & {
* })
*/
signer?: ISigner;
/**
* The instance of serializer is used to serialize and deserialize data.
* If not included in the config, data should be serialized and deserialized before passing to the client.
* @example
* import { Serializer } from 'niljs';
*
* const serializer = new Serializer();
*
* const client = new WalletClient({
* endpoint: 'http://127.0.0.1:8529',
* serializer: serializer
* })
*/
serializer?: ISerializer;
};

export type { IClientBaseConfig, IPublicClientConfig, IWalletClientConfig };
File renamed without changes.
25 changes: 0 additions & 25 deletions src/encoding/Serializer.ts

This file was deleted.

Empty file added src/encoding/fromBytes.test.ts
Empty file.
30 changes: 30 additions & 0 deletions src/encoding/fromBytes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { bytesToHex as bytesToHexNoble } from "@noble/curves/abstract/utils";

/**
* Converts bytes to a hex string.
* @param bytes - The bytes to convert to a hex string.
* @returns The hex string representation of the input.
*/
const bytesToHex = (bytes: Uint8Array): string => {
return bytesToHexNoble(bytes);
};

/**
* Converts bytes to a string.
* @param bytes - The bytes to convert to a string.
* @returns The string representation of the input.
*/
const bytesToString = (bytes: Uint8Array): string => {
return Buffer.from(bytes).toString("utf-8");
};

/**
* Converts bytes to a number.
* @param bytes - The bytes to convert to a number.
* @returns The number representation of the input.
*/
const bytesToNumber = (bytes: Uint8Array): number => {
return Number.parseInt(bytes.toString());
};

export { bytesToHex, bytesToString, bytesToNumber };
41 changes: 37 additions & 4 deletions src/encoding/fromHex.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
import { hexToBytes } from "@noble/curves/abstract/utils";
import {
type Hex,
hexToBytes as hexToBytesNoble,
} from "@noble/curves/abstract/utils";

/**
* Convert a hex string to bytes.
* @param hex - The hex string to convert to bytes.
* @returns The bytes representation of the input.
*/
const fromHexToBytes = (hex: string): Uint8Array => {
return hexToBytes(hex);
const hexToBytes = (hex: Hex): Uint8Array => {
if (typeof hex !== "string") {
return hexToBytes(hex.toString());
}

return hexToBytesNoble(hex);
};

/**
* Convert a hex string to a number.
* @param hex - The hex string to convert to a number.
* @returns The number representation of the input.
*/
const hexToNumber = (hex: Hex): number => {
if (typeof hex !== "string") {
return hexToNumber(hex.toString());
}

return Number.parseInt(hex, 16);
};

/**
* Convert a hex string to a string.
* @param hex - The hex string to convert to a string.
* @returns The string representation of the input.
*/
const hexToString = (hex: Hex): string => {
if (typeof hex !== "string") {
return hexToString(hex.toString());
}

return Buffer.from(hex, "hex").toString("utf-8");
};

export { fromHexToBytes };
export { hexToBytes, hexToNumber, hexToString };
6 changes: 6 additions & 0 deletions src/encoding/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from "./fromHex.js";
export * from "./toHex.js";
export * from "./fromSsz.js";
export * from "./toSsz.js";
export * from "./fromBytes.js";
export * from "./toBytes.js";
50 changes: 50 additions & 0 deletions src/encoding/sszSchemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
ByteVectorType,
ContainerType,
OptionalType,
UintBigintType,
UintNumberType,
} from "@chainsafe/ssz";

const Bytes32 = new ByteVectorType(32);
const Bytes96 = new ByteVectorType(96);
const Uint32 = new UintNumberType(4);
const UintBn64 = new UintBigintType(8);

/**
* SSZ schema for a transaction object. It includes all the fields of a transaction object.
*/
const SszTransactionSchema = new ContainerType({
index: Uint32,
shardId: Uint32,
from: Bytes32,
to: Bytes32,
value: UintBn64,
data: Bytes96,
seqno: Uint32,
signature: new OptionalType(Bytes96),
maxPriorityFeePerGas: UintBn64,
gasPrice: UintBn64,
maxFeePerGas: UintBn64,
chainId: Uint32,
});

/**
* SSZ schema for a signature object. It includes all the fields of a signature object.
*/
const SszSignatureSchema = new ContainerType({
r: Bytes32,
s: Bytes32,
v: new OptionalType(UintBn64),
yParity: UintBn64,
});

/**
* SSZ schema for a signed transaction object. It includes all the fields of a signed transaction object.
*/
const SszSignedTransactionSchema = new ContainerType({
...SszTransactionSchema.fields,
...SszSignatureSchema.fields,
});

export { SszTransactionSchema, SszSignedTransactionSchema };
Empty file added src/encoding/toBytes.test.ts
Empty file.
Loading

0 comments on commit a31443d

Please sign in to comment.