From 97bb06730bcb528d0590d70f87d8cfa0a1a23367 Mon Sep 17 00:00:00 2001 From: painterpuppets Date: Fri, 24 Jun 2022 02:10:53 +0800 Subject: [PATCH] feat: add omnilock administrator mode example --- examples/omni-lock-administrator/.gitignore | 2 + examples/omni-lock-administrator/README.md | 45 + .../generated/omni.d.ts | 427 +++ .../omni-lock-administrator/generated/omni.js | 2579 +++++++++++++++++ examples/omni-lock-administrator/index.ts | 369 +++ examples/omni-lock-administrator/lib.ts | 423 +++ examples/omni-lock-administrator/package.json | 18 + .../omni-lock-administrator/tsconfig.json | 10 + yarn.lock | 48 +- 9 files changed, 3911 insertions(+), 10 deletions(-) create mode 100644 examples/omni-lock-administrator/.gitignore create mode 100644 examples/omni-lock-administrator/README.md create mode 100644 examples/omni-lock-administrator/generated/omni.d.ts create mode 100644 examples/omni-lock-administrator/generated/omni.js create mode 100644 examples/omni-lock-administrator/index.ts create mode 100644 examples/omni-lock-administrator/lib.ts create mode 100644 examples/omni-lock-administrator/package.json create mode 100644 examples/omni-lock-administrator/tsconfig.json diff --git a/examples/omni-lock-administrator/.gitignore b/examples/omni-lock-administrator/.gitignore new file mode 100644 index 000000000..f6145084c --- /dev/null +++ b/examples/omni-lock-administrator/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +index.js diff --git a/examples/omni-lock-administrator/README.md b/examples/omni-lock-administrator/README.md new file mode 100644 index 000000000..e67d7ad88 --- /dev/null +++ b/examples/omni-lock-administrator/README.md @@ -0,0 +1,45 @@ +# Lumos & Omnilock Administrator mode + +Nervos maintains a powerful lock +called [Omni Lock](https://github.com/XuJiandong/docs-bank/blob/master/omni_lock.md) (previously named RC lock), which +can use private key as a signer. This example will show how to use Lumos to send a transaction using Omni lock Administrator mode + +## Quick Start + +``` +$ yarn +$ yarn build +``` + +use lib +``` +$ node --experimental-repl-await + +# import lib +node> omni = require("./index") + +# generate white list sparse merkle tree root & proof +node> { root, proof } = omni.generateWLRule([omni.alice.lockScript.args]) + +# deploy white list cell with typeId +node> rceCellScript = await omni.deployRCE({ from: omni.alice.address, fromPrivKey: omni.alice.privKey, root }) + +# generate bob's omnilock administrator addr +node> bobOmniAddr = omni.generateOmniLockAdministratorAddress(omni.bob.lockScript.args.substring(2), rceCellScript.CODE_HASH.substring(2)) + +# get test CKB from https://faucet.nervos.org/ + +# wait a moment, and we can find bob's omnilock administrator addr has test CKB + +# unlock by administrator (alice) pubkey hash +node> txSkeleton = await omni.buildTransferByOmnilockAdministrator({ + rceCellConfig: rceCellScript, + from: bobOmniAddr, + to: omni.alice.address, + amount: "10000000000", +}); + +node> signedTx = omni.sealOmnilockTxSkeleton(txSkeleton, omni.alice.privKey, omni.alice.lockScript.args, proof); + +node> txHash = await omni.rpc.send_transaction(signedTx, "passthrough"); +``` diff --git a/examples/omni-lock-administrator/generated/omni.d.ts b/examples/omni-lock-administrator/generated/omni.d.ts new file mode 100644 index 000000000..3fbef6ef1 --- /dev/null +++ b/examples/omni-lock-administrator/generated/omni.d.ts @@ -0,0 +1,427 @@ +export interface CastToArrayBuffer { + toArrayBuffer(): ArrayBuffer; +} + +export type CanCastToArrayBuffer = ArrayBuffer | CastToArrayBuffer; + +export interface CreateOptions { + validate?: boolean; +} + +export interface UnionType { + type: string; + value: any; +} + +export function SerializeIdentity(value: CanCastToArrayBuffer): ArrayBuffer; +export class Identity { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): number; + raw(): ArrayBuffer; + static size(): Number; +} + +export function SerializeRcIdentity(value: object): ArrayBuffer; +export class RcIdentity { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getIdentity(): Identity; + getProofs(): SmtProofEntryVec; +} + +export function SerializeRcIdentityOpt(value: object | null): ArrayBuffer; +export class RcIdentityOpt { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + value(): RcIdentity; + hasValue(): boolean; +} + +export function SerializeRcLockWitnessLock(value: object): ArrayBuffer; +export class RcLockWitnessLock { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getSignature(): BytesOpt; + getRcIdentity(): RcIdentityOpt; + getPreimage(): BytesOpt; +} + +export function SerializeScriptVec(value: Array): ArrayBuffer; +export class ScriptVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): Script; + length(): number; +} + +export function SerializeScriptVecOpt(value: Array | null): ArrayBuffer; +export class ScriptVecOpt { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + value(): ScriptVec; + hasValue(): boolean; +} + +export function SerializeXudtWitnessInput(value: object): ArrayBuffer; +export class XudtWitnessInput { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getRawExtensionData(): ScriptVecOpt; + getExtensionData(): BytesVec; +} + +export function SerializeRCRule(value: object): ArrayBuffer; +export class RCRule { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + static size(): Number; + getSmtRoot(): Byte32; + getFlags(): number; +} + +export function SerializeRCCellVec(value: Array): ArrayBuffer; +export class RCCellVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): Byte32; + length(): number; +} + +export function SerializeRCData(value: UnionType): ArrayBuffer; +export class RCData { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + unionType(): string; + value(): any; +} + +export function SerializeSmtProof(value: CanCastToArrayBuffer): ArrayBuffer; +export class SmtProof { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): number; + raw(): ArrayBuffer; + length(): number; +} + +export function SerializeSmtProofEntry(value: object): ArrayBuffer; +export class SmtProofEntry { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getMask(): number; + getProof(): SmtProof; +} + +export function SerializeSmtProofEntryVec(value: Array): ArrayBuffer; +export class SmtProofEntryVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): SmtProofEntry; + length(): number; +} + +export function SerializeSmtUpdateItem(value: object): ArrayBuffer; +export class SmtUpdateItem { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + static size(): Number; + getKey(): Byte32; + getPackedValues(): number; +} + +export function SerializeSmtUpdateItemVec(value: Array): ArrayBuffer; +export class SmtUpdateItemVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): SmtUpdateItem; + length(): number; +} + +export function SerializeSmtUpdateAction(value: object): ArrayBuffer; +export class SmtUpdateAction { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getUpdates(): SmtUpdateItemVec; + getProof(): SmtProof; +} + +export function SerializeXudtData(value: object): ArrayBuffer; +export class XudtData { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getLock(): Bytes; + getData(): BytesVec; +} + +export function SerializeUint32(value: CanCastToArrayBuffer): ArrayBuffer; +export class Uint32 { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): number; + raw(): ArrayBuffer; + toBigEndianUint32(): number; + toLittleEndianUint32(): number; + static size(): Number; +} + +export function SerializeUint64(value: CanCastToArrayBuffer): ArrayBuffer; +export class Uint64 { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): number; + raw(): ArrayBuffer; + static size(): Number; +} + +export function SerializeUint128(value: CanCastToArrayBuffer): ArrayBuffer; +export class Uint128 { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): number; + raw(): ArrayBuffer; + static size(): Number; +} + +export function SerializeByte32(value: CanCastToArrayBuffer): ArrayBuffer; +export class Byte32 { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): number; + raw(): ArrayBuffer; + static size(): Number; +} + +export function SerializeUint256(value: CanCastToArrayBuffer): ArrayBuffer; +export class Uint256 { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): number; + raw(): ArrayBuffer; + static size(): Number; +} + +export function SerializeBytes(value: CanCastToArrayBuffer): ArrayBuffer; +export class Bytes { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): number; + raw(): ArrayBuffer; + length(): number; +} + +export function SerializeBytesOpt(value: CanCastToArrayBuffer | null): ArrayBuffer; +export class BytesOpt { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + value(): Bytes; + hasValue(): boolean; +} + +export function SerializeBytesVec(value: Array): ArrayBuffer; +export class BytesVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): Bytes; + length(): number; +} + +export function SerializeByte32Vec(value: Array): ArrayBuffer; +export class Byte32Vec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): Byte32; + length(): number; +} + +export function SerializeScriptOpt(value: object | null): ArrayBuffer; +export class ScriptOpt { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + value(): Script; + hasValue(): boolean; +} + +export function SerializeProposalShortId(value: CanCastToArrayBuffer): ArrayBuffer; +export class ProposalShortId { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): number; + raw(): ArrayBuffer; + static size(): Number; +} + +export function SerializeUncleBlockVec(value: Array): ArrayBuffer; +export class UncleBlockVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): UncleBlock; + length(): number; +} + +export function SerializeTransactionVec(value: Array): ArrayBuffer; +export class TransactionVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): Transaction; + length(): number; +} + +export function SerializeProposalShortIdVec(value: Array): ArrayBuffer; +export class ProposalShortIdVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): ProposalShortId; + length(): number; +} + +export function SerializeCellDepVec(value: Array): ArrayBuffer; +export class CellDepVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): CellDep; + length(): number; +} + +export function SerializeCellInputVec(value: Array): ArrayBuffer; +export class CellInputVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): CellInput; + length(): number; +} + +export function SerializeCellOutputVec(value: Array): ArrayBuffer; +export class CellOutputVec { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + indexAt(i: number): CellOutput; + length(): number; +} + +export function SerializeScript(value: object): ArrayBuffer; +export class Script { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getCodeHash(): Byte32; + getHashType(): number; + getArgs(): Bytes; +} + +export function SerializeOutPoint(value: object): ArrayBuffer; +export class OutPoint { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + static size(): Number; + getTxHash(): Byte32; + getIndex(): Uint32; +} + +export function SerializeCellInput(value: object): ArrayBuffer; +export class CellInput { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + static size(): Number; + getSince(): Uint64; + getPreviousOutput(): OutPoint; +} + +export function SerializeCellOutput(value: object): ArrayBuffer; +export class CellOutput { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getCapacity(): Uint64; + getLock(): Script; + getType(): ScriptOpt; +} + +export function SerializeCellDep(value: object): ArrayBuffer; +export class CellDep { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + static size(): Number; + getOutPoint(): OutPoint; + getDepType(): number; +} + +export function SerializeRawTransaction(value: object): ArrayBuffer; +export class RawTransaction { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getVersion(): Uint32; + getCellDeps(): CellDepVec; + getHeaderDeps(): Byte32Vec; + getInputs(): CellInputVec; + getOutputs(): CellOutputVec; + getOutputsData(): BytesVec; +} + +export function SerializeTransaction(value: object): ArrayBuffer; +export class Transaction { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getRaw(): RawTransaction; + getWitnesses(): BytesVec; +} + +export function SerializeRawHeader(value: object): ArrayBuffer; +export class RawHeader { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + static size(): Number; + getVersion(): Uint32; + getCompactTarget(): Uint32; + getTimestamp(): Uint64; + getNumber(): Uint64; + getEpoch(): Uint64; + getParentHash(): Byte32; + getTransactionsRoot(): Byte32; + getProposalsHash(): Byte32; + getUnclesHash(): Byte32; + getDao(): Byte32; +} + +export function SerializeHeader(value: object): ArrayBuffer; +export class Header { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + static size(): Number; + getRaw(): RawHeader; + getNonce(): Uint128; +} + +export function SerializeUncleBlock(value: object): ArrayBuffer; +export class UncleBlock { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getHeader(): Header; + getProposals(): ProposalShortIdVec; +} + +export function SerializeBlock(value: object): ArrayBuffer; +export class Block { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getHeader(): Header; + getUncles(): UncleBlockVec; + getTransactions(): TransactionVec; + getProposals(): ProposalShortIdVec; +} + +export function SerializeCellbaseWitness(value: object): ArrayBuffer; +export class CellbaseWitness { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getLock(): Script; + getMessage(): Bytes; +} + +export function SerializeWitnessArgs(value: object): ArrayBuffer; +export class WitnessArgs { + constructor(reader: CanCastToArrayBuffer, options?: CreateOptions); + validate(compatible?: boolean): void; + getLock(): BytesOpt; + getInputType(): BytesOpt; + getOutputType(): BytesOpt; +} + diff --git a/examples/omni-lock-administrator/generated/omni.js b/examples/omni-lock-administrator/generated/omni.js new file mode 100644 index 000000000..3502a7232 --- /dev/null +++ b/examples/omni-lock-administrator/generated/omni.js @@ -0,0 +1,2579 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.ScriptVecOpt = exports.ScriptVec = exports.ScriptOpt = exports.Script = exports.RcLockWitnessLock = exports.RcIdentityOpt = exports.RcIdentity = exports.RawTransaction = exports.RawHeader = exports.RCRule = exports.RCData = exports.RCCellVec = exports.ProposalShortIdVec = exports.ProposalShortId = exports.OutPoint = exports.Identity = exports.Header = exports.CellbaseWitness = exports.CellOutputVec = exports.CellOutput = exports.CellInputVec = exports.CellInput = exports.CellDepVec = exports.CellDep = exports.BytesVec = exports.BytesOpt = exports.Bytes = exports.Byte32Vec = exports.Byte32 = exports.Block = void 0; +exports.SerializeBlock = SerializeBlock; +exports.SerializeByte32 = SerializeByte32; +exports.SerializeByte32Vec = SerializeByte32Vec; +exports.SerializeBytes = SerializeBytes; +exports.SerializeBytesOpt = SerializeBytesOpt; +exports.SerializeBytesVec = SerializeBytesVec; +exports.SerializeCellDep = SerializeCellDep; +exports.SerializeCellDepVec = SerializeCellDepVec; +exports.SerializeCellInput = SerializeCellInput; +exports.SerializeCellInputVec = SerializeCellInputVec; +exports.SerializeCellOutput = SerializeCellOutput; +exports.SerializeCellOutputVec = SerializeCellOutputVec; +exports.SerializeCellbaseWitness = SerializeCellbaseWitness; +exports.SerializeHeader = SerializeHeader; +exports.SerializeIdentity = SerializeIdentity; +exports.SerializeOutPoint = SerializeOutPoint; +exports.SerializeProposalShortId = SerializeProposalShortId; +exports.SerializeProposalShortIdVec = SerializeProposalShortIdVec; +exports.SerializeRCCellVec = SerializeRCCellVec; +exports.SerializeRCData = SerializeRCData; +exports.SerializeRCRule = SerializeRCRule; +exports.SerializeRawHeader = SerializeRawHeader; +exports.SerializeRawTransaction = SerializeRawTransaction; +exports.SerializeRcIdentity = SerializeRcIdentity; +exports.SerializeRcIdentityOpt = SerializeRcIdentityOpt; +exports.SerializeRcLockWitnessLock = SerializeRcLockWitnessLock; +exports.SerializeScript = SerializeScript; +exports.SerializeScriptOpt = SerializeScriptOpt; +exports.SerializeScriptVec = SerializeScriptVec; +exports.SerializeScriptVecOpt = SerializeScriptVecOpt; +exports.SerializeSmtProof = SerializeSmtProof; +exports.SerializeSmtProofEntry = SerializeSmtProofEntry; +exports.SerializeSmtProofEntryVec = SerializeSmtProofEntryVec; +exports.SerializeSmtUpdateAction = SerializeSmtUpdateAction; +exports.SerializeSmtUpdateItem = SerializeSmtUpdateItem; +exports.SerializeSmtUpdateItemVec = SerializeSmtUpdateItemVec; +exports.SerializeTransaction = SerializeTransaction; +exports.SerializeTransactionVec = SerializeTransactionVec; +exports.SerializeUint128 = SerializeUint128; +exports.SerializeUint256 = SerializeUint256; +exports.SerializeUint32 = SerializeUint32; +exports.SerializeUint64 = SerializeUint64; +exports.SerializeUncleBlock = SerializeUncleBlock; +exports.SerializeUncleBlockVec = SerializeUncleBlockVec; +exports.SerializeWitnessArgs = SerializeWitnessArgs; +exports.SerializeXudtData = SerializeXudtData; +exports.SerializeXudtWitnessInput = SerializeXudtWitnessInput; +exports.XudtWitnessInput = exports.XudtData = exports.WitnessArgs = exports.UncleBlockVec = exports.UncleBlock = exports.Uint64 = exports.Uint32 = exports.Uint256 = exports.Uint128 = exports.TransactionVec = exports.Transaction = exports.SmtUpdateItemVec = exports.SmtUpdateItem = exports.SmtUpdateAction = exports.SmtProofEntryVec = exports.SmtProofEntry = exports.SmtProof = void 0; + +function dataLengthError(actual, required) { + throw new Error(`Invalid data length! Required: ${required}, actual: ${actual}`); +} + +function assertDataLength(actual, required) { + if (actual !== required) { + dataLengthError(actual, required); + } +} + +function assertArrayBuffer(reader) { + if (reader instanceof Object && reader.toArrayBuffer instanceof Function) { + reader = reader.toArrayBuffer(); + } + + if (!(reader instanceof ArrayBuffer)) { + throw new Error("Provided value must be an ArrayBuffer or can be transformed into ArrayBuffer!"); + } + + return reader; +} + +function verifyAndExtractOffsets(view, expectedFieldCount, compatible) { + if (view.byteLength < 4) { + dataLengthError(view.byteLength, ">4"); + } + + const requiredByteLength = view.getUint32(0, true); + assertDataLength(view.byteLength, requiredByteLength); + + if (requiredByteLength === 4) { + return [requiredByteLength]; + } + + if (requiredByteLength < 8) { + dataLengthError(view.byteLength, ">8"); + } + + const firstOffset = view.getUint32(4, true); + + if (firstOffset % 4 !== 0 || firstOffset < 8) { + throw new Error(`Invalid first offset: ${firstOffset}`); + } + + const itemCount = firstOffset / 4 - 1; + + if (itemCount < expectedFieldCount) { + throw new Error(`Item count not enough! Required: ${expectedFieldCount}, actual: ${itemCount}`); + } else if (!compatible && itemCount > expectedFieldCount) { + throw new Error(`Item count is more than required! Required: ${expectedFieldCount}, actual: ${itemCount}`); + } + + if (requiredByteLength < firstOffset) { + throw new Error(`First offset is larger than byte length: ${firstOffset}`); + } + + const offsets = []; + + for (let i = 0; i < itemCount; i++) { + const start = 4 + i * 4; + offsets.push(view.getUint32(start, true)); + } + + offsets.push(requiredByteLength); + + for (let i = 0; i < offsets.length - 1; i++) { + if (offsets[i] > offsets[i + 1]) { + throw new Error(`Offset index ${i}: ${offsets[i]} is larger than offset index ${i + 1}: ${offsets[i + 1]}`); + } + } + + return offsets; +} + +function serializeTable(buffers) { + const itemCount = buffers.length; + let totalSize = 4 * (itemCount + 1); + const offsets = []; + + for (let i = 0; i < itemCount; i++) { + offsets.push(totalSize); + totalSize += buffers[i].byteLength; + } + + const buffer = new ArrayBuffer(totalSize); + const array = new Uint8Array(buffer); + const view = new DataView(buffer); + view.setUint32(0, totalSize, true); + + for (let i = 0; i < itemCount; i++) { + view.setUint32(4 + i * 4, offsets[i], true); + array.set(new Uint8Array(buffers[i]), offsets[i]); + } + + return buffer; +} + +class Identity { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, 21); + } + + indexAt(i) { + return this.view.getUint8(i); + } + + raw() { + return this.view.buffer; + } + + static size() { + return 21; + } + +} + +exports.Identity = Identity; + +function SerializeIdentity(value) { + const buffer = assertArrayBuffer(value); + assertDataLength(buffer.byteLength, 21); + return buffer; +} + +class RcIdentity { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new Identity(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new SmtProofEntryVec(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + } + + getIdentity() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Identity(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getProofs() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new SmtProofEntryVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.RcIdentity = RcIdentity; + +function SerializeRcIdentity(value) { + const buffers = []; + buffers.push(SerializeIdentity(value.identity)); + buffers.push(SerializeSmtProofEntryVec(value.proofs)); + return serializeTable(buffers); +} + +class RcIdentityOpt { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.hasValue()) { + this.value().validate(compatible); + } + } + + value() { + return new RcIdentity(this.view.buffer, { + validate: false + }); + } + + hasValue() { + return this.view.byteLength > 0; + } + +} + +exports.RcIdentityOpt = RcIdentityOpt; + +function SerializeRcIdentityOpt(value) { + if (value) { + return SerializeRcIdentity(value); + } else { + return new ArrayBuffer(0); + } +} + +class RcLockWitnessLock { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new BytesOpt(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new RcIdentityOpt(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + new BytesOpt(this.view.buffer.slice(offsets[2], offsets[3]), { + validate: false + }).validate(); + } + + getSignature() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new BytesOpt(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getRcIdentity() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new RcIdentityOpt(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getPreimage() { + const start = 12; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new BytesOpt(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.RcLockWitnessLock = RcLockWitnessLock; + +function SerializeRcLockWitnessLock(value) { + const buffers = []; + buffers.push(SerializeBytesOpt(value.signature)); + buffers.push(SerializeRcIdentityOpt(value.rc_identity)); + buffers.push(SerializeBytesOpt(value.preimage)); + return serializeTable(buffers); +} + +class ScriptVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + + for (let i = 0; i < offsets.length - 1; i++) { + new Script(this.view.buffer.slice(offsets[i], offsets[i + 1]), { + validate: false + }).validate(); + } + } + + length() { + if (this.view.byteLength < 8) { + return 0; + } else { + return this.view.getUint32(4, true) / 4 - 1; + } + } + + indexAt(i) { + const start = 4 + i * 4; + const offset = this.view.getUint32(start, true); + let offset_end = this.view.byteLength; + + if (i + 1 < this.length()) { + offset_end = this.view.getUint32(start + 4, true); + } + + return new Script(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.ScriptVec = ScriptVec; + +function SerializeScriptVec(value) { + return serializeTable(value.map(item => SerializeScript(item))); +} + +class ScriptVecOpt { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.hasValue()) { + this.value().validate(compatible); + } + } + + value() { + return new ScriptVec(this.view.buffer, { + validate: false + }); + } + + hasValue() { + return this.view.byteLength > 0; + } + +} + +exports.ScriptVecOpt = ScriptVecOpt; + +function SerializeScriptVecOpt(value) { + if (value) { + return SerializeScriptVec(value); + } else { + return new ArrayBuffer(0); + } +} + +class XudtWitnessInput { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new ScriptVecOpt(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new BytesVec(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + } + + getRawExtensionData() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new ScriptVecOpt(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getExtensionData() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new BytesVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.XudtWitnessInput = XudtWitnessInput; + +function SerializeXudtWitnessInput(value) { + const buffers = []; + buffers.push(SerializeScriptVecOpt(value.raw_extension_data)); + buffers.push(SerializeBytesVec(value.extension_data)); + return serializeTable(buffers); +} + +class RCRule { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + getSmtRoot() { + return new Byte32(this.view.buffer.slice(0, 0 + Byte32.size()), { + validate: false + }); + } + + getFlags() { + return this.view.getUint8(0 + Byte32.size()); + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, RCRule.size()); + this.getSmtRoot().validate(compatible); + } + + static size() { + return 0 + Byte32.size() + 1; + } + +} + +exports.RCRule = RCRule; + +function SerializeRCRule(value) { + const array = new Uint8Array(0 + Byte32.size() + 1); + const view = new DataView(array.buffer); + array.set(new Uint8Array(SerializeByte32(value.smt_root)), 0); + view.setUint8(0 + Byte32.size(), value.flags); + return array.buffer; +} + +class RCCellVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.view.byteLength < 4) { + dataLengthError(this.view.byteLength, ">4"); + } + + const requiredByteLength = this.length() * Byte32.size() + 4; + assertDataLength(this.view.byteLength, requiredByteLength); + + for (let i = 0; i < 0; i++) { + const item = this.indexAt(i); + item.validate(compatible); + } + } + + indexAt(i) { + return new Byte32(this.view.buffer.slice(4 + i * Byte32.size(), 4 + (i + 1) * Byte32.size()), { + validate: false + }); + } + + length() { + return this.view.getUint32(0, true); + } + +} + +exports.RCCellVec = RCCellVec; + +function SerializeRCCellVec(value) { + const array = new Uint8Array(4 + Byte32.size() * value.length); + new DataView(array.buffer).setUint32(0, value.length, true); + + for (let i = 0; i < value.length; i++) { + const itemBuffer = SerializeByte32(value[i]); + array.set(new Uint8Array(itemBuffer), 4 + i * Byte32.size()); + } + + return array.buffer; +} + +class RCData { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.view.byteLength < 4) { + assertDataLength(this.view.byteLength, ">4"); + } + + const t = this.view.getUint32(0, true); + + switch (t) { + case 0: + new RCRule(this.view.buffer.slice(4), { + validate: false + }).validate(); + break; + + case 1: + new RCCellVec(this.view.buffer.slice(4), { + validate: false + }).validate(); + break; + + default: + throw new Error(`Invalid type: ${t}`); + } + } + + unionType() { + const t = this.view.getUint32(0, true); + + switch (t) { + case 0: + return "RCRule"; + + case 1: + return "RCCellVec"; + + default: + throw new Error(`Invalid type: ${t}`); + } + } + + value() { + const t = this.view.getUint32(0, true); + + switch (t) { + case 0: + return new RCRule(this.view.buffer.slice(4), { + validate: false + }); + + case 1: + return new RCCellVec(this.view.buffer.slice(4), { + validate: false + }); + + default: + throw new Error(`Invalid type: ${t}`); + } + } + +} + +exports.RCData = RCData; + +function SerializeRCData(value) { + switch (value.type) { + case "RCRule": + { + const itemBuffer = SerializeRCRule(value.value); + const array = new Uint8Array(4 + itemBuffer.byteLength); + const view = new DataView(array.buffer); + view.setUint32(0, 0, true); + array.set(new Uint8Array(itemBuffer), 4); + return array.buffer; + } + + case "RCCellVec": + { + const itemBuffer = SerializeRCCellVec(value.value); + const array = new Uint8Array(4 + itemBuffer.byteLength); + const view = new DataView(array.buffer); + view.setUint32(0, 1, true); + array.set(new Uint8Array(itemBuffer), 4); + return array.buffer; + } + + default: + throw new Error(`Invalid type: ${value.type}`); + } +} + +class SmtProof { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.view.byteLength < 4) { + dataLengthError(this.view.byteLength, ">4"); + } + + const requiredByteLength = this.length() + 4; + assertDataLength(this.view.byteLength, requiredByteLength); + } + + raw() { + return this.view.buffer.slice(4); + } + + indexAt(i) { + return this.view.getUint8(4 + i); + } + + length() { + return this.view.getUint32(0, true); + } + +} + +exports.SmtProof = SmtProof; + +function SerializeSmtProof(value) { + const item = assertArrayBuffer(value); + const array = new Uint8Array(4 + item.byteLength); + new DataView(array.buffer).setUint32(0, item.byteLength, true); + array.set(new Uint8Array(item), 4); + return array.buffer; +} + +class SmtProofEntry { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + + if (offsets[1] - offsets[0] !== 1) { + throw new Error(`Invalid offset for mask: ${offsets[0]} - ${offsets[1]}`); + } + + new SmtProof(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + } + + getMask() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new DataView(this.view.buffer.slice(offset, offset_end)).getUint8(0); + } + + getProof() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new SmtProof(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.SmtProofEntry = SmtProofEntry; + +function SerializeSmtProofEntry(value) { + const buffers = []; + const maskView = new DataView(new ArrayBuffer(1)); + maskView.setUint8(0, value.mask); + buffers.push(maskView.buffer); + buffers.push(SerializeSmtProof(value.proof)); + return serializeTable(buffers); +} + +class SmtProofEntryVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + + for (let i = 0; i < offsets.length - 1; i++) { + new SmtProofEntry(this.view.buffer.slice(offsets[i], offsets[i + 1]), { + validate: false + }).validate(); + } + } + + length() { + if (this.view.byteLength < 8) { + return 0; + } else { + return this.view.getUint32(4, true) / 4 - 1; + } + } + + indexAt(i) { + const start = 4 + i * 4; + const offset = this.view.getUint32(start, true); + let offset_end = this.view.byteLength; + + if (i + 1 < this.length()) { + offset_end = this.view.getUint32(start + 4, true); + } + + return new SmtProofEntry(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.SmtProofEntryVec = SmtProofEntryVec; + +function SerializeSmtProofEntryVec(value) { + return serializeTable(value.map(item => SerializeSmtProofEntry(item))); +} + +class SmtUpdateItem { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + getKey() { + return new Byte32(this.view.buffer.slice(0, 0 + Byte32.size()), { + validate: false + }); + } + + getPackedValues() { + return this.view.getUint8(0 + Byte32.size()); + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, SmtUpdateItem.size()); + this.getKey().validate(compatible); + } + + static size() { + return 0 + Byte32.size() + 1; + } + +} + +exports.SmtUpdateItem = SmtUpdateItem; + +function SerializeSmtUpdateItem(value) { + const array = new Uint8Array(0 + Byte32.size() + 1); + const view = new DataView(array.buffer); + array.set(new Uint8Array(SerializeByte32(value.key)), 0); + view.setUint8(0 + Byte32.size(), value.packed_values); + return array.buffer; +} + +class SmtUpdateItemVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.view.byteLength < 4) { + dataLengthError(this.view.byteLength, ">4"); + } + + const requiredByteLength = this.length() * SmtUpdateItem.size() + 4; + assertDataLength(this.view.byteLength, requiredByteLength); + + for (let i = 0; i < 0; i++) { + const item = this.indexAt(i); + item.validate(compatible); + } + } + + indexAt(i) { + return new SmtUpdateItem(this.view.buffer.slice(4 + i * SmtUpdateItem.size(), 4 + (i + 1) * SmtUpdateItem.size()), { + validate: false + }); + } + + length() { + return this.view.getUint32(0, true); + } + +} + +exports.SmtUpdateItemVec = SmtUpdateItemVec; + +function SerializeSmtUpdateItemVec(value) { + const array = new Uint8Array(4 + SmtUpdateItem.size() * value.length); + new DataView(array.buffer).setUint32(0, value.length, true); + + for (let i = 0; i < value.length; i++) { + const itemBuffer = SerializeSmtUpdateItem(value[i]); + array.set(new Uint8Array(itemBuffer), 4 + i * SmtUpdateItem.size()); + } + + return array.buffer; +} + +class SmtUpdateAction { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new SmtUpdateItemVec(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new SmtProof(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + } + + getUpdates() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new SmtUpdateItemVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getProof() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new SmtProof(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.SmtUpdateAction = SmtUpdateAction; + +function SerializeSmtUpdateAction(value) { + const buffers = []; + buffers.push(SerializeSmtUpdateItemVec(value.updates)); + buffers.push(SerializeSmtProof(value.proof)); + return serializeTable(buffers); +} + +class XudtData { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new Bytes(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new BytesVec(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + } + + getLock() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Bytes(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getData() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new BytesVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.XudtData = XudtData; + +function SerializeXudtData(value) { + const buffers = []; + buffers.push(SerializeBytes(value.lock)); + buffers.push(SerializeBytesVec(value.data)); + return serializeTable(buffers); +} + +class Uint32 { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, 4); + } + + indexAt(i) { + return this.view.getUint8(i); + } + + raw() { + return this.view.buffer; + } + + toBigEndianUint32() { + return this.view.getUint32(0, false); + } + + toLittleEndianUint32() { + return this.view.getUint32(0, true); + } + + static size() { + return 4; + } + +} + +exports.Uint32 = Uint32; + +function SerializeUint32(value) { + const buffer = assertArrayBuffer(value); + assertDataLength(buffer.byteLength, 4); + return buffer; +} + +class Uint64 { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, 8); + } + + indexAt(i) { + return this.view.getUint8(i); + } + + raw() { + return this.view.buffer; + } + + static size() { + return 8; + } + +} + +exports.Uint64 = Uint64; + +function SerializeUint64(value) { + const buffer = assertArrayBuffer(value); + assertDataLength(buffer.byteLength, 8); + return buffer; +} + +class Uint128 { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, 16); + } + + indexAt(i) { + return this.view.getUint8(i); + } + + raw() { + return this.view.buffer; + } + + static size() { + return 16; + } + +} + +exports.Uint128 = Uint128; + +function SerializeUint128(value) { + const buffer = assertArrayBuffer(value); + assertDataLength(buffer.byteLength, 16); + return buffer; +} + +class Byte32 { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, 32); + } + + indexAt(i) { + return this.view.getUint8(i); + } + + raw() { + return this.view.buffer; + } + + static size() { + return 32; + } + +} + +exports.Byte32 = Byte32; + +function SerializeByte32(value) { + const buffer = assertArrayBuffer(value); + assertDataLength(buffer.byteLength, 32); + return buffer; +} + +class Uint256 { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, 32); + } + + indexAt(i) { + return this.view.getUint8(i); + } + + raw() { + return this.view.buffer; + } + + static size() { + return 32; + } + +} + +exports.Uint256 = Uint256; + +function SerializeUint256(value) { + const buffer = assertArrayBuffer(value); + assertDataLength(buffer.byteLength, 32); + return buffer; +} + +class Bytes { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.view.byteLength < 4) { + dataLengthError(this.view.byteLength, ">4"); + } + + const requiredByteLength = this.length() + 4; + assertDataLength(this.view.byteLength, requiredByteLength); + } + + raw() { + return this.view.buffer.slice(4); + } + + indexAt(i) { + return this.view.getUint8(4 + i); + } + + length() { + return this.view.getUint32(0, true); + } + +} + +exports.Bytes = Bytes; + +function SerializeBytes(value) { + const item = assertArrayBuffer(value); + const array = new Uint8Array(4 + item.byteLength); + new DataView(array.buffer).setUint32(0, item.byteLength, true); + array.set(new Uint8Array(item), 4); + return array.buffer; +} + +class BytesOpt { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.hasValue()) { + this.value().validate(compatible); + } + } + + value() { + return new Bytes(this.view.buffer, { + validate: false + }); + } + + hasValue() { + return this.view.byteLength > 0; + } + +} + +exports.BytesOpt = BytesOpt; + +function SerializeBytesOpt(value) { + if (value) { + return SerializeBytes(value); + } else { + return new ArrayBuffer(0); + } +} + +class BytesVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + + for (let i = 0; i < offsets.length - 1; i++) { + new Bytes(this.view.buffer.slice(offsets[i], offsets[i + 1]), { + validate: false + }).validate(); + } + } + + length() { + if (this.view.byteLength < 8) { + return 0; + } else { + return this.view.getUint32(4, true) / 4 - 1; + } + } + + indexAt(i) { + const start = 4 + i * 4; + const offset = this.view.getUint32(start, true); + let offset_end = this.view.byteLength; + + if (i + 1 < this.length()) { + offset_end = this.view.getUint32(start + 4, true); + } + + return new Bytes(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.BytesVec = BytesVec; + +function SerializeBytesVec(value) { + return serializeTable(value.map(item => SerializeBytes(item))); +} + +class Byte32Vec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.view.byteLength < 4) { + dataLengthError(this.view.byteLength, ">4"); + } + + const requiredByteLength = this.length() * Byte32.size() + 4; + assertDataLength(this.view.byteLength, requiredByteLength); + + for (let i = 0; i < 0; i++) { + const item = this.indexAt(i); + item.validate(compatible); + } + } + + indexAt(i) { + return new Byte32(this.view.buffer.slice(4 + i * Byte32.size(), 4 + (i + 1) * Byte32.size()), { + validate: false + }); + } + + length() { + return this.view.getUint32(0, true); + } + +} + +exports.Byte32Vec = Byte32Vec; + +function SerializeByte32Vec(value) { + const array = new Uint8Array(4 + Byte32.size() * value.length); + new DataView(array.buffer).setUint32(0, value.length, true); + + for (let i = 0; i < value.length; i++) { + const itemBuffer = SerializeByte32(value[i]); + array.set(new Uint8Array(itemBuffer), 4 + i * Byte32.size()); + } + + return array.buffer; +} + +class ScriptOpt { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.hasValue()) { + this.value().validate(compatible); + } + } + + value() { + return new Script(this.view.buffer, { + validate: false + }); + } + + hasValue() { + return this.view.byteLength > 0; + } + +} + +exports.ScriptOpt = ScriptOpt; + +function SerializeScriptOpt(value) { + if (value) { + return SerializeScript(value); + } else { + return new ArrayBuffer(0); + } +} + +class ProposalShortId { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, 10); + } + + indexAt(i) { + return this.view.getUint8(i); + } + + raw() { + return this.view.buffer; + } + + static size() { + return 10; + } + +} + +exports.ProposalShortId = ProposalShortId; + +function SerializeProposalShortId(value) { + const buffer = assertArrayBuffer(value); + assertDataLength(buffer.byteLength, 10); + return buffer; +} + +class UncleBlockVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + + for (let i = 0; i < offsets.length - 1; i++) { + new UncleBlock(this.view.buffer.slice(offsets[i], offsets[i + 1]), { + validate: false + }).validate(); + } + } + + length() { + if (this.view.byteLength < 8) { + return 0; + } else { + return this.view.getUint32(4, true) / 4 - 1; + } + } + + indexAt(i) { + const start = 4 + i * 4; + const offset = this.view.getUint32(start, true); + let offset_end = this.view.byteLength; + + if (i + 1 < this.length()) { + offset_end = this.view.getUint32(start + 4, true); + } + + return new UncleBlock(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.UncleBlockVec = UncleBlockVec; + +function SerializeUncleBlockVec(value) { + return serializeTable(value.map(item => SerializeUncleBlock(item))); +} + +class TransactionVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + + for (let i = 0; i < offsets.length - 1; i++) { + new Transaction(this.view.buffer.slice(offsets[i], offsets[i + 1]), { + validate: false + }).validate(); + } + } + + length() { + if (this.view.byteLength < 8) { + return 0; + } else { + return this.view.getUint32(4, true) / 4 - 1; + } + } + + indexAt(i) { + const start = 4 + i * 4; + const offset = this.view.getUint32(start, true); + let offset_end = this.view.byteLength; + + if (i + 1 < this.length()) { + offset_end = this.view.getUint32(start + 4, true); + } + + return new Transaction(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.TransactionVec = TransactionVec; + +function SerializeTransactionVec(value) { + return serializeTable(value.map(item => SerializeTransaction(item))); +} + +class ProposalShortIdVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.view.byteLength < 4) { + dataLengthError(this.view.byteLength, ">4"); + } + + const requiredByteLength = this.length() * ProposalShortId.size() + 4; + assertDataLength(this.view.byteLength, requiredByteLength); + + for (let i = 0; i < 0; i++) { + const item = this.indexAt(i); + item.validate(compatible); + } + } + + indexAt(i) { + return new ProposalShortId(this.view.buffer.slice(4 + i * ProposalShortId.size(), 4 + (i + 1) * ProposalShortId.size()), { + validate: false + }); + } + + length() { + return this.view.getUint32(0, true); + } + +} + +exports.ProposalShortIdVec = ProposalShortIdVec; + +function SerializeProposalShortIdVec(value) { + const array = new Uint8Array(4 + ProposalShortId.size() * value.length); + new DataView(array.buffer).setUint32(0, value.length, true); + + for (let i = 0; i < value.length; i++) { + const itemBuffer = SerializeProposalShortId(value[i]); + array.set(new Uint8Array(itemBuffer), 4 + i * ProposalShortId.size()); + } + + return array.buffer; +} + +class CellDepVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.view.byteLength < 4) { + dataLengthError(this.view.byteLength, ">4"); + } + + const requiredByteLength = this.length() * CellDep.size() + 4; + assertDataLength(this.view.byteLength, requiredByteLength); + + for (let i = 0; i < 0; i++) { + const item = this.indexAt(i); + item.validate(compatible); + } + } + + indexAt(i) { + return new CellDep(this.view.buffer.slice(4 + i * CellDep.size(), 4 + (i + 1) * CellDep.size()), { + validate: false + }); + } + + length() { + return this.view.getUint32(0, true); + } + +} + +exports.CellDepVec = CellDepVec; + +function SerializeCellDepVec(value) { + const array = new Uint8Array(4 + CellDep.size() * value.length); + new DataView(array.buffer).setUint32(0, value.length, true); + + for (let i = 0; i < value.length; i++) { + const itemBuffer = SerializeCellDep(value[i]); + array.set(new Uint8Array(itemBuffer), 4 + i * CellDep.size()); + } + + return array.buffer; +} + +class CellInputVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + if (this.view.byteLength < 4) { + dataLengthError(this.view.byteLength, ">4"); + } + + const requiredByteLength = this.length() * CellInput.size() + 4; + assertDataLength(this.view.byteLength, requiredByteLength); + + for (let i = 0; i < 0; i++) { + const item = this.indexAt(i); + item.validate(compatible); + } + } + + indexAt(i) { + return new CellInput(this.view.buffer.slice(4 + i * CellInput.size(), 4 + (i + 1) * CellInput.size()), { + validate: false + }); + } + + length() { + return this.view.getUint32(0, true); + } + +} + +exports.CellInputVec = CellInputVec; + +function SerializeCellInputVec(value) { + const array = new Uint8Array(4 + CellInput.size() * value.length); + new DataView(array.buffer).setUint32(0, value.length, true); + + for (let i = 0; i < value.length; i++) { + const itemBuffer = SerializeCellInput(value[i]); + array.set(new Uint8Array(itemBuffer), 4 + i * CellInput.size()); + } + + return array.buffer; +} + +class CellOutputVec { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + + for (let i = 0; i < offsets.length - 1; i++) { + new CellOutput(this.view.buffer.slice(offsets[i], offsets[i + 1]), { + validate: false + }).validate(); + } + } + + length() { + if (this.view.byteLength < 8) { + return 0; + } else { + return this.view.getUint32(4, true) / 4 - 1; + } + } + + indexAt(i) { + const start = 4 + i * 4; + const offset = this.view.getUint32(start, true); + let offset_end = this.view.byteLength; + + if (i + 1 < this.length()) { + offset_end = this.view.getUint32(start + 4, true); + } + + return new CellOutput(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.CellOutputVec = CellOutputVec; + +function SerializeCellOutputVec(value) { + return serializeTable(value.map(item => SerializeCellOutput(item))); +} + +class Script { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new Byte32(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + + if (offsets[2] - offsets[1] !== 1) { + throw new Error(`Invalid offset for hash_type: ${offsets[1]} - ${offsets[2]}`); + } + + new Bytes(this.view.buffer.slice(offsets[2], offsets[3]), { + validate: false + }).validate(); + } + + getCodeHash() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Byte32(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getHashType() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new DataView(this.view.buffer.slice(offset, offset_end)).getUint8(0); + } + + getArgs() { + const start = 12; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new Bytes(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.Script = Script; + +function SerializeScript(value) { + const buffers = []; + buffers.push(SerializeByte32(value.code_hash)); + const hashTypeView = new DataView(new ArrayBuffer(1)); + hashTypeView.setUint8(0, value.hash_type); + buffers.push(hashTypeView.buffer); + buffers.push(SerializeBytes(value.args)); + return serializeTable(buffers); +} + +class OutPoint { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + getTxHash() { + return new Byte32(this.view.buffer.slice(0, 0 + Byte32.size()), { + validate: false + }); + } + + getIndex() { + return new Uint32(this.view.buffer.slice(0 + Byte32.size(), 0 + Byte32.size() + Uint32.size()), { + validate: false + }); + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, OutPoint.size()); + this.getTxHash().validate(compatible); + this.getIndex().validate(compatible); + } + + static size() { + return 0 + Byte32.size() + Uint32.size(); + } + +} + +exports.OutPoint = OutPoint; + +function SerializeOutPoint(value) { + const array = new Uint8Array(0 + Byte32.size() + Uint32.size()); + const view = new DataView(array.buffer); + array.set(new Uint8Array(SerializeByte32(value.tx_hash)), 0); + array.set(new Uint8Array(SerializeUint32(value.index)), 0 + Byte32.size()); + return array.buffer; +} + +class CellInput { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + getSince() { + return new Uint64(this.view.buffer.slice(0, 0 + Uint64.size()), { + validate: false + }); + } + + getPreviousOutput() { + return new OutPoint(this.view.buffer.slice(0 + Uint64.size(), 0 + Uint64.size() + OutPoint.size()), { + validate: false + }); + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, CellInput.size()); + this.getSince().validate(compatible); + this.getPreviousOutput().validate(compatible); + } + + static size() { + return 0 + Uint64.size() + OutPoint.size(); + } + +} + +exports.CellInput = CellInput; + +function SerializeCellInput(value) { + const array = new Uint8Array(0 + Uint64.size() + OutPoint.size()); + const view = new DataView(array.buffer); + array.set(new Uint8Array(SerializeUint64(value.since)), 0); + array.set(new Uint8Array(SerializeOutPoint(value.previous_output)), 0 + Uint64.size()); + return array.buffer; +} + +class CellOutput { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new Uint64(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new Script(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + new ScriptOpt(this.view.buffer.slice(offsets[2], offsets[3]), { + validate: false + }).validate(); + } + + getCapacity() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Uint64(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getLock() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Script(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getType() { + const start = 12; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new ScriptOpt(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.CellOutput = CellOutput; + +function SerializeCellOutput(value) { + const buffers = []; + buffers.push(SerializeUint64(value.capacity)); + buffers.push(SerializeScript(value.lock)); + buffers.push(SerializeScriptOpt(value.type_)); + return serializeTable(buffers); +} + +class CellDep { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + getOutPoint() { + return new OutPoint(this.view.buffer.slice(0, 0 + OutPoint.size()), { + validate: false + }); + } + + getDepType() { + return this.view.getUint8(0 + OutPoint.size()); + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, CellDep.size()); + this.getOutPoint().validate(compatible); + } + + static size() { + return 0 + OutPoint.size() + 1; + } + +} + +exports.CellDep = CellDep; + +function SerializeCellDep(value) { + const array = new Uint8Array(0 + OutPoint.size() + 1); + const view = new DataView(array.buffer); + array.set(new Uint8Array(SerializeOutPoint(value.out_point)), 0); + view.setUint8(0 + OutPoint.size(), value.dep_type); + return array.buffer; +} + +class RawTransaction { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new Uint32(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new CellDepVec(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + new Byte32Vec(this.view.buffer.slice(offsets[2], offsets[3]), { + validate: false + }).validate(); + new CellInputVec(this.view.buffer.slice(offsets[3], offsets[4]), { + validate: false + }).validate(); + new CellOutputVec(this.view.buffer.slice(offsets[4], offsets[5]), { + validate: false + }).validate(); + new BytesVec(this.view.buffer.slice(offsets[5], offsets[6]), { + validate: false + }).validate(); + } + + getVersion() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Uint32(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getCellDeps() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new CellDepVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getHeaderDeps() { + const start = 12; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Byte32Vec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getInputs() { + const start = 16; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new CellInputVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getOutputs() { + const start = 20; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new CellOutputVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getOutputsData() { + const start = 24; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new BytesVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.RawTransaction = RawTransaction; + +function SerializeRawTransaction(value) { + const buffers = []; + buffers.push(SerializeUint32(value.version)); + buffers.push(SerializeCellDepVec(value.cell_deps)); + buffers.push(SerializeByte32Vec(value.header_deps)); + buffers.push(SerializeCellInputVec(value.inputs)); + buffers.push(SerializeCellOutputVec(value.outputs)); + buffers.push(SerializeBytesVec(value.outputs_data)); + return serializeTable(buffers); +} + +class Transaction { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new RawTransaction(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new BytesVec(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + } + + getRaw() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new RawTransaction(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getWitnesses() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new BytesVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.Transaction = Transaction; + +function SerializeTransaction(value) { + const buffers = []; + buffers.push(SerializeRawTransaction(value.raw)); + buffers.push(SerializeBytesVec(value.witnesses)); + return serializeTable(buffers); +} + +class RawHeader { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + getVersion() { + return new Uint32(this.view.buffer.slice(0, 0 + Uint32.size()), { + validate: false + }); + } + + getCompactTarget() { + return new Uint32(this.view.buffer.slice(0 + Uint32.size(), 0 + Uint32.size() + Uint32.size()), { + validate: false + }); + } + + getTimestamp() { + return new Uint64(this.view.buffer.slice(0 + Uint32.size() + Uint32.size(), 0 + Uint32.size() + Uint32.size() + Uint64.size()), { + validate: false + }); + } + + getNumber() { + return new Uint64(this.view.buffer.slice(0 + Uint32.size() + Uint32.size() + Uint64.size(), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size()), { + validate: false + }); + } + + getEpoch() { + return new Uint64(this.view.buffer.slice(0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size(), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size()), { + validate: false + }); + } + + getParentHash() { + return new Byte32(this.view.buffer.slice(0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size(), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size()), { + validate: false + }); + } + + getTransactionsRoot() { + return new Byte32(this.view.buffer.slice(0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size(), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size()), { + validate: false + }); + } + + getProposalsHash() { + return new Byte32(this.view.buffer.slice(0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size(), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size() + Byte32.size()), { + validate: false + }); + } + + getUnclesHash() { + return new Byte32(this.view.buffer.slice(0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size() + Byte32.size(), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size() + Byte32.size() + Byte32.size()), { + validate: false + }); + } + + getDao() { + return new Byte32(this.view.buffer.slice(0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size() + Byte32.size() + Byte32.size(), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size() + Byte32.size() + Byte32.size() + Byte32.size()), { + validate: false + }); + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, RawHeader.size()); + this.getVersion().validate(compatible); + this.getCompactTarget().validate(compatible); + this.getTimestamp().validate(compatible); + this.getNumber().validate(compatible); + this.getEpoch().validate(compatible); + this.getParentHash().validate(compatible); + this.getTransactionsRoot().validate(compatible); + this.getProposalsHash().validate(compatible); + this.getUnclesHash().validate(compatible); + this.getDao().validate(compatible); + } + + static size() { + return 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size() + Byte32.size() + Byte32.size() + Byte32.size(); + } + +} + +exports.RawHeader = RawHeader; + +function SerializeRawHeader(value) { + const array = new Uint8Array(0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size() + Byte32.size() + Byte32.size() + Byte32.size()); + const view = new DataView(array.buffer); + array.set(new Uint8Array(SerializeUint32(value.version)), 0); + array.set(new Uint8Array(SerializeUint32(value.compact_target)), 0 + Uint32.size()); + array.set(new Uint8Array(SerializeUint64(value.timestamp)), 0 + Uint32.size() + Uint32.size()); + array.set(new Uint8Array(SerializeUint64(value.number)), 0 + Uint32.size() + Uint32.size() + Uint64.size()); + array.set(new Uint8Array(SerializeUint64(value.epoch)), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size()); + array.set(new Uint8Array(SerializeByte32(value.parent_hash)), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size()); + array.set(new Uint8Array(SerializeByte32(value.transactions_root)), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size()); + array.set(new Uint8Array(SerializeByte32(value.proposals_hash)), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size()); + array.set(new Uint8Array(SerializeByte32(value.uncles_hash)), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size() + Byte32.size()); + array.set(new Uint8Array(SerializeByte32(value.dao)), 0 + Uint32.size() + Uint32.size() + Uint64.size() + Uint64.size() + Uint64.size() + Byte32.size() + Byte32.size() + Byte32.size() + Byte32.size()); + return array.buffer; +} + +class Header { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + getRaw() { + return new RawHeader(this.view.buffer.slice(0, 0 + RawHeader.size()), { + validate: false + }); + } + + getNonce() { + return new Uint128(this.view.buffer.slice(0 + RawHeader.size(), 0 + RawHeader.size() + Uint128.size()), { + validate: false + }); + } + + validate(compatible = false) { + assertDataLength(this.view.byteLength, Header.size()); + this.getRaw().validate(compatible); + this.getNonce().validate(compatible); + } + + static size() { + return 0 + RawHeader.size() + Uint128.size(); + } + +} + +exports.Header = Header; + +function SerializeHeader(value) { + const array = new Uint8Array(0 + RawHeader.size() + Uint128.size()); + const view = new DataView(array.buffer); + array.set(new Uint8Array(SerializeRawHeader(value.raw)), 0); + array.set(new Uint8Array(SerializeUint128(value.nonce)), 0 + RawHeader.size()); + return array.buffer; +} + +class UncleBlock { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new Header(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new ProposalShortIdVec(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + } + + getHeader() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Header(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getProposals() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new ProposalShortIdVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.UncleBlock = UncleBlock; + +function SerializeUncleBlock(value) { + const buffers = []; + buffers.push(SerializeHeader(value.header)); + buffers.push(SerializeProposalShortIdVec(value.proposals)); + return serializeTable(buffers); +} + +class Block { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new Header(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new UncleBlockVec(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + new TransactionVec(this.view.buffer.slice(offsets[2], offsets[3]), { + validate: false + }).validate(); + new ProposalShortIdVec(this.view.buffer.slice(offsets[3], offsets[4]), { + validate: false + }).validate(); + } + + getHeader() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Header(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getUncles() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new UncleBlockVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getTransactions() { + const start = 12; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new TransactionVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getProposals() { + const start = 16; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new ProposalShortIdVec(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.Block = Block; + +function SerializeBlock(value) { + const buffers = []; + buffers.push(SerializeHeader(value.header)); + buffers.push(SerializeUncleBlockVec(value.uncles)); + buffers.push(SerializeTransactionVec(value.transactions)); + buffers.push(SerializeProposalShortIdVec(value.proposals)); + return serializeTable(buffers); +} + +class CellbaseWitness { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new Script(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new Bytes(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + } + + getLock() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new Script(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getMessage() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new Bytes(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.CellbaseWitness = CellbaseWitness; + +function SerializeCellbaseWitness(value) { + const buffers = []; + buffers.push(SerializeScript(value.lock)); + buffers.push(SerializeBytes(value.message)); + return serializeTable(buffers); +} + +class WitnessArgs { + constructor(reader, { + validate = true + } = {}) { + this.view = new DataView(assertArrayBuffer(reader)); + + if (validate) { + this.validate(); + } + } + + validate(compatible = false) { + const offsets = verifyAndExtractOffsets(this.view, 0, true); + new BytesOpt(this.view.buffer.slice(offsets[0], offsets[1]), { + validate: false + }).validate(); + new BytesOpt(this.view.buffer.slice(offsets[1], offsets[2]), { + validate: false + }).validate(); + new BytesOpt(this.view.buffer.slice(offsets[2], offsets[3]), { + validate: false + }).validate(); + } + + getLock() { + const start = 4; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new BytesOpt(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getInputType() { + const start = 8; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.getUint32(start + 4, true); + return new BytesOpt(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + + getOutputType() { + const start = 12; + const offset = this.view.getUint32(start, true); + const offset_end = this.view.byteLength; + return new BytesOpt(this.view.buffer.slice(offset, offset_end), { + validate: false + }); + } + +} + +exports.WitnessArgs = WitnessArgs; + +function SerializeWitnessArgs(value) { + const buffers = []; + buffers.push(SerializeBytesOpt(value.lock)); + buffers.push(SerializeBytesOpt(value.input_type)); + buffers.push(SerializeBytesOpt(value.output_type)); + return serializeTable(buffers); +} diff --git a/examples/omni-lock-administrator/index.ts b/examples/omni-lock-administrator/index.ts new file mode 100644 index 000000000..cda9330e0 --- /dev/null +++ b/examples/omni-lock-administrator/index.ts @@ -0,0 +1,369 @@ +import { + config, + Script, + Address, + helpers, + Indexer, + RPC, + commons, + hd, + toolkit, + HexString, + utils, + BI, + Cell, + core, +} from "@ckb-lumos/lumos"; +import { SerializeRcLockWitnessLock } from "./generated/omni"; +import { generateSingleProof, ProofScheme, ProofMask, buildRcVec, h256ToHex, hexToH256, H256 } from './lib'; +const { Reader } = toolkit; +const { deploy } = commons; + +export const CONFIG = config.createConfig({ + PREFIX: "ckt", + SCRIPTS: { + ...config.predefined.AGGRON4.SCRIPTS, + // for more about Omni lock, please check https://github.com/XuJiandong/docs-bank/blob/master/omni_lock.md + OMNI_LOCK: { + CODE_HASH: "0xf329effd1c475a2978453c8600e1eaf0bc2087ee093c3ee64cc96ec6847752cb", + HASH_TYPE: "type", + TX_HASH: "0x27b62d8be8ed80b9f56ee0fe41355becdb6f6a40aeba82d3900434f43b1c8b60", + INDEX: "0x0", + DEP_TYPE: "code", + }, + }, +}); + +config.initializeConfig(CONFIG); + +const CKB_RPC_URL = "https://testnet.ckb.dev/rpc"; +const CKB_INDEXER_URL = "https://testnet.ckb.dev/indexer"; + +type Account = { + lockScript: Script; + address: Address; + pubKey: string; + privKey: string; +}; + +const generateSECP256K1Account = (privKey: string): Account => { + const pubKey = hd.key.privateToPublic(privKey); + const args = hd.key.publicKeyToBlake160(pubKey); + const template = CONFIG.SCRIPTS["SECP256K1_BLAKE160"]!; + const lockScript = { + code_hash: template.CODE_HASH, + hash_type: template.HASH_TYPE, + args: args, + }; + const address = helpers.encodeToAddress(lockScript, { config: CONFIG }); + return { + lockScript, + address, + pubKey, + privKey, + }; +}; + +export const alice = generateSECP256K1Account("0xfd686a48908e8caf97723578bf85f746e1e1d8956cb132f6a2e92e7234a2a245"); +export const bob = generateSECP256K1Account("0x5368b818f59570b5bc078a6a564f098a191dcb8938d95c413be5065fd6c42d32"); + +export const rpc = new RPC(CKB_RPC_URL); +export const indexer = new Indexer(CKB_INDEXER_URL, CKB_RPC_URL); + +export const deployRCEVecCell = async (option: { + from: Address; + fromPrivKey: HexString; + rceCellHashs: HexString[]; +}): Promise => { + const rcData = buildRcVec(option.rceCellHashs); + + return deployRCECell({ from: option.from, fromPrivKey: option.fromPrivKey, rcData }) +} + +export const deployRCECell = async (option: { + from: Address; + fromPrivKey: HexString; + rcData: H256; +}): Promise => { + const { txSkeleton, typeId: RCETypeScript } = await deploy.generateDeployWithTypeIdTx({ + cellProvider: indexer, + scriptBinary: new Uint8Array(option.rcData), + fromInfo: option.from, + config: CONFIG, + }) + + const tx = sealTxSkeleton(txSkeleton, option.fromPrivKey); + const txHash = await rpc.send_transaction(tx); + + return { + CODE_HASH: utils.computeScriptHash(RCETypeScript), + HASH_TYPE: "type", + TX_HASH: txHash, + INDEX: "0x0", + DEP_TYPE: "code", + }; + +} + +const sealTxSkeleton = (skeleton: helpers.TransactionSkeletonType, privateKey: string) => { + let txSkeleton = commons.common.prepareSigningEntries(skeleton); + const message = txSkeleton.get("signingEntries").get(0)?.message; + const Sig = hd.key.signRecoverable(message!, privateKey); + return helpers.sealTransaction(txSkeleton, [Sig]); +}; + +export function toMessages(tx: helpers.TransactionSkeletonType) { + const hasher = new utils.CKBHasher(); + + // locks you want to sign + const signLock = tx.inputs.get(0)?.cell_output.lock!; + const messageGroup = commons.createP2PKHMessageGroup(tx, [signLock], { + hasher: { + update: (message) => hasher.update(message.buffer), + digest: () => new Uint8Array(hasher.digestReader().toArrayBuffer()), + }, + }); + + return messageGroup[0]; +} + +interface SmtProof { + mask: HexString, + proof: HexString, +} + +export const sealOmnilockTxSkeleton = ( + txSkeleton: helpers.TransactionSkeletonType, + privateKey: string, + identity: HexString, + proofs: Array, +) => { + const OMNI_SIGNATURE_PLACEHOLDER = new Reader( + "0x" + + "00".repeat( + SerializeRcLockWitnessLock({ + signature: new Reader("0x" + "00".repeat(65)), + rc_identity: { + identity: new Reader(identity).toArrayBuffer(), + proofs: proofs.map(p => ({ + mask: new Reader(p.mask), + proof: new Reader(p.proof), + })) + }, + }).byteLength + ) + ); + + const newWitnessArgs = { lock: OMNI_SIGNATURE_PLACEHOLDER }; + const witness = new Reader( + core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs(newWitnessArgs)) + ).serializeJson(); + + // fill txSkeleton's witness with 0 + for (let i = 0; i < txSkeleton.inputs.toArray().length; i++) { + txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.push(witness)); + } + + const messages = toMessages(txSkeleton); + const signature = hd.key.signRecoverable(messages.message, privateKey); + + const signedWitnessArgs = new Reader( + SerializeRcLockWitnessLock({ + signature: new Reader(signature), + rc_identity: { + identity: new Reader(identity).toArrayBuffer(), + proofs: proofs.map(p => ({ + mask: new Reader(p.mask), + proof: new Reader(p.proof), + })), + }, + }) + ); + + const signedWitness = new Reader( + core.SerializeWitnessArgs(toolkit.normalizers.NormalizeWitnessArgs({ lock: signedWitnessArgs })) + ).serializeJson(); + + txSkeleton = txSkeleton.update("witnesses", (witnesses) => witnesses.set(0, signedWitness)); + return helpers.createTransactionFromSkeleton(txSkeleton); +}; + +export interface TransferOptions { + from: string; + to: string; + amount: string; + rceCells: Array; + omniIdentity?: { + identity: string; + proofs: string; + }; +} + +export async function buildTransferByOmnilockAdministrator(options: TransferOptions) { + let tx = helpers.TransactionSkeleton({}); + const fromScript = helpers.parseAddress(options.from); + const toScript = helpers.parseAddress(options.to); + + // additional 1 ckb for tx fee + // the tx fee could calculated by tx size + // this is just a simple example + const neededCapacity = BI.from(options.amount).add(100000000); + let collectedSum = BI.from(0); + const collectedCells: Cell[] = []; + const collector = indexer.collector({ lock: fromScript, type: "empty" }); + for await (const cell of collector.collect()) { + collectedSum = collectedSum.add(cell.cell_output.capacity); + collectedCells.push(cell); + if (BI.from(collectedSum).gte(neededCapacity)) break; + } + + if (collectedSum.lt(neededCapacity)) { + throw new Error(`Not enough CKB, expected: ${neededCapacity}, actual: ${collectedSum} `); + } + + const transferOutput: Cell = { + cell_output: { + capacity: BI.from(options.amount).toHexString(), + lock: toScript, + }, + data: "0x", + }; + + const changeOutput: Cell = { + cell_output: { + capacity: collectedSum.sub(neededCapacity).toHexString(), + lock: fromScript, + }, + data: "0x", + }; + + tx = tx.update("inputs", (inputs) => inputs.push(...collectedCells)); + tx = tx.update("outputs", (outputs) => outputs.push(transferOutput, changeOutput)); + tx = tx.update("cellDeps", (cellDeps) => + cellDeps.push( + // secp256k1 + { + out_point: { + tx_hash: CONFIG.SCRIPTS.SECP256K1_BLAKE160.TX_HASH, + index: CONFIG.SCRIPTS.SECP256K1_BLAKE160.INDEX, + }, + dep_type: CONFIG.SCRIPTS.SECP256K1_BLAKE160.DEP_TYPE, + }, + // omni lock dep + { + out_point: { + tx_hash: CONFIG.SCRIPTS.OMNI_LOCK.TX_HASH, + index: CONFIG.SCRIPTS.OMNI_LOCK.INDEX, + }, + dep_type: CONFIG.SCRIPTS.OMNI_LOCK.DEP_TYPE, + }, + // RCE cells + ...options.rceCells.map(cell => ({ + out_point: { + tx_hash: cell.TX_HASH, + index: cell.INDEX, + }, + dep_type: cell.DEP_TYPE, + })) + ) + ); + + return tx; +} + +export const generateOmniLockAdministratorAddress = (pubHash: string, typeId: string) => { + const template = CONFIG.SCRIPTS["OMNI_LOCK"]!; + const lockScript: Script = { + code_hash: template.CODE_HASH, + hash_type: template.HASH_TYPE, + args: `0x00${pubHash}01${typeId}`, + }; + + return helpers.encodeToAddress(lockScript, { config: CONFIG }); +} + +const main = async () => { + const RC_IDENTITY = '0x00' + bob.lockScript.args.substring(2); + const { proof: wlProof, rcData: wlRcData } = generateSingleProof( + ProofScheme.OnWhiteList, + [hexToH256(RC_IDENTITY)] + ) + + const { proof: blProof, rcData: blRcData } = generateSingleProof( + ProofScheme.NotOnBlackList, + [hexToH256(RC_IDENTITY)] + ) + + // const wlRceCellScript = await deployRCECell({ + // from: alice.address, + // fromPrivKey: alice.privKey, + // rcData: wlRcData, + // }); + + // generate by upper ↑ + const wlRceCellScript: config.ScriptConfig = { + CODE_HASH: '0x7ff95669983cb7c54b8cb302f2f202a8d178c1b76f53c0fa76ba03ec97df6042', + HASH_TYPE: 'type', + TX_HASH: '0x584f8105aa6a39bd139a0f2321ea991b5033612f31339f5ddcf9eaf7e097af13', + INDEX: '0x0', + DEP_TYPE: 'code' + } + + // const blRceCellScript = await deployRCECell({ + // from: alice.address, + // fromPrivKey: alice.privKey, + // rcData: blRcData, + // }); + + // generate by upper ↑ + const blRceCellScript: config.ScriptConfig = { + CODE_HASH: '0x80a3aa808da4baf19728ba98297c3e96f10a6eccba7a10f66e35a8ef0845ab44', + HASH_TYPE: 'type', + TX_HASH: '0xa57484c8627a6c8f1ca47e95e2c1c26315154e2c5e34f0f02f4a9678411c889d', + INDEX: '0x0', + DEP_TYPE: 'code' + } + + // const RceVecCellScript = await deployRCEVecCell({ + // from: alice.address, + // fromPrivKey: alice.privKey, + // rceCellHashs: [wlRceCellScript.CODE_HASH, blRceCellScript.CODE_HASH], + // }); + + // generate by upper ↑ + const RceVecCellScript: config.ScriptConfig = { + CODE_HASH: '0x23abc4961b1dfecc316f6cbf45e16da19faa50980ac6a8fd81e7df4211069945', + HASH_TYPE: 'type', + TX_HASH: '0x0d65b11ef71dcd7d41d3b3ec345fd41dcbebd555074419afe1511a94f31dc9c2', + INDEX: '0x0', + DEP_TYPE: 'code' + } + + const finalCellScript: config.ScriptConfig = RceVecCellScript; + + const smtProofs = [blProof, wlProof].map((proof) => ({ + mask: ProofMask.BothOn, + proof: h256ToHex(proof), + })); + + const TYPE_ID = finalCellScript.CODE_HASH.substring(2); + const aliceOmniAddr = generateOmniLockAdministratorAddress(alice.lockScript.args.substring(2), TYPE_ID); + + const txSkeleton = await buildTransferByOmnilockAdministrator({ + rceCells: [wlRceCellScript, blRceCellScript, finalCellScript], + from: aliceOmniAddr, + to: bob.address, + amount: "10000000000", + }); + + const signedTx = sealOmnilockTxSkeleton( + txSkeleton, + bob.privKey, + RC_IDENTITY, + smtProofs, + ); + const txHash = await rpc.send_transaction(signedTx, "passthrough"); + console.log(txHash) +} + +main() diff --git a/examples/omni-lock-administrator/lib.ts b/examples/omni-lock-administrator/lib.ts new file mode 100644 index 000000000..df09ce11e --- /dev/null +++ b/examples/omni-lock-administrator/lib.ts @@ -0,0 +1,423 @@ +import { HexString } from "@ckb-lumos/base"; +import { Reader } from "@ckb-lumos/toolkit"; +import { H256, Hasher, SparseMerkleTree } from "sparse-merkle-tree-ts"; +import * as util from "@nervosnetwork/ckb-sdk-utils"; +import { Blake2b } from "@nervosnetwork/ckb-sdk-utils/lib/crypto/blake2b"; +import { SerializeRCData } from "./generated/omni"; +export { H256 }; + +export const SMT_EXISTING: H256 = new H256([ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +]); + +export const SMT_NOT_EXISTING: H256 = new H256([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +]); + +export const WHITE_BLACK_LIST_MASK = 0x2; +// on(1): emergency halt mode +// off(0): not int emergency halt mode +export const EMERGENCY_HALT_MODE_MASK = 0x1; + +export enum ProofMask { + OnlyInput = "0x01", + OnlyOutput = "0x02", + BothOn = "0x03", +} + +export enum ProofScheme { + OnWhiteList, + NotOnWhiteList, + OnBlackList, + NotOnBlackList, + EmergencyHaltMode, +} + +class Blake2bHasher extends Hasher { + hasher: Blake2b; + + constructor() { + super(); + + this.hasher = util.blake2b(32, null, null, new TextEncoder().encode("ckb-default-hash")); + } + + update(h: H256): this { + this.hasher.update(h); + + return this; + } + + final(): H256 { + return new H256(this.hasher.final("binary") as Uint8Array); + } +} + +const newSMT = (items: [H256, H256][]) => { + const tree = new SparseMerkleTree(() => new Blake2bHasher()); + + items.map((item) => { + tree.update(item[0], item[1]); + }); + + return tree; +}; + +export const buildSmtOnWl = (hashes: H256[], on: boolean = true): { root: H256; proof: H256 } => { + const existingPairs: [H256, H256][] = hashes.map((hash) => [hash, SMT_EXISTING]); + + // this is the hash on white list, and "hashes" are on that. + const keyOnWl1 = new H256([ + 111, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + + const keyOnWl2: H256 = new H256([ + 222, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + + let pairs: [H256, H256][] = [ + [keyOnWl1, SMT_EXISTING], + [keyOnWl2, SMT_EXISTING], + ]; + + if (on) { + pairs = [...pairs, ...existingPairs]; + } + + const smt = newSMT(pairs); + const root = smt.root; + + const proof = smt.merkle_proof(existingPairs.map((p) => p[0])); + const compiledProof = proof.compile(existingPairs); + + return { root, proof: new H256(compiledProof) }; +}; + +export const buildSmtOnBl = (hashes: H256[], on: boolean = true): { root: H256; proof: H256 } => { + const testPairs: [H256, H256][] = hashes.map((hash) => [hash, SMT_NOT_EXISTING]); + + // this is the hash on black list, but "hashes" are not on that. + const keyOnBl1: H256 = new H256([ + 111, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + + const keyOnBl2: H256 = new H256([ + 222, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + + let pairs: [H256, H256][] = [ + [keyOnBl1, SMT_EXISTING], + [keyOnBl2, SMT_EXISTING], + ]; + + const smt = newSMT(pairs); + const root = smt.root; + + const proof = smt.merkle_proof(testPairs.map((p) => p[0])); + const compiledProof = proof.compile(testPairs); + + if (on) { + root.get_bit(0) ? root.clear_bit(0) : root.set_bit(0); + return { root, proof: new H256(compiledProof) }; + } + + return { root, proof: new H256(compiledProof) }; +}; + +export const buildRcRule = (smtRoot: H256, config: { isBlack?: boolean; isEmergency?: boolean } = {}) => { + const flag = (() => { + if (!config.isBlack) { + return WHITE_BLACK_LIST_MASK; + } + + if (config.isEmergency) { + return EMERGENCY_HALT_MODE_MASK; + } + + return 0; + })(); + + const data = SerializeRCData({ + type: "RCRule", + value: { + smt_root: new Reader(smtRoot).toArrayBuffer(), + flags: flag, + }, + }); + + return new H256(data); +}; + +export const buildRcVec = (RCEcellHashs: Array) => { + const data = SerializeRCData({ + type: "RCCellVec", + value: RCEcellHashs.map(cellHash => new Reader(cellHash).toArrayBuffer()), + }); + + return new H256(data); +}; + +export const generateSingleProof = (scheme: ProofScheme, hashes: H256[]): { + proof: H256, + rcData: H256, +} => { + const { isBlackList, isEmergencyHalt, smtRoot, proof } = (() => { + const isBlackList = false; + const isEmergencyHalt = false; + + if (scheme === ProofScheme.OnWhiteList) { + const res = buildSmtOnWl(hashes); + return { + isBlackList, + isEmergencyHalt, + smtRoot: res.root, + proof: res.proof, + }; + } + + if (scheme === ProofScheme.NotOnWhiteList) { + const res = buildSmtOnWl(hashes, false); + return { + isBlackList, + isEmergencyHalt, + smtRoot: res.root, + proof: res.proof, + }; + } + + if (scheme === ProofScheme.OnBlackList) { + const res = buildSmtOnBl(hashes); + return { + isBlackList: true, + isEmergencyHalt, + smtRoot: res.root, + proof: res.proof, + }; + } + + if (scheme === ProofScheme.NotOnBlackList) { + const res = buildSmtOnBl(hashes, false); + return { + isBlackList: true, + isEmergencyHalt, + smtRoot: res.root, + proof: res.proof, + }; + } + + if (scheme === ProofScheme.EmergencyHaltMode) { + return { + isBlackList, + isEmergencyHalt: true, + smtRoot: H256.zero(), + proof: H256.zero(), + }; + } + + return { + isBlackList, + isEmergencyHalt, + smtRoot: H256.zero(), + proof: H256.zero(), + }; + })(); + + return { + proof, + rcData: buildRcRule(smtRoot, { isBlack: isBlackList, isEmergency: isEmergencyHalt }) + }; +}; + +export const h256ToHex = (h256: H256): HexString => { + return new Reader(h256).serializeJson(); +}; + +export const hexToH256 = (hex: HexString): H256 => { + return new H256(new Reader(hex).toArrayBuffer()); +}; diff --git a/examples/omni-lock-administrator/package.json b/examples/omni-lock-administrator/package.json new file mode 100644 index 000000000..9c3c147e7 --- /dev/null +++ b/examples/omni-lock-administrator/package.json @@ -0,0 +1,18 @@ +{ + "private": true, + "name": "@lumos-examples/omni-lock-administrator", + "version": "0.17.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "lint": "tsc --noEmit" + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "@ckb-lumos/lumos": "0.17.0", + "sparse-merkle-tree-ts": "^0.2.3" + } +} diff --git a/examples/omni-lock-administrator/tsconfig.json b/examples/omni-lock-administrator/tsconfig.json new file mode 100644 index 000000000..139c52fb2 --- /dev/null +++ b/examples/omni-lock-administrator/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es6", + "lib": ["dom"], + "jsx": "react", + "module": "CommonJS", + "skipLibCheck": true, + "esModuleInterop": true, + } +} diff --git a/yarn.lock b/yarn.lock index 22f600fe8..b06556fe0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2137,6 +2137,22 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" +"@nervosnetwork/ckb-sdk-utils@^0.101.0": + version "0.101.0" + resolved "https://registry.npmjs.org/@nervosnetwork/ckb-sdk-utils/-/ckb-sdk-utils-0.101.0.tgz#f51b6dc6df9299544217984c2c3e29e09d050c60" + integrity sha512-1Cm66rQ8XawavZ69TjMiPRECzX77ndRf8DPUskn0QXyy9YU9bDGNSWVgIBnt0T5Nxl4Y+itAWFukltoEWpzU3A== + dependencies: + "@nervosnetwork/ckb-types" "0.101.0" + bech32 "2.0.0" + elliptic "6.5.4" + jsbi "3.1.3" + tslib "2.3.1" + +"@nervosnetwork/ckb-types@0.101.0": + version "0.101.0" + resolved "https://registry.npmjs.org/@nervosnetwork/ckb-types/-/ckb-types-0.101.0.tgz#84c5f590bd8e2e06fdf56bab0c09995da0ada4ca" + integrity sha512-J82ANP44TvdYY1fCEom9RodMIFJYCb2oteSCU1TD9bY3tZZFrhRi+Muz0qh6f9wrR5F3NzdXQD8wJoPZ3Exr9Q== + "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": version "2.1.8-no-fsevents.3" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" @@ -4116,7 +4132,7 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bech32@^2.0.0: +bech32@2.0.0, bech32@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== @@ -5778,7 +5794,7 @@ electron-to-chromium@^1.4.17: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.38.tgz#10ea58d73d36b13e78d5024f3b74a352d3958d01" integrity sha512-WhHt3sZazKj0KK/UpgsbGQnUUoFeAHVishzHFExMxagpZgjiGYSC9S0ZlbhCfSH2L2i+2A1yyqOIliTctMx7KQ== -elliptic@^6.5.3, elliptic@^6.5.4: +elliptic@6.5.4, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -7087,7 +7103,7 @@ hash.js@^1.0.0, hash.js@^1.0.3: highlight.js@^10.0.0, highlight.js@^10.4.1, highlight.js@^9.15.6: version "10.7.3" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== highlight.js@~10.4.0: @@ -7948,6 +7964,11 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsbi@3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/jsbi/-/jsbi-3.1.3.tgz#f024b340032f7c7caaa6ca4b32b55e8d33f6e897" + integrity sha512-nBJqA0C6Qns+ZxurbEoIR56wyjiUszpNy70FHvxO5ervMoCbZVE3z3kxr5nKGhlxr/9MhKTSUBs7cAwwuf3g9w== + jsbi@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-4.1.0.tgz#f0c25ed8881bc09891334b03a3952efaa7add337" @@ -7995,7 +8016,7 @@ json-schema-traverse@^0.4.1: json-schema@0.2.3, json-schema@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-source-map@^0.6.1: @@ -11271,6 +11292,13 @@ source-map@^0.7.3, source-map@~0.7.2: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +sparse-merkle-tree-ts@^0.2.3: + version "0.2.3" + resolved "https://registry.npmjs.org/sparse-merkle-tree-ts/-/sparse-merkle-tree-ts-0.2.3.tgz#144dc546a66ca6a0fadbe3847518001df0cdc8f9" + integrity sha512-uaZ8NCbDVMDQexGVfSHITDWAgx6bWCXkHpjYkxl8rl4rupVKaO6MpgOCoFeKra53Y6ChAgwGQS+EArhpTuHbtA== + dependencies: + "@nervosnetwork/ckb-sdk-utils" "^0.101.0" + spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -11902,7 +11930,7 @@ trim-newlines@^3.0.0: trim-off-newlines@^1.0.1, trim-off-newlines@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.3.tgz#8df24847fcb821b0ab27d58ab6efec9f2fe961a1" + resolved "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.3.tgz#8df24847fcb821b0ab27d58ab6efec9f2fe961a1" integrity sha512-kh6Tu6GbeSNMGfrrZh6Bb/4ZEHV1QlB4xNDBeog8Y9/QwFlKTRyWvY3Fs9tRDAMZliVUwieMgEdIeL/FtqjkJg== trim-repeated@^1.0.0: @@ -11961,16 +11989,16 @@ tsconfig-paths@^3.12.0, tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" +tslib@2.3.1, tslib@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tslib@^1.8.1, tslib@^1.9.0: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== -tslib@^2.1.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"