Skip to content

Commit

Permalink
Merge pull request #156 from fleet-sdk/add-testnet-compiler-opt
Browse files Browse the repository at this point in the history
Add `network` compiler flag
  • Loading branch information
arobsn authored Dec 6, 2024
2 parents 4528c44 + 82cec97 commit 52501f5
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changeset/five-news-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fleet-sdk/compiler": patch
---

Add `network` flag to compiler options
5 changes: 5 additions & 0 deletions .changeset/hot-bats-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fleet-sdk/core": patch
---

Override ErgoTree encoding network from constructor params
5 changes: 5 additions & 0 deletions .changeset/tame-files-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fleet-sdk/core": patch
---

Add `ErgoTree.encode()` method
25 changes: 24 additions & 1 deletion packages/compiler/src/compiler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
type CompilerOptions,
parseNamedConstantsMap
} from "./compiler";
import { ErgoAddress, Network } from "@fleet-sdk/core";

const compilerTestVectors: {
name: string;
Expand Down Expand Up @@ -107,7 +108,29 @@ describe("ErgoScript Compiler", () => {
}
});

it("Should use default if not compiler options is set", () => {
const testnetScript = 'PK("3WzH5yEJongYHmBJnoMs3zeK3t3fouMi3pigKdEURWcD61pU6Eve")';
const mainnetScript = 'PK("9f3iPJTiciBYA6DnTeGy98CvrwyEhiP7wNrhDrQ1QeKPRhTmaqQ")';

it("Should compile for testnet", () => {
expect(() => compile(testnetScript, { network: "testnet" })).not.to.throw();
});

it("should override network from compiler options", () => {
const testnetTree = compile(testnetScript, { network: "testnet" });
expect(ErgoAddress.decode(testnetTree.encode()).network).to.be.equal(Network.Testnet);

const mainnetTree = compile(mainnetScript, { network: "mainnet" });
expect(ErgoAddress.decode(mainnetTree.encode()).network).to.be.equal(Network.Mainnet);
});

it("Should compile for mainnet", () => {
// should default to mainnet
expect(() => compile(mainnetScript)).not.to.throw();
// explicitly set mainnet
expect(() => compile(mainnetScript, { network: "mainnet" })).not.to.throw();
});

it("Should use default if no compiler options is set", () => {
const { version, segregateConstants } = compilerDefaults;

const tree = compile("sigmaProp(HEIGHT > 100)");
Expand Down
64 changes: 49 additions & 15 deletions packages/compiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
ensureDefaults,
ergoTreeHeaderFlags,
isEmpty,
isHex
isHex,
Network
} from "@fleet-sdk/common";
import { SConstant } from "@fleet-sdk/serializer";
import {
Expand All @@ -14,14 +15,42 @@ import {
} from "sigmastate-js/main";
import { CompilerOutput } from "./compilerOutput";

export type NamedConstantsMap = {
[key: string]: string | Value | SConstant;
};

type CompilerOptionsBase = {
/**
* Optional version number of the ErgoTree output.
* @default 1
*/
version?: number;

/**
* Map of named constants.
*/
map?: NamedConstantsMap;

/**
* Segregate Sigma constants in the ErgoTree output.
* @default true
*/
segregateConstants?: boolean;

/**
* Network type, either "mainnet" or "testnet".
* @default "mainnet"
*/
network?: "mainnet" | "testnet";
};

export type CompilerOptionsForErgoTreeV0 = CompilerOptionsBase & {
version?: 0;

/**
* Include size in the ErgoTree header.
* @default false
*/
includeSize?: boolean;
};

Expand All @@ -31,47 +60,52 @@ export type CompilerOptionsForErgoTreeV1 = CompilerOptionsBase & {

export type CompilerOptions = CompilerOptionsForErgoTreeV0 | CompilerOptionsForErgoTreeV1;

export type NamedConstantsMap = {
[key: string]: string | Value | SConstant;
};

export const compilerDefaults: Required<CompilerOptions> = {
version: 1,
map: {},
segregateConstants: true
segregateConstants: true,
network: "mainnet"
};

/**
* Compiles a given ErgoScript with specified compiler options.
*
* @param script - The script to be compiled.
* @param options - Optional compiler options to customize the compilation process.
* @returns The output of the compilation process.
*/
export function compile(script: string, options?: CompilerOptions): CompilerOutput {
const opt = ensureDefaults(options, compilerDefaults);
assert(opt.version < 8, `Version should be lower than 8, got ${opt.version}`);

let headerFlags = 0x00 | opt.version;

if (opt.version > 0 || (opt.version === 0 && opt.includeSize)) {
headerFlags |= ergoTreeHeaderFlags.sizeInclusion;
}

const tree = SigmaCompiler$.forMainnet().compile(
const compiler =
opt.network === "mainnet" ? SigmaCompiler$.forMainnet() : SigmaCompiler$.forTestnet();

const tree = compiler.compile(
parseNamedConstantsMap(opt.map),
opt.segregateConstants,
headerFlags,
script
);

return new CompilerOutput(tree);
return new CompilerOutput(
tree,
opt.network === "mainnet" ? Network.Mainnet : Network.Testnet
);
}

export function parseNamedConstantsMap(
map: NamedConstantsMap
): SigmaCompilerNamedConstantsMap {
if (isEmpty(map)) {
return map;
}
if (isEmpty(map)) return map;

const sigmaMap: SigmaCompilerNamedConstantsMap = {};
for (const key in map) {
sigmaMap[key] = toSigmaConstant(map[key]);
}
for (const key in map) sigmaMap[key] = toSigmaConstant(map[key]);

return sigmaMap;
}
Expand Down
22 changes: 11 additions & 11 deletions packages/compiler/src/compilerOutput.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { ErgoTree } from "@fleet-sdk/core";
import { ErgoTree, type Network } from "@fleet-sdk/core";
import { hex } from "@fleet-sdk/crypto";
import type { ErgoTree as SigmaErgoTree, Value as SigmaValue } from "sigmastate-js/main";
import { ContractTemplate } from "./contractTemplate";

export class CompilerOutput extends ErgoTree {
private readonly _tree: SigmaErgoTree;
private _template?: ContractTemplate;
readonly #tree: SigmaErgoTree;
#template?: ContractTemplate;

constructor(tree: SigmaErgoTree) {
super(hex.decode(tree.toHex()));
this._tree = tree;
constructor(tree: SigmaErgoTree, network?: Network) {
super(hex.decode(tree.toHex()), network);
this.#tree = tree;
}

get template(): ContractTemplate {
if (!this._template) {
const templateBytes = hex.decode(this._tree.templateHex());
this._template = new ContractTemplate(templateBytes);
if (!this.#template) {
const templateBytes = hex.decode(this.#tree.templateHex());
this.#template = new ContractTemplate(templateBytes);
}

return this._template;
return this.#template;
}

get constants(): SigmaValue[] {
return this._tree.constants();
return this.#tree.constants();
}
}
54 changes: 54 additions & 0 deletions packages/core/src/models/ergoTree.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { hex } from "@fleet-sdk/crypto";
import { describe, expect, it, test } from "vitest";
import { ErgoTree } from "./ergoTree";
import { Network } from "@fleet-sdk/common";
import { ErgoAddress } from "./ergoAddress";

describe("ErgoTree model", () => {
test.each([
Expand Down Expand Up @@ -56,3 +58,55 @@ describe("ErgoTree model", () => {
expect(tree.toAddress().ergoTree).to.be.equal(treeHex);
});
});

describe("Encoding", () => {
const treeHex = "100104c801d191a37300";

it("Should default to mainnet", () => {
const tree = new ErgoTree(treeHex);
expect(tree.toAddress().network).to.be.equal(Network.Mainnet);
});

it("Should override encoding network from constructor params", () => {
const mainnetTree = new ErgoTree(treeHex, Network.Mainnet);
expect(mainnetTree.toAddress().network).to.be.equal(Network.Mainnet);

const testnetTree = new ErgoTree(treeHex, Network.Testnet);
expect(testnetTree.toAddress().network).to.be.equal(Network.Testnet);
});

it("Should explicitly encode for testnet", () => {
const tree = new ErgoTree(treeHex);
expect(tree.toAddress(Network.Testnet).network).to.be.equal(Network.Testnet);
});

it("Should explicitly encode for mainnet", () => {
const tree = new ErgoTree(treeHex);
expect(tree.toAddress(Network.Mainnet).network).to.be.equal(Network.Mainnet);
});

it("Should explicitly encode for a network despite of constructor params", () => {
const tree1 = new ErgoTree(treeHex, Network.Mainnet);
expect(tree1.toAddress(Network.Testnet).network).to.be.equal(Network.Testnet);

const tree2 = new ErgoTree(treeHex, Network.Testnet);
expect(tree2.toAddress(Network.Mainnet).network).to.be.equal(Network.Mainnet);
});

it("Should encode to Base58 string", () => {
// should default to mainnet
expect(ErgoAddress.decode(new ErgoTree(treeHex).encode()).network).to.be.equal(
Network.Mainnet
);

// should encode for testnet
expect(
ErgoAddress.decode(new ErgoTree(treeHex).encode(Network.Testnet)).network
).to.be.equal(Network.Testnet);

// should override encoding network from constructor params
expect(
ErgoAddress.decode(new ErgoTree(treeHex, Network.Testnet).encode()).network
).to.be.equal(Network.Testnet);
});
});
30 changes: 21 additions & 9 deletions packages/core/src/models/ergoTree.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import { ergoTreeHeaderFlags, type HexString, Network } from "@fleet-sdk/common";
import {
ergoTreeHeaderFlags,
Network,
type Base58String,
type HexString
} from "@fleet-sdk/common";
import { hex } from "@fleet-sdk/crypto";
import { ErgoAddress } from "./ergoAddress";

const VERSION_MASK = 0x07;

export class ErgoTree {
private _bytes: Uint8Array;
#bytes: Uint8Array;
#network: Network;

constructor(input: HexString | Uint8Array) {
constructor(input: HexString | Uint8Array, network?: Network) {
if (typeof input === "string") {
this._bytes = hex.decode(input);
this.#bytes = hex.decode(input);
} else {
this._bytes = input;
this.#bytes = input;
}

this.#network = network ?? Network.Mainnet;
}

get header(): number {
return this._bytes[0];
return this.#bytes[0];
}

get version(): number {
Expand All @@ -32,14 +40,18 @@ export class ErgoTree {
}

toBytes(): Uint8Array {
return this._bytes;
return this.#bytes;
}

toHex(): HexString {
return hex.encode(this.toBytes());
}

toAddress(network = Network.Mainnet): ErgoAddress {
return ErgoAddress.fromErgoTree(this.toHex(), network);
toAddress(network?: Network): ErgoAddress {
return ErgoAddress.fromErgoTree(this.toHex(), network ?? this.#network);
}

encode(network?: Network): Base58String {
return this.toAddress(network).encode();
}
}

0 comments on commit 52501f5

Please sign in to comment.