diff --git a/package.json b/package.json index cfefa952..700aa403 100644 --- a/package.json +++ b/package.json @@ -146,4 +146,4 @@ "eslint" ] } -} +} \ No newline at end of file diff --git a/src.ts/_tests/test-contract-integ.ts b/src.ts/_tests/test-contract-integ.ts index 8c33e26f..3032f740 100644 --- a/src.ts/_tests/test-contract-integ.ts +++ b/src.ts/_tests/test-contract-integ.ts @@ -29,7 +29,7 @@ describe("Tests contract integration", function() { before(async function() { this.timeout(100000); - const factory = new quais.ContractFactory(abi, bytecode, wallet); + const factory = new quais.ContractFactory(abi, bytecode, wallet as quais.ContractRunner); contract = await factory.deploy(constructorArgs.name, constructorArgs.symbol, constructorArgs.totalSupply, { gasLimit: 5000000 }) as Contract; address = await contract.getAddress(); @@ -66,7 +66,7 @@ describe("Tests contract integration", function() { const CustomContract = quais.BaseContract.buildClass(abi); - const contract = new CustomContract(address, wallet); //quais.Contract.from(address, abi, signer); + const contract = new CustomContract(address, wallet as quais.ContractRunner ); //quais.Contract.from(address, abi, signer); await stall(30000); // Test implicit staticCall (i.e. view/pure) { diff --git a/src.ts/_tests/test-contract.ts b/src.ts/_tests/test-contract.ts index dbb80f8d..2ee6386d 100644 --- a/src.ts/_tests/test-contract.ts +++ b/src.ts/_tests/test-contract.ts @@ -4,7 +4,7 @@ import assert from "assert"; import { getProvider, setupProviders } from "./create-provider.js"; import { - Contract, ContractFactory, isError, Typed, + Contract, ContractFactory, ContractRunner, isError, Typed, } from "../index.js"; import TestContract from "./contracts/TestContract.js" import TypedContract from "./contracts/TypedContract.js" @@ -23,7 +23,7 @@ describe("Test Contract", function() { before( async function () { this.timeout(60000); await stall(10000); - const factory = new ContractFactory(abi, bytecode, wallet); + const factory = new ContractFactory(abi, bytecode, wallet as ContractRunner); contract = await factory.deploy({gasLimit: 5000000, maxFeePerGas: quais.parseUnits('10', 'gwei'), maxPriorityFeePerGas: quais.parseUnits('3', 'gwei')}) as Contract; addr = await contract.getAddress(); console.log("Contract deployed to: ", addr); @@ -254,7 +254,7 @@ describe("Test Typed Contract Interaction", function() { let addr: string before( async function () { this.timeout(120000); - const factory = new ContractFactory(abi, bytecode, wallet); + const factory = new ContractFactory(abi, bytecode, wallet as ContractRunner); contract = await factory.deploy({gasLimit: 5000000, maxFeePerGas: quais.parseUnits('10', 'gwei'), maxPriorityFeePerGas: quais.parseUnits('3', 'gwei'),}) as Contract; addr = await contract.getAddress(); console.log("Contract deployed to: ", addr); diff --git a/src.ts/_tests/test-provider-jsonrpc.ts b/src.ts/_tests/test-provider-jsonrpc.ts index d53f3152..06d514a0 100644 --- a/src.ts/_tests/test-provider-jsonrpc.ts +++ b/src.ts/_tests/test-provider-jsonrpc.ts @@ -3,8 +3,9 @@ import assert from "assert"; import { id, isError, makeError, toUtf8Bytes, toUtf8String, FetchRequest, - JsonRpcProvider, Transaction, Wallet + JsonRpcProvider, Wallet } from "../index.js"; +import { QuaiTransaction } from "../transaction/quai-transaction.js"; const StatusMessages: Record = { 200: "OK", @@ -93,7 +94,7 @@ describe("Ensure Catchable Errors", function() { value: 1, }; const txSign = await wallet.signTransaction(txInfo); - const txObj = Transaction.from(txSign); + const txObj = QuaiTransaction.from(txSign); let count = 0; @@ -155,7 +156,7 @@ describe("Ensure Catchable Errors", function() { value: 1, }; const txSign = await wallet.signTransaction(txInfo); - const txObj = Transaction.from(txSign); + const txObj = QuaiTransaction.from(txSign); let count = 0; diff --git a/src.ts/_tests/test-providers-data.ts b/src.ts/_tests/test-providers-data.ts index 248c46e1..dab7cf88 100644 --- a/src.ts/_tests/test-providers-data.ts +++ b/src.ts/_tests/test-providers-data.ts @@ -209,7 +209,6 @@ describe("Test Provider Block operations", function() { parentHash: rpcBlock.parentHash, parentEntropy: rpcBlock.parentEntropy.map((entropy: string) => BigInt(entropy)), extTransactions: rpcBlock.extTransactions, - timestamp: Number(rpcBlock.timestamp), nonce: rpcBlock.nonce, difficulty: BigInt(rpcBlock.difficulty), gasLimit: BigInt(rpcBlock.gasLimit), diff --git a/src.ts/_tests/test-providers-errors.ts b/src.ts/_tests/test-providers-errors.ts index f20930a0..5349d053 100644 --- a/src.ts/_tests/test-providers-errors.ts +++ b/src.ts/_tests/test-providers-errors.ts @@ -10,9 +10,8 @@ import { import { getProvider, setupProviders, providerNames } from "./create-provider.js"; import { stall } from "./utils.js"; -import type { TransactionResponse } from "../index.js"; -//require('dotenv').config(); import dotenv from "dotenv"; +import { QuaiTransactionResponse } from "../providers/provider.js"; dotenv.config(); type TestCustomError = { @@ -187,14 +186,14 @@ describe("Test Provider Blockchain Errors", function() { const w = wallet.connect(provider); - let tx1: null | TransactionResponse = null; + let tx1: null | QuaiTransactionResponse = null; let nonce: null | number = null;; for (let i = 0; i < 10; i++) { nonce = await w.getNonce("pending"); try { tx1 = await w.sendTransaction({ nonce, to: wallet, from: wallet2, value - }); + }) as QuaiTransactionResponse; } catch (error: any) { // Another CI host beat us to this nonce if (isError(error, "REPLACEMENT_UNDERPRICED") || isError(error, "NONCE_EXPIRED")) { @@ -244,7 +243,7 @@ describe("Test Provider Blockchain Errors", function() { console.log(tx); }, (error) => { return (isError(error, "INSUFFICIENT_FUNDS") && - typeof(error.transaction.from) === "string" && + "from" in error.transaction && typeof(error.transaction.from) === "string" && error.transaction.from.toLowerCase() === w.address.toLowerCase()); }); }); diff --git a/src.ts/_tests/test-transaction.ts b/src.ts/_tests/test-transaction.ts index 128ae8cf..514e6b4c 100644 --- a/src.ts/_tests/test-transaction.ts +++ b/src.ts/_tests/test-transaction.ts @@ -2,7 +2,8 @@ import assert from "assert"; import { loadTests } from "./utils.js"; import type { TestCaseTransaction, TestCaseTransactionTx } from "./types.js"; -import { isError, Transaction } from "../index.js"; +import {isError} from "../index.js"; +import {QuaiTransaction} from "../transaction/quai-transaction"; const BN_0 = BigInt(0); @@ -20,7 +21,7 @@ describe("Tests Unsigned Transaction Serializing", function() { maxFeePerGas: undefined, maxPriorityFeePerGas: undefined }); - const tx = Transaction.from(txData); + const tx = QuaiTransaction.from(txData); assert.equal(tx.unsignedSerialized, test.unsignedEip155, "unsignedEip155"); }); } @@ -39,7 +40,7 @@ describe("Tests Signed Transaction Serializing", function() { maxPriorityFeePerGas: 0, signature: test.signatureEip155 }); - const tx = Transaction.from(txData); + const tx = QuaiTransaction.from(txData); assert.equal(tx.serialized, test.signedEip155, "signedEip155"); }); } @@ -50,7 +51,7 @@ function assertTxUint(actual: null | bigint, _expected: undefined | string, name assert.equal(actual, expected, name); } -function assertTxEqual(actual: Transaction, expected: TestCaseTransactionTx): void { +function assertTxEqual(actual: QuaiTransaction, expected: TestCaseTransactionTx): void { assert.equal(actual.to, expected.to, "to"); assert.equal(actual.nonce, expected.nonce, "nonce"); @@ -95,7 +96,7 @@ describe("Tests Unsigned Transaction Parsing", function() { for (const test of tests) { if (!test.unsignedEip155) { continue; } it(`parses unsigned EIP-155 transaction: ${ test.name }`, function() { - const tx = Transaction.from(test.unsignedEip155); + const tx = QuaiTransaction.from(test.unsignedEip155); const expected = addDefaults(test.transaction); expected.maxFeePerGas = 0; @@ -112,7 +113,7 @@ describe("Tests Signed Transaction Parsing", function() { for (const test of tests) { if (!test.unsignedEip155) { continue; } it(`parses signed EIP-155 transaction: ${ test.name }`, function() { - let tx = Transaction.from(test.signedEip155); + let tx = QuaiTransaction.from(test.signedEip155); const expected = addDefaults(test.transaction); expected.maxFeePerGas = 0; expected.maxPriorityFeePerGas = 0; @@ -169,7 +170,7 @@ describe("Tests Transaction Parameters", function() { assert.throws(() => { // The access list is a single value: 0x09 instead of // structured data - const result = Transaction.from(data); + const result = QuaiTransaction.from(data); console.log(result) }, (error: any) => { return (isError(error, "INVALID_ARGUMENT") && diff --git a/src.ts/abi/interface.ts b/src.ts/abi/interface.ts index f91fa0de..f34aa72e 100644 --- a/src.ts/abi/interface.ts +++ b/src.ts/abi/interface.ts @@ -343,7 +343,7 @@ export class Interface { try { frags.push(Fragment.from(a)); } catch (error) { - console.log("EE", error); + console.log("Error parsing ABI fragment", error); } } diff --git a/src.ts/address/contract-address.ts b/src.ts/address/contract-address.ts index eec0ea6b..7bff3806 100644 --- a/src.ts/address/contract-address.ts +++ b/src.ts/address/contract-address.ts @@ -1,6 +1,12 @@ import { keccak256 } from "../crypto/index.js"; import { - concat, dataSlice, getBigInt, getBytes, assertArgument, encodeProto + concat, + dataSlice, + getBigInt, + getBytes, + assertArgument, + zeroPadValue, + toBeHex, toBigInt } from "../utils/index.js"; import { getAddress } from "./address.js"; @@ -31,17 +37,9 @@ import type { BigNumberish, BytesLike } from "../utils/index.js"; export function getCreateAddress(tx: { from: string, nonce: BigNumberish }): string { const from = getAddress(tx.from); const nonce = getBigInt(tx.nonce, "tx.nonce"); + const nonceBytes = zeroPadValue(toBeHex(toBigInt(nonce)), 8); - let nonceHex = nonce.toString(16); - if (nonceHex === "0") { - nonceHex = "0x"; - } else if (nonceHex.length % 2) { - nonceHex = "0x0" + nonceHex; - } else { - nonceHex = "0x" + nonceHex; - } - - return getAddress(dataSlice(keccak256(encodeProto([ from, nonceHex ])), 12)); + return getAddress(dataSlice(keccak256(concat([getAddress(from), nonceBytes ])), 12)) } /** diff --git a/src.ts/contract/contract.ts b/src.ts/contract/contract.ts index cb195758..7d461b46 100644 --- a/src.ts/contract/contract.ts +++ b/src.ts/contract/contract.ts @@ -2,7 +2,13 @@ import { Interface, Typed } from "../abi/index.js"; import { isAddressable, resolveAddress } from "../address/index.js"; // import from provider.ts instead of index.ts to prevent circular dep // from quaiscanProvider -import { copyRequest, Log, TransactionResponse } from "../providers/provider.js"; +import { + copyRequest, + Log, + QuaiTransactionRequest, + QuaiTransactionResponse, + TransactionResponse, +} from "../providers/provider.js"; import { defineProperties, getBigInt, isCallException, isHexString, resolveProperties, isError, assert, assertArgument @@ -129,13 +135,13 @@ export async function copyOverrides(arg: any, // Create a shallow copy (we'll deep-ify anything needed during normalizing) const overrides = copyRequest(_overrides); - assertArgument(overrides.to == null || (allowed || [ ]).indexOf("to") >= 0, - "cannot override to", "overrides.to", overrides.to); - assertArgument(overrides.data == null || (allowed || [ ]).indexOf("data") >= 0, - "cannot override data", "overrides.data", overrides.data); + assertArgument((! ("to" in overrides) || overrides.to == null) || (allowed || [ ]).indexOf("to") >= 0, + "cannot override to", "overrides.to", overrides); + assertArgument((! ("data" in overrides) || overrides.data == null) || (allowed || [ ]).indexOf("data") >= 0, + "cannot override data", "overrides.data", overrides); // Resolve any from - if (overrides.from) { overrides.from = overrides.from; } + if ("from" in overrides && overrides.from) { overrides.from = await overrides.from; } return >overrides; } @@ -156,7 +162,7 @@ export async function resolveArgs(_runner: null | ContractRunner, inputs: Readon function buildWrappedFallback(contract: BaseContract): WrappedFallback { - const populateTransaction = async function(overrides?: Omit): Promise { + const populateTransaction = async function(overrides?: Omit): Promise { // If an overrides was passed in, copy it and normalize the values const tx: ContractTransaction = (await copyOverrides<"data">(overrides, [ "data" ])); @@ -190,7 +196,7 @@ function buildWrappedFallback(contract: BaseContract): WrappedFallback { return tx; } - const staticCall = async function(overrides?: Omit): Promise { + const staticCall = async function(overrides?: Omit): Promise { const runner = getRunner(contract.runner, "call"); assert(canCall(runner), "contract runner does not support calling", "UNSUPPORTED_OPERATION", { operation: "call" }); @@ -207,19 +213,19 @@ function buildWrappedFallback(contract: BaseContract): WrappedFallback { } } - const send = async function(overrides?: Omit): Promise { + const send = async function(overrides?: Omit): Promise { const runner = contract.runner; assert(canSend(runner), "contract runner does not support sending transactions", "UNSUPPORTED_OPERATION", { operation: "sendTransaction" }); - const tx = await runner.sendTransaction(await populateTransaction(overrides)); + const tx = await runner.sendTransaction(await populateTransaction(overrides)) as QuaiTransactionResponse; const provider = getProvider(contract.runner); // @TODO: the provider can be null; make a custom dummy provider that will throw a // meaningful error return new ContractTransactionResponse(contract.interface, provider, tx); } - const estimateGas = async function(overrides?: Omit): Promise { + const estimateGas = async function(overrides?: Omit): Promise { const runner = getRunner(contract.runner, "estimateGas"); assert(canEstimate(runner), "contract runner does not support gas estimation", "UNSUPPORTED_OPERATION", { operation: "estimateGas" }); @@ -227,7 +233,7 @@ function buildWrappedFallback(contract: BaseContract): WrappedFallback { return await runner.estimateGas(await populateTransaction(overrides)); } - const method = async (overrides?: Omit) => { + const method = async (overrides?: Omit) => { return await send(overrides); }; @@ -297,7 +303,7 @@ function buildWrappedMethod = Array, R = any, D extend assert(canSend(runner), "contract runner does not support sending transactions", "UNSUPPORTED_OPERATION", { operation: "sendTransaction" }); - const tx = await runner.sendTransaction(await populateTransaction(...args)); + const tx = await runner.sendTransaction(await populateTransaction(...args)) as QuaiTransactionResponse; const provider = getProvider(contract.runner); // @TODO: the provider can be null; make a custom dummy provider that will throw a // meaningful error @@ -669,7 +675,7 @@ export class BaseContract implements Addressable, EventEmitterable { + tx: QuaiTransactionRequest + ): Promise { if (tx.nonce == null && tx.from) { tx.nonce = await this.runner?.provider?.getTransactionCount(tx.from); } diff --git a/src.ts/contract/types.ts b/src.ts/contract/types.ts index 0886ef1f..2d9c3732 100644 --- a/src.ts/contract/types.ts +++ b/src.ts/contract/types.ts @@ -2,10 +2,11 @@ import type { EventFragment, FunctionFragment, Result, Typed } from "../abi/index.js"; import type { - TransactionRequest, PreparedTransactionRequest, TopicFilter + TransactionRequest, TopicFilter } from "../providers/index.js"; import type { ContractTransactionResponse } from "./wrappers.js"; +import {QuaiPreparedTransactionRequest} from "../providers/provider"; /** @@ -48,7 +49,7 @@ export interface DeferredTopicFilter { /** * When populating a transaction this type is returned. */ -export interface ContractTransaction extends PreparedTransactionRequest { +export interface ContractTransaction extends QuaiPreparedTransactionRequest { /** * The target address. */ diff --git a/src.ts/contract/wrappers.ts b/src.ts/contract/wrappers.ts index 9bd4081f..f3498dc2 100644 --- a/src.ts/contract/wrappers.ts +++ b/src.ts/contract/wrappers.ts @@ -1,7 +1,7 @@ // import from provider.ts instead of index.ts to prevent circular dep // from quaiscanProvider import { - Block, Log, TransactionReceipt, TransactionResponse + Block, Log, QuaiTransactionResponse, TransactionReceipt, TransactionResponse } from "../providers/provider.js"; import { defineProperties, EventPayload } from "../utils/index.js"; @@ -112,13 +112,13 @@ export class ContractTransactionReceipt extends TransactionReceipt { * A **ContractTransactionResponse** will return a * [[ContractTransactionReceipt]] when waited on. */ -export class ContractTransactionResponse extends TransactionResponse { +export class ContractTransactionResponse extends QuaiTransactionResponse { readonly #iface: Interface; /** * @_ignore: */ - constructor(iface: Interface, provider: Provider, tx: TransactionResponse) { + constructor(iface: Interface, provider: Provider, tx: QuaiTransactionResponse) { super(tx, provider); this.#iface = iface; } diff --git a/src.ts/providers/abstract-provider.ts b/src.ts/providers/abstract-provider.ts index 0267665a..3d25d131 100644 --- a/src.ts/providers/abstract-provider.ts +++ b/src.ts/providers/abstract-provider.ts @@ -14,24 +14,36 @@ // migrate the listener to the static event. We also need to maintain a map // of Signer/ENS name to address so we can sync respond to listenerCount. -import { resolveAddress } from "../address/index.js"; +import { getAddress, resolveAddress } from "../address/index.js"; import { ShardData } from "../constants/index.js"; -import { Transaction } from "../transaction/index.js"; +import { TxInput, TxOutput} from "../transaction/index.js"; import { Outpoint } from "../transaction/utxo.js"; import { hexlify, isHexString, - getBigInt, getNumber, - makeError, assert, assertArgument, + getBigInt, getBytes, getNumber, + makeError, assert, assertArgument, FetchRequest, toQuantity, - defineProperties, EventPayload, resolveProperties, + defineProperties, EventPayload, resolveProperties, decodeProtoTransaction, } from "../utils/index.js"; import { formatBlock, formatLog, formatTransactionReceipt, formatTransactionResponse } from "./format.js"; import { Network } from "./network.js"; -import { copyRequest, Block, FeeData, Log, TransactionReceipt, TransactionResponse } from "./provider.js"; +import { + copyRequest, + Block, + FeeData, + Log, + TransactionReceipt, + TransactionResponse, + addressFromTransactionRequest, + QiPreparedTransactionRequest, + QuaiPreparedTransactionRequest, + QuaiTransactionResponse, + QiTransactionResponse, QuaiTransactionRequest +} from "./provider.js"; import type { Addressable, AddressLike } from "../address/index.js"; import type { BigNumberish } from "../utils/index.js"; @@ -39,17 +51,21 @@ import type { Listener } from "../utils/index.js"; import type { Networkish } from "./network.js"; import type { FetchUrlFeeDataNetworkPlugin } from "./plugins-network.js"; -//import type { MaxPriorityFeePlugin } from "./plugins-network.js"; import type { - BlockParams, LogParams, TransactionReceiptParams, + BlockParams, LogParams, QiTransactionResponseParams, TransactionReceiptParams, TransactionResponseParams } from "./formatting.js"; import type { BlockTag, EventFilter, Filter, FilterByBlockHash, OrphanFilter, - PreparedTransactionRequest, Provider, ProviderEvent, - TransactionRequest + Provider, ProviderEvent, + TransactionRequest, } from "./provider.js"; +import { WorkObjectLike } from "../transaction/work-object.js"; +import {QiTransaction} from "../transaction/qi-transaction"; +import {QuaiTransaction} from "../transaction/quai-transaction"; +import {QuaiTransactionResponseParams} from "./formatting.js"; +import {keccak256, SigningKey} from "../crypto"; type Timer = ReturnType; @@ -330,7 +346,9 @@ export type PerformActionFilter = { /** * A normalized transactions used for [[PerformActionRequest]] objects. */ -export interface PerformActionTransaction extends PreparedTransactionRequest { +export type PerformActionTransaction = QuaiPerformActionTransaction | QiPerformActionTransaction; + +export interface QuaiPerformActionTransaction extends QuaiPreparedTransactionRequest { /** * The ``to`` address of the transaction. */ @@ -340,8 +358,19 @@ export interface PerformActionTransaction extends PreparedTransactionRequest { * The sender of the transaction. */ from: string; + + [key: string]: any; +} + +export interface QiPerformActionTransaction extends QiPreparedTransactionRequest { + inputs?: Array; + outputs?: Array; + + [key: string]: any; } + + /** * The [[AbstractProvider]] methods will normalize all values and pass this * type to [[AbstractProvider-_perform]]. @@ -427,6 +456,8 @@ export type PerformActionRequest = { shard: string } | { method: "getProtocolExpansionNumber", +} | { + method: "getPendingHeader", }; @@ -456,15 +487,6 @@ const defaultOptions = { pollingInterval: 4000 }; -// type CcipArgs = { -// sender: string; -// urls: Array; -// calldata: string; -// selector: string; -// extraData: string; -// errorArgs: Array -// }; - /** * An **AbstractProvider** provides a base class for other sub-classes to * implement the [[Provider]] API by normalizing input arguments and @@ -545,7 +567,7 @@ export class AbstractProvider implements Provider { this.#connect.push(urls); const shards = await this.getRunningLocations(); shards.forEach((shard) => { - const port = 9100 + 20 * shard[0] + shard[1]; + const port = 9200 + 20 * shard[0] + shard[1]; this._urlMap.set(`0x${shard[0].toString(16)}${shard[1].toString(16)}`, urls.url.split(":")[0] + ":" + urls.url.split(":")[1] + ":" + port); }); return; @@ -556,7 +578,7 @@ export class AbstractProvider implements Provider { this.#connect.push(new FetchRequest(primeUrl)); const shards = await this.getRunningLocations(); // Now waits for the previous to complete shards.forEach((shard) => { - const port = 9100 + 20 * shard[0] + shard[1]; + const port = 9200 + 20 * shard[0] + shard[1]; this._urlMap.set(`0x${shard[0].toString(16)}${shard[1].toString(16)}`, url.split(":")[0] + ":" + url.split(":")[1] + ":" + port); }); } @@ -698,6 +720,8 @@ export class AbstractProvider implements Provider { * sub-class of [[Block]]. */ _wrapBlock(value: BlockParams, network: Network): Block { + // Handle known node by -> remove null values from the number array + value.number = Array.isArray(value.number) ? value.number.filter((n: any) => n != null) : value.number; return new Block(formatBlock(value), this); } @@ -725,7 +749,12 @@ export class AbstractProvider implements Provider { * alternate sub-class of [[TransactionResponse]]. */ _wrapTransactionResponse(tx: TransactionResponseParams, network: Network): TransactionResponse { - return new TransactionResponse(formatTransactionResponse(tx), this); + if ("from" in tx) { + return new QuaiTransactionResponse(formatTransactionResponse(tx) as QuaiTransactionResponseParams, this); + + } else { + return new QiTransactionResponse(formatTransactionResponse(tx) as QiTransactionResponseParams, this); + } } /** @@ -892,12 +921,23 @@ export class AbstractProvider implements Provider { const request = copyRequest(_request); const promises: Array> = []; - ["to", "from"].forEach((key) => { + ["to", "from", "inputs", "outputs"].forEach((key) => { if ((request)[key] == null) { return; } - const addr = resolveAddress((request)[key]); + const addr = Array.isArray((request)[key]) + ? ( + "address" in request[key][0] + ? ((request)[key]).map(it => resolveAddress(it.Address)) + : ((request)[key]).map(it => resolveAddress(getAddress(keccak256("0x" + SigningKey.computePublicKey(it.pubKey).substring(4)).substring(26)))) + ) : resolveAddress((request)[key]); if (isPromise(addr)) { - promises.push((async function () { (request)[key] = await addr; })()); + if (Array.isArray(addr)) { + for (let i = 0; i < addr.length; i++) { + promises.push((async function () { (request)[key][i].address = await addr[i]; })()); + } + } else { + promises.push((async function () { (request)[key] = await addr; })()); + } } else { (request)[key] = addr; } @@ -990,7 +1030,9 @@ export class AbstractProvider implements Provider { try { const value = await this.#perform({ method: "getGasPrice", txType, shard: shard }); return getBigInt(value, "%response"); - } catch (error) { } + } catch (error) { + console.log(error) + } return null })()), priorityFee: ((async () => { @@ -1030,14 +1072,13 @@ export class AbstractProvider implements Provider { async estimateGas(_tx: TransactionRequest): Promise { let tx = this._getTransactionRequest(_tx); if (isPromise(tx)) { tx = await tx; } - const shard = await this.shardFromAddress(tx.from) + const shard = await this.shardFromAddress(addressFromTransactionRequest(tx)) return getBigInt(await this.#perform({ method: "estimateGas", transaction: tx, shard: shard }), "%response"); } async #call(tx: PerformActionTransaction, blockTag: string, attempt: number): Promise { - // This came in as a PerformActionTransaction, so to/from are safe; we can cast const transaction = copyRequest(tx); @@ -1045,55 +1086,6 @@ export class AbstractProvider implements Provider { return hexlify(await this._perform({ method: "call", transaction, blockTag })); } catch (error: any) { - // CCIP Read OffchainLookup - // if (!this.disableCcipRead && isCallException(error) && error.data && attempt >= 0 && blockTag === "latest" && transaction.to != null && dataSlice(error.data, 0, 4) === "0x556f1830") { - // const data = error.data; - - // const txSender = await resolveAddress(transaction.to, this); - - // // Parse the CCIP Read Arguments - // let ccipArgs: CcipArgs; - // try { - // ccipArgs = parseOffchainLookup(dataSlice(error.data, 4)); - // } catch (error: any) { - // assert(false, error.message, "OFFCHAIN_FAULT", { - // reason: "BAD_DATA", transaction, info: { data } }); - // } - - // // Check the sender of the OffchainLookup matches the transaction - // assert(ccipArgs.sender.toLowerCase() === txSender.toLowerCase(), - // "CCIP Read sender mismatch", "CALL_EXCEPTION", { - // action: "call", - // data, - // reason: "OffchainLookup", - // transaction: transaction, // @TODO: populate data? - // invocation: null, - // revert: { - // signature: "OffchainLookup(address,string[],bytes,bytes4,bytes)", - // name: "OffchainLookup", - // args: ccipArgs.errorArgs - // } - // }); - - // const ccipResult = await this.ccipReadFetch(transaction, ccipArgs.calldata, ccipArgs.urls); - // assert(ccipResult != null, "CCIP Read failed to fetch data", "OFFCHAIN_FAULT", { - // reason: "FETCH_FAILED", transaction, info: { data: error.data, errorArgs: ccipArgs.errorArgs } }); - - // const tx = { - // to: txSender, - // data: concat([ ccipArgs.selector, encodeBytes([ ccipResult, ccipArgs.extraData ]) ]) - // }; - - // this.emit("debug", { action: "sendCcipReadCall", transaction: tx }); - // try { - // const result = await this.#call(tx, blockTag, attempt + 1); - // this.emit("debug", { action: "receiveCcipReadCallResult", transaction: Object.assign({ }, tx), result }); - // return result; - // } catch (error) { - // this.emit("debug", { action: "receiveCcipReadCallError", transaction: Object.assign({ }, tx), error }); - // throw error; - // } - // } throw error; } @@ -1107,8 +1099,8 @@ export class AbstractProvider implements Provider { return value; } - async call(_tx: TransactionRequest): Promise { - const shard = await this.shardFromAddress(_tx.from); + async call(_tx: QuaiTransactionRequest): Promise { + const shard = await this.shardFromAddress(addressFromTransactionRequest(_tx)); const { tx, blockTag } = await resolveProperties({ tx: this._getTransactionRequest(_tx), blockTag: this._getBlockTag(shard, _tx.blockTag) @@ -1152,11 +1144,13 @@ export class AbstractProvider implements Provider { return hexlify(await this.#getAccountValue({ method: "getStorage", position }, address, blockTag)); } - - //TODO: Provider method which gets unspent utxos for an address + async getPendingHeader(): Promise { + return await this.#perform({ method: "getPendingHeader" }); + } // Write async broadcastTransaction(shard: string, signedTx: string): Promise { + const type = decodeProtoTransaction(getBytes(signedTx)).type; const { blockNumber, hash, network } = await resolveProperties({ blockNumber: this.getBlockNumber(shard), hash: this._perform({ @@ -1167,13 +1161,14 @@ export class AbstractProvider implements Provider { network: this.getNetwork() }); - const tx = Transaction.from(signedTx); + let tx = type == 2 ? QiTransaction.from(signedTx) : QuaiTransaction.from(signedTx); + this.#validateTransactionHash(tx.hash || '', hash) tx.hash = hash; return this._wrapTransactionResponse(tx, network).replaceableTransaction(blockNumber); } - async #validateTransactionHash(computedHash: string, nodehash: string) { + #validateTransactionHash(computedHash: string, nodehash: string) { if (computedHash.substring(0, 4) !== nodehash.substring(0, 4)) throw new Error("Transaction hash mismatch in origin Zone"); if (computedHash.substring(6, 8) !== nodehash.substring(6, 8)) @@ -1293,7 +1288,7 @@ export class AbstractProvider implements Provider { } } } catch (error) { - console.log("EEE", error); + console.log("Error occured while waiting for transaction:", error); } this.once("block", listener); }); @@ -1639,136 +1634,3 @@ export class AbstractProvider implements Provider { } } } - - -// function _parseString(result: string, start: number): null | string { -// try { -// const bytes = _parseBytes(result, start); -// if (bytes) { return toUtf8String(bytes); } -// } catch(error) { } -// return null; -// } - -// function _parseBytes(result: string, start: number): null | string { -// if (result === "0x") { return null; } -// try { -// const offset = getNumber(dataSlice(result, start, start + 32)); -// const length = getNumber(dataSlice(result, offset, offset + 32)); -// -// return dataSlice(result, offset + 32, offset + 32 + length); -// } catch (error) { } -// return null; -// } - -// function numPad(value: number): Uint8Array { -// const result = toBeArray(value); -// if (result.length > 32) { throw new Error("internal; should not happen"); } -// -// const padded = new Uint8Array(32); -// padded.set(result, 32 - result.length); -// return padded; -// } - -// function bytesPad(value: Uint8Array): Uint8Array { -// if ((value.length % 32) === 0) { return value; } -// -// const result = new Uint8Array(Math.ceil(value.length / 32) * 32); -// result.set(value); -// return result; -// } - -// const empty: Uint8Array = new Uint8Array([ ]); - -// ABI Encodes a series of (bytes, bytes, ...) -// function encodeBytes(datas: Array): string { -// const result: Array = [ ]; -// -// let byteCount = 0; -// -// // Add place-holders for pointers as we add items -// for (let i = 0; i < datas.length; i++) { -// result.push(empty); -// byteCount += 32; -// } -// -// for (let i = 0; i < datas.length; i++) { -// const data = getBytes(datas[i]); -// -// // Update the bytes offset -// result[i] = numPad(byteCount); -// -// // The length and padded value of data -// result.push(numPad(data.length)); -// result.push(bytesPad(data)); -// byteCount += 32 + Math.ceil(data.length / 32) * 32; -// } -// -// return concat(result); -// } - -// const zeros = "0x0000000000000000000000000000000000000000000000000000000000000000" -// function parseOffchainLookup(data: string): CcipArgs { -// const result: CcipArgs = { -// sender: "", urls: [ ], calldata: "", selector: "", extraData: "", errorArgs: [ ] -// }; -// -// assert(dataLength(data) >= 5 * 32, "insufficient OffchainLookup data", "OFFCHAIN_FAULT", { -// reason: "insufficient OffchainLookup data" -// }); -// -// const sender = dataSlice(data, 0, 32); -// assert(dataSlice(sender, 0, 12) === dataSlice(zeros, 0, 12), "corrupt OffchainLookup sender", "OFFCHAIN_FAULT", { -// reason: "corrupt OffchainLookup sender" -// }); -// result.sender = dataSlice(sender, 12); -// -// // Read the URLs from the response -// try { -// const urls: Array = []; -// const urlsOffset = getNumber(dataSlice(data, 32, 64)); -// const urlsLength = getNumber(dataSlice(data, urlsOffset, urlsOffset + 32)); -// const urlsData = dataSlice(data, urlsOffset + 32); -// for (let u = 0; u < urlsLength; u++) { -// const url = _parseString(urlsData, u * 32); -// if (url == null) { throw new Error("abort"); } -// urls.push(url); -// } -// result.urls = urls; -// } catch (error) { -// assert(false, "corrupt OffchainLookup urls", "OFFCHAIN_FAULT", { -// reason: "corrupt OffchainLookup urls" -// }); -// } -// -// // Get the CCIP calldata to forward -// try { -// const calldata = _parseBytes(data, 64); -// if (calldata == null) { throw new Error("abort"); } -// result.calldata = calldata; -// } catch (error) { -// assert(false, "corrupt OffchainLookup calldata", "OFFCHAIN_FAULT", { -// reason: "corrupt OffchainLookup calldata" -// }); -// } -// -// // Get the callbackSelector (bytes4) -// assert(dataSlice(data, 100, 128) === dataSlice(zeros, 0, 28), "corrupt OffchainLookup callbaackSelector", "OFFCHAIN_FAULT", { -// reason: "corrupt OffchainLookup callbaackSelector" -// }); -// result.selector = dataSlice(data, 96, 100); -// -// // Get the extra data to send back to the contract as context -// try { -// const extraData = _parseBytes(data, 128); -// if (extraData == null) { throw new Error("abort"); } -// result.extraData = extraData; -// } catch (error) { -// assert(false, "corrupt OffchainLookup extraData", "OFFCHAIN_FAULT", { -// reason: "corrupt OffchainLookup extraData" -// }); -// } -// -// result.errorArgs = "sender,urls,calldata,selector,extraData".split(/,/).map((k) => (result)[k]) -// -// return result; -// } diff --git a/src.ts/providers/abstract-signer.ts b/src.ts/providers/abstract-signer.ts index 2c045049..e7f73540 100644 --- a/src.ts/providers/abstract-signer.ts +++ b/src.ts/providers/abstract-signer.ts @@ -5,13 +5,12 @@ * * @_section: api/providers/abstract-signer: Subclassing Signer [abstract-signer] */ -import {AddressLike, resolveAddress} from "../address/index.js"; -import { Transaction } from "../transaction/index.js"; +import { AddressLike, resolveAddress } from "../address/index.js"; import { defineProperties, getBigInt, resolveProperties, assert, assertArgument, isUTXOAddress } from "../utils/index.js"; -import { copyRequest } from "./provider.js"; +import {addressFromTransactionRequest, copyRequest, QiTransactionRequest, QuaiTransactionRequest} from "./provider.js"; import type { TypedDataDomain, TypedDataField } from "../hash/index.js"; import type { TransactionLike } from "../transaction/index.js"; @@ -21,13 +20,15 @@ import type { } from "./provider.js"; import type { Signer } from "./signer.js"; import { getTxType } from "../utils/index.js"; +import {QiTransaction, QiTransactionLike} from "../transaction/qi-transaction"; +import {QuaiTransaction, QuaiTransactionLike} from "../transaction/quai-transaction"; function checkProvider(signer: AbstractSigner, operation: string): Provider { if (signer.provider) { return signer.provider; } assert(false, "missing provider", "UNSUPPORTED_OPERATION", { operation }); } -async function populate(signer: AbstractSigner, tx: TransactionRequest): Promise> { +async function populate(signer: AbstractSigner, tx: TransactionRequest): Promise { let pop: any = copyRequest(tx); if (pop.to != null) { pop.to = resolveAddress(pop.to); } @@ -95,20 +96,16 @@ export abstract class AbstractSigner

> { + async populateCall(tx: TransactionRequest): Promise { const pop = await populate(this, tx); return pop; } - // async populateQiTransaction(tx: TransactionRequest): Promise> { - - // } - - async populateTransaction(tx: TransactionRequest): Promise> { + async populateQuaiTransaction(tx: QuaiTransactionRequest): Promise { const provider = checkProvider(this, "populateTransaction"); - const shard = await this.shardFromAddress(tx.from) + const shard = await this.shardFromAddress(tx.from); - const pop = await populate(this, tx); + const pop = await populate(this, tx) as QuaiTransactionLike; if (pop.type == null) { pop.type = await getTxType(pop.from ?? null, pop.to ?? null); @@ -118,12 +115,16 @@ export abstract class AbstractSigner

> { - + async populateQiTransaction(tx: QiTransactionRequest): Promise { + const pop = { inputsUTXO: tx.inputs, outputsUTXO: tx.outputs, + chainId: tx.chainId, type: 2, - from: String(tx.from) } //@TOOD: Don't await all over the place; save them up for @@ -168,7 +169,7 @@ export abstract class AbstractSigner

{ - return checkProvider(this, "estimateGas").estimateGas(await this.populateCall(tx)); + return checkProvider(this, "estimateGas").estimateGas(await this.populateCall(tx)); } async call(tx: TransactionRequest): Promise { @@ -178,23 +179,21 @@ export abstract class AbstractSigner

{ const provider = checkProvider(this, "sendTransaction"); let sender = await this.getAddress() - tx.from = sender - const shard = await this.shardFromAddress(tx.from) + const shard = await this.shardFromAddress(addressFromTransactionRequest(tx)) let pop; + let txObj; if (isUTXOAddress(sender)) { - pop = await this.populateUTXOTransaction(tx); + pop = await this.populateQiTransaction(tx); + txObj = QiTransaction.from(pop); } else { - pop = await this.populateTransaction(tx); + pop = await this.populateQuaiTransaction(tx as QuaiTransactionRequest); + txObj = QuaiTransaction.from(pop); } -// delete pop.from; - const txObj = Transaction.from(pop); - const signedTx = await this.signTransaction(txObj); - // console.log("signedTX: ", JSON.stringify(txObj)) - return await provider.broadcastTransaction(shard, signedTx); + return await provider.broadcastTransaction(shard, signedTx, "from" in tx ? tx.from : undefined); } abstract signTransaction(tx: TransactionRequest): Promise; @@ -232,7 +231,7 @@ export class VoidSigner extends AbstractSigner { } #throwUnsupported(suffix: string, operation: string): never { - assert(false, `VoidSigner cannot sign ${ suffix }`, "UNSUPPORTED_OPERATION", { operation }); + assert(false, `VoidSigner cannot sign ${suffix}`, "UNSUPPORTED_OPERATION", { operation }); } async signTransaction(tx: TransactionRequest): Promise { diff --git a/src.ts/providers/contracts.ts b/src.ts/providers/contracts.ts index 57ca77b4..b27bb8f3 100644 --- a/src.ts/providers/contracts.ts +++ b/src.ts/providers/contracts.ts @@ -1,5 +1,5 @@ import type { - Provider, TransactionRequest, TransactionResponse + Provider, QuaiTransactionRequest, TransactionRequest, TransactionResponse } from "./provider.js"; /** @@ -28,7 +28,7 @@ export interface ContractRunner { /** * Required for pure, view or static calls to contracts. */ - call?: (tx: TransactionRequest) => Promise; + call?: (tx: QuaiTransactionRequest) => Promise; /** * Required for state mutating calls diff --git a/src.ts/providers/format.ts b/src.ts/providers/format.ts index 1c16ca25..b6e95c6b 100644 --- a/src.ts/providers/format.ts +++ b/src.ts/providers/format.ts @@ -6,7 +6,9 @@ import { Signature } from "../crypto/index.js" import { accessListify } from "../transaction/index.js"; import { getBigInt, getNumber, isHexString, zeroPadValue, - assert, assertArgument + assert, assertArgument, + BigNumberish, + toBeArray } from "../utils/index.js"; import type { @@ -20,7 +22,7 @@ const BN_0 = BigInt(0); export type FormatFunc = (value: any) => any; export function allowNull(format: FormatFunc, nullValue?: any): FormatFunc { - return (function(value: any) { + return (function (value: any) { if (value == null) { return nullValue; } return format(value); }); @@ -38,7 +40,7 @@ export function arrayOf(format: FormatFunc): FormatFunc { // from the result object. Calls preserve `this`. export function object(format: Record, altNames?: Record>): FormatFunc { return ((value: any) => { - const result: any = { }; + const result: any = {}; for (const key in format) { let srcKey = key; if (altNames && key in altNames && !(srcKey in value)) { @@ -54,8 +56,8 @@ export function object(format: Record, altNames?: Record { - if (typeof(tx) === "string") { return tx; } + if (typeof (tx) === "string") { return tx; } return formatTransactionResponse(tx); }); result.extTransactions = value.extTransactions.map((tx: string | TransactionResponseParams) => { - if (typeof(tx) === "string") { return tx; } + if (typeof (tx) === "string") { return tx; } return formatTransactionResponse(tx); }); return result; @@ -168,7 +173,7 @@ const _formatReceiptLog = object({ index: getNumber, blockHash: formatHash, }, { - index: [ "logIndex" ] + index: ["logIndex"] }); export function formatReceiptLog(value: any): LogParams { @@ -190,7 +195,7 @@ const _formatEtx = object({ from: allowNull(getAddress, null), hash: formatHash, }, { - from: [ "sender" ], + from: ["sender"], }); export function formatEtx(value: any): EtxParams { @@ -216,8 +221,8 @@ const _formatTransactionReceipt = object({ type: allowNull(getNumber, 0), etxs: arrayOf(formatEtx), }, { - hash: [ "transactionHash" ], - index: [ "transactionIndex" ], + hash: ["transactionHash"], + index: ["transactionIndex"], }); export function formatTransactionReceipt(value: any): TransactionReceiptParams { @@ -257,15 +262,15 @@ export function formatTransactionResponse(value: any): TransactionResponseParams gasLimit: getBigInt, to: allowNull(getAddress, null), value: getBigInt, - nonce: getNumber, + nonce: getNumber, creates: allowNull(getAddress, null), chainId: allowNull(getBigInt, null), }, { - data: [ "input" ], - gasLimit: [ "gas" ], - index: [ "transactionIndex" ], + data: ["input"], + gasLimit: ["gas"], + index: ["transactionIndex"], })(value); // If to and creates are empty, populate the creates from the value @@ -275,7 +280,7 @@ export function formatTransactionResponse(value: any): TransactionResponseParams // Add an access list to supported transaction types if ((value.type === 1 || value.type === 2) && value.accessList == null) { - result.accessList = [ ]; + result.accessList = []; } // Compute the signature diff --git a/src.ts/providers/formatting.ts b/src.ts/providers/formatting.ts index d6fb7598..70ff0dc3 100644 --- a/src.ts/providers/formatting.ts +++ b/src.ts/providers/formatting.ts @@ -5,8 +5,7 @@ */ import type { Signature } from "../crypto/index.js"; -import type { AccessList } from "../transaction/index.js"; -import type { UTXOTransactionInput, UTXOTransactionOutput } from "../transaction/utxo.js"; +import type {AccessList, TxInput, TxOutput} from "../transaction/index.js"; ////////////////////// // Block @@ -26,12 +25,6 @@ export interface BlockParams { */ number: Array | number; - /** - * The timestamp for this block, which is the number of seconds - * since epoch that this block was included. - */ - timestamp: number; - /** * The hash of the previous block in the blockchain. The genesis block * has the parentHash of the [[ZeroHash]]. @@ -107,13 +100,13 @@ export interface BlockParams { /** * The list of transactions in the block. */ - transactions: ReadonlyArray; + transactions: ReadonlyArray; transactionsRoot: string; extRollupRoot: string; - extTransactions: ReadonlyArray; + extTransactions: ReadonlyArray; extTransactionsRoot: string; }; @@ -292,6 +285,7 @@ export interface TransactionReceiptParams { +export type TransactionResponseParams = QuaiTransactionResponseParams | QiTransactionResponseParams; ////////////////////// // Transaction Response @@ -300,7 +294,7 @@ export interface TransactionReceiptParams { * a **TransactionResponseParams** encodes the minimal required properties * for a formatted transaction response. */ -export interface TransactionResponseParams { +export interface QuaiTransactionResponseParams { /** * The block number of the block that included this transaction. */ @@ -381,10 +375,45 @@ export interface TransactionResponseParams { * The transaction access list. */ accessList: null | AccessList; +}; + +export interface QiTransactionResponseParams { + /** + * The block number of the block that included this transaction. + */ + blockNumber: null | number; + + /** + * The block hash of the block that included this transaction. + */ + blockHash: null | string; + + /** + * The transaction hash. + */ + hash: string; + + /** + * The transaction index. + */ + index: bigint; + + + type: number; + + /** + * The chain ID this transaction is valid on. + */ + chainId: bigint; + + /** + * The signature of the transaction. + */ + signature: string; - outputsUTXO ?: UTXOTransactionOutput[]; + txOutputs ?: TxOutput[]; - inputsUTXO ?: UTXOTransactionInput[]; + txInputs ?: TxInput[]; }; diff --git a/src.ts/providers/index.ts b/src.ts/providers/index.ts index a8804379..c5d18c03 100644 --- a/src.ts/providers/index.ts +++ b/src.ts/providers/index.ts @@ -108,7 +108,7 @@ export type { FallbackProviderOptions } from "./provider-fallback.js"; export type { JsonRpcPayload, JsonRpcResult, JsonRpcError, JsonRpcApiProviderOptions, - JsonRpcTransactionRequest, + JsonRpcTransactionRequest, QuaiJsonRpcTransactionRequest, QiJsonRpcTransactionRequest } from "./provider-jsonrpc.js"; export type { diff --git a/src.ts/providers/network.ts b/src.ts/providers/network.ts index 16f0f32a..e712b22d 100644 --- a/src.ts/providers/network.ts +++ b/src.ts/providers/network.ts @@ -5,7 +5,6 @@ * @_subsection: api/providers:Networks [networks] */ -import { accessListify } from "../transaction/index.js"; import { getBigInt, assert, assertArgument } from "../utils/index.js"; import { @@ -13,7 +12,6 @@ import { } from "./plugins-network.js"; import type { BigNumberish } from "../utils/index.js"; -import type { TransactionLike } from "../transaction/index.js"; import type { NetworkPlugin } from "./plugins-network.js"; @@ -192,30 +190,30 @@ export class Network { * A GasCostPlugin can be attached to override the default * values. */ - computeIntrinsicGas(tx: TransactionLike): number { - const costs = this.getPlugin("org.quais.plugins.network.GasCost") || (new GasCostPlugin()); - - let gas = costs.txBase; - if (tx.to == null) { gas += costs.txCreate; } - if (tx.data) { - for (let i = 2; i < tx.data.length; i += 2) { - if (tx.data.substring(i, i + 2) === "00") { - gas += costs.txDataZero; - } else { - gas += costs.txDataNonzero; - } - } - } - - if (tx.accessList) { - const accessList = accessListify(tx.accessList); - for (const addr in accessList) { - gas += costs.txAccessListAddress + costs.txAccessListStorageKey * accessList[addr].storageKeys.length; - } - } - - return gas; - } +// computeIntrinsicGas(tx: TransactionLike): number { +// const costs = this.getPlugin("org.quais.plugins.network.GasCost") || (new GasCostPlugin()); +// +// let gas = costs.txBase; +// if (tx.to == null) { gas += costs.txCreate; } +// if (tx.data) { +// for (let i = 2; i < tx.data.length; i += 2) { +// if (tx.data.substring(i, i + 2) === "00") { +// gas += costs.txDataZero; +// } else { +// gas += costs.txDataNonzero; +// } +// } +// } +// +// if (tx.accessList) { +// const accessList = accessListify(tx.accessList); +// for (const addr in accessList) { +// gas += costs.txAccessListAddress + costs.txAccessListStorageKey * accessList[addr].storageKeys.length; +// } +// } +// +// return gas; +// } /** * Returns a new Network for the %%network%% name or chainId. diff --git a/src.ts/providers/provider-fallback.ts b/src.ts/providers/provider-fallback.ts index cba88603..b4d9c153 100644 --- a/src.ts/providers/provider-fallback.ts +++ b/src.ts/providers/provider-fallback.ts @@ -13,6 +13,7 @@ import { Network } from "./network.js" import type { PerformActionRequest } from "./abstract-provider.js"; import type { Networkish } from "./network.js" +import {QuaiTransactionRequest} from "./provider"; const BN_1 = BigInt("1"); const BN_2 = BigInt("2"); @@ -441,7 +442,7 @@ export class FallbackProvider extends AbstractProvider { case "broadcastTransaction": return await provider.broadcastTransaction(req.shard, req.signedTransaction); case "call": - return await provider.call(Object.assign({}, req.transaction, { blockTag: req.blockTag })); + return await provider.call(Object.assign({}, req.transaction as QuaiTransactionRequest, { blockTag: req.blockTag })); case "chainId": return (await provider.getNetwork()).chainId; case "estimateGas": diff --git a/src.ts/providers/provider-jsonrpc.ts b/src.ts/providers/provider-jsonrpc.ts index 1993c903..971b9cb9 100644 --- a/src.ts/providers/provider-jsonrpc.ts +++ b/src.ts/providers/provider-jsonrpc.ts @@ -35,8 +35,11 @@ import type { TransactionLike } from "../transaction/index.js"; import type { PerformActionRequest, Subscriber, Subscription } from "./abstract-provider.js"; import type { Networkish } from "./network.js"; -import type { Provider, TransactionRequest, TransactionResponse } from "./provider.js"; +import type {Provider, QuaiTransactionRequest, TransactionRequest} from "./provider.js"; import type { Signer } from "./signer.js"; +import {QuaiTransactionLike} from "../transaction/quai-transaction"; +import {TransactionResponse, addressFromTransactionRequest} from "./provider.js"; +import { UTXOEntry, UTXOTransactionOutput } from "../transaction/utxo.js"; type Timer = ReturnType; @@ -69,11 +72,6 @@ function stall(duration: number): Promise { return new Promise((resolve) => { setTimeout(resolve, duration); }); } -function getLowerCase(value: string): string { - if (value) { return value.toLowerCase(); } - return value; -} - /** * A JSON-RPC payload, which are sent to a JSON-RPC server. */ @@ -198,11 +196,32 @@ const defaultOptions = { cacheTimeout: 250 } +export interface AbstractJsonRpcTransactionRequest { + + /** + * The chain ID the transaction is valid on. + */ + chainId?: string; + + /** + * The [[link-eip-2718]] transaction type. + */ + type?: string; +} + +export type JsonRpcTransactionRequest = QiJsonRpcTransactionRequest | QuaiJsonRpcTransactionRequest; + +export interface QiJsonRpcTransactionRequest extends AbstractJsonRpcTransactionRequest { + txInputs?: Array; + + txOutputs?: Array; +} + /** * A **JsonRpcTransactionRequest** is formatted as needed by the JSON-RPC * Ethereum API specification. */ -export interface JsonRpcTransactionRequest { +export interface QuaiJsonRpcTransactionRequest extends AbstractJsonRpcTransactionRequest { /** * The sender address to use when signing. */ @@ -218,16 +237,6 @@ export interface JsonRpcTransactionRequest { */ data?: string; - /** - * The chain ID the transaction is valid on. - */ - chainId?: string; - - /** - * The [[link-eip-2718]] transaction type. - */ - type?: string; - /** * The maximum amount of gas to allow a transaction to consume. * @@ -269,183 +278,199 @@ export interface JsonRpcTransactionRequest { // @TODO: Unchecked Signers -export class JsonRpcSigner extends AbstractSigner { - address!: string; - - constructor(provider: JsonRpcApiProvider, address: string) { - super(provider); - address = getAddress(address); - defineProperties(this, { address }); - } - - connect(provider: null | Provider): Signer { - assert(false, "cannot reconnect JsonRpcSigner", "UNSUPPORTED_OPERATION", { - operation: "signer.connect" - }); - } - - async getAddress(): Promise { - return this.address; - } - - // JSON-RPC will automatially fill in nonce, etc. so we just check from - async populateTransaction(tx: TransactionRequest): Promise> { - return await this.populateCall(tx); - } - - // Returns just the hash of the transaction after sent, which is what - // the bare JSON-RPC API does; - async sendUncheckedTransaction(_tx: TransactionRequest): Promise { - const tx = deepCopy(_tx); - - const promises: Array> = []; - - // Make sure the from matches the sender - if (tx.from) { - const _from = tx.from; - promises.push((async () => { - const from = await resolveAddress(_from); - assertArgument(from != null && from.toLowerCase() === this.address.toLowerCase(), - "from address mismatch", "transaction", _tx); - tx.from = from; - })()); + export class JsonRpcSigner extends AbstractSigner { + address!: string; + + constructor(provider: JsonRpcApiProvider, address: string) { + super(provider); + address = getAddress(address); + defineProperties(this, { address }); + } + + connect(provider: null | Provider): Signer { + assert(false, "cannot reconnect JsonRpcSigner", "UNSUPPORTED_OPERATION", { + operation: "signer.connect" + }); + } + + async getAddress(): Promise { + return this.address; + } + + // JSON-RPC will automatially fill in nonce, etc. so we just check from + async populateQuaiTransaction(tx: QuaiTransactionRequest): Promise { + return await this.populateCall(tx) as QuaiTransactionLike; + } + + // Returns just the hash of the transaction after sent, which is what + // the bare JSON-RPC API does; + async sendUncheckedTransaction(_tx: TransactionRequest): Promise { + const tx = deepCopy(_tx); + + const promises: Array> = []; + + if ("from" in tx) { + // Make sure the from matches the sender + if (tx.from) { + const _from = tx.from; + promises.push((async () => { + const from = await resolveAddress(_from); + assertArgument(from != null && from.toLowerCase() === this.address.toLowerCase(), + "from address mismatch", "transaction", _tx); + tx.from = from; + })()); + } else { + tx.from = this.address; + } + + // The JSON-RPC for quai_sendTransaction uses 90000 gas; if the user + // wishes to use this, it is easy to specify explicitly, otherwise + // we look it up for them. + if (tx.gasLimit == null) { + promises.push((async () => { + tx.gasLimit = await this.provider.estimateGas({ ...tx, from: this.address }); + })()); + } + + // The address may be an ENS name or Addressable + if (tx.to != null) { + const _to = tx.to; + promises.push((async () => { + tx.to = await resolveAddress(_to); + })()); + } + } else { + // Make sure the from matches the sender + if (tx.outputs) { + for (let i = 0; i < tx.outputs.length; i++) { + if (tx.outputs[i].Address) { + promises.push((async () => { + tx.outputs![i].Address = await resolveAddress(tx.outputs![i].Address); + })()); + } + } + } + } + + // Wait until all of our properties are filled in + if (promises.length) { await Promise.all(promises); } + const hexTx = this.provider.getRpcTransaction(tx); + + return this.provider.send("quai_sendTransaction", [hexTx]); + } + + async sendTransaction(tx: TransactionRequest): Promise { + const shard = await this.shardFromAddress(addressFromTransactionRequest(tx)) + // This cannot be mined any earlier than any recent block + const blockNumber = await this.provider.getBlockNumber(shard); + // Send the transaction + const hash = await this.sendUncheckedTransaction(tx); + + // Unfortunately, JSON-RPC only provides and opaque transaction hash + // for a response, and we need the actual transaction, so we poll + // for it; it should show up very quickly + return await (new Promise((resolve, reject) => { + const timeouts = [1000, 100]; + let invalids = 0; + + const checkTx = async () => { + + try { + // Try getting the transaction + const tx = await this.provider.getTransaction(hash); + + if (tx != null) { + resolve(tx.replaceableTransaction(blockNumber)); + return; + } + + } catch (error) { + + // If we were cancelled: stop polling. + // If the data is bad: the node returns bad transactions + // If the network changed: calling again will also fail + // If unsupported: likely destroyed + if (isError(error, "CANCELLED") || isError(error, "BAD_DATA") || + isError(error, "NETWORK_ERROR" || isError(error, "UNSUPPORTED_OPERATION"))) { + + if (error.info == null) { error.info = {}; } + error.info.sendTransactionHash = hash; + + reject(error); + return; + } + + // Stop-gap for misbehaving backends; see #4513 + if (isError(error, "INVALID_ARGUMENT")) { + invalids++; + if (error.info == null) { error.info = {}; } + error.info.sendTransactionHash = hash; + if (invalids > 10) { + reject(error); + return; + } + } + + // Notify anyone that cares; but we will try again, since + // it is likely an intermittent service error + this.provider.emit("error", makeError("failed to fetch transation after sending (will try again)", "UNKNOWN_ERROR", { error })); + } + + // Wait another 4 seconds + this.provider._setTimeout(() => { checkTx(); }, timeouts.pop() || 4000); + }; + checkTx(); + })); + } + + async signTransaction(_tx: TransactionRequest): Promise { + const tx = deepCopy(_tx); + + // QuaiTransactionRequest + if ('from' in tx) { + if (tx.from) { + const from = await resolveAddress(tx.from); + assertArgument(from != null && from.toLowerCase() === this.address.toLowerCase(), + "from address mismatch", "transaction", _tx); + tx.from = from; + } else { + tx.from = this.address; + } } else { - tx.from = this.address; + throw new Error("No QI signing implementation in provider-jsonrpc"); } + const hexTx = this.provider.getRpcTransaction(tx); + return await this.provider.send("quai_signTransaction", [hexTx]); + } - // The JSON-RPC for quai_sendTransaction uses 90000 gas; if the user - // wishes to use this, it is easy to specify explicitly, otherwise - // we look it up for them. - if (tx.gasLimit == null) { - promises.push((async () => { - tx.gasLimit = await this.provider.estimateGas({ ...tx, from: this.address }); - })()); - } - - // The address may be an ENS name or Addressable - if (tx.to != null) { - const _to = tx.to; - promises.push((async () => { - tx.to = await resolveAddress(_to); - })()); - } - // Wait until all of our properties are filled in - if (promises.length) { await Promise.all(promises); } - const hexTx = this.provider.getRpcTransaction(tx); - - return this.provider.send("quai_sendTransaction", [hexTx]); - } + async signMessage(_message: string | Uint8Array): Promise { + const message = ((typeof (_message) === "string") ? toUtf8Bytes(_message) : _message); + return await this.provider.send("personal_sign", [ + hexlify(message), this.address.toLowerCase()]); + } - async sendTransaction(tx: TransactionRequest): Promise { - // This cannot be mined any earlier than any recent block - const shard = await this.shardFromAddress(tx.from); - const blockNumber = await this.provider.getBlockNumber(shard); - // Send the transaction - const hash = await this.sendUncheckedTransaction(tx); - - // Unfortunately, JSON-RPC only provides and opaque transaction hash - // for a response, and we need the actual transaction, so we poll - // for it; it should show up very quickly - return await (new Promise((resolve, reject) => { - const timeouts = [1000, 100]; - let invalids = 0; - - const checkTx = async () => { - - try { - // Try getting the transaction - const tx = await this.provider.getTransaction(hash); + async signTypedData(domain: TypedDataDomain, types: Record>, _value: Record): Promise { + const value = deepCopy(_value); - if (tx != null) { - resolve(tx.replaceableTransaction(blockNumber)); - return; - } - - } catch (error) { - - // If we were cancelled: stop polling. - // If the data is bad: the node returns bad transactions - // If the network changed: calling again will also fail - // If unsupported: likely destroyed - if (isError(error, "CANCELLED") || isError(error, "BAD_DATA") || - isError(error, "NETWORK_ERROR" || isError(error, "UNSUPPORTED_OPERATION"))) { - - if (error.info == null) { error.info = {}; } - error.info.sendTransactionHash = hash; - - reject(error); - return; - } - - // Stop-gap for misbehaving backends; see #4513 - if (isError(error, "INVALID_ARGUMENT")) { - invalids++; - if (error.info == null) { error.info = {}; } - error.info.sendTransactionHash = hash; - if (invalids > 10) { - reject(error); - return; - } - } - - // Notify anyone that cares; but we will try again, since - // it is likely an intermittent service error - this.provider.emit("error", makeError("failed to fetch transation after sending (will try again)", "UNKNOWN_ERROR", { error })); - } - - // Wait another 4 seconds - this.provider._setTimeout(() => { checkTx(); }, timeouts.pop() || 4000); - }; - checkTx(); - })); - } - - async signTransaction(_tx: TransactionRequest): Promise { - const tx = deepCopy(_tx); - - // Make sure the from matches the sender - if (tx.from) { - const from = await resolveAddress(tx.from); - assertArgument(from != null && from.toLowerCase() === this.address.toLowerCase(), - "from address mismatch", "transaction", _tx); - tx.from = from; - } else { - tx.from = this.address; - } - - const hexTx = this.provider.getRpcTransaction(tx); - return await this.provider.send("quai_signTransaction", [hexTx]); - } - - - async signMessage(_message: string | Uint8Array): Promise { - const message = ((typeof (_message) === "string") ? toUtf8Bytes(_message) : _message); - return await this.provider.send("personal_sign", [ - hexlify(message), this.address.toLowerCase()]); - } - - async signTypedData(domain: TypedDataDomain, types: Record>, _value: Record): Promise { - const value = deepCopy(_value); - return await this.provider.send("quai_signTypedData_v4", [ this.address.toLowerCase(), JSON.stringify(TypedDataEncoder.getPayload(domain, types, value)) ]); } - async unlock(password: string): Promise { - return this.provider.send("personal_unlockAccount", [ - this.address.toLowerCase(), password, null]); - } + async unlock(password: string): Promise { + return this.provider.send("personal_unlockAccount", [ + this.address.toLowerCase(), password, null]); + } - // https://github.com/ethereum/wiki/wiki/JSON-RPC#quai_sign - async _legacySignMessage(_message: string | Uint8Array): Promise { - const message = ((typeof (_message) === "string") ? toUtf8Bytes(_message) : _message); - return await this.provider.send("quai_sign", [ - this.address.toLowerCase(), hexlify(message)]); - } -} + // https://github.com/ethereum/wiki/wiki/JSON-RPC#quai_sign + async _legacySignMessage(_message: string | Uint8Array): Promise { + const message = ((typeof (_message) === "string") ? toUtf8Bytes(_message) : _message); + return await this.provider.send("quai_sign", [ + this.address.toLowerCase(), hexlify(message)]); + } + } type ResolveFunc = (result: JsonRpcResult) => void; type RejectFunc = (error: Error) => void; @@ -822,6 +847,7 @@ export abstract class JsonRpcApiProvider extends AbstractProvider { getRpcTransaction(tx: TransactionRequest): JsonRpcTransactionRequest { const result: JsonRpcTransactionRequest = {}; + if ('from' in tx){ // JSON-RPC now requires numeric values to be "quantity" values ["chainId", "gasLimit", "gasPrice", "type", "maxFeePerGas", "maxPriorityFeePerGas", "nonce", "value"].forEach((key) => { if ((tx)[key] == null) { return; } @@ -837,11 +863,13 @@ export abstract class JsonRpcApiProvider extends AbstractProvider { }); // Normalize the access list object - if (tx.accessList) { - result["accessList"] = accessListify(tx.accessList); + if ("accessList" in tx && tx.accessList) { + (result as QuaiJsonRpcTransactionRequest)["accessList"] = accessListify(tx.accessList); } - - return result; + } else { + throw new Error('No Qi getRPCTransaction implementation yet') + } + return result } /** @@ -865,34 +893,37 @@ export abstract class JsonRpcApiProvider extends AbstractProvider { case "getMaxPriorityFeePerGas": return { method: "quai_maxPriorityFeePerGas", args: [] }; + case "getPendingHeader": + return { method: "quai_getPendingHeader", args: [] }; + case "getBalance": return { method: "quai_getBalance", - args: [getLowerCase(req.address), req.blockTag] + args: [req.address, req.blockTag] }; case "getOutpointsByAddress": return { method: "quai_getOutpointsByAddress", - args: [getLowerCase(req.address)] + args: [req.address] }; case "getTransactionCount": return { method: "quai_getTransactionCount", - args: [getLowerCase(req.address), req.blockTag] + args: [req.address, req.blockTag] }; case "getCode": return { method: "quai_getCode", - args: [getLowerCase(req.address), req.blockTag] + args: [req.address, req.blockTag] }; case "getStorage": return { method: "quai_getStorageAt", args: [ - getLowerCase(req.address), + req.address, ("0x" + req.position.toString(16)), req.blockTag ] @@ -979,13 +1010,6 @@ export abstract class JsonRpcApiProvider extends AbstractProvider { } case "getLogs": - if (req.filter && req.filter.address != null) { - if (Array.isArray(req.filter.address)) { - req.filter.address = req.filter.address.map(getLowerCase); - } else { - req.filter.address = getLowerCase(req.filter.address); - } - } return { method: "quai_getLogs", args: [req.filter] }; } @@ -1048,7 +1072,7 @@ export abstract class JsonRpcApiProvider extends AbstractProvider { } if (method === "quai_sendRawTransaction" || method === "quai_sendTransaction") { - const transaction = >((payload).params[0]); + const transaction = ((payload).params[0]); if (message.match(/insufficient funds|base fee exceeds gas limit/i)) { return makeError("insufficient funds for intrinsic transaction cost", "INSUFFICIENT_FUNDS", { diff --git a/src.ts/providers/provider.ts b/src.ts/providers/provider.ts index 0bf7248f..837c24bc 100644 --- a/src.ts/providers/provider.ts +++ b/src.ts/providers/provider.ts @@ -3,12 +3,14 @@ import { defineProperties, getBigInt, getNumber, hexlify, resolveProperties, assert, assertArgument, isError, makeError } from "../utils/index.js"; +import { getAddress } from "../address/index.js"; import { accessListify } from "../transaction/index.js"; +import {keccak256, SigningKey} from "../crypto"; import type { AddressLike } from "../address/index.js"; import type { BigNumberish, EventEmitterable } from "../utils/index.js"; import type { Signature } from "../crypto/index.js"; -import type { AccessList, AccessListish, TransactionLike } from "../transaction/index.js"; +import type { AccessList, AccessListish } from "../transaction/index.js"; import type { ContractRunner } from "./contracts.js"; import type { Network } from "./network.js"; @@ -33,9 +35,11 @@ const BN_0 = BigInt(0); export type BlockTag = BigNumberish | string; import { - BlockParams, LogParams, TransactionReceiptParams, - TransactionResponseParams + BlockParams, LogParams, QiTransactionResponseParams, QuaiTransactionResponseParams, TransactionReceiptParams } from "./formatting.js"; +import { WorkObjectLike } from "../transaction/work-object.js"; +import {QiTransactionLike} from "../transaction/qi-transaction"; +import {QuaiTransactionLike} from "../transaction/quai-transaction"; // ----------------------- @@ -114,6 +118,16 @@ export class FeeData { } } +export function addressFromTransactionRequest(tx: TransactionRequest): AddressLike { + //return 'from' in tx ? tx.from : tx.inputs[0].address; + if ('from' in tx) { + return tx.from; + } + if (tx.inputs) { + return getAddress(keccak256("0x" + SigningKey.computePublicKey(tx.inputs[0].pubKey).substring(4)).substring(26)); + } + throw new Error("Unable to determine sender from transaction inputs or from field"); +} /** * A **TransactionRequest** is a transactions with potentially various * properties not defined, or with less strict types for its values. @@ -121,7 +135,9 @@ export class FeeData { * This is used to pass to various operations, which will internally * coerce any types and populate any necessary values. */ -export interface TransactionRequest { +export type TransactionRequest = QuaiTransactionRequest | QiTransactionRequest; + +export interface QuaiTransactionRequest { /** * The transaction type. */ @@ -199,9 +215,21 @@ export interface TransactionRequest { /** * When using ``call`` or ``estimateGas``, this allows a specific * block to be queried. Many backends do not support this and when - * unsupported errors are silently squelched and ``"latest"`` is used. + * unsupported errors are silently squelched and ``"latest"`` is used. */ blockTag?: BlockTag; +}; + +export interface QiTransactionRequest { + /** + * The transaction type. + */ + type?: null | number; + + /** + * The chain ID for the network this transaction is valid on. + */ + chainId?: null | BigNumberish; inputs?: null | Array; @@ -212,7 +240,9 @@ export interface TransactionRequest { * A **PreparedTransactionRequest** is identical to a [[TransactionRequest]] * except all the property types are strictly enforced. */ -export interface PreparedTransactionRequest { +export type PreparedTransactionRequest = QuaiPreparedTransactionRequest | QiPreparedTransactionRequest; + +export interface QuaiPreparedTransactionRequest { /** * The transaction type. */ @@ -293,9 +323,21 @@ export interface PreparedTransactionRequest { /** * When using ``call`` or ``estimateGas``, this allows a specific * block to be queried. Many backends do not support this and when - * unsupported errors are silently squelched and ``"latest"`` is used. + * unsupported errors are silently squelched and ``"latest"`` is used. */ blockTag?: BlockTag; +} + +export interface QiPreparedTransactionRequest { + /** + * The transaction type. + */ + type?: number; + + /** + * The chain ID for the network this transaction is valid on. + */ + chainId?: bigint; inputs?: null | Array; @@ -310,10 +352,10 @@ export function copyRequest(req: TransactionRequest): PreparedTransactionRequest const result: any = {}; // These could be addresses, ENS names or Addressables - if (req.to) { result.to = req.to; } - if (req.from) { result.from = req.from; } + if ("to" in req && req.to) { result.to = req.to; } + if ("from" in req && req.from) { result.from = req.from; } - if (req.data) { result.data = hexlify(req.data); } + if ("data" in req && req.data) { result.data = hexlify(req.data); } const bigIntKeys = "chainId,gasLimit,gasPrice,maxFeePerGas,maxPriorityFeePerGas,value".split(/,/); for (const key of bigIntKeys) { @@ -327,20 +369,26 @@ export function copyRequest(req: TransactionRequest): PreparedTransactionRequest result[key] = getNumber((req)[key], `request.${key}`); } - if (req.accessList) { + if ("accessList" in req && req.accessList) { result.accessList = accessListify(req.accessList); } if ("blockTag" in req) { result.blockTag = req.blockTag; } - if ("enableCcipRead" in req) { - result.enableCcipRead = !!req.enableCcipRead - } - if ("customData" in req) { result.customData = req.customData; } + if ("inputs" in req && req.inputs) { + result.inputs = req.inputs.map(entry => ({...entry})); + } + + if ("outputs" in req && req.outputs) { + result.outputs = req.outputs.map(entry => ({...entry})); + } + + + return result; } @@ -382,6 +430,9 @@ export interface MinedBlock extends Block { readonly miner: string; } + + + /** * A **Block** represents the data associated with a full block on * Ethereum. @@ -484,10 +535,10 @@ export class Block implements BlockParams, Iterable { readonly utxoRoot!: string; readonly uncles!: Array | null; - readonly #transactions: Array; + readonly #transactions: Array; readonly transactionsRoot: string; readonly extRollupRoot: string; - readonly #extTransactions: Array; + readonly #extTransactions: Array; readonly extTransactionsRoot: string; /** @@ -501,14 +552,14 @@ export class Block implements BlockParams, Iterable { this.#transactions = block.transactions.map((tx) => { if (typeof (tx) !== "string") { - return new TransactionResponse(tx, provider); + return new QuaiTransactionResponse(tx, provider); } return tx; }); this.#extTransactions = block.extTransactions.map((tx) => { if (typeof (tx) !== "string") { - return new TransactionResponse(tx, provider); + return new QuaiTransactionResponse(tx, provider); } return tx; }); @@ -525,7 +576,6 @@ export class Block implements BlockParams, Iterable { hash: getValue(block.hash), number: block.number, - timestamp: block.timestamp, parentHash: block.parentHash, @@ -1213,7 +1263,26 @@ export class TransactionReceipt implements TransactionReceiptParams, Iterable, TransactionResponseParams { +export class QuaiTransactionResponse implements QuaiTransactionLike, QuaiTransactionResponseParams { /** * The provider this is connected to, which will influence how its * methods will resolve its async inspection methods. @@ -1351,16 +1421,12 @@ export class TransactionResponse implements TransactionLike, Transaction */ readonly accessList!: null | AccessList; - readonly inputs?: Array; - - readonly outputs?: Array; - #startBlock: number; /** * @_ignore: */ - constructor(tx: TransactionResponseParams, provider: Provider) { + constructor(tx: QuaiTransactionResponseParams, provider: Provider) { this.provider = provider; this.blockNumber = (tx.blockNumber != null) ? tx.blockNumber : null; @@ -1436,8 +1502,13 @@ export class TransactionResponse implements TransactionLike, Transaction * provider. This can be used if you have an unmined transaction * and wish to get an up-to-date populated instance. */ - async getTransaction(): Promise { - return this.provider.getTransaction(this.hash); + async getTransaction(): Promise { + const transaction = this.provider.getTransaction(this.hash); + if (transaction instanceof QuaiTransactionResponse) { + return transaction as QuaiTransactionResponse; + } else { + return null; + } } /** @@ -1523,7 +1594,7 @@ export class TransactionResponse implements TransactionLike, Transaction for (let i = 0; i < block.length; i++) { const tx: TransactionResponse = await block.getTransaction(i); - if (tx.from === this.from && tx.nonce === this.nonce) { + if ('from' in tx && tx.from === this.from && tx.nonce === this.nonce) { // Get the receipt if (stopScanning) { return null; } const receipt = await this.provider.getTransactionReceipt(tx.hash); @@ -1655,7 +1726,7 @@ export class TransactionResponse implements TransactionLike, Transaction * non-null property values for properties that are null for * unmined transactions. */ - isMined(): this is MinedTransactionResponse { + isMined(): this is QuaiMinedTransactionResponse { return (this.blockHash != null); } @@ -1692,9 +1763,399 @@ export class TransactionResponse implements TransactionLike, Transaction * primarily for internal use. Setting an incorrect %%startBlock%% can * have devastating performance consequences if used incorrectly. */ - replaceableTransaction(startBlock: number): TransactionResponse { + replaceableTransaction(startBlock: number): QuaiTransactionResponse { assertArgument(Number.isInteger(startBlock) && startBlock >= 0, "invalid startBlock", "startBlock", startBlock); - const tx = new TransactionResponse(this, this.provider); + const tx = new QuaiTransactionResponse(this, this.provider); + tx.#startBlock = startBlock; + return tx; + } +} + +export class QiTransactionResponse implements QiTransactionLike, QiTransactionResponseParams { + /** + * The provider this is connected to, which will influence how its + * methods will resolve its async inspection methods. + */ + readonly provider: Provider; + + /** + * The block number of the block that this transaction was included in. + * + * This is ``null`` for pending transactions. + */ + readonly blockNumber: null | number; + + /** + * The blockHash of the block that this transaction was included in. + * + * This is ``null`` for pending transactions. + */ + readonly blockHash: null | string; + + /** + * The index within the block that this transaction resides at. + */ + readonly index!: bigint; + + /** + * The transaction hash. + */ + readonly hash!: string; + + /** + * The [[link-eip-2718]] transaction envelope type. This is + * ``0`` for legacy transactions types. + */ + readonly type!: number; + + /** + * The chain ID. + */ + readonly chainId!: bigint; + + /** + * The signature. + */ + readonly signature!: string; + + readonly txInputs?: Array; + + readonly txOutputs?: Array; + + // @ts-ignore + #startBlock: number; + /** + * @_ignore: + */ + constructor(tx: QiTransactionResponseParams, provider: Provider) { + this.provider = provider; + + this.blockNumber = (tx.blockNumber != null) ? tx.blockNumber : null; + this.blockHash = (tx.blockHash != null) ? tx.blockHash : null; + + this.hash = tx.hash; + this.index = tx.index; + + this.type = tx.type; + + this.chainId = tx.chainId; + this.signature = tx.signature; + + this.#startBlock = -1; + + this.txInputs = tx.txInputs; + this.txOutputs = tx.txOutputs; + } + + /** + * Returns a JSON-compatible representation of this transaction. + */ + toJSON(): any { + const { + blockNumber, blockHash, index, hash, type, + signature, txInputs, txOutputs + } = this; + let result = { + _type: "TransactionReceipt", + blockNumber, blockHash, + chainId: toJson(this.chainId), + hash, + signature, index, type, + txInputs: JSON.parse(JSON.stringify(txInputs)), + txOutputs: JSON.parse(JSON.stringify(txOutputs)), + + } + + return result; + } + + + /** + * Resolves to the Block that this transaction was included in. + * + * This will return null if the transaction has not been included yet. + */ + async getBlock(shard: string): Promise { + let blockNumber = this.blockNumber; + if (blockNumber == null) { + const tx = await this.getTransaction(); + if (tx) { blockNumber = tx.blockNumber; } + } + if (blockNumber == null) { return null; } + const block = this.provider.getBlock(shard, blockNumber); + if (block == null) { throw new Error("TODO"); } + return block; + } + + /** + * Resolves to this transaction being re-requested from the + * provider. This can be used if you have an unmined transaction + * and wish to get an up-to-date populated instance. + */ + async getTransaction(): Promise { + const transaction = this.provider.getTransaction(this.hash); + if (transaction instanceof QiTransactionResponse) { + return transaction as QiTransactionResponse; + } else { + return null; + } + } + + /** + * Resolve to the number of confirmations this transaction has. + */ + async confirmations(): Promise { + const shard = shardFromHash(this.hash); + if (this.blockNumber == null) { + const { tx, blockNumber } = await resolveProperties({ + tx: this.getTransaction(), + blockNumber: this.provider.getBlockNumber(shard) + }); + + // Not mined yet... + if (tx == null || tx.blockNumber == null) { return 0; } + + return blockNumber - tx.blockNumber + 1; + } + + const blockNumber = await this.provider.getBlockNumber(shard); + return blockNumber - this.blockNumber + 1; + } + + /** + * Resolves once this transaction has been mined and has + * %%confirms%% blocks including it (default: ``1``) with an + * optional %%timeout%%. + * + * This can resolve to ``null`` only if %%confirms%% is ``0`` + * and the transaction has not been mined, otherwise this will + * wait until enough confirmations have completed. + */ +// async wait(_confirms?: number, _timeout?: number): Promise { +// const confirms = (_confirms == null) ? 1 : _confirms; +// const timeout = (_timeout == null) ? 0 : _timeout; +// +// let startBlock = this.#startBlock +// let nextScan = -1; +// let stopScanning = (startBlock === -1) ? true : false; +// const shard = shardFromHash(this.hash); +// const checkReplacement = async () => { +// // Get the current transaction count for this sender +// if (stopScanning) { return null; } +// const { blockNumber, nonce } = await resolveProperties({ +// blockNumber: this.provider.getBlockNumber(shard), +// nonce: this.provider.getTransactionCount(this.from) +// }); +// +// // No transaction or our nonce has not been mined yet; but we +// // can start scanning later when we do start +// if (nonce < this.nonce) { +// startBlock = blockNumber; +// return; +// } +// +// // We were mined; no replacement +// if (stopScanning) { return null; } +// const mined = await this.getTransaction(); +// if (mined && mined.blockNumber != null) { return; } +// +// // We were replaced; start scanning for that transaction +// +// // Starting to scan; look back a few extra blocks for safety +// if (nextScan === -1) { +// nextScan = startBlock - 3; +// if (nextScan < this.#startBlock) { nextScan = this.#startBlock; } +// } +// +// while (nextScan <= blockNumber) { +// // Get the next block to scan +// if (stopScanning) { return null; } +// const block = await this.provider.getBlock(shard, nextScan, true); +// +// // This should not happen; but we'll try again shortly +// if (block == null) { return; } +// +// // We were mined; no replacement +// for (const hash of block) { +// if (hash === this.hash) { return; } +// } +// +// // Search for the transaction that replaced us +// for (let i = 0; i < block.length; i++) { +// const tx: TransactionResponse = await block.getTransaction(i); +// +// if (tx.from === this.from && tx.nonce === this.nonce) { +// // Get the receipt +// if (stopScanning) { return null; } +// const receipt = await this.provider.getTransactionReceipt(tx.hash); +// +// // This should not happen; but we'll try again shortly +// if (receipt == null) { return; } +// +// // We will retry this on the next block (this case could be optimized) +// if ((blockNumber - receipt.blockNumber + 1) < confirms) { return; } +// +// // The reason we were replaced +// let reason: "replaced" | "repriced" | "cancelled" = "replaced"; +// if (tx.data === this.data && tx.to === this.to && tx.value === this.value) { +// reason = "repriced"; +// } else if (tx.data === "0x" && tx.from === tx.to && tx.value === BN_0) { +// reason = "cancelled" +// } +// +// assert(false, "transaction was replaced", "TRANSACTION_REPLACED", { +// cancelled: (reason === "replaced" || reason === "cancelled"), +// reason, +// replacement: tx.replaceableTransaction(startBlock), +// hash: tx.hash, +// receipt +// }); +// } +// } +// +// nextScan++; +// } +// return; +// }; +// +// const checkReceipt = (receipt: null | TransactionReceipt) => { +// if (receipt == null || receipt.status !== 0) { return receipt; } +// assert(false, "transaction execution reverted", "CALL_EXCEPTION", { +// action: "sendTransaction", +// data: null, reason: null, invocation: null, revert: null, +// transaction: { +// to: receipt.to, +// from: receipt.from, +// data: "" // @TODO: in v7, split out sendTransaction properties +// }, receipt +// }); +// }; +// +// const receipt = await this.provider.getTransactionReceipt(this.hash); +// +// if (confirms === 0) { return checkReceipt(receipt); } +// +// if (receipt) { +// if ((await receipt.confirmations()) >= confirms) { +// return checkReceipt(receipt); +// } +// +// } else { +// // Check for a replacement; throws if a replacement was found +// await checkReplacement(); +// +// // Allow null only when the confirms is 0 +// if (confirms === 0) { return null; } +// } +// +// const waiter = new Promise((resolve, reject) => { +// // List of things to cancel when we have a result (one way or the other) +// const cancellers: Array<() => void> = []; +// const cancel = () => { cancellers.forEach((c) => c()); }; +// +// // On cancel, stop scanning for replacements +// cancellers.push(() => { stopScanning = true; }); +// +// // Set up any timeout requested +// if (timeout > 0) { +// const timer = setTimeout(() => { +// cancel(); +// reject(makeError("wait for transaction timeout", "TIMEOUT")); +// }, timeout); +// cancellers.push(() => { clearTimeout(timer); }); +// } +// +// const txListener = async (receipt: TransactionReceipt) => { +// // Done; return it! +// if ((await receipt.confirmations()) >= confirms) { +// cancel(); +// try { +// resolve(checkReceipt(receipt)); +// } catch (error) { reject(error); } +// } +// }; +// cancellers.push(() => { this.provider.off(this.hash, txListener); }); +// this.provider.on(this.hash, txListener); +// // We support replacement detection; start checking +// if (startBlock >= 0) { +// const replaceListener = async () => { +// try { +// // Check for a replacement; this throws only if one is found +// await checkReplacement(); +// +// } catch (error) { +// // We were replaced (with enough confirms); re-throw the error +// if (isError(error, "TRANSACTION_REPLACED")) { +// cancel(); +// reject(error); +// return; +// } +// } +// +// // Rescheudle a check on the next block +// if (!stopScanning) { +// this.provider.once("block", replaceListener); +// } +// }; +// cancellers.push(() => { this.provider.off("block", replaceListener); }); +// this.provider.once("block", replaceListener); +// } +// }); +// +// return await >waiter; +// } + + /** + * Returns ``true`` if this transaction has been included. + * + * This is effective only as of the time the TransactionResponse + * was instantiated. To get up-to-date information, use + * [[getTransaction]]. + * + * This provides a Type Guard that this transaction will have + * non-null property values for properties that are null for + * unmined transactions. + */ + isMined(): this is QiMinedTransactionResponse { + return (this.blockHash != null); + } + + /** + * Returns a filter which can be used to listen for orphan events + * that evict this transaction. + */ + removedEvent(): OrphanFilter { + assert(this.isMined(), "unmined transaction canot be orphaned", + "UNSUPPORTED_OPERATION", { operation: "removeEvent()" }); + return createRemovedTransactionFilter(this); + } + + /** + * Returns a filter which can be used to listen for orphan events + * that re-order this event against %%other%%. + */ + reorderedEvent(other?: TransactionResponse): OrphanFilter { + assert(this.isMined(), "unmined transaction canot be orphaned", + "UNSUPPORTED_OPERATION", { operation: "removeEvent()" }); + + assert(!other || other.isMined(), "unmined 'other' transaction canot be orphaned", + "UNSUPPORTED_OPERATION", { operation: "removeEvent()" }); + + return createReorderedTransactionFilter(this, other); + } + + /** + * Returns a new TransactionResponse instance which has the ability to + * detect (and throw an error) if the transaction is replaced, which + * will begin scanning at %%startBlock%%. + * + * This should generally not be used by developers and is intended + * primarily for internal use. Setting an incorrect %%startBlock%% can + * have devastating performance consequences if used incorrectly. + */ + replaceableTransaction(startBlock: number): QiTransactionResponse { + assertArgument(Number.isInteger(startBlock) && startBlock >= 0, "invalid startBlock", "startBlock", startBlock); + const tx = new QiTransactionResponse(this, this.provider); tx.#startBlock = startBlock; return tx; } @@ -1908,6 +2369,11 @@ export interface Provider extends ContractRunner, EventEmitterable; + /** + * Get a work object to package a transaction in. + */ + getPendingHeader(): Promise; + //////////////////// // Account @@ -1923,7 +2389,7 @@ export interface Provider extends ContractRunner, EventEmitterable; /** - * Get the UTXO entries for %%address%%. + * Get the UTXO entries for %%address%%. * * @note On nodes without archive access enabled, the %%blockTag%% may be * **silently ignored** by the node, which may cause issues if relied on. @@ -1978,7 +2444,7 @@ export interface Provider extends ContractRunner, EventEmitterable; + broadcastTransaction(shard: string, signedTx: string, from?: AddressLike): Promise; //////////////////// diff --git a/src.ts/providers/signer-noncemanager.ts b/src.ts/providers/signer-noncemanager.ts index 724bfe89..1590394c 100644 --- a/src.ts/providers/signer-noncemanager.ts +++ b/src.ts/providers/signer-noncemanager.ts @@ -4,9 +4,11 @@ import { AbstractSigner } from "./abstract-signer.js"; import type { TypedDataDomain, TypedDataField } from "../hash/index.js"; import type { - BlockTag, Provider, TransactionRequest, TransactionResponse + BlockTag, Provider, QuaiTransactionRequest, TransactionRequest } from "./provider.js"; import type { Signer } from "./signer.js"; +import {QuaiTransactionResponse} from "./provider.js"; +import {QuaiTransactionLike} from "../transaction/quai-transaction"; /** @@ -72,16 +74,16 @@ export class NonceManager extends AbstractSigner { this.#noncePromise = null; } - async sendTransaction(tx: TransactionRequest): Promise { + async sendTransaction(tx: QuaiTransactionRequest): Promise { const noncePromise = this.getNonce("pending"); this.increment(); - tx = await this.signer.populateTransaction(tx); + tx = await this.signer.populateQuaiTransaction(tx) as QuaiTransactionLike; tx.nonce = await noncePromise; // @TODO: Maybe handle interesting/recoverable errors? // Like don't increment if the tx was certainly not sent - return await this.signer.sendTransaction(tx); + return await this.signer.sendTransaction(tx) as QuaiTransactionResponse; } signTransaction(tx: TransactionRequest): Promise { diff --git a/src.ts/providers/signer.ts b/src.ts/providers/signer.ts index bc1f3b47..771cc37c 100644 --- a/src.ts/providers/signer.ts +++ b/src.ts/providers/signer.ts @@ -55,7 +55,7 @@ export interface Signer extends Addressable, ContractRunner { * * @param tx - The call to prepare */ - populateCall(tx: TransactionRequest): Promise>; + populateCall(tx: TransactionRequest): Promise; /** * Prepares a {@link TransactionRequest} for sending to the network by @@ -74,7 +74,7 @@ export interface Signer extends Addressable, ContractRunner { * * @param tx - The call to prepare */ - populateTransaction(tx: TransactionRequest): Promise>; + populateQuaiTransaction(tx: TransactionRequest): Promise; //////////////////// diff --git a/src.ts/quais.ts b/src.ts/quais.ts index a1b738bd..98db6351 100644 --- a/src.ts/quais.ts +++ b/src.ts/quais.ts @@ -84,7 +84,7 @@ export { export { accessListify, computeAddress, recoverAddress, - Transaction, FewestCoinSelector + AbstractTransaction, FewestCoinSelector } from "./transaction/index.js"; export { diff --git a/src.ts/transaction/abstract-coinselector.ts b/src.ts/transaction/abstract-coinselector.ts index 422d0130..2456d208 100644 --- a/src.ts/transaction/abstract-coinselector.ts +++ b/src.ts/transaction/abstract-coinselector.ts @@ -29,7 +29,7 @@ export abstract class AbstractCoinSelector { get availableUXTOs(): UTXO[] { return this.#availableUXTOs; } set availableUXTOs(value: UTXOLike[]) { - this.#availableUXTOs = value.map((val: UTXOLike) => { + this.#availableUXTOs = value.map((val) => { const utxo = UTXO.from(val); this._validateUTXO(utxo); return utxo; @@ -38,12 +38,12 @@ export abstract class AbstractCoinSelector { get spendOutputs(): UTXO[] { return this.#spendOutputs; } set spendOutputs(value: UTXOLike[]) { - this.#spendOutputs = value.map((utxo: UTXOLike) => UTXO.from(utxo)); + this.#spendOutputs = value.map((utxo) => UTXO.from(utxo)); } get changeOutputs(): UTXO[] { return this.#changeOutputs; } set changeOutputs(value: UTXOLike[]) { - this.#changeOutputs = value.map((utxo: UTXOLike) => UTXO.from(utxo)); + this.#changeOutputs = value.map((utxo) => UTXO.from(utxo)); } /** diff --git a/src.ts/transaction/abstract-transaction.ts b/src.ts/transaction/abstract-transaction.ts new file mode 100644 index 00000000..bdc5220c --- /dev/null +++ b/src.ts/transaction/abstract-transaction.ts @@ -0,0 +1,243 @@ + +import { Signature } from "../crypto/index.js"; +import { + getBigInt, + assert, assertArgument +} from "../utils/index.js"; + +import type { BigNumberish } from "../utils/index.js"; +import type { SignatureLike } from "../crypto/index.js"; +import { encodeProtoTransaction } from "../utils/proto-encode.js"; +import type {TxInput, TxOutput} from "./utxo.js"; + +export interface TransactionLike { + /** + * The type. + */ + type: null | number; + + /** + * The chain ID the transaction is valid on. + */ + chainId?: null | BigNumberish; + + /** + * The signature for the transaction + */ + + signature?: null | SignatureLike; + + hash?: null | string; +} + +export interface ProtoTransaction { + type: number + to?: Uint8Array + nonce?: number + value?: Uint8Array + gas?: number + data?: Uint8Array + chain_id: Uint8Array + gas_fee_cap?: Uint8Array + gas_tip_cap?: Uint8Array + access_list?: ProtoAccessList + etx_gas_limit?: number + etx_gas_price?: Uint8Array + etx_gas_tip?: Uint8Array + etx_data?: Uint8Array + etx_access_list?: ProtoAccessList + v?: Uint8Array + r?: Uint8Array + s?: Uint8Array + originating_tx_hash?: string + etx_index?: number + etx_sender?: Uint8Array + tx_ins?: Array + tx_outs?: Array + signature?: Uint8Array +} + +export interface ProtoAccessList { + access_tuples: Array +} + +export interface ProtoAccessTuple { + address: Uint8Array + storage_key: Array +} + + +/** + * A **Transaction** describes an operation to be executed on + * Ethereum by an Externally Owned Account (EOA). It includes + * who (the [[to]] address), what (the [[data]]) and how much (the + * [[value]] in ether) the operation should entail. + * + * @example: + * tx = new Transaction() + * //_result: + * + * tx.data = "0x1234"; + * //_result: + */ +type allowedSignatureTypes = Signature | string +export abstract class AbstractTransaction implements TransactionLike { + protected _type: number | null; + protected _signature: null | S; + protected _chainId: bigint; + + /** + * The transaction type. + * + * If null, the type will be automatically inferred based on + * explicit properties. + */ + get type(): null | number { return this._type; } + set type(value: null | number | string) { + switch (value) { + case null: + this._type = null; + break; + case 0: case "standard": + this._type = 0; + break; + // case 1: case "external": + // this.#type = 1; + // break; + case 2: case "utxo": + this._type = 2; + break; + default: + assertArgument(false, "unsupported transaction type", "type", value); + } + } + + /** + * The name of the transaction type. + */ + get typeName(): null | string { + switch (this.type) { + case 0: return "standard"; + case 1: return "external"; + case 2: return "utxo"; + } + + return null; + } + + + /** + * The chain ID this transaction is valid on. + */ + get chainId(): bigint { return this._chainId; } + set chainId(value: BigNumberish) { this._chainId = getBigInt(value); } + + /** + * If signed, the signature for this transaction. + */ + get signature(): S { return (this._signature || null) as S; } + set signature(value: S) { + if (typeof value === 'string') { + this._signature = value as S + } else { + this._signature = ((value == null) ? null : Signature.from(value)) as S; + } + } + /** + * Creates a new Transaction with default values. + */ + constructor() { + this._type = null; + this._chainId = BigInt(0); + this._signature = null; + } + + /** + * The pre-image hash of this transaction. + * + * This is the digest that a [[Signer]] must sign to authorize + * this transaction. + */ + get unsignedHash(): string { + throw new Error("Method not implemented."); + } + + /** + * Returns true if signed. + * + * This provides a Type Guard that properties requiring a signed + * transaction are non-null. + */ + isSigned(): this is (AbstractTransaction & { type: number, typeName: string, from: string, signature: Signature }) { + //isSigned(): this is SignedTransaction { + return this.signature != null; + } + + /** + * The serialized transaction. + * + * This throws if the transaction is unsigned. For the pre-image, + * use [[unsignedSerialized]]. + */ + get serialized(): string { + assert(this.signature != null, "cannot serialize unsigned transaction; maybe you meant .unsignedSerialized", "UNSUPPORTED_OPERATION", { operation: ".serialized" }); + return this.#serialize(); + } + + /** + * The transaction pre-image. + * + * The hash of this is the digest which needs to be signed to + * authorize this transaction. + */ + get unsignedSerialized(): string { + return this.#serialize(); + } + + /** + * Return the most "likely" type; currently the highest + * supported transaction type. + */ + inferType(): number { + return (this.inferTypes().pop()); + } + + /** + * Validates the explicit properties and returns a list of compatible + * transaction types. + */ + abstract inferTypes(): Array + + /** + * Create a copy of this transaciton. + */ + abstract clone(): AbstractTransaction + + /** + * Return a JSON-friendly object. + */ + abstract toJSON(): TransactionLike + + /** + * Return a protobuf-friendly JSON object. + */ + abstract toProtobuf(): ProtoTransaction + + abstract get originShard(): string | undefined + + abstract get destShard(): string | undefined + + get isExternal(): boolean { + return this.originShard !== this.destShard + } + + /** + * Serializes the WorkObject to a string. + * + * @returns The serialized string representation of the WorkObject. + */ + #serialize(): string { + return encodeProtoTransaction(this.toProtobuf()); + } +} + diff --git a/src.ts/transaction/index.ts b/src.ts/transaction/index.ts index 588ea239..598569f3 100644 --- a/src.ts/transaction/index.ts +++ b/src.ts/transaction/index.ts @@ -26,10 +26,10 @@ export type AccessListish = AccessList | export { accessListify } from "./accesslist.js"; export { computeAddress, recoverAddress } from "./address.js"; -export { Transaction } from "./transaction.js"; +export { AbstractTransaction } from "./abstract-transaction.js"; export { FewestCoinSelector } from "./coinselector-fewest.js"; -export type { TransactionLike } from "./transaction.js"; +export type { TransactionLike } from "./abstract-transaction.js"; export type {TxInput, TxOutput} from "./utxo.js"; \ No newline at end of file diff --git a/src.ts/transaction/qi-transaction.ts b/src.ts/transaction/qi-transaction.ts new file mode 100644 index 00000000..68aff334 --- /dev/null +++ b/src.ts/transaction/qi-transaction.ts @@ -0,0 +1,220 @@ +import {keccak256} from "../crypto"; +import {AbstractTransaction, computeAddress, TransactionLike, TxInput, TxOutput} from "./index"; +import { + assertArgument, + decodeProtoTransaction, + getBytes, getShardForAddress, + hexlify, isUTXOAddress, + toBigInt +} from "../utils"; +import {formatNumber} from "../providers/format"; +import { ProtoTransaction} from "./abstract-transaction"; + +export interface QiTransactionLike extends TransactionLike{ + + txInputs?: null | TxInput[]; + txOutputs?: null | TxOutput[]; + +} + +export class QiTransaction extends AbstractTransaction implements QiTransactionLike { + + #txInputs?: null | TxInput[]; + #txOutputs?: null | TxOutput[]; + #hash: null | string; + + get txInputs(): TxInput[] { + return (this.#txInputs ?? []).map(entry => ({...entry})); + } + set txInputs(value: TxInput[]) { + this.#txInputs = value.map(entry => ({...entry})); + } + get txOutputs(): TxOutput[] { + return (this.#txOutputs ?? []).map(output => ({...output})); + } + set txOutputs(value: TxOutput[]) { + this.#txOutputs = value.map(output => ({...output})); + } + + get hash(): null | string { + if (this.signature == null) { return null; } + if (this.#hash) { return this.#hash; } + return keccak256(this.serialized); + } + set hash(value: null | string) { + this.#hash = value; + } + + get unsignedHash(): string { + return keccak256(this.unsignedSerialized); + } + + get originShard(): string | undefined { + const pubKey = hexlify(this.txInputs[0].pubKey); + const senderAddr = computeAddress(pubKey || "") + + return getShardForAddress(senderAddr)?.byte.slice(2); + } + + get destShard(): string | undefined { + return getShardForAddress(this.txOutputs[0].Address || "")?.byte.slice(2); + } + + getTransactionHash (data: Uint8Array): string { + if (this.txInputs.length < 1 || this.txOutputs.length < 1) { + throw new Error("Transaction must have at least one input and one output"); + } + const destUtxo = isUTXOAddress(this.txOutputs[0].Address || ""); + + const pubKey = hexlify(this.txInputs[0].pubKey); + const senderAddr = computeAddress(pubKey || "") + + const originUtxo = isUTXOAddress(senderAddr); + + if (!this.destShard|| !this.originShard) { + throw new Error("Invalid Shard for from or to address"); + } + if(this.isExternal && destUtxo !== originUtxo) { + throw new Error("Cross-shard & cross-ledger transactions are not supported"); + } + + let hash = keccak256(data) + hash = '0x' + this.originShard+ (originUtxo ? 'F' : '1') + hash.charAt(5) + this.originShard+ (destUtxo ? 'F' : '1') + hash.slice(9) + + //TODO alter comparison + return hash; + + } + + + /** + * Creates a new Transaction with default values. + */ + constructor() { + super(); + this.#txInputs = []; + this.#txOutputs = []; + this.#hash = null; + } + + + /** + * Validates the explicit properties and returns a list of compatible + * transaction types. + */ + inferTypes(): Array { + + const types: Array = []; + + // Explicit type + if (this.type != null) { + types.push(this.type); + + } else { + types.push(2); + + } + + types.sort(); + + return types; + } + + /** + * Create a copy of this transaciton. + */ + clone(): QiTransaction { + return QiTransaction.from(this); + } + + /** + * Return a JSON-friendly object. + */ + toJSON(): TransactionLike { + const s = (v: null | bigint) => { + if (v == null) { return null; } + return v.toString(); + }; + + return { + type: this.type, + chainId: s(this.chainId), + signature: this.signature ? this.signature : null, + hash: this.hash, + txInputs: this.txInputs, + txOutputs: this.txOutputs, + } as QiTransactionLike; + } + + /** + * Return a protobuf-friendly JSON object. + */ + toProtobuf(): ProtoTransaction { + const protoTx: ProtoTransaction = { + type: (this.type || 2), + chain_id: formatNumber(this.chainId || 0, "chainId"), + tx_ins: this.txInputs, + tx_outs: this.txOutputs, + } + + if (this.signature) { + protoTx.signature = getBytes(this.signature) + } + return protoTx; + } + + /** + * Create a **Transaction** from a serialized transaction or a + * Transaction-like object. + */ + static from(tx: string | QiTransactionLike): QiTransaction { + if (typeof (tx) === "string") { + const decodedProtoTx: ProtoTransaction = decodeProtoTransaction(getBytes(tx)); + const payload = getBytes(tx); + return QiTransaction.fromProto(decodedProtoTx, payload); + } + + const result = new QiTransaction(); + if (tx.type != null) { result.type = tx.type; } + if (tx.chainId != null) { result.chainId = tx.chainId; } + if (tx.signature != null) { result.signature = tx.signature as string; } + if (tx.txInputs != null) { result.txInputs = tx.txInputs as TxInput[]; } + if (tx.txOutputs != null) { result.txOutputs = tx.txOutputs as TxOutput[]; } + + if (tx.hash != null) { + assertArgument(result.isSigned(), "unsigned transaction cannot define hash", "tx", tx); + result.hash = tx.hash; + } + + return result; + } + + /** + * Create a **Transaction** from a ProtoTransaction object. + */ + static fromProto(protoTx: ProtoTransaction, payload?: Uint8Array): QiTransaction { + + // TODO: Fix this because new tx instance requires a 'from' address + // if (this.signature == null) { return null; } + const tx = new QiTransaction(); + + tx.type = protoTx.type; + tx.chainId = toBigInt(protoTx.chain_id); + + if (protoTx.type == 2) { + tx.txInputs = protoTx.tx_ins ?? [] + tx.txOutputs = protoTx.tx_outs ?? [] + } + + if (protoTx.signature) { + tx.signature = hexlify(protoTx.signature); + } + + if (payload) { + tx.hash = tx.getTransactionHash(payload); + } + + + return tx; + } +} diff --git a/src.ts/transaction/quai-transaction.ts b/src.ts/transaction/quai-transaction.ts new file mode 100644 index 00000000..21a69de5 --- /dev/null +++ b/src.ts/transaction/quai-transaction.ts @@ -0,0 +1,439 @@ +import {keccak256, Signature,} from "../crypto"; +import {AccessList, accessListify, AccessListish, AbstractTransaction, TransactionLike, recoverAddress} from "./index"; +import { + assert, + assertArgument, + BigNumberish, + BytesLike, decodeProtoTransaction, + encodeProtoTransaction, + getBigInt, + getBytes, + getNumber, + getShardForAddress, + hexlify, isUTXOAddress, + toBeArray, toBigInt, zeroPadValue +} from "../utils"; +import {getAddress} from "../address"; +import {formatNumber, handleNumber} from "../providers/format"; +import { ProtoTransaction} from "./abstract-transaction"; + +export interface QuaiTransactionLike extends TransactionLike{ + + /** + * The recipient address or ``null`` for an ``init`` transaction. + */ + to?: null | string; + + /** + * The sender. + */ + from: string; + /** + * The nonce. + */ + nonce?: null | number; + + /** + * The maximum amount of gas that can be used. + */ + gasLimit?: null | BigNumberish; + + /** + * The gas price for legacy and berlin transactions. + */ + gasPrice?: null | BigNumberish; + + /** + * The maximum priority fee per gas for london transactions. + */ + maxPriorityFeePerGas?: null | BigNumberish; + + /** + * The maximum total fee per gas for london transactions. + */ + maxFeePerGas?: null | BigNumberish; + + /** + * The data. + */ + data?: null | string; + + /** + * The value (in wei) to send. + */ + value?: null | BigNumberish; + + /** + * The access list for berlin and london transactions. + */ + accessList?: null | AccessListish; + +} + +export function _parseSignature(fields: Array): Signature { + let yParity: number; + try { + yParity = handleNumber(fields[0], "yParity"); + if (yParity !== 0 && yParity !== 1) { throw new Error("bad yParity"); } + } catch (error) { + assertArgument(false, "invalid yParity", "yParity", fields[0]); + } + + const r = zeroPadValue(fields[1], 32); + const s = zeroPadValue(fields[2], 32); + + return Signature.from({ r, s, yParity }); +} + +export class QuaiTransaction extends AbstractTransaction implements QuaiTransactionLike { + #to: null | string; + #data: string; + #nonce: number; + #gasLimit: bigint; + #gasPrice: null | bigint; + #maxPriorityFeePerGas: null | bigint; + #maxFeePerGas: null | bigint; + #value: bigint; + #accessList: null | AccessList; + #hash: null | string; + from: string; + + /** + * The ``to`` address for the transaction or ``null`` if the + * transaction is an ``init`` transaction. + */ + get to(): null | string { return this.#to; } + set to(value: null | string) { + this.#to = (value == null) ? null : getAddress(value); + } + + get hash(): null | string { + if (this.signature == null) { return null; } + if (this.#hash) { return this.#hash; } + return keccak256(this.serialized); + } + set hash(value: null | string) { + this.#hash = value; + } + + get unsignedHash(): string { + return keccak256(this.unsignedSerialized); + } + + get originShard(): string | undefined { + const senderAddr = this.from + + return getShardForAddress(senderAddr)?.byte.slice(2); + } + + get destShard(): string | undefined { + return getShardForAddress(this.to || "")?.byte.slice(2); + } + + getTransactionHash (data: Uint8Array): string { + const destUtxo = isUTXOAddress(this.to || ""); + + const originUtxo = isUTXOAddress(this.from); + + if (!this.destShard|| !this.originShard) { + throw new Error("Invalid Shard for from or to address"); + } + if(this.isExternal && destUtxo !== originUtxo) { + throw new Error("Cross-shard & cross-ledger transactions are not supported"); + } + + let hash = keccak256(data) + hash = '0x' + this.originShard+ (originUtxo ? 'F' : '1') + hash.charAt(5) + this.originShard+ (destUtxo ? 'F' : '1') + hash.slice(9) + + //TODO alter comparison + return hash; + + } + + /** + * The transaction nonce. + */ + get nonce(): number { return this.#nonce; } + set nonce(value: BigNumberish) { this.#nonce = getNumber(value, "value"); } + + /** + * The gas limit. + */ + get gasLimit(): bigint { return this.#gasLimit; } + set gasLimit(value: BigNumberish) { this.#gasLimit = getBigInt(value); } + + /** + * The gas price. + * + * On legacy networks this defines the fee that will be paid. On + * EIP-1559 networks, this should be ``null``. + */ + get gasPrice(): null | bigint { + const value = this.#gasPrice; + return value; + } + set gasPrice(value: null | BigNumberish) { + this.#gasPrice = (value == null) ? null : getBigInt(value, "gasPrice"); + } + + /** + * The maximum priority fee per unit of gas to pay. On legacy + * networks this should be ``null``. + */ + get maxPriorityFeePerGas(): null | bigint { + const value = this.#maxPriorityFeePerGas; + if (value == null) { + return null; + } + return value; + } + set maxPriorityFeePerGas(value: null | BigNumberish) { + this.#maxPriorityFeePerGas = (value == null) ? null : getBigInt(value, "maxPriorityFeePerGas"); + } + + /** + * The maximum total fee per unit of gas to pay. On legacy + * networks this should be ``null``. + */ + get maxFeePerGas(): null | bigint { + const value = this.#maxFeePerGas; + if (value == null) { + return null; + } + return value; + } + set maxFeePerGas(value: null | BigNumberish) { + this.#maxFeePerGas = (value == null) ? null : getBigInt(value, "maxFeePerGas"); + } + + /** + * The transaction data. For ``init`` transactions this is the + * deployment code. + */ + get data(): string { return this.#data; } + set data(value: BytesLike) { this.#data = hexlify(value); } + + /** + * The amount of ether to send in this transactions. + */ + get value(): bigint { return this.#value; } + set value(value: BigNumberish) { + this.#value = getBigInt(value, "value"); + } + + /** + * The access list. + * + * An access list permits discounted (but pre-paid) access to + * bytecode and state variable access within contract execution. + */ + get accessList(): null | AccessList { + const value = this.#accessList || null; + if (value == null) { + return null; + } + return value; + } + set accessList(value: null | AccessListish) { + this.#accessList = (value == null) ? null : accessListify(value); + } + + + /** + * Creates a new Transaction with default values. + */ + constructor(from: string) { + super(); + this.#to = null; + this.#nonce = 0; + this.#gasLimit = BigInt(0); + this.#gasPrice = null; + this.#maxPriorityFeePerGas = null; + this.#maxFeePerGas = null; + this.#data = "0x"; + this.#value = BigInt(0); + this.#accessList = null; + this.#hash = null; + this.from = from + } + + /** + * Validates the explicit properties and returns a list of compatible + * transaction types. + */ + inferTypes(): Array { + + + if (this.maxFeePerGas != null && this.maxPriorityFeePerGas != null) { + assert(this.maxFeePerGas >= this.maxPriorityFeePerGas, "priorityFee cannot be more than maxFee", "BAD_DATA", { value: this }); + } + + assert((this.type !== 0 && this.type !== 1), "transaction type cannot have externalGasLimit, externalGasTip, externalGasPrice, externalData, or externalAccessList", "BAD_DATA", { value: this }); + + const types: Array = []; + + // Explicit type + if (this.type != null) { + types.push(this.type); + + } else { + types.push(0); + + } + + types.sort(); + + return types; + } + + /** + * Create a copy of this transaciton. + */ + clone(): QuaiTransaction { + return QuaiTransaction.from(this); + } + + /** + * Return a JSON-friendly object. + */ + toJSON(): QuaiTransactionLike { + const s = (v: null | bigint) => { + if (v == null) { return null; } + return v.toString(); + }; + + + return { + type: this.type, + to: this.to, + from: this.from, + data: this.data, + nonce: this.nonce, + gasLimit: s(this.gasLimit), + gasPrice: s(this.gasPrice), + maxPriorityFeePerGas: s(this.maxPriorityFeePerGas), + maxFeePerGas: s(this.maxFeePerGas), + value: s(this.value), + chainId: s(this.chainId), + signature: this.signature ? this.signature.toJSON() : null, + hash: this.hash, + accessList: this.accessList, + } as QuaiTransactionLike; + } + + /** + * Return a protobuf-friendly JSON object. + */ + toProtobuf(): ProtoTransaction { + const protoTx: ProtoTransaction = { + type: (this.type || 0), + chain_id: formatNumber(this.chainId || 0, "chainId"), + nonce: (this.nonce || 0), + gas_tip_cap: formatNumber(this.maxPriorityFeePerGas || 0, "maxPriorityFeePerGas"), + gas_fee_cap: formatNumber(this.maxFeePerGas || 0, "maxFeePerGas"), + gas: Number(this.gasLimit || 0), + to: this.to != null ? getBytes(this.to as string) : new Uint8Array(0), + value: formatNumber(this.value || 0, "value"), + data: getBytes(this.data || "0x"), + access_list: { access_tuples: [] }, + } + + if (this.signature) { + protoTx.v = formatNumber(this.signature.yParity, "yParity") + protoTx.r = toBeArray(this.signature.r) + protoTx.s = toBeArray(this.signature.s) + } + return protoTx; + } + + /** + * Create a **Transaction** from a serialized transaction or a + * Transaction-like object. + */ + static from(tx: string | QuaiTransactionLike): QuaiTransaction { + if (typeof (tx) === "string") { + const decodedProtoTx: ProtoTransaction = decodeProtoTransaction(getBytes(tx)); + const payload = getBytes(tx); + return QuaiTransaction.fromProto(decodedProtoTx, payload); + } + + const result = new QuaiTransaction(tx.from); + if (tx.type != null) { result.type = tx.type; } + if (tx.to != null) { result.to = tx.to; } + if (tx.nonce != null) { result.nonce = tx.nonce; } + if (tx.gasLimit != null) { result.gasLimit = tx.gasLimit; } + if (tx.maxPriorityFeePerGas != null) { result.maxPriorityFeePerGas = tx.maxPriorityFeePerGas; } + if (tx.maxFeePerGas != null) { result.maxFeePerGas = tx.maxFeePerGas; } + if (tx.data != null) { result.data = tx.data; } + if (tx.value != null) { result.value = tx.value; } + if (tx.chainId != null) { result.chainId = tx.chainId; } + if (tx.signature != null) { result.signature = Signature.from(tx.signature); } + if (tx.accessList != null) { result.accessList = tx.accessList; } + + + if (tx.hash != null) { + assertArgument(result.isSigned(), "unsigned transaction cannot define hash", "tx", tx); + result.hash = tx.hash; + } + + if (tx.from != null) { + assertArgument(result.from.toLowerCase() === (tx.from || "").toLowerCase(), "from mismatch", "tx", tx); + result.from = tx.from; + } + return result; + } + + /** + * Create a **Transaction** from a ProtoTransaction object. + */ + static fromProto(protoTx: ProtoTransaction, payload?: Uint8Array): QuaiTransaction { + + // TODO: Fix this because new tx instance requires a 'from' address + let signature: null | Signature = null + let address + if (protoTx.v && protoTx.r && protoTx.s) { + const signatureFields = [ + hexlify(protoTx.v!), + hexlify(protoTx.r!), + hexlify(protoTx.s!), + ]; + signature = _parseSignature(signatureFields); + + const protoTxCopy = structuredClone(protoTx) + + delete protoTxCopy.v + delete protoTxCopy.r + delete protoTxCopy.s + delete protoTxCopy.signature + delete protoTxCopy.etx_sender + delete protoTxCopy.etx_index + + address = recoverAddress(keccak256(encodeProtoTransaction(protoTxCopy)), signature); + } else { + address = "" + } + + const tx = new QuaiTransaction(address); + + if (signature) { + tx.signature = signature; + } + tx.type = protoTx.type; + tx.chainId = toBigInt(protoTx.chain_id); + tx.nonce = Number(protoTx.nonce); + tx.maxPriorityFeePerGas = toBigInt(protoTx.gas_tip_cap!); + tx.maxFeePerGas = toBigInt(protoTx.gas_fee_cap!); + tx.gasLimit = toBigInt(protoTx.gas!); + tx.to = hexlify(protoTx.to!); + tx.value = toBigInt(protoTx.value!); + tx.data = hexlify(protoTx.data!); + tx.accessList = protoTx.access_list!.access_tuples.map(tuple => ({ + address: hexlify(tuple.address), + storageKeys: tuple.storage_key.map(key => hexlify(key)) + })); + if (payload) { + tx.hash = tx.getTransactionHash(payload); + } + return tx; + } +} diff --git a/src.ts/transaction/transaction.ts b/src.ts/transaction/transaction.ts deleted file mode 100644 index c81e16f4..00000000 --- a/src.ts/transaction/transaction.ts +++ /dev/null @@ -1,621 +0,0 @@ - -import {getAddress} from "../address/index.js"; -import { keccak256, Signature, SigningKey } from "../crypto/index.js"; -import { - getBytes, getBigInt, getNumber, hexlify, - assert, assertArgument, toBeArray, zeroPadValue, encodeProto, decodeProto, toBigInt, getShardForAddress, isUTXOAddress -} from "../utils/index.js"; - -import { accessListify } from "./accesslist.js"; -import { computeAddress, recoverAddress } from "./address.js"; - -import type { BigNumberish, BytesLike } from "../utils/index.js"; -import type { SignatureLike } from "../crypto/index.js"; -import type { AccessList, AccessListish } from "./index.js"; -import type { TxInput, TxOutput } from "./utxo.js"; - -export interface TransactionLike { - /** - * The type. - */ - type?: null | number; - - /** - * The recipient address or ``null`` for an ``init`` transaction. - */ - to?: null | A; - - /** - * The sender. - */ - from: A; - /** - * The nonce. - */ - nonce?: null | number; - - /** - * The maximum amount of gas that can be used. - */ - gasLimit?: null | BigNumberish; - - /** - * The gas price for legacy and berlin transactions. - */ - gasPrice?: null | BigNumberish; - - /** - * The maximum priority fee per gas for london transactions. - */ - maxPriorityFeePerGas?: null | BigNumberish; - - /** - * The maximum total fee per gas for london transactions. - */ - maxFeePerGas?: null | BigNumberish; - - /** - * The data. - */ - data?: null | string; - - /** - * The value (in wei) to send. - */ - value?: null | BigNumberish; - - /** - * The chain ID the transaction is valid on. - */ - chainId?: null | BigNumberish; - - /** - * The transaction hash. - */ - hash?: null | string; - - /** - * The signature provided by the sender. - */ - signature?: null | SignatureLike; - - /** - * The access list for berlin and london transactions. - */ - accessList?: null | AccessListish; - - - inputsUTXO?: null | Array; - - outputsUTXO?: null | Array; -} - -function handleNumber(_value: string, param: string): number { - if (_value === "0x") { return 0; } - return getNumber(_value, param); -} - -function formatNumber(_value: BigNumberish, name: string): Uint8Array { - const value = getBigInt(_value, "value"); - const result = toBeArray(value); - assertArgument(result.length <= 32, `value too large`, `tx.${ name }`, value); - return result; -} - -function _parseSignature(tx: TransactionLike, fields: Array): void { - let yParity: number; - try { - yParity = handleNumber(fields[0], "yParity"); - if (yParity !== 0 && yParity !== 1) { throw new Error("bad yParity"); } - } catch (error) { - assertArgument(false, "invalid yParity", "yParity", fields[0]); - } - - const r = zeroPadValue(fields[1], 32); - const s = zeroPadValue(fields[2], 32); - - const signature = Signature.from({ r, s, yParity }); - tx.signature = signature; -} - -function _parse(data: Uint8Array): TransactionLike { - const decodedTx: any = decodeProto(getBytes(data)); - const tx: TransactionLike = { - type: decodedTx.type, - from: decodedTx.from, - chainId: toBigInt(decodedTx.chain_id), - nonce: decodedTx.nonce, - maxPriorityFeePerGas: toBigInt(decodedTx.gas_tip_cap), - maxFeePerGas: toBigInt(decodedTx.gas_fee_cap), - gasLimit: toBigInt(decodedTx.gas), - to: hexlify(decodedTx.to), - value: toBigInt(decodedTx.value), - data: hexlify(decodedTx.data), - accessList: decodedTx.access_list.access_tuples , - }; - - const signatureFields = [ - hexlify(decodedTx.v), - hexlify(decodedTx.r), - hexlify(decodedTx.s), - ] - - _parseSignature(tx, signatureFields); - - tx.hash = getTransactionHash(tx, data); - - // Compute the sender address since it's not included in the transaction - tx.from = recoverAddress(tx.hash, tx.signature!); - - return tx; -} - -function getTransactionHash (tx: TransactionLike, data: Uint8Array): string { - const destShardbyte = getShardForAddress(tx.to || "")?.byte.slice(2); - const destUtxo = isUTXOAddress(tx.to || ""); - - const pubKey = Transaction.from(tx).fromPublicKey - const senderAddr = computeAddress(pubKey || "") - - const originShardByte = getShardForAddress(senderAddr)?.byte.slice(2); - const originUtxo = isUTXOAddress(senderAddr); - - if (!destShardbyte || !originShardByte) { - throw new Error("Invalid Shard for from or to address"); - } - if(destShardbyte !== originShardByte && destUtxo !== originUtxo) { - throw new Error("Cross-shard & cross-ledger transactions are not supported"); - } - - let hash = keccak256(data) - hash = '0x' + originShardByte + (originUtxo ? 'F' : '1') + hash.charAt(5) + originShardByte + (destUtxo ? 'F' : '1') + hash.slice(9) - - //TODO alter comparison - return hash; -} - - -function _serialize(tx: TransactionLike, sig?: Signature): string { - const formattedTx: any = { - chain_id: formatNumber(tx.chainId || 0, "chainId"), - nonce: (tx.nonce || 0), - gas_tip_cap: formatNumber(tx.maxPriorityFeePerGas || 0, "maxPriorityFeePerGas"), - gas_fee_cap: formatNumber(tx.maxFeePerGas || 0, "maxFeePerGas"), - gas: Number(tx.gasLimit || 0), - to: tx.to != null ? getBytes(tx.to) : "0x", - value: formatNumber(tx.value || 0, "value"), - data: getBytes(tx.data || "0x"), - access_list: {access_tuples: tx.accessList || []}, - type: (tx.type || 0), - } - - if (tx.type == 2){ - formattedTx.tx_ins = tx.inputsUTXO - formattedTx.tx_outs = tx.outputsUTXO - } - - if (sig) { - formattedTx.v = formatNumber(sig.yParity, "yParity"), - formattedTx.r = toBeArray(sig.r), - formattedTx.s = toBeArray(sig.s) - } - - return encodeProto(formattedTx); -} - -/** - * A **Transaction** describes an operation to be executed on - * Ethereum by an Externally Owned Account (EOA). It includes - * who (the [[to]] address), what (the [[data]]) and how much (the - * [[value]] in ether) the operation should entail. - * - * @example: - * tx = new Transaction() - * //_result: - * - * tx.data = "0x1234"; - * //_result: - */ -export class Transaction implements TransactionLike { - #type: null | number; - #to: null | string; - #data: string; - #nonce: number; - #gasLimit: bigint; - #gasPrice: null | bigint; - #maxPriorityFeePerGas: null | bigint; - #maxFeePerGas: null | bigint; - #value: bigint; - #chainId: bigint; - #sig: null | Signature; - #accessList: null | AccessList; - #hash: null | string; - #inputsUTXO: null | Array; - #outputsUTXO: null | Array; - from: string; - - /** - * The transaction type. - * - * If null, the type will be automatically inferred based on - * explicit properties. - */ - get type(): null | number { return this.#type; } - set type(value: null | number | string) { - switch (value) { - case null: - this.#type = null; - break; - case 0: case "standard": - this.#type = 0; - break; - // case 1: case "external": - // this.#type = 1; - // break; - case 2: case "utxo": - this.#type = 2; - break; - default: - assertArgument(false, "unsupported transaction type", "type", value); - } - } - - /** - * The name of the transaction type. - */ - get typeName(): null | string { - switch (this.type) { - case 0: return "standard"; - case 1: return "external"; - case 2: return "utxo"; - } - - return null; - } - - /** - * The ``to`` address for the transaction or ``null`` if the - * transaction is an ``init`` transaction. - */ - get to(): null | string { return this.#to; } - set to(value: null | string) { - this.#to = (value == null) ? null: getAddress(value); - } - - /** - * The transaction nonce. - */ - get nonce(): number { return this.#nonce; } - set nonce(value: BigNumberish) { this.#nonce = getNumber(value, "value"); } - - /** - * The gas limit. - */ - get gasLimit(): bigint { return this.#gasLimit; } - set gasLimit(value: BigNumberish) { this.#gasLimit = getBigInt(value); } - - /** - * The gas price. - * - * On legacy networks this defines the fee that will be paid. On - * EIP-1559 networks, this should be ``null``. - */ - get gasPrice(): null | bigint { - const value = this.#gasPrice; - return value; - } - set gasPrice(value: null | BigNumberish) { - this.#gasPrice = (value == null) ? null: getBigInt(value, "gasPrice"); - } - - /** - * The maximum priority fee per unit of gas to pay. On legacy - * networks this should be ``null``. - */ - get maxPriorityFeePerGas(): null | bigint { - const value = this.#maxPriorityFeePerGas; - if (value == null) { - return null; - } - return value; - } - set maxPriorityFeePerGas(value: null | BigNumberish) { - this.#maxPriorityFeePerGas = (value == null) ? null: getBigInt(value, "maxPriorityFeePerGas"); - } - - /** - * The maximum total fee per unit of gas to pay. On legacy - * networks this should be ``null``. - */ - get maxFeePerGas(): null | bigint { - const value = this.#maxFeePerGas; - if (value == null) { - return null; - } - return value; - } - set maxFeePerGas(value: null | BigNumberish) { - this.#maxFeePerGas = (value == null) ? null: getBigInt(value, "maxFeePerGas"); - } - - /** - * The transaction data. For ``init`` transactions this is the - * deployment code. - */ - get data(): string { return this.#data; } - set data(value: BytesLike) { this.#data = hexlify(value); } - - /** - * The amount of ether to send in this transactions. - */ - get value(): bigint { return this.#value; } - set value(value: BigNumberish) { - this.#value = getBigInt(value, "value"); - } - - /** - * The chain ID this transaction is valid on. - */ - get chainId(): bigint { return this.#chainId; } - set chainId(value: BigNumberish) { this.#chainId = getBigInt(value); } - - /** - * If signed, the signature for this transaction. - */ - get signature(): null | Signature { return this.#sig || null; } - set signature(value: null | SignatureLike) { - this.#sig = (value == null) ? null: Signature.from(value); - } - - /** - * The access list. - * - * An access list permits discounted (but pre-paid) access to - * bytecode and state variable access within contract execution. - */ - get accessList(): null | AccessList { - const value = this.#accessList || null; - if (value == null) { - return null; - } - return value; - } - set accessList(value: null | AccessListish) { - this.#accessList = (value == null) ? null: accessListify(value); - } - - - get inputsUTXO(): null | Array { return this.#inputsUTXO; } - set inputsUTXO(value: null | Array) { this.#inputsUTXO = value; } - - get outputsUTXO(): null | Array { return this.#outputsUTXO; } - set outputsUTXO(value: null | Array) { this.#outputsUTXO = value; } - - - /** - * Creates a new Transaction with default values. - */ - constructor(from: string) { - this.#type = null; - this.#to = null; - this.#nonce = 0; - this.#gasLimit = BigInt(0); - this.#gasPrice = null; - this.#maxPriorityFeePerGas = null; - this.#maxFeePerGas = null; - this.#data = "0x"; - this.#value = BigInt(0); - this.#chainId = BigInt(0); - this.#sig = null; - this.#accessList = null; - this.#hash = null; - this.#inputsUTXO = null; - this.#outputsUTXO = null; - this.from = from - } - - /** - * The transaction hash, if signed. Otherwise, ``null``. - */ - get hash(): null | string { - if (this.signature == null) { return null; } - if (this.#hash) { return this.#hash; } - return keccak256(this.serialized); - } - set hash(value: null | string) { - this.#hash = value; - } - - - /** - * The pre-image hash of this transaction. - * - * This is the digest that a [[Signer]] must sign to authorize - * this transaction. - */ - get unsignedHash(): string { - return keccak256(this.unsignedSerialized); - } - - /** - * The public key of the sender, if signed. Otherwise, ``null``. - */ - get fromPublicKey(): null | string { - if (this.signature == null) { return null; } - return SigningKey.recoverPublicKey(this.unsignedHash, this.signature); - } - - /** - * Returns true if signed. - * - * This provides a Type Guard that properties requiring a signed - * transaction are non-null. - */ - isSigned(): this is (Transaction & { type: number, typeName: string, from: string, signature: Signature }) { - //isSigned(): this is SignedTransaction { - return this.signature != null; - } - - /** - * The serialized transaction. - * - * This throws if the transaction is unsigned. For the pre-image, - * use [[unsignedSerialized]]. - */ - get serialized(): string { - assert(this.signature != null, "cannot serialize unsigned transaction; maybe you meant .unsignedSerialized", "UNSUPPORTED_OPERATION", { operation: ".serialized"}); - - return _serialize(this, this.signature); - } - - /** - * The transaction pre-image. - * - * The hash of this is the digest which needs to be signed to - * authorize this transaction. - */ - get unsignedSerialized(): string { - return _serialize(this); - } - - /** - * Return the most "likely" type; currently the highest - * supported transaction type. - */ - inferType(): number { - return (this.inferTypes().pop()); - } - - /** - * Validates the explicit properties and returns a list of compatible - * transaction types. - */ - inferTypes(): Array { - - - if (this.maxFeePerGas != null && this.maxPriorityFeePerGas != null) { - assert(this.maxFeePerGas >= this.maxPriorityFeePerGas, "priorityFee cannot be more than maxFee", "BAD_DATA", { value: this }); - } - - //if (this.type === 2 && hasGasPrice) { - // throw new Error("eip-1559 transaction cannot have gasPrice"); - //} - - assert((this.type !== 0 && this.type !== 1), "transaction type cannot have externalGasLimit, externalGasTip, externalGasPrice, externalData, or externalAccessList", "BAD_DATA", { value: this }); - - const types: Array = [ ]; - - // Explicit type - if (this.type != null) { - types.push(this.type); - - } else { - types.push(0); - - } - - types.sort(); - - return types; - } - - /** - * Create a copy of this transaciton. - */ - clone(): Transaction { - return Transaction.from(this); - } - - /** - * Return a JSON-friendly object. - */ - toJSON(): any { - const s = (v: null | bigint) => { - if (v == null) { return null; } - return v.toString(); - }; - - function processArrayWithBigInt(items: TxOutput[]): any[]; - function processArrayWithBigInt(items: TxInput[]): any[]; - - function processArrayWithBigInt(items: TxOutput[] | TxInput[]): any[] { - if (items.length === 0) { - return []; - } - - if ('Address' in items[0]) { - // Process as Output - return (items as TxOutput[]).map(({ Address, Denomination }) => ({ - Address, - Denomination: Denomination.toString() - })); - } else { - // Process as Input - return (items as TxInput[]).map(({ txhash, index, pubKey }) => ({ - txhash, - index, - pubKey: hexlify(pubKey) - })); - } - } - - return { - type: this.type, - to: this.to, - //from: this.from, - data: this.data, - nonce: this.nonce, - gasLimit: s(this.gasLimit), - gasPrice: s(this.gasPrice), - maxPriorityFeePerGas: s(this.maxPriorityFeePerGas), - maxFeePerGas: s(this.maxFeePerGas), - value: s(this.value), - chainId: s(this.chainId), - sig: this.signature ? this.signature.toJSON() : null, - accessList: this.accessList, - inputsUTXO: processArrayWithBigInt(this.inputsUTXO || []), - outputsUTXO: processArrayWithBigInt(this.outputsUTXO || []), - }; - } - - - /** - * Create a **Transaction** from a serialized transaction or a - * Transaction-like object. - */ - static from(tx: string | TransactionLike): Transaction { -// if (tx == null) { return new Transaction(); } - - if (typeof(tx) === "string") { - const payload = getBytes(tx); - return Transaction.from(_parse(payload)); - } - const result = new Transaction(tx.from); - if (tx.type != null) { result.type = tx.type; } - if (tx.to != null) { result.to = tx.to; } - if (tx.nonce != null) { result.nonce = tx.nonce; } - if (tx.gasLimit != null) { result.gasLimit = tx.gasLimit; } - if (tx.maxPriorityFeePerGas != null) { result.maxPriorityFeePerGas = tx.maxPriorityFeePerGas; } - if (tx.maxFeePerGas != null) { result.maxFeePerGas = tx.maxFeePerGas; } - if (tx.data != null) { result.data = tx.data; } - if (tx.value != null) { result.value = tx.value; } - if (tx.chainId != null) { result.chainId = tx.chainId; } - if (tx.signature != null) { result.signature = Signature.from(tx.signature); } - if (tx.accessList != null) { result.accessList = tx.accessList; } - if (tx.inputsUTXO != null) { result.inputsUTXO = tx.inputsUTXO; } - if (tx.outputsUTXO != null) { result.outputsUTXO = tx.outputsUTXO; } - - - if (tx.hash != null) { - assertArgument(result.isSigned(), "unsigned transaction cannot define hash", "tx", tx); - result.hash = tx.hash; - } - - if (tx.from != null) { -// assertArgument(result.isSigned(), "unsigned transaction cannot define from", "tx", tx); - assertArgument(result.from.toLowerCase() === (tx.from || "").toLowerCase(), "from mismatch", "tx", tx); - result.from = tx.from; - } - return result; - } -} \ No newline at end of file diff --git a/src.ts/transaction/utxo.ts b/src.ts/transaction/utxo.ts index 6808d676..37be2e9d 100644 --- a/src.ts/transaction/utxo.ts +++ b/src.ts/transaction/utxo.ts @@ -14,11 +14,18 @@ export type UTXOTransactionInput = { pubKey: Uint8Array; }; -export interface UTXOEntry { +export interface UTXOEntry extends UTXOEntryLike{ denomination: null | bigint; - address: null | string; + address: string; }; +export interface UTXOEntryLike { + denomination: null | BigNumberish; + address: null | string; +} + +export type UTXOTransactionOutputLike = UTXOEntryLike; + export type UTXOTransactionOutput = UTXOEntry; export type TxOutput = { @@ -34,7 +41,7 @@ export type TxInput = { export interface UTXOEntry { denomination: null | bigint; - address: null | string; + address: string; }; @@ -132,9 +139,9 @@ export class UTXO implements UTXOLike { this.#index = value; } - get address(): null | string { return this.#address; } - set address(value: null | string) { - this.#address = (value == null) ? null : getAddress(value); + get address(): string { return this.#address || ""; } + set address(value: string) { + this.#address = getAddress(value); } get denomination(): null | bigint { return this.#denomination; } diff --git a/src.ts/transaction/work-object.ts b/src.ts/transaction/work-object.ts new file mode 100644 index 00000000..a5feff02 --- /dev/null +++ b/src.ts/transaction/work-object.ts @@ -0,0 +1,424 @@ +import { formatNumber } from "../providers/format" +import { assert, getBytes, getNumber, hexlify } from "../quais" +import { decodeProtoWorkObject } from "../utils" +import { encodeProtoWorkObject } from "../utils/proto-encode" +import { ProtoTransaction } from "./abstract-transaction" +import { QuaiTransaction, QuaiTransactionLike } from "./quai-transaction" + +/** + * Interface representing a WorkObject, which includes + * header, body, and transaction information. + */ +export interface WorkObjectLike { + /** Header information of the WorkObject. */ + woHeader: WorkObjectHeaderLike + + /** Body information of the WorkObject. */ + woBody: WorkObjectBodyLike + + /** Transaction information associated with the WorkObject. */ + tx: QuaiTransactionLike +} + +/** + * Interface representing the header information of a WorkObject. + */ +export interface WorkObjectHeaderLike { + /** The difficulty of the WorkObject. */ + difficulty: string + + /** Hash of the WorkObject header. */ + headerHash: string + + /** Location information of the WorkObject. */ + location: number[] + + /** Hash of the parent WorkObject. */ + parentHash: string + + /** Nonce of the WorkObject. */ + nonce: string + + /** Number of the WorkObject. */ + number: string + + /** Transaction hash associated with the WorkObject. */ + txHash: string +} + +/** + * Interface representing the body information of a WorkObject. + */ +export interface WorkObjectBodyLike { + /** External transactions included in the WorkObject. */ + extTransactions: WorkObjectLike[] + + /** Header information of the WorkObject. */ + header: HeaderLike + + /** Manifest of the block. */ + manifest: BlockManifest + + /** Transactions included in the WorkObject. */ + transactions: WorkObjectLike[] + + /** Uncles (or ommer blocks) of the WorkObject. */ + uncles: WorkObjectLike[] +} + +/** + * Interface representing the header information within the body of a WorkObject. + */ +export interface HeaderLike { + /** Base fee per gas. */ + baseFeePerGas: string + + /** EVM root hash. */ + evmRoot: string + + /** External rollup root hash. */ + extRollupRoot: string + + /** Root hash of external transactions. */ + extTransactionsRoot: string + + /** Hash of the external transaction set. */ + etxSetHash: string + + /** Extra data included in the block. */ + extraData: string + + /** Gas limit for the block. */ + gasLimit: string + + /** Gas used by the block. */ + gasUsed: string + + /** Hashes of the block manifest. */ + manifestHash: string[] + + /** Miner address. */ + miner: string + + /** Block number. */ + number: string[] + + /** Parent delta S values. */ + parentDeltaS: string[] + + /** Parent entropy values. */ + parentEntropy: string[] + + /** Parent hash values. */ + parentHash: string[] + + /** Receipts root hash. */ + receiptsRoot: string + + /** SHA3 uncles hash. */ + sha3Uncles: string + + /** Transactions root hash. */ + transactionsRoot: string + + /** UTXO root hash. */ + utxoRoot: string + + /** Hash of the block. */ + hash?: string + + /** Seal hash of the block. */ + sealHash?: string + + /** Proof-of-Work hash. */ + PowHash?: string + + /** Proof-of-Work digest. */ + PowDigest?: string +} + +/** Type representing a block manifest as an array of strings. */ +export type BlockManifest = string[] + +/** Interface representing the header within the body of a WorkObject in protobuf format. */ +export interface ProtoHeader { + base_fee?: Uint8Array | null; + coinbase?: Uint8Array | null; + evm_root?: ProtoHash | null; + etx_hash?: ProtoHash | null; + etx_rollup_hash?: ProtoHash | null; + etx_set_hash?: ProtoHash | null; + extra?: Uint8Array | null; + gas_limit?: number | null; + gas_used?: number | null; + manifest_hash: ProtoHash[] | null; + number: Uint8Array[] | null; + parent_delta_s: Uint8Array[] | null; + parent_entropy: Uint8Array[] | null; + parent_hash: ProtoHash[] | null; + receipt_hash?: ProtoHash | null; + time?: bigint | null; + tx_hash?: ProtoHash | null; + uncle_hash?: ProtoHash | null; + utxo_root?: ProtoHash | null; +} + +/** Interface representing the header of a WorkObject in protobuf format. */ +export interface ProtoWorkObjectHeader { + difficulty?: Uint8Array | null; + header_hash?: ProtoHash | null; + location?: ProtoLocation | null; + nonce?: number | null; + number?: Uint8Array | null; + parent_hash?: ProtoHash | null; + tx_hash?: ProtoHash | null; + mix_hash?: ProtoHash | null; +} + +/** Interface representing the body of a WorkObject in protobuf format. */ +export interface ProtoWorkObjectBody { + ext_transactions?: ProtoWorkObjects | null; + header?: ProtoHeader | null; + manifest?: ProtoManifest | null; + transactions?: ProtoWorkObjects | null; + uncles?: ProtoWorkObjects | null; +} + +/** Interface representing the protobuf format of a WorkObject. */ +export interface ProtoWorkObject { + wo_body?: ProtoWorkObjectBody | null; + wo_header?: ProtoWorkObjectHeader | null; + tx?: ProtoTransaction | null; +} + +/** Interface representing an array of ProtoWorkObject. */ +interface ProtoWorkObjects { work_objects: ProtoWorkObject[]; } + +/** Interface representing an array of ProtoTransaction. */ +// interface ProtoTransactions { transactions: ProtoTransaction[]; } + +/** Interface representing a single hash value in a protobuf format. */ +export interface ProtoHash { value: Uint8Array } + +/** Interface representing multiple hash values in a protobuf format. */ +export interface ProtoHashes { hashes: ProtoHash[] } + +/** Interface representing a location value in a protobuf format. */ +export interface ProtoLocation { value: Uint8Array } + +/** Interface representing a manifest in a protobuf format. */ +export interface ProtoManifest { manifest: ProtoHash[] } + +/** + * Represents a WorkObject, which includes header, body, and transaction information. + */ +export class WorkObject { + #woHeader: WorkObjectHeaderLike; + #woBody: WorkObjectBodyLike; + #tx: QuaiTransaction; + + /** + * Constructs a WorkObject instance. + * + * @param woHeader The header information of the WorkObject. + * @param woBody The body information of the WorkObject. + * @param tx The transaction associated with the WorkObject. + * @param signature The signature of the transaction (optional). + */ + constructor(woHeader: WorkObjectHeaderLike, woBody: WorkObjectBodyLike, tx: QuaiTransactionLike) { + this.#woHeader = woHeader; + this.#woBody = woBody; + this.#tx = QuaiTransaction.from(tx); + + this.#woHeader.txHash = this.#tx.hash!; + this.#validate(); + } + + /** Gets the header information of the WorkObject. */ + get woHeader(): WorkObjectHeaderLike { return this.#woHeader; } + set woHeader(value: WorkObjectHeaderLike) { this.#woHeader = value; } + + /** Gets the body information of the WorkObject. */ + get woBody(): WorkObjectBodyLike { return this.#woBody; } + set woBody(value: WorkObjectBodyLike) { this.#woBody = value; } + + /** Gets the transaction associated with the WorkObject. */ + get tx(): QuaiTransaction { return this.#tx; } + set tx(value: QuaiTransactionLike) { this.#tx = QuaiTransaction.from(value); } + + /** + * Gets the serialized representation of the WorkObject. + * Throws an error if the WorkObject transaction is unsigned. + */ + get serialized(): string { + assert(this.#tx.signature != null, "cannot serialize unsigned work object; maybe you meant .unsignedSerialized", "UNSUPPORTED_OPERATION", { operation: ".serialized" }); + return this.#serialize(); + } + + /** + * Gets the pre-image of the WorkObject. + * The hash of this is the digest which needs to be signed to authorize this WorkObject. + */ + get unsignedSerialized(): string { + return this.#serialize(); + } + + /** + * Creates a clone of the current WorkObject. + * + * @returns A new WorkObject instance that is a clone of the current instance. + */ + clone(): WorkObject { + return WorkObject.from(this); + } + + /** + * Converts the WorkObject to a JSON-like object. + * + * @returns The WorkObject as a WorkObjectLike object. + */ + toJSON(): WorkObjectLike { + return { + woHeader: this.woHeader, + woBody: this.woBody, + tx: this.tx.toJSON(), + }; + } + + /** + * Converts the WorkObject to its protobuf representation. + * + * @returns The WorkObject as a ProtoWorkObject. + */ + toProtobuf(): ProtoWorkObject { + return { + wo_header: { + difficulty: getBytes(this.woHeader.difficulty, "difficulty"), + header_hash: { value: getBytes(this.woHeader.headerHash, "header_hash") }, + location: { value: new Uint8Array(this.woHeader.location) }, + nonce: getNumber(this.woHeader.nonce, "nonce"), + number: formatNumber(this.woHeader.number, "number"), + parent_hash: { value: getBytes(this.woHeader.parentHash, "parent_hash") }, + tx_hash: { value: getBytes(this.woHeader.txHash, "tx_hash") }, + }, + // wo_body: { + // ext_transactions: { work_objects: this.woBody.extTransactions.map(etx => WorkObject.from(etx).toProtobuf()) }, + // header: { + // base_fee: getBytes(this.woBody.header.baseFeePerGas, "base_fee"), + // coinbase: getBytes(this.woBody.header.miner, "coinbase"), + // evm_root: { value: getBytes(this.woBody.header.evmRoot, "evm_root") }, + // etx_hash: { value: getBytes(this.woBody.header.extTransactionsRoot, "etx_hash") }, + // etx_rollup_hash: { value: getBytes(this.woBody.header.extRollupRoot, "etx_rollup_hash") }, + // etx_set_hash: { value: getBytes(this.woBody.header.etxSetHash, "etx_set_hash") }, + // extra: getBytes(this.woBody.header.extraData, "extra"), + // gas_limit: getNumber(this.woBody.header.gasLimit, "gas_limit"), + // gas_used: getNumber(this.woBody.header.gasUsed, "gas_used"), + // manifest_hash: this.woBody.header.manifestHash.map(h => ({ value: getBytes(h, "manifest_hash") })), + // number: this.woBody.header.number.map(n => formatNumber(n, "number")), + // parent_delta_s: this.woBody.header.parentDeltaS.map(h => formatNumber(h, "parent_delta_s")), + // parent_entropy: this.woBody.header.parentEntropy.map(h => formatNumber(h, "parent_entropy")), + // parent_hash: this.woBody.header.parentHash.map(h => ({ value: getBytes(h, "parent_hash") })), + // receipt_hash: { value: getBytes(this.woBody.header.receiptsRoot, "receipt_hash") }, + // tx_hash: { value: getBytes(this.woBody.header.transactionsRoot, "tx_hash") }, + // uncle_hash: { value: getBytes(this.woBody.header.sha3Uncles, "uncle_hash") }, + // utxo_root: { value: getBytes(this.woBody.header.utxoRoot) }, + // }, + // transactions: { work_objects: this.woBody.transactions.map(tx => WorkObject.from(tx).toProtobuf()) }, + // uncles: { work_objects: this.woBody.uncles.map(uncle => WorkObject.from(uncle).toProtobuf()) }, + // manifest: { manifest: this.woBody.manifest.map(m => ({ value: getBytes(m) })) }, + // }, + wo_body: null, + tx: this.tx.toProtobuf(), + }; + } + + /** + * Creates a WorkObject instance from a WorkObjectLike object. + * + * @param data The WorkObjectLike object to create the WorkObject from. + * @returns A new WorkObject instance. + */ + static from(wo: string | WorkObjectLike): WorkObject { + if (typeof (wo) === "string") { + const decodedProtoWo: ProtoWorkObject = decodeProtoWorkObject(getBytes(wo)); + return WorkObject.fromProto(decodedProtoWo); + } + + return new WorkObject(wo.woHeader, wo.woBody, wo.tx); + } + + /** + * Creates a WorkObject instance from a ProtoWorkObject object. + * + * @param protoWo The ProtoWorkObject object to create the WorkObject from. + * @returns A new WorkObject instance. + */ + static fromProto(protoWo: ProtoWorkObject): WorkObject { + // Assuming methods to convert ProtoHeader and ProtoWorkObjects to their respective interfaces + const woHeader: WorkObjectHeaderLike = { + difficulty: hexlify(protoWo.wo_header?.difficulty || new Uint8Array()), + headerHash: hexlify(protoWo.wo_header?.header_hash?.value || new Uint8Array()), + location: protoWo.wo_header?.location?.value ? Array.from(protoWo.wo_header.location.value) : [], + nonce: protoWo.wo_header?.nonce?.toString() || "0", + number: hexlify(protoWo.wo_header?.number || new Uint8Array()), + parentHash: hexlify(protoWo.wo_header?.parent_hash?.value || new Uint8Array()), + txHash: hexlify(protoWo.wo_header?.tx_hash?.value || new Uint8Array()), + }; + + const woBody: WorkObjectBodyLike = { + extTransactions: protoWo.wo_body?.ext_transactions?.work_objects.map(WorkObject.fromProto) || [], + header: { + baseFeePerGas: hexlify(protoWo.wo_body?.header?.base_fee || new Uint8Array()), + evmRoot: hexlify(protoWo.wo_body?.header?.evm_root?.value || new Uint8Array()), + extRollupRoot: hexlify(protoWo.wo_body?.header?.etx_rollup_hash?.value || new Uint8Array()), + extTransactionsRoot: hexlify(protoWo.wo_body?.header?.etx_hash?.value || new Uint8Array()), + etxSetHash: hexlify(protoWo.wo_body?.header?.etx_set_hash?.value || new Uint8Array()), + extraData: hexlify(protoWo.wo_body?.header?.extra || new Uint8Array()), + gasLimit: protoWo.wo_body?.header?.gas_limit?.toString() || "0", + gasUsed: protoWo.wo_body?.header?.gas_used?.toString() || "0", + manifestHash: protoWo.wo_body?.header?.manifest_hash?.map(hash => hexlify(hash.value)) || [], + miner: hexlify(protoWo.wo_body?.header?.coinbase || new Uint8Array()), + number: protoWo.wo_body?.header?.number?.map(n => hexlify(n)) || [], + parentDeltaS: protoWo.wo_body?.header?.parent_delta_s?.map(h => hexlify(h)) || [], + parentEntropy: protoWo.wo_body?.header?.parent_entropy?.map(h => hexlify(h)) || [], + parentHash: protoWo.wo_body?.header?.parent_hash?.map(hash => hexlify(hash.value)) || [], + receiptsRoot: hexlify(protoWo.wo_body?.header?.receipt_hash?.value || new Uint8Array()), + sha3Uncles: hexlify(protoWo.wo_body?.header?.uncle_hash?.value || new Uint8Array()), + transactionsRoot: hexlify(protoWo.wo_body?.header?.tx_hash?.value || new Uint8Array()), + utxoRoot: hexlify(protoWo.wo_body?.header?.utxo_root?.value || new Uint8Array()), + }, + manifest: protoWo.wo_body?.manifest?.manifest.map(hash => hexlify(hash.value)) || [], + transactions: protoWo.wo_body?.transactions?.work_objects.map(WorkObject.fromProto) || [], + uncles: protoWo.wo_body?.uncles?.work_objects.map(WorkObject.fromProto) || [], + }; + + // Convert ProtoTransaction to TransactionLike using Transaction.fromProto + + if (protoWo.tx) { + const tx: QuaiTransactionLike = QuaiTransaction.fromProto(protoWo.tx).toJSON() + return new WorkObject(woHeader, woBody, tx); + } else { + throw new Error("Invalid ProtoWorkObject: missing transaction"); + } + } + + /** + * Serializes the WorkObject to a string. + * + * @returns The serialized string representation of the WorkObject. + */ + #serialize(): string { + return encodeProtoWorkObject(this.toProtobuf()); + } + + /** + * Validates the WorkObject. + * Ensures that the body header number and parent hashes are of the correct length. + * + * TODO: This method should validate the entire WorkObject. + */ + #validate(): void { + this.#woBody.header.number = this.#woBody.header.number.slice(0, 2); + this.#woBody.header.parentHash = this.#woBody.header.parentHash.slice(0, 2); + } +} \ No newline at end of file diff --git a/src.ts/utils/ProtoBuf/proto_block.proto b/src.ts/utils/ProtoBuf/proto_block.proto index c56cdccd..88004e6a 100644 --- a/src.ts/utils/ProtoBuf/proto_block.proto +++ b/src.ts/utils/ProtoBuf/proto_block.proto @@ -5,19 +5,6 @@ option go_package = "github.com/dominant-strategies/go-quai/core/types"; import "proto_common.proto"; -// This file defines all the ProtoBuf definitions related to core -message ProtoBlock { - optional ProtoHeader header = 1; - optional ProtoBody body = 2; -} - -message ProtoBody { - optional ProtoTransactions txs = 1; - optional ProtoHeaders uncles = 2; - optional ProtoTransactions etxs = 3; - optional ProtoManifest manifest = 4; -} - message ProtoHeader { repeated common.ProtoHash parent_hash = 1; optional common.ProtoHash uncle_hash = 2; @@ -31,17 +18,24 @@ message ProtoHeader { optional bytes difficulty = 10; repeated bytes parent_entropy = 11; repeated bytes parent_delta_s = 12; - repeated bytes number = 13; - optional uint64 gas_limit = 14; - optional uint64 gas_used = 15; - optional bytes base_fee = 16; - optional common.ProtoLocation location = 17; - optional uint64 time = 18; - optional bytes extra = 19; - optional common.ProtoHash mix_hash = 20; - optional uint64 nonce = 21; - optional common.ProtoHash utxo_root = 22; - optional common.ProtoHash etx_set_hash = 23; + repeated bytes parent_uncled_sub_delta_s = 13; + optional bytes uncled_s = 14; + repeated bytes number = 15; + optional uint64 gas_limit = 16; + optional uint64 gas_used = 17; + optional bytes base_fee = 18; + optional common.ProtoLocation location = 19; + optional bytes extra = 20; + optional common.ProtoHash mix_hash = 21; + optional uint64 nonce = 22; + optional common.ProtoHash utxo_root = 23; + optional common.ProtoHash etx_set_hash = 24; + optional uint64 efficiency_score = 25; + optional uint64 threshold_count = 26; + optional uint64 expansion_number = 27; + optional common.ProtoHash etx_eligible_slices = 28; + optional common.ProtoHash prime_terminus = 29; + optional common.ProtoHash interlink_root_hash = 30; } message ProtoTransaction { @@ -74,6 +68,39 @@ message ProtoManifest { repeated common.ProtoHash manifest = 1; } message ProtoAccessList { repeated ProtoAccessTuple access_tuples = 1; } +message ProtoWorkObjectHeader { + optional common.ProtoHash header_hash = 1; + optional common.ProtoHash parent_hash = 2; + optional bytes number = 3; + optional bytes difficulty = 4; + optional common.ProtoHash tx_hash = 5; + optional uint64 nonce = 6; + optional common.ProtoLocation location = 7; + optional common.ProtoHash mix_hash = 8; + optional uint64 time = 9; +} + +message ProtoWorkObjectHeaders { + repeated ProtoWorkObjectHeader wo_headers = 1; +} + +message ProtoWorkObjectBody { + optional ProtoHeader header = 1; + optional ProtoTransactions transactions = 2; + optional ProtoWorkObjectHeaders uncles = 3; + optional ProtoTransactions ext_transactions = 4; + optional ProtoManifest manifest = 5; + optional common.ProtoHashes interlink_hashes = 6; +} + +message ProtoWorkObject { + optional ProtoWorkObjectHeader wo_header = 1; + optional ProtoWorkObjectBody wo_body = 2; + optional ProtoTransaction tx = 3; +} + +message ProtoWorkObjects { repeated ProtoWorkObject work_objects = 1; } + message ProtoAccessTuple { bytes address = 1; repeated common.ProtoHash storage_key = 2; @@ -102,7 +129,7 @@ message ProtoLogForStorage { message ProtoLogsForStorage { repeated ProtoLogForStorage logs = 1; } message ProtoPendingHeader { - optional ProtoHeader header = 1; + optional ProtoWorkObject wo = 1; optional ProtoTermini termini = 2; } @@ -111,17 +138,15 @@ message ProtoTermini { repeated common.ProtoHash sub_termini = 2; } -message ProtoEtxSet { - optional bytes etx_hashes = 1; -} +message ProtoEtxSet { optional bytes etx_hashes = 1; } message ProtoPendingEtxs { - optional ProtoHeader header = 1; + optional ProtoWorkObject header = 1; optional ProtoTransactions etxs = 2; } message ProtoPendingEtxsRollup { - optional ProtoHeader header = 1; + optional ProtoWorkObject header = 1; optional ProtoTransactions etxs_rollup = 2; } diff --git a/src.ts/utils/ProtoBuf/proto_block.ts b/src.ts/utils/ProtoBuf/proto_block.ts index 8c397c0a..7fd4526e 100644 --- a/src.ts/utils/ProtoBuf/proto_block.ts +++ b/src.ts/utils/ProtoBuf/proto_block.ts @@ -6,315 +6,14 @@ import * as dependency_1 from "./proto_common"; import * as pb_1 from "google-protobuf"; export namespace block { - export class ProtoBlock extends pb_1.Message { - #one_of_decls: number[][] = [[1], [2]]; - constructor(data?: any[] | ({} & (({ - header?: ProtoHeader; - }) | ({ - body?: ProtoBody; - })))) { - super(); - pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); - if (!Array.isArray(data) && typeof data == "object") { - if ("header" in data && data.header != undefined) { - this.header = data.header; - } - if ("body" in data && data.body != undefined) { - this.body = data.body; - } - } - } - get header() { - return pb_1.Message.getWrapperField(this, ProtoHeader, 1) as ProtoHeader; - } - set header(value: ProtoHeader) { - pb_1.Message.setOneofWrapperField(this, 1, this.#one_of_decls[0], value); - } - get has_header() { - return pb_1.Message.getField(this, 1) != null; - } - get body() { - return pb_1.Message.getWrapperField(this, ProtoBody, 2) as ProtoBody; - } - set body(value: ProtoBody) { - pb_1.Message.setOneofWrapperField(this, 2, this.#one_of_decls[1], value); - } - get has_body() { - return pb_1.Message.getField(this, 2) != null; - } - get _header() { - const cases: { - [index: number]: "none" | "header"; - } = { - 0: "none", - 1: "header" - }; - return cases[pb_1.Message.computeOneofCase(this, [1])]; - } - get _body() { - const cases: { - [index: number]: "none" | "body"; - } = { - 0: "none", - 2: "body" - }; - return cases[pb_1.Message.computeOneofCase(this, [2])]; - } - static fromObject(data: { - header?: ReturnType; - body?: ReturnType; - }): ProtoBlock { - const message = new ProtoBlock({}); - if (data.header != null) { - message.header = ProtoHeader.fromObject(data.header); - } - if (data.body != null) { - message.body = ProtoBody.fromObject(data.body); - } - return message; - } - toObject() { - const data: { - header?: ReturnType; - body?: ReturnType; - } = {}; - if (this.header != null) { - data.header = this.header.toObject(); - } - if (this.body != null) { - data.body = this.body.toObject(); - } - return data; - } - serialize(): Uint8Array; - serialize(w: pb_1.BinaryWriter): void; - serialize(w?: pb_1.BinaryWriter): Uint8Array | void { - const writer = w || new pb_1.BinaryWriter(); - if (this.has_header) - writer.writeMessage(1, this.header, () => this.header.serialize(writer)); - if (this.has_body) - writer.writeMessage(2, this.body, () => this.body.serialize(writer)); - if (!w) - return writer.getResultBuffer(); - } - static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoBlock { - const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoBlock(); - while (reader.nextField()) { - if (reader.isEndGroup()) - break; - switch (reader.getFieldNumber()) { - case 1: - reader.readMessage(message.header, () => message.header = ProtoHeader.deserialize(reader)); - break; - case 2: - reader.readMessage(message.body, () => message.body = ProtoBody.deserialize(reader)); - break; - default: reader.skipField(); - } - } - return message; - } - serializeBinary(): Uint8Array { - return this.serialize(); - } - static deserializeBinary(bytes: Uint8Array): ProtoBlock { - return ProtoBlock.deserialize(bytes); - } - } - export class ProtoBody extends pb_1.Message { - #one_of_decls: number[][] = [[1], [2], [3], [4]]; - constructor(data?: any[] | ({} & (({ - txs?: ProtoTransactions; - }) | ({ - uncles?: ProtoHeaders; - }) | ({ - etxs?: ProtoTransactions; - }) | ({ - manifest?: ProtoManifest; - })))) { - super(); - pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); - if (!Array.isArray(data) && typeof data == "object") { - if ("txs" in data && data.txs != undefined) { - this.txs = data.txs; - } - if ("uncles" in data && data.uncles != undefined) { - this.uncles = data.uncles; - } - if ("etxs" in data && data.etxs != undefined) { - this.etxs = data.etxs; - } - if ("manifest" in data && data.manifest != undefined) { - this.manifest = data.manifest; - } - } - } - get txs() { - return pb_1.Message.getWrapperField(this, ProtoTransactions, 1) as ProtoTransactions; - } - set txs(value: ProtoTransactions) { - pb_1.Message.setOneofWrapperField(this, 1, this.#one_of_decls[0], value); - } - get has_txs() { - return pb_1.Message.getField(this, 1) != null; - } - get uncles() { - return pb_1.Message.getWrapperField(this, ProtoHeaders, 2) as ProtoHeaders; - } - set uncles(value: ProtoHeaders) { - pb_1.Message.setOneofWrapperField(this, 2, this.#one_of_decls[1], value); - } - get has_uncles() { - return pb_1.Message.getField(this, 2) != null; - } - get etxs() { - return pb_1.Message.getWrapperField(this, ProtoTransactions, 3) as ProtoTransactions; - } - set etxs(value: ProtoTransactions) { - pb_1.Message.setOneofWrapperField(this, 3, this.#one_of_decls[2], value); - } - get has_etxs() { - return pb_1.Message.getField(this, 3) != null; - } - get manifest() { - return pb_1.Message.getWrapperField(this, ProtoManifest, 4) as ProtoManifest; - } - set manifest(value: ProtoManifest) { - pb_1.Message.setOneofWrapperField(this, 4, this.#one_of_decls[3], value); - } - get has_manifest() { - return pb_1.Message.getField(this, 4) != null; - } - get _txs() { - const cases: { - [index: number]: "none" | "txs"; - } = { - 0: "none", - 1: "txs" - }; - return cases[pb_1.Message.computeOneofCase(this, [1])]; - } - get _uncles() { - const cases: { - [index: number]: "none" | "uncles"; - } = { - 0: "none", - 2: "uncles" - }; - return cases[pb_1.Message.computeOneofCase(this, [2])]; - } - get _etxs() { - const cases: { - [index: number]: "none" | "etxs"; - } = { - 0: "none", - 3: "etxs" - }; - return cases[pb_1.Message.computeOneofCase(this, [3])]; - } - get _manifest() { - const cases: { - [index: number]: "none" | "manifest"; - } = { - 0: "none", - 4: "manifest" - }; - return cases[pb_1.Message.computeOneofCase(this, [4])]; - } - static fromObject(data: { - txs?: ReturnType; - uncles?: ReturnType; - etxs?: ReturnType; - manifest?: ReturnType; - }): ProtoBody { - const message = new ProtoBody({}); - if (data.txs != null) { - message.txs = ProtoTransactions.fromObject(data.txs); - } - if (data.uncles != null) { - message.uncles = ProtoHeaders.fromObject(data.uncles); - } - if (data.etxs != null) { - message.etxs = ProtoTransactions.fromObject(data.etxs); - } - if (data.manifest != null) { - message.manifest = ProtoManifest.fromObject(data.manifest); - } - return message; - } - toObject() { - const data: { - txs?: ReturnType; - uncles?: ReturnType; - etxs?: ReturnType; - manifest?: ReturnType; - } = {}; - if (this.txs != null) { - data.txs = this.txs.toObject(); - } - if (this.uncles != null) { - data.uncles = this.uncles.toObject(); - } - if (this.etxs != null) { - data.etxs = this.etxs.toObject(); - } - if (this.manifest != null) { - data.manifest = this.manifest.toObject(); - } - return data; - } - serialize(): Uint8Array; - serialize(w: pb_1.BinaryWriter): void; - serialize(w?: pb_1.BinaryWriter): Uint8Array | void { - const writer = w || new pb_1.BinaryWriter(); - if (this.has_txs) - writer.writeMessage(1, this.txs, () => this.txs.serialize(writer)); - if (this.has_uncles) - writer.writeMessage(2, this.uncles, () => this.uncles.serialize(writer)); - if (this.has_etxs) - writer.writeMessage(3, this.etxs, () => this.etxs.serialize(writer)); - if (this.has_manifest) - writer.writeMessage(4, this.manifest, () => this.manifest.serialize(writer)); - if (!w) - return writer.getResultBuffer(); - } - static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoBody { - const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoBody(); - while (reader.nextField()) { - if (reader.isEndGroup()) - break; - switch (reader.getFieldNumber()) { - case 1: - reader.readMessage(message.txs, () => message.txs = ProtoTransactions.deserialize(reader)); - break; - case 2: - reader.readMessage(message.uncles, () => message.uncles = ProtoHeaders.deserialize(reader)); - break; - case 3: - reader.readMessage(message.etxs, () => message.etxs = ProtoTransactions.deserialize(reader)); - break; - case 4: - reader.readMessage(message.manifest, () => message.manifest = ProtoManifest.deserialize(reader)); - break; - default: reader.skipField(); - } - } - return message; - } - serializeBinary(): Uint8Array { - return this.serialize(); - } - static deserializeBinary(bytes: Uint8Array): ProtoBody { - return ProtoBody.deserialize(bytes); - } - } export class ProtoHeader extends pb_1.Message { - #one_of_decls: number[][] = [[2], [3], [4], [5], [6], [7], [9], [10], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23]]; + #one_of_decls: number[][] = [[2], [3], [4], [5], [6], [7], [9], [10], [14], [16], [17], [18], [19], [20], [21], [22], [23], [24], [25], [26], [27], [28], [29], [30]]; constructor(data?: any[] | ({ parent_hash?: dependency_1.common.ProtoHash[]; manifest_hash?: dependency_1.common.ProtoHash[]; parent_entropy?: Uint8Array[]; parent_delta_s?: Uint8Array[]; + parent_uncled_sub_delta_s?: Uint8Array[]; number?: Uint8Array[]; } & (({ uncle_hash?: dependency_1.common.ProtoHash; @@ -332,6 +31,8 @@ export namespace block { receipt_hash?: dependency_1.common.ProtoHash; }) | ({ difficulty?: Uint8Array; + }) | ({ + uncled_s?: Uint8Array; }) | ({ gas_limit?: number; }) | ({ @@ -340,8 +41,6 @@ export namespace block { base_fee?: Uint8Array; }) | ({ location?: dependency_1.common.ProtoLocation; - }) | ({ - time?: number; }) | ({ extra?: Uint8Array; }) | ({ @@ -352,9 +51,21 @@ export namespace block { utxo_root?: dependency_1.common.ProtoHash; }) | ({ etx_set_hash?: dependency_1.common.ProtoHash; + }) | ({ + efficiency_score?: number; + }) | ({ + threshold_count?: number; + }) | ({ + expansion_number?: number; + }) | ({ + etx_eligible_slices?: dependency_1.common.ProtoHash; + }) | ({ + prime_terminus?: dependency_1.common.ProtoHash; + }) | ({ + interlink_root_hash?: dependency_1.common.ProtoHash; })))) { super(); - pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1, 8, 11, 12, 13], this.#one_of_decls); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1, 8, 11, 12, 13, 15], this.#one_of_decls); if (!Array.isArray(data) && typeof data == "object") { if ("parent_hash" in data && data.parent_hash != undefined) { this.parent_hash = data.parent_hash; @@ -392,6 +103,12 @@ export namespace block { if ("parent_delta_s" in data && data.parent_delta_s != undefined) { this.parent_delta_s = data.parent_delta_s; } + if ("parent_uncled_sub_delta_s" in data && data.parent_uncled_sub_delta_s != undefined) { + this.parent_uncled_sub_delta_s = data.parent_uncled_sub_delta_s; + } + if ("uncled_s" in data && data.uncled_s != undefined) { + this.uncled_s = data.uncled_s; + } if ("number" in data && data.number != undefined) { this.number = data.number; } @@ -407,9 +124,6 @@ export namespace block { if ("location" in data && data.location != undefined) { this.location = data.location; } - if ("time" in data && data.time != undefined) { - this.time = data.time; - } if ("extra" in data && data.extra != undefined) { this.extra = data.extra; } @@ -425,6 +139,24 @@ export namespace block { if ("etx_set_hash" in data && data.etx_set_hash != undefined) { this.etx_set_hash = data.etx_set_hash; } + if ("efficiency_score" in data && data.efficiency_score != undefined) { + this.efficiency_score = data.efficiency_score; + } + if ("threshold_count" in data && data.threshold_count != undefined) { + this.threshold_count = data.threshold_count; + } + if ("expansion_number" in data && data.expansion_number != undefined) { + this.expansion_number = data.expansion_number; + } + if ("etx_eligible_slices" in data && data.etx_eligible_slices != undefined) { + this.etx_eligible_slices = data.etx_eligible_slices; + } + if ("prime_terminus" in data && data.prime_terminus != undefined) { + this.prime_terminus = data.prime_terminus; + } + if ("interlink_root_hash" in data && data.interlink_root_hash != undefined) { + this.interlink_root_hash = data.interlink_root_hash; + } } } get parent_hash() { @@ -523,101 +255,161 @@ export namespace block { set parent_delta_s(value: Uint8Array[]) { pb_1.Message.setField(this, 12, value); } - get number() { + get parent_uncled_sub_delta_s() { return pb_1.Message.getFieldWithDefault(this, 13, []) as Uint8Array[]; } - set number(value: Uint8Array[]) { + set parent_uncled_sub_delta_s(value: Uint8Array[]) { pb_1.Message.setField(this, 13, value); } + get uncled_s() { + return pb_1.Message.getFieldWithDefault(this, 14, new Uint8Array(0)) as Uint8Array; + } + set uncled_s(value: Uint8Array) { + pb_1.Message.setOneofField(this, 14, this.#one_of_decls[8], value); + } + get has_uncled_s() { + return pb_1.Message.getField(this, 14) != null; + } + get number() { + return pb_1.Message.getFieldWithDefault(this, 15, []) as Uint8Array[]; + } + set number(value: Uint8Array[]) { + pb_1.Message.setField(this, 15, value); + } get gas_limit() { - return pb_1.Message.getFieldWithDefault(this, 14, 0) as number; + return pb_1.Message.getFieldWithDefault(this, 16, 0) as number; } set gas_limit(value: number) { - pb_1.Message.setOneofField(this, 14, this.#one_of_decls[8], value); + pb_1.Message.setOneofField(this, 16, this.#one_of_decls[9], value); } get has_gas_limit() { - return pb_1.Message.getField(this, 14) != null; + return pb_1.Message.getField(this, 16) != null; } get gas_used() { - return pb_1.Message.getFieldWithDefault(this, 15, 0) as number; + return pb_1.Message.getFieldWithDefault(this, 17, 0) as number; } set gas_used(value: number) { - pb_1.Message.setOneofField(this, 15, this.#one_of_decls[9], value); + pb_1.Message.setOneofField(this, 17, this.#one_of_decls[10], value); } get has_gas_used() { - return pb_1.Message.getField(this, 15) != null; + return pb_1.Message.getField(this, 17) != null; } get base_fee() { - return pb_1.Message.getFieldWithDefault(this, 16, new Uint8Array(0)) as Uint8Array; + return pb_1.Message.getFieldWithDefault(this, 18, new Uint8Array(0)) as Uint8Array; } set base_fee(value: Uint8Array) { - pb_1.Message.setOneofField(this, 16, this.#one_of_decls[10], value); + pb_1.Message.setOneofField(this, 18, this.#one_of_decls[11], value); } get has_base_fee() { - return pb_1.Message.getField(this, 16) != null; + return pb_1.Message.getField(this, 18) != null; } get location() { - return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoLocation, 17) as dependency_1.common.ProtoLocation; + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoLocation, 19) as dependency_1.common.ProtoLocation; } set location(value: dependency_1.common.ProtoLocation) { - pb_1.Message.setOneofWrapperField(this, 17, this.#one_of_decls[11], value); + pb_1.Message.setOneofWrapperField(this, 19, this.#one_of_decls[12], value); } get has_location() { - return pb_1.Message.getField(this, 17) != null; - } - get time() { - return pb_1.Message.getFieldWithDefault(this, 18, 0) as number; - } - set time(value: number) { - pb_1.Message.setOneofField(this, 18, this.#one_of_decls[12], value); - } - get has_time() { - return pb_1.Message.getField(this, 18) != null; + return pb_1.Message.getField(this, 19) != null; } get extra() { - return pb_1.Message.getFieldWithDefault(this, 19, new Uint8Array(0)) as Uint8Array; + return pb_1.Message.getFieldWithDefault(this, 20, new Uint8Array(0)) as Uint8Array; } set extra(value: Uint8Array) { - pb_1.Message.setOneofField(this, 19, this.#one_of_decls[13], value); + pb_1.Message.setOneofField(this, 20, this.#one_of_decls[13], value); } get has_extra() { - return pb_1.Message.getField(this, 19) != null; + return pb_1.Message.getField(this, 20) != null; } get mix_hash() { - return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 20) as dependency_1.common.ProtoHash; + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 21) as dependency_1.common.ProtoHash; } set mix_hash(value: dependency_1.common.ProtoHash) { - pb_1.Message.setOneofWrapperField(this, 20, this.#one_of_decls[14], value); + pb_1.Message.setOneofWrapperField(this, 21, this.#one_of_decls[14], value); } get has_mix_hash() { - return pb_1.Message.getField(this, 20) != null; + return pb_1.Message.getField(this, 21) != null; } get nonce() { - return pb_1.Message.getFieldWithDefault(this, 21, 0) as number; + return pb_1.Message.getFieldWithDefault(this, 22, 0) as number; } set nonce(value: number) { - pb_1.Message.setOneofField(this, 21, this.#one_of_decls[15], value); + pb_1.Message.setOneofField(this, 22, this.#one_of_decls[15], value); } get has_nonce() { - return pb_1.Message.getField(this, 21) != null; + return pb_1.Message.getField(this, 22) != null; } get utxo_root() { - return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 22) as dependency_1.common.ProtoHash; + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 23) as dependency_1.common.ProtoHash; } set utxo_root(value: dependency_1.common.ProtoHash) { - pb_1.Message.setOneofWrapperField(this, 22, this.#one_of_decls[16], value); + pb_1.Message.setOneofWrapperField(this, 23, this.#one_of_decls[16], value); } get has_utxo_root() { - return pb_1.Message.getField(this, 22) != null; + return pb_1.Message.getField(this, 23) != null; } get etx_set_hash() { - return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 23) as dependency_1.common.ProtoHash; + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 24) as dependency_1.common.ProtoHash; } set etx_set_hash(value: dependency_1.common.ProtoHash) { - pb_1.Message.setOneofWrapperField(this, 23, this.#one_of_decls[17], value); + pb_1.Message.setOneofWrapperField(this, 24, this.#one_of_decls[17], value); } get has_etx_set_hash() { - return pb_1.Message.getField(this, 23) != null; + return pb_1.Message.getField(this, 24) != null; + } + get efficiency_score() { + return pb_1.Message.getFieldWithDefault(this, 25, 0) as number; + } + set efficiency_score(value: number) { + pb_1.Message.setOneofField(this, 25, this.#one_of_decls[18], value); + } + get has_efficiency_score() { + return pb_1.Message.getField(this, 25) != null; + } + get threshold_count() { + return pb_1.Message.getFieldWithDefault(this, 26, 0) as number; + } + set threshold_count(value: number) { + pb_1.Message.setOneofField(this, 26, this.#one_of_decls[19], value); + } + get has_threshold_count() { + return pb_1.Message.getField(this, 26) != null; + } + get expansion_number() { + return pb_1.Message.getFieldWithDefault(this, 27, 0) as number; + } + set expansion_number(value: number) { + pb_1.Message.setOneofField(this, 27, this.#one_of_decls[20], value); + } + get has_expansion_number() { + return pb_1.Message.getField(this, 27) != null; + } + get etx_eligible_slices() { + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 28) as dependency_1.common.ProtoHash; + } + set etx_eligible_slices(value: dependency_1.common.ProtoHash) { + pb_1.Message.setOneofWrapperField(this, 28, this.#one_of_decls[21], value); + } + get has_etx_eligible_slices() { + return pb_1.Message.getField(this, 28) != null; + } + get prime_terminus() { + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 29) as dependency_1.common.ProtoHash; + } + set prime_terminus(value: dependency_1.common.ProtoHash) { + pb_1.Message.setOneofWrapperField(this, 29, this.#one_of_decls[22], value); + } + get has_prime_terminus() { + return pb_1.Message.getField(this, 29) != null; + } + get interlink_root_hash() { + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 30) as dependency_1.common.ProtoHash; + } + set interlink_root_hash(value: dependency_1.common.ProtoHash) { + pb_1.Message.setOneofWrapperField(this, 30, this.#one_of_decls[23], value); + } + get has_interlink_root_hash() { + return pb_1.Message.getField(this, 30) != null; } get _uncle_hash() { const cases: { @@ -691,95 +483,149 @@ export namespace block { }; return cases[pb_1.Message.computeOneofCase(this, [10])]; } + get _uncled_s() { + const cases: { + [index: number]: "none" | "uncled_s"; + } = { + 0: "none", + 14: "uncled_s" + }; + return cases[pb_1.Message.computeOneofCase(this, [14])]; + } get _gas_limit() { const cases: { [index: number]: "none" | "gas_limit"; } = { 0: "none", - 14: "gas_limit" + 16: "gas_limit" }; - return cases[pb_1.Message.computeOneofCase(this, [14])]; + return cases[pb_1.Message.computeOneofCase(this, [16])]; } get _gas_used() { const cases: { [index: number]: "none" | "gas_used"; } = { 0: "none", - 15: "gas_used" + 17: "gas_used" }; - return cases[pb_1.Message.computeOneofCase(this, [15])]; + return cases[pb_1.Message.computeOneofCase(this, [17])]; } get _base_fee() { const cases: { [index: number]: "none" | "base_fee"; } = { 0: "none", - 16: "base_fee" + 18: "base_fee" }; - return cases[pb_1.Message.computeOneofCase(this, [16])]; + return cases[pb_1.Message.computeOneofCase(this, [18])]; } get _location() { const cases: { [index: number]: "none" | "location"; } = { 0: "none", - 17: "location" - }; - return cases[pb_1.Message.computeOneofCase(this, [17])]; - } - get _time() { - const cases: { - [index: number]: "none" | "time"; - } = { - 0: "none", - 18: "time" + 19: "location" }; - return cases[pb_1.Message.computeOneofCase(this, [18])]; + return cases[pb_1.Message.computeOneofCase(this, [19])]; } get _extra() { const cases: { [index: number]: "none" | "extra"; } = { 0: "none", - 19: "extra" + 20: "extra" }; - return cases[pb_1.Message.computeOneofCase(this, [19])]; + return cases[pb_1.Message.computeOneofCase(this, [20])]; } get _mix_hash() { const cases: { [index: number]: "none" | "mix_hash"; } = { 0: "none", - 20: "mix_hash" + 21: "mix_hash" }; - return cases[pb_1.Message.computeOneofCase(this, [20])]; + return cases[pb_1.Message.computeOneofCase(this, [21])]; } get _nonce() { const cases: { [index: number]: "none" | "nonce"; } = { 0: "none", - 21: "nonce" + 22: "nonce" }; - return cases[pb_1.Message.computeOneofCase(this, [21])]; + return cases[pb_1.Message.computeOneofCase(this, [22])]; } get _utxo_root() { const cases: { [index: number]: "none" | "utxo_root"; } = { 0: "none", - 22: "utxo_root" + 23: "utxo_root" }; - return cases[pb_1.Message.computeOneofCase(this, [22])]; + return cases[pb_1.Message.computeOneofCase(this, [23])]; } get _etx_set_hash() { const cases: { [index: number]: "none" | "etx_set_hash"; } = { 0: "none", - 23: "etx_set_hash" + 24: "etx_set_hash" }; - return cases[pb_1.Message.computeOneofCase(this, [23])]; + return cases[pb_1.Message.computeOneofCase(this, [24])]; + } + get _efficiency_score() { + const cases: { + [index: number]: "none" | "efficiency_score"; + } = { + 0: "none", + 25: "efficiency_score" + }; + return cases[pb_1.Message.computeOneofCase(this, [25])]; + } + get _threshold_count() { + const cases: { + [index: number]: "none" | "threshold_count"; + } = { + 0: "none", + 26: "threshold_count" + }; + return cases[pb_1.Message.computeOneofCase(this, [26])]; + } + get _expansion_number() { + const cases: { + [index: number]: "none" | "expansion_number"; + } = { + 0: "none", + 27: "expansion_number" + }; + return cases[pb_1.Message.computeOneofCase(this, [27])]; + } + get _etx_eligible_slices() { + const cases: { + [index: number]: "none" | "etx_eligible_slices"; + } = { + 0: "none", + 28: "etx_eligible_slices" + }; + return cases[pb_1.Message.computeOneofCase(this, [28])]; + } + get _prime_terminus() { + const cases: { + [index: number]: "none" | "prime_terminus"; + } = { + 0: "none", + 29: "prime_terminus" + }; + return cases[pb_1.Message.computeOneofCase(this, [29])]; + } + get _interlink_root_hash() { + const cases: { + [index: number]: "none" | "interlink_root_hash"; + } = { + 0: "none", + 30: "interlink_root_hash" + }; + return cases[pb_1.Message.computeOneofCase(this, [30])]; } static fromObject(data: { parent_hash?: ReturnType[]; @@ -794,17 +640,24 @@ export namespace block { difficulty?: Uint8Array; parent_entropy?: Uint8Array[]; parent_delta_s?: Uint8Array[]; + parent_uncled_sub_delta_s?: Uint8Array[]; + uncled_s?: Uint8Array; number?: Uint8Array[]; gas_limit?: number; gas_used?: number; base_fee?: Uint8Array; location?: ReturnType; - time?: number; extra?: Uint8Array; mix_hash?: ReturnType; nonce?: number; utxo_root?: ReturnType; etx_set_hash?: ReturnType; + efficiency_score?: number; + threshold_count?: number; + expansion_number?: number; + etx_eligible_slices?: ReturnType; + prime_terminus?: ReturnType; + interlink_root_hash?: ReturnType; }): ProtoHeader { const message = new ProtoHeader({}); if (data.parent_hash != null) { @@ -843,6 +696,12 @@ export namespace block { if (data.parent_delta_s != null) { message.parent_delta_s = data.parent_delta_s; } + if (data.parent_uncled_sub_delta_s != null) { + message.parent_uncled_sub_delta_s = data.parent_uncled_sub_delta_s; + } + if (data.uncled_s != null) { + message.uncled_s = data.uncled_s; + } if (data.number != null) { message.number = data.number; } @@ -858,9 +717,6 @@ export namespace block { if (data.location != null) { message.location = dependency_1.common.ProtoLocation.fromObject(data.location); } - if (data.time != null) { - message.time = data.time; - } if (data.extra != null) { message.extra = data.extra; } @@ -876,6 +732,24 @@ export namespace block { if (data.etx_set_hash != null) { message.etx_set_hash = dependency_1.common.ProtoHash.fromObject(data.etx_set_hash); } + if (data.efficiency_score != null) { + message.efficiency_score = data.efficiency_score; + } + if (data.threshold_count != null) { + message.threshold_count = data.threshold_count; + } + if (data.expansion_number != null) { + message.expansion_number = data.expansion_number; + } + if (data.etx_eligible_slices != null) { + message.etx_eligible_slices = dependency_1.common.ProtoHash.fromObject(data.etx_eligible_slices); + } + if (data.prime_terminus != null) { + message.prime_terminus = dependency_1.common.ProtoHash.fromObject(data.prime_terminus); + } + if (data.interlink_root_hash != null) { + message.interlink_root_hash = dependency_1.common.ProtoHash.fromObject(data.interlink_root_hash); + } return message; } toObject() { @@ -892,17 +766,24 @@ export namespace block { difficulty?: Uint8Array; parent_entropy?: Uint8Array[]; parent_delta_s?: Uint8Array[]; + parent_uncled_sub_delta_s?: Uint8Array[]; + uncled_s?: Uint8Array; number?: Uint8Array[]; gas_limit?: number; gas_used?: number; base_fee?: Uint8Array; location?: ReturnType; - time?: number; extra?: Uint8Array; mix_hash?: ReturnType; nonce?: number; utxo_root?: ReturnType; etx_set_hash?: ReturnType; + efficiency_score?: number; + threshold_count?: number; + expansion_number?: number; + etx_eligible_slices?: ReturnType; + prime_terminus?: ReturnType; + interlink_root_hash?: ReturnType; } = {}; if (this.parent_hash != null) { data.parent_hash = this.parent_hash.map((item: dependency_1.common.ProtoHash) => item.toObject()); @@ -940,6 +821,12 @@ export namespace block { if (this.parent_delta_s != null) { data.parent_delta_s = this.parent_delta_s; } + if (this.parent_uncled_sub_delta_s != null) { + data.parent_uncled_sub_delta_s = this.parent_uncled_sub_delta_s; + } + if (this.uncled_s != null) { + data.uncled_s = this.uncled_s; + } if (this.number != null) { data.number = this.number; } @@ -955,9 +842,6 @@ export namespace block { if (this.location != null) { data.location = this.location.toObject(); } - if (this.time != null) { - data.time = this.time; - } if (this.extra != null) { data.extra = this.extra; } @@ -973,6 +857,24 @@ export namespace block { if (this.etx_set_hash != null) { data.etx_set_hash = this.etx_set_hash.toObject(); } + if (this.efficiency_score != null) { + data.efficiency_score = this.efficiency_score; + } + if (this.threshold_count != null) { + data.threshold_count = this.threshold_count; + } + if (this.expansion_number != null) { + data.expansion_number = this.expansion_number; + } + if (this.etx_eligible_slices != null) { + data.etx_eligible_slices = this.etx_eligible_slices.toObject(); + } + if (this.prime_terminus != null) { + data.prime_terminus = this.prime_terminus.toObject(); + } + if (this.interlink_root_hash != null) { + data.interlink_root_hash = this.interlink_root_hash.toObject(); + } return data; } serialize(): Uint8Array; @@ -1003,28 +905,42 @@ export namespace block { writer.writeRepeatedBytes(11, this.parent_entropy); if (this.parent_delta_s.length) writer.writeRepeatedBytes(12, this.parent_delta_s); + if (this.parent_uncled_sub_delta_s.length) + writer.writeRepeatedBytes(13, this.parent_uncled_sub_delta_s); + if (this.has_uncled_s) + writer.writeBytes(14, this.uncled_s); if (this.number.length) - writer.writeRepeatedBytes(13, this.number); + writer.writeRepeatedBytes(15, this.number); if (this.has_gas_limit) - writer.writeUint64(14, this.gas_limit); + writer.writeUint64(16, this.gas_limit); if (this.has_gas_used) - writer.writeUint64(15, this.gas_used); + writer.writeUint64(17, this.gas_used); if (this.has_base_fee) - writer.writeBytes(16, this.base_fee); + writer.writeBytes(18, this.base_fee); if (this.has_location) - writer.writeMessage(17, this.location, () => this.location.serialize(writer)); - if (this.has_time) - writer.writeUint64(18, this.time); + writer.writeMessage(19, this.location, () => this.location.serialize(writer)); if (this.has_extra) - writer.writeBytes(19, this.extra); + writer.writeBytes(20, this.extra); if (this.has_mix_hash) - writer.writeMessage(20, this.mix_hash, () => this.mix_hash.serialize(writer)); + writer.writeMessage(21, this.mix_hash, () => this.mix_hash.serialize(writer)); if (this.has_nonce) - writer.writeUint64(21, this.nonce); + writer.writeUint64(22, this.nonce); if (this.has_utxo_root) - writer.writeMessage(22, this.utxo_root, () => this.utxo_root.serialize(writer)); + writer.writeMessage(23, this.utxo_root, () => this.utxo_root.serialize(writer)); if (this.has_etx_set_hash) - writer.writeMessage(23, this.etx_set_hash, () => this.etx_set_hash.serialize(writer)); + writer.writeMessage(24, this.etx_set_hash, () => this.etx_set_hash.serialize(writer)); + if (this.has_efficiency_score) + writer.writeUint64(25, this.efficiency_score); + if (this.has_threshold_count) + writer.writeUint64(26, this.threshold_count); + if (this.has_expansion_number) + writer.writeUint64(27, this.expansion_number); + if (this.has_etx_eligible_slices) + writer.writeMessage(28, this.etx_eligible_slices, () => this.etx_eligible_slices.serialize(writer)); + if (this.has_prime_terminus) + writer.writeMessage(29, this.prime_terminus, () => this.prime_terminus.serialize(writer)); + if (this.has_interlink_root_hash) + writer.writeMessage(30, this.interlink_root_hash, () => this.interlink_root_hash.serialize(writer)); if (!w) return writer.getResultBuffer(); } @@ -1074,35 +990,56 @@ export namespace block { pb_1.Message.addToRepeatedField(message, 13, reader.readBytes()); break; case 14: - message.gas_limit = reader.readUint64(); + message.uncled_s = reader.readBytes(); break; case 15: - message.gas_used = reader.readUint64(); + pb_1.Message.addToRepeatedField(message, 15, reader.readBytes()); break; case 16: - message.base_fee = reader.readBytes(); + message.gas_limit = reader.readUint64(); break; case 17: - reader.readMessage(message.location, () => message.location = dependency_1.common.ProtoLocation.deserialize(reader)); + message.gas_used = reader.readUint64(); break; case 18: - message.time = reader.readUint64(); + message.base_fee = reader.readBytes(); break; case 19: - message.extra = reader.readBytes(); + reader.readMessage(message.location, () => message.location = dependency_1.common.ProtoLocation.deserialize(reader)); break; case 20: - reader.readMessage(message.mix_hash, () => message.mix_hash = dependency_1.common.ProtoHash.deserialize(reader)); + message.extra = reader.readBytes(); break; case 21: - message.nonce = reader.readUint64(); + reader.readMessage(message.mix_hash, () => message.mix_hash = dependency_1.common.ProtoHash.deserialize(reader)); break; case 22: - reader.readMessage(message.utxo_root, () => message.utxo_root = dependency_1.common.ProtoHash.deserialize(reader)); + message.nonce = reader.readUint64(); break; case 23: + reader.readMessage(message.utxo_root, () => message.utxo_root = dependency_1.common.ProtoHash.deserialize(reader)); + break; + case 24: reader.readMessage(message.etx_set_hash, () => message.etx_set_hash = dependency_1.common.ProtoHash.deserialize(reader)); break; + case 25: + message.efficiency_score = reader.readUint64(); + break; + case 26: + message.threshold_count = reader.readUint64(); + break; + case 27: + message.expansion_number = reader.readUint64(); + break; + case 28: + reader.readMessage(message.etx_eligible_slices, () => message.etx_eligible_slices = dependency_1.common.ProtoHash.deserialize(reader)); + break; + case 29: + reader.readMessage(message.prime_terminus, () => message.prime_terminus = dependency_1.common.ProtoHash.deserialize(reader)); + break; + case 30: + reader.readMessage(message.interlink_root_hash, () => message.interlink_root_hash = dependency_1.common.ProtoHash.deserialize(reader)); + break; default: reader.skipField(); } } @@ -1855,27 +1792,896 @@ export namespace block { } } } - get transactions() { - return pb_1.Message.getRepeatedWrapperField(this, ProtoTransaction, 1) as ProtoTransaction[]; + get transactions() { + return pb_1.Message.getRepeatedWrapperField(this, ProtoTransaction, 1) as ProtoTransaction[]; + } + set transactions(value: ProtoTransaction[]) { + pb_1.Message.setRepeatedWrapperField(this, 1, value); + } + static fromObject(data: { + transactions?: ReturnType[]; + }): ProtoTransactions { + const message = new ProtoTransactions({}); + if (data.transactions != null) { + message.transactions = data.transactions.map(item => ProtoTransaction.fromObject(item)); + } + return message; + } + toObject() { + const data: { + transactions?: ReturnType[]; + } = {}; + if (this.transactions != null) { + data.transactions = this.transactions.map((item: ProtoTransaction) => item.toObject()); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.transactions.length) + writer.writeRepeatedMessage(1, this.transactions, (item: ProtoTransaction) => item.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoTransactions { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoTransactions(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.transactions, () => pb_1.Message.addToRepeatedWrapperField(message, 1, ProtoTransaction.deserialize(reader), ProtoTransaction)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): ProtoTransactions { + return ProtoTransactions.deserialize(bytes); + } + } + export class ProtoHeaders extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + headers?: ProtoHeader[]; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("headers" in data && data.headers != undefined) { + this.headers = data.headers; + } + } + } + get headers() { + return pb_1.Message.getRepeatedWrapperField(this, ProtoHeader, 1) as ProtoHeader[]; + } + set headers(value: ProtoHeader[]) { + pb_1.Message.setRepeatedWrapperField(this, 1, value); + } + static fromObject(data: { + headers?: ReturnType[]; + }): ProtoHeaders { + const message = new ProtoHeaders({}); + if (data.headers != null) { + message.headers = data.headers.map(item => ProtoHeader.fromObject(item)); + } + return message; + } + toObject() { + const data: { + headers?: ReturnType[]; + } = {}; + if (this.headers != null) { + data.headers = this.headers.map((item: ProtoHeader) => item.toObject()); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.headers.length) + writer.writeRepeatedMessage(1, this.headers, (item: ProtoHeader) => item.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoHeaders { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoHeaders(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.headers, () => pb_1.Message.addToRepeatedWrapperField(message, 1, ProtoHeader.deserialize(reader), ProtoHeader)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): ProtoHeaders { + return ProtoHeaders.deserialize(bytes); + } + } + export class ProtoManifest extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + manifest?: dependency_1.common.ProtoHash[]; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("manifest" in data && data.manifest != undefined) { + this.manifest = data.manifest; + } + } + } + get manifest() { + return pb_1.Message.getRepeatedWrapperField(this, dependency_1.common.ProtoHash, 1) as dependency_1.common.ProtoHash[]; + } + set manifest(value: dependency_1.common.ProtoHash[]) { + pb_1.Message.setRepeatedWrapperField(this, 1, value); + } + static fromObject(data: { + manifest?: ReturnType[]; + }): ProtoManifest { + const message = new ProtoManifest({}); + if (data.manifest != null) { + message.manifest = data.manifest.map(item => dependency_1.common.ProtoHash.fromObject(item)); + } + return message; + } + toObject() { + const data: { + manifest?: ReturnType[]; + } = {}; + if (this.manifest != null) { + data.manifest = this.manifest.map((item: dependency_1.common.ProtoHash) => item.toObject()); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.manifest.length) + writer.writeRepeatedMessage(1, this.manifest, (item: dependency_1.common.ProtoHash) => item.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoManifest { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoManifest(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.manifest, () => pb_1.Message.addToRepeatedWrapperField(message, 1, dependency_1.common.ProtoHash.deserialize(reader), dependency_1.common.ProtoHash)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): ProtoManifest { + return ProtoManifest.deserialize(bytes); + } + } + export class ProtoAccessList extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + access_tuples?: ProtoAccessTuple[]; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("access_tuples" in data && data.access_tuples != undefined) { + this.access_tuples = data.access_tuples; + } + } + } + get access_tuples() { + return pb_1.Message.getRepeatedWrapperField(this, ProtoAccessTuple, 1) as ProtoAccessTuple[]; + } + set access_tuples(value: ProtoAccessTuple[]) { + pb_1.Message.setRepeatedWrapperField(this, 1, value); + } + static fromObject(data: { + access_tuples?: ReturnType[]; + }): ProtoAccessList { + const message = new ProtoAccessList({}); + if (data.access_tuples != null) { + message.access_tuples = data.access_tuples.map(item => ProtoAccessTuple.fromObject(item)); + } + return message; + } + toObject() { + const data: { + access_tuples?: ReturnType[]; + } = {}; + if (this.access_tuples != null) { + data.access_tuples = this.access_tuples.map((item: ProtoAccessTuple) => item.toObject()); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.access_tuples.length) + writer.writeRepeatedMessage(1, this.access_tuples, (item: ProtoAccessTuple) => item.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoAccessList { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoAccessList(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.access_tuples, () => pb_1.Message.addToRepeatedWrapperField(message, 1, ProtoAccessTuple.deserialize(reader), ProtoAccessTuple)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): ProtoAccessList { + return ProtoAccessList.deserialize(bytes); + } + } + export class ProtoWorkObjectHeader extends pb_1.Message { + #one_of_decls: number[][] = [[1], [2], [3], [4], [5], [6], [7], [8], [9]]; + constructor(data?: any[] | ({} & (({ + header_hash?: dependency_1.common.ProtoHash; + }) | ({ + parent_hash?: dependency_1.common.ProtoHash; + }) | ({ + number?: Uint8Array; + }) | ({ + difficulty?: Uint8Array; + }) | ({ + tx_hash?: dependency_1.common.ProtoHash; + }) | ({ + nonce?: number; + }) | ({ + location?: dependency_1.common.ProtoLocation; + }) | ({ + mix_hash?: dependency_1.common.ProtoHash; + }) | ({ + time?: number; + })))) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("header_hash" in data && data.header_hash != undefined) { + this.header_hash = data.header_hash; + } + if ("parent_hash" in data && data.parent_hash != undefined) { + this.parent_hash = data.parent_hash; + } + if ("number" in data && data.number != undefined) { + this.number = data.number; + } + if ("difficulty" in data && data.difficulty != undefined) { + this.difficulty = data.difficulty; + } + if ("tx_hash" in data && data.tx_hash != undefined) { + this.tx_hash = data.tx_hash; + } + if ("nonce" in data && data.nonce != undefined) { + this.nonce = data.nonce; + } + if ("location" in data && data.location != undefined) { + this.location = data.location; + } + if ("mix_hash" in data && data.mix_hash != undefined) { + this.mix_hash = data.mix_hash; + } + if ("time" in data && data.time != undefined) { + this.time = data.time; + } + } + } + get header_hash() { + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 1) as dependency_1.common.ProtoHash; + } + set header_hash(value: dependency_1.common.ProtoHash) { + pb_1.Message.setOneofWrapperField(this, 1, this.#one_of_decls[0], value); + } + get has_header_hash() { + return pb_1.Message.getField(this, 1) != null; + } + get parent_hash() { + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 2) as dependency_1.common.ProtoHash; + } + set parent_hash(value: dependency_1.common.ProtoHash) { + pb_1.Message.setOneofWrapperField(this, 2, this.#one_of_decls[1], value); + } + get has_parent_hash() { + return pb_1.Message.getField(this, 2) != null; + } + get number() { + return pb_1.Message.getFieldWithDefault(this, 3, new Uint8Array(0)) as Uint8Array; + } + set number(value: Uint8Array) { + pb_1.Message.setOneofField(this, 3, this.#one_of_decls[2], value); + } + get has_number() { + return pb_1.Message.getField(this, 3) != null; + } + get difficulty() { + return pb_1.Message.getFieldWithDefault(this, 4, new Uint8Array(0)) as Uint8Array; + } + set difficulty(value: Uint8Array) { + pb_1.Message.setOneofField(this, 4, this.#one_of_decls[3], value); + } + get has_difficulty() { + return pb_1.Message.getField(this, 4) != null; + } + get tx_hash() { + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 5) as dependency_1.common.ProtoHash; + } + set tx_hash(value: dependency_1.common.ProtoHash) { + pb_1.Message.setOneofWrapperField(this, 5, this.#one_of_decls[4], value); + } + get has_tx_hash() { + return pb_1.Message.getField(this, 5) != null; + } + get nonce() { + return pb_1.Message.getFieldWithDefault(this, 6, 0) as number; + } + set nonce(value: number) { + pb_1.Message.setOneofField(this, 6, this.#one_of_decls[5], value); + } + get has_nonce() { + return pb_1.Message.getField(this, 6) != null; + } + get location() { + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoLocation, 7) as dependency_1.common.ProtoLocation; + } + set location(value: dependency_1.common.ProtoLocation) { + pb_1.Message.setOneofWrapperField(this, 7, this.#one_of_decls[6], value); + } + get has_location() { + return pb_1.Message.getField(this, 7) != null; + } + get mix_hash() { + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHash, 8) as dependency_1.common.ProtoHash; + } + set mix_hash(value: dependency_1.common.ProtoHash) { + pb_1.Message.setOneofWrapperField(this, 8, this.#one_of_decls[7], value); + } + get has_mix_hash() { + return pb_1.Message.getField(this, 8) != null; + } + get time() { + return pb_1.Message.getFieldWithDefault(this, 9, 0) as number; + } + set time(value: number) { + pb_1.Message.setOneofField(this, 9, this.#one_of_decls[8], value); + } + get has_time() { + return pb_1.Message.getField(this, 9) != null; + } + get _header_hash() { + const cases: { + [index: number]: "none" | "header_hash"; + } = { + 0: "none", + 1: "header_hash" + }; + return cases[pb_1.Message.computeOneofCase(this, [1])]; + } + get _parent_hash() { + const cases: { + [index: number]: "none" | "parent_hash"; + } = { + 0: "none", + 2: "parent_hash" + }; + return cases[pb_1.Message.computeOneofCase(this, [2])]; + } + get _number() { + const cases: { + [index: number]: "none" | "number"; + } = { + 0: "none", + 3: "number" + }; + return cases[pb_1.Message.computeOneofCase(this, [3])]; + } + get _difficulty() { + const cases: { + [index: number]: "none" | "difficulty"; + } = { + 0: "none", + 4: "difficulty" + }; + return cases[pb_1.Message.computeOneofCase(this, [4])]; + } + get _tx_hash() { + const cases: { + [index: number]: "none" | "tx_hash"; + } = { + 0: "none", + 5: "tx_hash" + }; + return cases[pb_1.Message.computeOneofCase(this, [5])]; + } + get _nonce() { + const cases: { + [index: number]: "none" | "nonce"; + } = { + 0: "none", + 6: "nonce" + }; + return cases[pb_1.Message.computeOneofCase(this, [6])]; + } + get _location() { + const cases: { + [index: number]: "none" | "location"; + } = { + 0: "none", + 7: "location" + }; + return cases[pb_1.Message.computeOneofCase(this, [7])]; + } + get _mix_hash() { + const cases: { + [index: number]: "none" | "mix_hash"; + } = { + 0: "none", + 8: "mix_hash" + }; + return cases[pb_1.Message.computeOneofCase(this, [8])]; + } + get _time() { + const cases: { + [index: number]: "none" | "time"; + } = { + 0: "none", + 9: "time" + }; + return cases[pb_1.Message.computeOneofCase(this, [9])]; + } + static fromObject(data: { + header_hash?: ReturnType; + parent_hash?: ReturnType; + number?: Uint8Array; + difficulty?: Uint8Array; + tx_hash?: ReturnType; + nonce?: number; + location?: ReturnType; + mix_hash?: ReturnType; + time?: number; + }): ProtoWorkObjectHeader { + const message = new ProtoWorkObjectHeader({}); + if (data.header_hash != null) { + message.header_hash = dependency_1.common.ProtoHash.fromObject(data.header_hash); + } + if (data.parent_hash != null) { + message.parent_hash = dependency_1.common.ProtoHash.fromObject(data.parent_hash); + } + if (data.number != null) { + message.number = data.number; + } + if (data.difficulty != null) { + message.difficulty = data.difficulty; + } + if (data.tx_hash != null) { + message.tx_hash = dependency_1.common.ProtoHash.fromObject(data.tx_hash); + } + if (data.nonce != null) { + message.nonce = data.nonce; + } + if (data.location != null) { + message.location = dependency_1.common.ProtoLocation.fromObject(data.location); + } + if (data.mix_hash != null) { + message.mix_hash = dependency_1.common.ProtoHash.fromObject(data.mix_hash); + } + if (data.time != null) { + message.time = data.time; + } + return message; + } + toObject() { + const data: { + header_hash?: ReturnType; + parent_hash?: ReturnType; + number?: Uint8Array; + difficulty?: Uint8Array; + tx_hash?: ReturnType; + nonce?: number; + location?: ReturnType; + mix_hash?: ReturnType; + time?: number; + } = {}; + if (this.header_hash != null) { + data.header_hash = this.header_hash.toObject(); + } + if (this.parent_hash != null) { + data.parent_hash = this.parent_hash.toObject(); + } + if (this.number != null) { + data.number = this.number; + } + if (this.difficulty != null) { + data.difficulty = this.difficulty; + } + if (this.tx_hash != null) { + data.tx_hash = this.tx_hash.toObject(); + } + if (this.nonce != null) { + data.nonce = this.nonce; + } + if (this.location != null) { + data.location = this.location.toObject(); + } + if (this.mix_hash != null) { + data.mix_hash = this.mix_hash.toObject(); + } + if (this.time != null) { + data.time = this.time; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.has_header_hash) + writer.writeMessage(1, this.header_hash, () => this.header_hash.serialize(writer)); + if (this.has_parent_hash) + writer.writeMessage(2, this.parent_hash, () => this.parent_hash.serialize(writer)); + if (this.has_number) + writer.writeBytes(3, this.number); + if (this.has_difficulty) + writer.writeBytes(4, this.difficulty); + if (this.has_tx_hash) + writer.writeMessage(5, this.tx_hash, () => this.tx_hash.serialize(writer)); + if (this.has_nonce) + writer.writeUint64(6, this.nonce); + if (this.has_location) + writer.writeMessage(7, this.location, () => this.location.serialize(writer)); + if (this.has_mix_hash) + writer.writeMessage(8, this.mix_hash, () => this.mix_hash.serialize(writer)); + if (this.has_time) + writer.writeUint64(9, this.time); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoWorkObjectHeader { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoWorkObjectHeader(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.header_hash, () => message.header_hash = dependency_1.common.ProtoHash.deserialize(reader)); + break; + case 2: + reader.readMessage(message.parent_hash, () => message.parent_hash = dependency_1.common.ProtoHash.deserialize(reader)); + break; + case 3: + message.number = reader.readBytes(); + break; + case 4: + message.difficulty = reader.readBytes(); + break; + case 5: + reader.readMessage(message.tx_hash, () => message.tx_hash = dependency_1.common.ProtoHash.deserialize(reader)); + break; + case 6: + message.nonce = reader.readUint64(); + break; + case 7: + reader.readMessage(message.location, () => message.location = dependency_1.common.ProtoLocation.deserialize(reader)); + break; + case 8: + reader.readMessage(message.mix_hash, () => message.mix_hash = dependency_1.common.ProtoHash.deserialize(reader)); + break; + case 9: + message.time = reader.readUint64(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): ProtoWorkObjectHeader { + return ProtoWorkObjectHeader.deserialize(bytes); + } + } + export class ProtoWorkObjectHeaders extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + wo_headers?: ProtoWorkObjectHeader[]; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("wo_headers" in data && data.wo_headers != undefined) { + this.wo_headers = data.wo_headers; + } + } + } + get wo_headers() { + return pb_1.Message.getRepeatedWrapperField(this, ProtoWorkObjectHeader, 1) as ProtoWorkObjectHeader[]; + } + set wo_headers(value: ProtoWorkObjectHeader[]) { + pb_1.Message.setRepeatedWrapperField(this, 1, value); + } + static fromObject(data: { + wo_headers?: ReturnType[]; + }): ProtoWorkObjectHeaders { + const message = new ProtoWorkObjectHeaders({}); + if (data.wo_headers != null) { + message.wo_headers = data.wo_headers.map(item => ProtoWorkObjectHeader.fromObject(item)); + } + return message; + } + toObject() { + const data: { + wo_headers?: ReturnType[]; + } = {}; + if (this.wo_headers != null) { + data.wo_headers = this.wo_headers.map((item: ProtoWorkObjectHeader) => item.toObject()); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.wo_headers.length) + writer.writeRepeatedMessage(1, this.wo_headers, (item: ProtoWorkObjectHeader) => item.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoWorkObjectHeaders { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoWorkObjectHeaders(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.wo_headers, () => pb_1.Message.addToRepeatedWrapperField(message, 1, ProtoWorkObjectHeader.deserialize(reader), ProtoWorkObjectHeader)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): ProtoWorkObjectHeaders { + return ProtoWorkObjectHeaders.deserialize(bytes); + } + } + export class ProtoWorkObjectBody extends pb_1.Message { + #one_of_decls: number[][] = [[1], [2], [3], [4], [5], [6]]; + constructor(data?: any[] | ({} & (({ + header?: ProtoHeader; + }) | ({ + transactions?: ProtoTransactions; + }) | ({ + uncles?: ProtoWorkObjectHeaders; + }) | ({ + ext_transactions?: ProtoTransactions; + }) | ({ + manifest?: ProtoManifest; + }) | ({ + interlink_hashes?: dependency_1.common.ProtoHashes; + })))) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("header" in data && data.header != undefined) { + this.header = data.header; + } + if ("transactions" in data && data.transactions != undefined) { + this.transactions = data.transactions; + } + if ("uncles" in data && data.uncles != undefined) { + this.uncles = data.uncles; + } + if ("ext_transactions" in data && data.ext_transactions != undefined) { + this.ext_transactions = data.ext_transactions; + } + if ("manifest" in data && data.manifest != undefined) { + this.manifest = data.manifest; + } + if ("interlink_hashes" in data && data.interlink_hashes != undefined) { + this.interlink_hashes = data.interlink_hashes; + } + } + } + get header() { + return pb_1.Message.getWrapperField(this, ProtoHeader, 1) as ProtoHeader; + } + set header(value: ProtoHeader) { + pb_1.Message.setOneofWrapperField(this, 1, this.#one_of_decls[0], value); + } + get has_header() { + return pb_1.Message.getField(this, 1) != null; + } + get transactions() { + return pb_1.Message.getWrapperField(this, ProtoTransactions, 2) as ProtoTransactions; + } + set transactions(value: ProtoTransactions) { + pb_1.Message.setOneofWrapperField(this, 2, this.#one_of_decls[1], value); + } + get has_transactions() { + return pb_1.Message.getField(this, 2) != null; + } + get uncles() { + return pb_1.Message.getWrapperField(this, ProtoWorkObjectHeaders, 3) as ProtoWorkObjectHeaders; + } + set uncles(value: ProtoWorkObjectHeaders) { + pb_1.Message.setOneofWrapperField(this, 3, this.#one_of_decls[2], value); + } + get has_uncles() { + return pb_1.Message.getField(this, 3) != null; + } + get ext_transactions() { + return pb_1.Message.getWrapperField(this, ProtoTransactions, 4) as ProtoTransactions; + } + set ext_transactions(value: ProtoTransactions) { + pb_1.Message.setOneofWrapperField(this, 4, this.#one_of_decls[3], value); + } + get has_ext_transactions() { + return pb_1.Message.getField(this, 4) != null; + } + get manifest() { + return pb_1.Message.getWrapperField(this, ProtoManifest, 5) as ProtoManifest; + } + set manifest(value: ProtoManifest) { + pb_1.Message.setOneofWrapperField(this, 5, this.#one_of_decls[4], value); + } + get has_manifest() { + return pb_1.Message.getField(this, 5) != null; + } + get interlink_hashes() { + return pb_1.Message.getWrapperField(this, dependency_1.common.ProtoHashes, 6) as dependency_1.common.ProtoHashes; + } + set interlink_hashes(value: dependency_1.common.ProtoHashes) { + pb_1.Message.setOneofWrapperField(this, 6, this.#one_of_decls[5], value); + } + get has_interlink_hashes() { + return pb_1.Message.getField(this, 6) != null; + } + get _header() { + const cases: { + [index: number]: "none" | "header"; + } = { + 0: "none", + 1: "header" + }; + return cases[pb_1.Message.computeOneofCase(this, [1])]; + } + get _transactions() { + const cases: { + [index: number]: "none" | "transactions"; + } = { + 0: "none", + 2: "transactions" + }; + return cases[pb_1.Message.computeOneofCase(this, [2])]; + } + get _uncles() { + const cases: { + [index: number]: "none" | "uncles"; + } = { + 0: "none", + 3: "uncles" + }; + return cases[pb_1.Message.computeOneofCase(this, [3])]; + } + get _ext_transactions() { + const cases: { + [index: number]: "none" | "ext_transactions"; + } = { + 0: "none", + 4: "ext_transactions" + }; + return cases[pb_1.Message.computeOneofCase(this, [4])]; } - set transactions(value: ProtoTransaction[]) { - pb_1.Message.setRepeatedWrapperField(this, 1, value); + get _manifest() { + const cases: { + [index: number]: "none" | "manifest"; + } = { + 0: "none", + 5: "manifest" + }; + return cases[pb_1.Message.computeOneofCase(this, [5])]; + } + get _interlink_hashes() { + const cases: { + [index: number]: "none" | "interlink_hashes"; + } = { + 0: "none", + 6: "interlink_hashes" + }; + return cases[pb_1.Message.computeOneofCase(this, [6])]; } static fromObject(data: { - transactions?: ReturnType[]; - }): ProtoTransactions { - const message = new ProtoTransactions({}); + header?: ReturnType; + transactions?: ReturnType; + uncles?: ReturnType; + ext_transactions?: ReturnType; + manifest?: ReturnType; + interlink_hashes?: ReturnType; + }): ProtoWorkObjectBody { + const message = new ProtoWorkObjectBody({}); + if (data.header != null) { + message.header = ProtoHeader.fromObject(data.header); + } if (data.transactions != null) { - message.transactions = data.transactions.map(item => ProtoTransaction.fromObject(item)); + message.transactions = ProtoTransactions.fromObject(data.transactions); + } + if (data.uncles != null) { + message.uncles = ProtoWorkObjectHeaders.fromObject(data.uncles); + } + if (data.ext_transactions != null) { + message.ext_transactions = ProtoTransactions.fromObject(data.ext_transactions); + } + if (data.manifest != null) { + message.manifest = ProtoManifest.fromObject(data.manifest); + } + if (data.interlink_hashes != null) { + message.interlink_hashes = dependency_1.common.ProtoHashes.fromObject(data.interlink_hashes); } return message; } toObject() { const data: { - transactions?: ReturnType[]; + header?: ReturnType; + transactions?: ReturnType; + uncles?: ReturnType; + ext_transactions?: ReturnType; + manifest?: ReturnType; + interlink_hashes?: ReturnType; } = {}; + if (this.header != null) { + data.header = this.header.toObject(); + } if (this.transactions != null) { - data.transactions = this.transactions.map((item: ProtoTransaction) => item.toObject()); + data.transactions = this.transactions.toObject(); + } + if (this.uncles != null) { + data.uncles = this.uncles.toObject(); + } + if (this.ext_transactions != null) { + data.ext_transactions = this.ext_transactions.toObject(); + } + if (this.manifest != null) { + data.manifest = this.manifest.toObject(); + } + if (this.interlink_hashes != null) { + data.interlink_hashes = this.interlink_hashes.toObject(); } return data; } @@ -1883,19 +2689,44 @@ export namespace block { serialize(w: pb_1.BinaryWriter): void; serialize(w?: pb_1.BinaryWriter): Uint8Array | void { const writer = w || new pb_1.BinaryWriter(); - if (this.transactions.length) - writer.writeRepeatedMessage(1, this.transactions, (item: ProtoTransaction) => item.serialize(writer)); + if (this.has_header) + writer.writeMessage(1, this.header, () => this.header.serialize(writer)); + if (this.has_transactions) + writer.writeMessage(2, this.transactions, () => this.transactions.serialize(writer)); + if (this.has_uncles) + writer.writeMessage(3, this.uncles, () => this.uncles.serialize(writer)); + if (this.has_ext_transactions) + writer.writeMessage(4, this.ext_transactions, () => this.ext_transactions.serialize(writer)); + if (this.has_manifest) + writer.writeMessage(5, this.manifest, () => this.manifest.serialize(writer)); + if (this.has_interlink_hashes) + writer.writeMessage(6, this.interlink_hashes, () => this.interlink_hashes.serialize(writer)); if (!w) return writer.getResultBuffer(); } - static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoTransactions { - const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoTransactions(); + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoWorkObjectBody { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoWorkObjectBody(); while (reader.nextField()) { if (reader.isEndGroup()) break; switch (reader.getFieldNumber()) { case 1: - reader.readMessage(message.transactions, () => pb_1.Message.addToRepeatedWrapperField(message, 1, ProtoTransaction.deserialize(reader), ProtoTransaction)); + reader.readMessage(message.header, () => message.header = ProtoHeader.deserialize(reader)); + break; + case 2: + reader.readMessage(message.transactions, () => message.transactions = ProtoTransactions.deserialize(reader)); + break; + case 3: + reader.readMessage(message.uncles, () => message.uncles = ProtoWorkObjectHeaders.deserialize(reader)); + break; + case 4: + reader.readMessage(message.ext_transactions, () => message.ext_transactions = ProtoTransactions.deserialize(reader)); + break; + case 5: + reader.readMessage(message.manifest, () => message.manifest = ProtoManifest.deserialize(reader)); + break; + case 6: + reader.readMessage(message.interlink_hashes, () => message.interlink_hashes = dependency_1.common.ProtoHashes.deserialize(reader)); break; default: reader.skipField(); } @@ -1905,111 +2736,118 @@ export namespace block { serializeBinary(): Uint8Array { return this.serialize(); } - static deserializeBinary(bytes: Uint8Array): ProtoTransactions { - return ProtoTransactions.deserialize(bytes); + static deserializeBinary(bytes: Uint8Array): ProtoWorkObjectBody { + return ProtoWorkObjectBody.deserialize(bytes); } } - export class ProtoHeaders extends pb_1.Message { - #one_of_decls: number[][] = []; - constructor(data?: any[] | { - headers?: ProtoHeader[]; - }) { + export class ProtoWorkObject extends pb_1.Message { + #one_of_decls: number[][] = [[1], [2], [3]]; + constructor(data?: any[] | ({} & (({ + wo_header?: ProtoWorkObjectHeader; + }) | ({ + wo_body?: ProtoWorkObjectBody; + }) | ({ + tx?: ProtoTransaction; + })))) { super(); - pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1], this.#one_of_decls); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); if (!Array.isArray(data) && typeof data == "object") { - if ("headers" in data && data.headers != undefined) { - this.headers = data.headers; + if ("wo_header" in data && data.wo_header != undefined) { + this.wo_header = data.wo_header; + } + if ("wo_body" in data && data.wo_body != undefined) { + this.wo_body = data.wo_body; + } + if ("tx" in data && data.tx != undefined) { + this.tx = data.tx; } } } - get headers() { - return pb_1.Message.getRepeatedWrapperField(this, ProtoHeader, 1) as ProtoHeader[]; + get wo_header() { + return pb_1.Message.getWrapperField(this, ProtoWorkObjectHeader, 1) as ProtoWorkObjectHeader; } - set headers(value: ProtoHeader[]) { - pb_1.Message.setRepeatedWrapperField(this, 1, value); + set wo_header(value: ProtoWorkObjectHeader) { + pb_1.Message.setOneofWrapperField(this, 1, this.#one_of_decls[0], value); } - static fromObject(data: { - headers?: ReturnType[]; - }): ProtoHeaders { - const message = new ProtoHeaders({}); - if (data.headers != null) { - message.headers = data.headers.map(item => ProtoHeader.fromObject(item)); - } - return message; + get has_wo_header() { + return pb_1.Message.getField(this, 1) != null; } - toObject() { - const data: { - headers?: ReturnType[]; - } = {}; - if (this.headers != null) { - data.headers = this.headers.map((item: ProtoHeader) => item.toObject()); - } - return data; + get wo_body() { + return pb_1.Message.getWrapperField(this, ProtoWorkObjectBody, 2) as ProtoWorkObjectBody; } - serialize(): Uint8Array; - serialize(w: pb_1.BinaryWriter): void; - serialize(w?: pb_1.BinaryWriter): Uint8Array | void { - const writer = w || new pb_1.BinaryWriter(); - if (this.headers.length) - writer.writeRepeatedMessage(1, this.headers, (item: ProtoHeader) => item.serialize(writer)); - if (!w) - return writer.getResultBuffer(); + set wo_body(value: ProtoWorkObjectBody) { + pb_1.Message.setOneofWrapperField(this, 2, this.#one_of_decls[1], value); } - static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoHeaders { - const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoHeaders(); - while (reader.nextField()) { - if (reader.isEndGroup()) - break; - switch (reader.getFieldNumber()) { - case 1: - reader.readMessage(message.headers, () => pb_1.Message.addToRepeatedWrapperField(message, 1, ProtoHeader.deserialize(reader), ProtoHeader)); - break; - default: reader.skipField(); - } - } - return message; + get has_wo_body() { + return pb_1.Message.getField(this, 2) != null; } - serializeBinary(): Uint8Array { - return this.serialize(); + get tx() { + return pb_1.Message.getWrapperField(this, ProtoTransaction, 3) as ProtoTransaction; } - static deserializeBinary(bytes: Uint8Array): ProtoHeaders { - return ProtoHeaders.deserialize(bytes); + set tx(value: ProtoTransaction) { + pb_1.Message.setOneofWrapperField(this, 3, this.#one_of_decls[2], value); } - } - export class ProtoManifest extends pb_1.Message { - #one_of_decls: number[][] = []; - constructor(data?: any[] | { - manifest?: dependency_1.common.ProtoHash[]; - }) { - super(); - pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1], this.#one_of_decls); - if (!Array.isArray(data) && typeof data == "object") { - if ("manifest" in data && data.manifest != undefined) { - this.manifest = data.manifest; - } - } + get has_tx() { + return pb_1.Message.getField(this, 3) != null; } - get manifest() { - return pb_1.Message.getRepeatedWrapperField(this, dependency_1.common.ProtoHash, 1) as dependency_1.common.ProtoHash[]; + get _wo_header() { + const cases: { + [index: number]: "none" | "wo_header"; + } = { + 0: "none", + 1: "wo_header" + }; + return cases[pb_1.Message.computeOneofCase(this, [1])]; } - set manifest(value: dependency_1.common.ProtoHash[]) { - pb_1.Message.setRepeatedWrapperField(this, 1, value); + get _wo_body() { + const cases: { + [index: number]: "none" | "wo_body"; + } = { + 0: "none", + 2: "wo_body" + }; + return cases[pb_1.Message.computeOneofCase(this, [2])]; + } + get _tx() { + const cases: { + [index: number]: "none" | "tx"; + } = { + 0: "none", + 3: "tx" + }; + return cases[pb_1.Message.computeOneofCase(this, [3])]; } static fromObject(data: { - manifest?: ReturnType[]; - }): ProtoManifest { - const message = new ProtoManifest({}); - if (data.manifest != null) { - message.manifest = data.manifest.map(item => dependency_1.common.ProtoHash.fromObject(item)); + wo_header?: ReturnType; + wo_body?: ReturnType; + tx?: ReturnType; + }): ProtoWorkObject { + const message = new ProtoWorkObject({}); + if (data.wo_header != null) { + message.wo_header = ProtoWorkObjectHeader.fromObject(data.wo_header); + } + if (data.wo_body != null) { + message.wo_body = ProtoWorkObjectBody.fromObject(data.wo_body); + } + if (data.tx != null) { + message.tx = ProtoTransaction.fromObject(data.tx); } return message; } toObject() { const data: { - manifest?: ReturnType[]; + wo_header?: ReturnType; + wo_body?: ReturnType; + tx?: ReturnType; } = {}; - if (this.manifest != null) { - data.manifest = this.manifest.map((item: dependency_1.common.ProtoHash) => item.toObject()); + if (this.wo_header != null) { + data.wo_header = this.wo_header.toObject(); + } + if (this.wo_body != null) { + data.wo_body = this.wo_body.toObject(); + } + if (this.tx != null) { + data.tx = this.tx.toObject(); } return data; } @@ -2017,19 +2855,29 @@ export namespace block { serialize(w: pb_1.BinaryWriter): void; serialize(w?: pb_1.BinaryWriter): Uint8Array | void { const writer = w || new pb_1.BinaryWriter(); - if (this.manifest.length) - writer.writeRepeatedMessage(1, this.manifest, (item: dependency_1.common.ProtoHash) => item.serialize(writer)); + if (this.has_wo_header) + writer.writeMessage(1, this.wo_header, () => this.wo_header.serialize(writer)); + if (this.has_wo_body) + writer.writeMessage(2, this.wo_body, () => this.wo_body.serialize(writer)); + if (this.has_tx) + writer.writeMessage(3, this.tx, () => this.tx.serialize(writer)); if (!w) return writer.getResultBuffer(); } - static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoManifest { - const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoManifest(); + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoWorkObject { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoWorkObject(); while (reader.nextField()) { if (reader.isEndGroup()) break; switch (reader.getFieldNumber()) { case 1: - reader.readMessage(message.manifest, () => pb_1.Message.addToRepeatedWrapperField(message, 1, dependency_1.common.ProtoHash.deserialize(reader), dependency_1.common.ProtoHash)); + reader.readMessage(message.wo_header, () => message.wo_header = ProtoWorkObjectHeader.deserialize(reader)); + break; + case 2: + reader.readMessage(message.wo_body, () => message.wo_body = ProtoWorkObjectBody.deserialize(reader)); + break; + case 3: + reader.readMessage(message.tx, () => message.tx = ProtoTransaction.deserialize(reader)); break; default: reader.skipField(); } @@ -2039,44 +2887,44 @@ export namespace block { serializeBinary(): Uint8Array { return this.serialize(); } - static deserializeBinary(bytes: Uint8Array): ProtoManifest { - return ProtoManifest.deserialize(bytes); + static deserializeBinary(bytes: Uint8Array): ProtoWorkObject { + return ProtoWorkObject.deserialize(bytes); } } - export class ProtoAccessList extends pb_1.Message { + export class ProtoWorkObjects extends pb_1.Message { #one_of_decls: number[][] = []; constructor(data?: any[] | { - access_tuples?: ProtoAccessTuple[]; + work_objects?: ProtoWorkObject[]; }) { super(); pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1], this.#one_of_decls); if (!Array.isArray(data) && typeof data == "object") { - if ("access_tuples" in data && data.access_tuples != undefined) { - this.access_tuples = data.access_tuples; + if ("work_objects" in data && data.work_objects != undefined) { + this.work_objects = data.work_objects; } } } - get access_tuples() { - return pb_1.Message.getRepeatedWrapperField(this, ProtoAccessTuple, 1) as ProtoAccessTuple[]; + get work_objects() { + return pb_1.Message.getRepeatedWrapperField(this, ProtoWorkObject, 1) as ProtoWorkObject[]; } - set access_tuples(value: ProtoAccessTuple[]) { + set work_objects(value: ProtoWorkObject[]) { pb_1.Message.setRepeatedWrapperField(this, 1, value); } static fromObject(data: { - access_tuples?: ReturnType[]; - }): ProtoAccessList { - const message = new ProtoAccessList({}); - if (data.access_tuples != null) { - message.access_tuples = data.access_tuples.map(item => ProtoAccessTuple.fromObject(item)); + work_objects?: ReturnType[]; + }): ProtoWorkObjects { + const message = new ProtoWorkObjects({}); + if (data.work_objects != null) { + message.work_objects = data.work_objects.map(item => ProtoWorkObject.fromObject(item)); } return message; } toObject() { const data: { - access_tuples?: ReturnType[]; + work_objects?: ReturnType[]; } = {}; - if (this.access_tuples != null) { - data.access_tuples = this.access_tuples.map((item: ProtoAccessTuple) => item.toObject()); + if (this.work_objects != null) { + data.work_objects = this.work_objects.map((item: ProtoWorkObject) => item.toObject()); } return data; } @@ -2084,19 +2932,19 @@ export namespace block { serialize(w: pb_1.BinaryWriter): void; serialize(w?: pb_1.BinaryWriter): Uint8Array | void { const writer = w || new pb_1.BinaryWriter(); - if (this.access_tuples.length) - writer.writeRepeatedMessage(1, this.access_tuples, (item: ProtoAccessTuple) => item.serialize(writer)); + if (this.work_objects.length) + writer.writeRepeatedMessage(1, this.work_objects, (item: ProtoWorkObject) => item.serialize(writer)); if (!w) return writer.getResultBuffer(); } - static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoAccessList { - const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoAccessList(); + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoWorkObjects { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoWorkObjects(); while (reader.nextField()) { if (reader.isEndGroup()) break; switch (reader.getFieldNumber()) { case 1: - reader.readMessage(message.access_tuples, () => pb_1.Message.addToRepeatedWrapperField(message, 1, ProtoAccessTuple.deserialize(reader), ProtoAccessTuple)); + reader.readMessage(message.work_objects, () => pb_1.Message.addToRepeatedWrapperField(message, 1, ProtoWorkObject.deserialize(reader), ProtoWorkObject)); break; default: reader.skipField(); } @@ -2106,8 +2954,8 @@ export namespace block { serializeBinary(): Uint8Array { return this.serialize(); } - static deserializeBinary(bytes: Uint8Array): ProtoAccessList { - return ProtoAccessList.deserialize(bytes); + static deserializeBinary(bytes: Uint8Array): ProtoWorkObjects { + return ProtoWorkObjects.deserialize(bytes); } } export class ProtoAccessTuple extends pb_1.Message { @@ -2670,28 +3518,28 @@ export namespace block { export class ProtoPendingHeader extends pb_1.Message { #one_of_decls: number[][] = [[1], [2]]; constructor(data?: any[] | ({} & (({ - header?: ProtoHeader; + wo?: ProtoWorkObject; }) | ({ termini?: ProtoTermini; })))) { super(); pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); if (!Array.isArray(data) && typeof data == "object") { - if ("header" in data && data.header != undefined) { - this.header = data.header; + if ("wo" in data && data.wo != undefined) { + this.wo = data.wo; } if ("termini" in data && data.termini != undefined) { this.termini = data.termini; } } } - get header() { - return pb_1.Message.getWrapperField(this, ProtoHeader, 1) as ProtoHeader; + get wo() { + return pb_1.Message.getWrapperField(this, ProtoWorkObject, 1) as ProtoWorkObject; } - set header(value: ProtoHeader) { + set wo(value: ProtoWorkObject) { pb_1.Message.setOneofWrapperField(this, 1, this.#one_of_decls[0], value); } - get has_header() { + get has_wo() { return pb_1.Message.getField(this, 1) != null; } get termini() { @@ -2703,12 +3551,12 @@ export namespace block { get has_termini() { return pb_1.Message.getField(this, 2) != null; } - get _header() { + get _wo() { const cases: { - [index: number]: "none" | "header"; + [index: number]: "none" | "wo"; } = { 0: "none", - 1: "header" + 1: "wo" }; return cases[pb_1.Message.computeOneofCase(this, [1])]; } @@ -2722,12 +3570,12 @@ export namespace block { return cases[pb_1.Message.computeOneofCase(this, [2])]; } static fromObject(data: { - header?: ReturnType; + wo?: ReturnType; termini?: ReturnType; }): ProtoPendingHeader { const message = new ProtoPendingHeader({}); - if (data.header != null) { - message.header = ProtoHeader.fromObject(data.header); + if (data.wo != null) { + message.wo = ProtoWorkObject.fromObject(data.wo); } if (data.termini != null) { message.termini = ProtoTermini.fromObject(data.termini); @@ -2736,11 +3584,11 @@ export namespace block { } toObject() { const data: { - header?: ReturnType; + wo?: ReturnType; termini?: ReturnType; } = {}; - if (this.header != null) { - data.header = this.header.toObject(); + if (this.wo != null) { + data.wo = this.wo.toObject(); } if (this.termini != null) { data.termini = this.termini.toObject(); @@ -2751,8 +3599,8 @@ export namespace block { serialize(w: pb_1.BinaryWriter): void; serialize(w?: pb_1.BinaryWriter): Uint8Array | void { const writer = w || new pb_1.BinaryWriter(); - if (this.has_header) - writer.writeMessage(1, this.header, () => this.header.serialize(writer)); + if (this.has_wo) + writer.writeMessage(1, this.wo, () => this.wo.serialize(writer)); if (this.has_termini) writer.writeMessage(2, this.termini, () => this.termini.serialize(writer)); if (!w) @@ -2765,7 +3613,7 @@ export namespace block { break; switch (reader.getFieldNumber()) { case 1: - reader.readMessage(message.header, () => message.header = ProtoHeader.deserialize(reader)); + reader.readMessage(message.wo, () => message.wo = ProtoWorkObject.deserialize(reader)); break; case 2: reader.readMessage(message.termini, () => message.termini = ProtoTermini.deserialize(reader)); @@ -2954,7 +3802,7 @@ export namespace block { export class ProtoPendingEtxs extends pb_1.Message { #one_of_decls: number[][] = [[1], [2]]; constructor(data?: any[] | ({} & (({ - header?: ProtoHeader; + header?: ProtoWorkObject; }) | ({ etxs?: ProtoTransactions; })))) { @@ -2970,9 +3818,9 @@ export namespace block { } } get header() { - return pb_1.Message.getWrapperField(this, ProtoHeader, 1) as ProtoHeader; + return pb_1.Message.getWrapperField(this, ProtoWorkObject, 1) as ProtoWorkObject; } - set header(value: ProtoHeader) { + set header(value: ProtoWorkObject) { pb_1.Message.setOneofWrapperField(this, 1, this.#one_of_decls[0], value); } get has_header() { @@ -3006,12 +3854,12 @@ export namespace block { return cases[pb_1.Message.computeOneofCase(this, [2])]; } static fromObject(data: { - header?: ReturnType; + header?: ReturnType; etxs?: ReturnType; }): ProtoPendingEtxs { const message = new ProtoPendingEtxs({}); if (data.header != null) { - message.header = ProtoHeader.fromObject(data.header); + message.header = ProtoWorkObject.fromObject(data.header); } if (data.etxs != null) { message.etxs = ProtoTransactions.fromObject(data.etxs); @@ -3020,7 +3868,7 @@ export namespace block { } toObject() { const data: { - header?: ReturnType; + header?: ReturnType; etxs?: ReturnType; } = {}; if (this.header != null) { @@ -3049,7 +3897,7 @@ export namespace block { break; switch (reader.getFieldNumber()) { case 1: - reader.readMessage(message.header, () => message.header = ProtoHeader.deserialize(reader)); + reader.readMessage(message.header, () => message.header = ProtoWorkObject.deserialize(reader)); break; case 2: reader.readMessage(message.etxs, () => message.etxs = ProtoTransactions.deserialize(reader)); @@ -3069,7 +3917,7 @@ export namespace block { export class ProtoPendingEtxsRollup extends pb_1.Message { #one_of_decls: number[][] = [[1], [2]]; constructor(data?: any[] | ({} & (({ - header?: ProtoHeader; + header?: ProtoWorkObject; }) | ({ etxs_rollup?: ProtoTransactions; })))) { @@ -3085,9 +3933,9 @@ export namespace block { } } get header() { - return pb_1.Message.getWrapperField(this, ProtoHeader, 1) as ProtoHeader; + return pb_1.Message.getWrapperField(this, ProtoWorkObject, 1) as ProtoWorkObject; } - set header(value: ProtoHeader) { + set header(value: ProtoWorkObject) { pb_1.Message.setOneofWrapperField(this, 1, this.#one_of_decls[0], value); } get has_header() { @@ -3121,12 +3969,12 @@ export namespace block { return cases[pb_1.Message.computeOneofCase(this, [2])]; } static fromObject(data: { - header?: ReturnType; + header?: ReturnType; etxs_rollup?: ReturnType; }): ProtoPendingEtxsRollup { const message = new ProtoPendingEtxsRollup({}); if (data.header != null) { - message.header = ProtoHeader.fromObject(data.header); + message.header = ProtoWorkObject.fromObject(data.header); } if (data.etxs_rollup != null) { message.etxs_rollup = ProtoTransactions.fromObject(data.etxs_rollup); @@ -3135,7 +3983,7 @@ export namespace block { } toObject() { const data: { - header?: ReturnType; + header?: ReturnType; etxs_rollup?: ReturnType; } = {}; if (this.header != null) { @@ -3164,7 +4012,7 @@ export namespace block { break; switch (reader.getFieldNumber()) { case 1: - reader.readMessage(message.header, () => message.header = ProtoHeader.deserialize(reader)); + reader.readMessage(message.header, () => message.header = ProtoWorkObject.deserialize(reader)); break; case 2: reader.readMessage(message.etxs_rollup, () => message.etxs_rollup = ProtoTransactions.deserialize(reader)); diff --git a/src.ts/utils/ProtoBuf/proto_common.proto b/src.ts/utils/ProtoBuf/proto_common.proto index fde7d2d7..f2c83bed 100644 --- a/src.ts/utils/ProtoBuf/proto_common.proto +++ b/src.ts/utils/ProtoBuf/proto_common.proto @@ -11,3 +11,7 @@ message ProtoHash { bytes value = 1; } message ProtoHashes { repeated ProtoHash hashes = 1; } message ProtoAddress { bytes value = 1; } + +message ProtoNumber { uint64 value = 1; } + +message ProtoLocations { repeated ProtoLocation locations = 1; } \ No newline at end of file diff --git a/src.ts/utils/ProtoBuf/proto_common.ts b/src.ts/utils/ProtoBuf/proto_common.ts index a2313e4f..fcc3d3af 100644 --- a/src.ts/utils/ProtoBuf/proto_common.ts +++ b/src.ts/utils/ProtoBuf/proto_common.ts @@ -273,4 +273,138 @@ export namespace common { return ProtoAddress.deserialize(bytes); } } + export class ProtoNumber extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + value?: number; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("value" in data && data.value != undefined) { + this.value = data.value; + } + } + } + get value() { + return pb_1.Message.getFieldWithDefault(this, 1, 0) as number; + } + set value(value: number) { + pb_1.Message.setField(this, 1, value); + } + static fromObject(data: { + value?: number; + }): ProtoNumber { + const message = new ProtoNumber({}); + if (data.value != null) { + message.value = data.value; + } + return message; + } + toObject() { + const data: { + value?: number; + } = {}; + if (this.value != null) { + data.value = this.value; + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.value != 0) + writer.writeUint64(1, this.value); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoNumber { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoNumber(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + message.value = reader.readUint64(); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): ProtoNumber { + return ProtoNumber.deserialize(bytes); + } + } + export class ProtoLocations extends pb_1.Message { + #one_of_decls: number[][] = []; + constructor(data?: any[] | { + locations?: ProtoLocation[]; + }) { + super(); + pb_1.Message.initialize(this, Array.isArray(data) ? data : [], 0, -1, [1], this.#one_of_decls); + if (!Array.isArray(data) && typeof data == "object") { + if ("locations" in data && data.locations != undefined) { + this.locations = data.locations; + } + } + } + get locations() { + return pb_1.Message.getRepeatedWrapperField(this, ProtoLocation, 1) as ProtoLocation[]; + } + set locations(value: ProtoLocation[]) { + pb_1.Message.setRepeatedWrapperField(this, 1, value); + } + static fromObject(data: { + locations?: ReturnType[]; + }): ProtoLocations { + const message = new ProtoLocations({}); + if (data.locations != null) { + message.locations = data.locations.map(item => ProtoLocation.fromObject(item)); + } + return message; + } + toObject() { + const data: { + locations?: ReturnType[]; + } = {}; + if (this.locations != null) { + data.locations = this.locations.map((item: ProtoLocation) => item.toObject()); + } + return data; + } + serialize(): Uint8Array; + serialize(w: pb_1.BinaryWriter): void; + serialize(w?: pb_1.BinaryWriter): Uint8Array | void { + const writer = w || new pb_1.BinaryWriter(); + if (this.locations.length) + writer.writeRepeatedMessage(1, this.locations, (item: ProtoLocation) => item.serialize(writer)); + if (!w) + return writer.getResultBuffer(); + } + static deserialize(bytes: Uint8Array | pb_1.BinaryReader): ProtoLocations { + const reader = bytes instanceof pb_1.BinaryReader ? bytes : new pb_1.BinaryReader(bytes), message = new ProtoLocations(); + while (reader.nextField()) { + if (reader.isEndGroup()) + break; + switch (reader.getFieldNumber()) { + case 1: + reader.readMessage(message.locations, () => pb_1.Message.addToRepeatedWrapperField(message, 1, ProtoLocation.deserialize(reader), ProtoLocation)); + break; + default: reader.skipField(); + } + } + return message; + } + serializeBinary(): Uint8Array { + return this.serialize(); + } + static deserializeBinary(bytes: Uint8Array): ProtoLocations { + return ProtoLocations.deserialize(bytes); + } + } } diff --git a/src.ts/utils/index.ts b/src.ts/utils/index.ts index 891b7c25..3e4fd753 100644 --- a/src.ts/utils/index.ts +++ b/src.ts/utils/index.ts @@ -35,8 +35,8 @@ export { export { resolveProperties, defineProperties } from "./properties.js"; -export { encodeProto } from "./proto-encode.js"; -export { decodeProto } from "./proto-decode.js"; +export { encodeProtoTransaction, encodeProtoWorkObject } from "./proto-encode.js"; +export { decodeProtoTransaction, decodeProtoWorkObject } from "./proto-decode.js"; export { formatQuai, parseQuai, formatEther, parseEther, formatUnits, parseUnits } from "./units.js"; diff --git a/src.ts/utils/proto-decode.ts b/src.ts/utils/proto-decode.ts index e0e02861..a66b221c 100644 --- a/src.ts/utils/proto-decode.ts +++ b/src.ts/utils/proto-decode.ts @@ -1,12 +1,13 @@ +import { ProtoTransaction } from "../transaction/abstract-transaction"; +import { ProtoWorkObject } from "../transaction/work-object"; import * as Proto from "./ProtoBuf/proto_block" -function _decode(object: any): any { - const tx = Proto.block.ProtoTransaction.deserialize(object); - const result = tx.toObject(); - return result; +export function decodeProtoTransaction(bytes: Uint8Array): ProtoTransaction { + const tx = Proto.block.ProtoTransaction.deserialize(bytes); + return tx.toObject() as ProtoTransaction; } -export function decodeProto(object: Uint8Array): string{ - // console.log('Test decode') - return _decode(object); +export function decodeProtoWorkObject(bytes: Uint8Array): ProtoWorkObject { + const wo = Proto.block.ProtoWorkObject.deserialize(bytes); + return wo.toObject() as ProtoWorkObject; } \ No newline at end of file diff --git a/src.ts/utils/proto-encode.ts b/src.ts/utils/proto-encode.ts index 422f402e..44e69642 100644 --- a/src.ts/utils/proto-encode.ts +++ b/src.ts/utils/proto-encode.ts @@ -1,12 +1,15 @@ +import { ProtoTransaction } from "../transaction/abstract-transaction"; +import { ProtoWorkObject } from "../transaction/work-object"; import { hexlify } from "./data"; import * as Proto from "./ProtoBuf/proto_block" -function _encode(object: any): Uint8Array { - const tx = Proto.block.ProtoTransaction.fromObject(object); - const result = tx.serialize(); - return result; +export function encodeProtoTransaction(protoTx: ProtoTransaction): string { + const tx = Proto.block.ProtoTransaction.fromObject(protoTx as any); + return hexlify(tx.serialize()); +} + +export function encodeProtoWorkObject(protoWo: ProtoWorkObject): string { + const wo = Proto.block.ProtoWorkObject.fromObject(protoWo as any); + return hexlify(wo.serialize()); } -export function encodeProto(object: any): string{ - return hexlify(_encode(object)); -} \ No newline at end of file diff --git a/src.ts/utils/proto.ts b/src.ts/utils/proto.ts index 159cc4d8..70f585d7 100644 --- a/src.ts/utils/proto.ts +++ b/src.ts/utils/proto.ts @@ -1,2 +1,3 @@ -export { encodeProto } from "./proto-encode.js"; -export { decodeProto } from "./proto-decode.js"; \ No newline at end of file +export { encodeProtoTransaction, encodeProtoWorkObject } from "./proto-encode.js"; +export { decodeProtoTransaction, decodeProtoWorkObject } from "./proto-decode.js"; + diff --git a/src.ts/wallet/base-wallet.ts b/src.ts/wallet/base-wallet.ts index 45452b99..cf9b06a4 100644 --- a/src.ts/wallet/base-wallet.ts +++ b/src.ts/wallet/base-wallet.ts @@ -1,15 +1,16 @@ import { getAddress, resolveAddress } from "../address/index.js"; import { hashMessage, TypedDataEncoder } from "../hash/index.js"; import { AbstractSigner } from "../providers/index.js"; -import { computeAddress, Transaction } from "../transaction/index.js"; +import { computeAddress } from "../transaction/index.js"; import { - resolveProperties, assertArgument + resolveProperties, assertArgument } from "../utils/index.js"; import type { SigningKey } from "../crypto/index.js"; import type { TypedDataDomain, TypedDataField } from "../hash/index.js"; -import type { Provider, TransactionRequest } from "../providers/index.js"; -import type { TransactionLike } from "../transaction/index.js"; +import type { Provider } from "../providers/index.js"; +import { QuaiTransactionRequest } from "../providers/provider.js"; +import { QuaiTransaction, QuaiTransactionLike } from "../transaction/quai-transaction.js"; /** * The **BaseWallet** is a stream-lined implementation of a @@ -40,7 +41,7 @@ export class BaseWallet extends AbstractSigner { constructor(privateKey: SigningKey, provider?: null | Provider) { super(provider); - assertArgument(privateKey && typeof(privateKey.sign) === "function", "invalid private key", "privateKey", "[ REDACTED ]"); + assertArgument(privateKey && typeof (privateKey.sign) === "function", "invalid private key", "privateKey", "[ REDACTED ]"); this.#signingKey = privateKey; @@ -48,7 +49,6 @@ export class BaseWallet extends AbstractSigner { } // Store private values behind getters to reduce visibility - // in console.log /** * The address of this wallet. @@ -72,13 +72,13 @@ export class BaseWallet extends AbstractSigner { return new BaseWallet(this.#signingKey, provider); } - async signTransaction(tx: TransactionRequest ): Promise { + async signTransaction(tx: QuaiTransactionRequest ): Promise { // Replace any Addressable or ENS name with an address const { to, from } = await resolveProperties({ to: (tx.to ? resolveAddress(tx.to): undefined), from: (tx.from ? resolveAddress(tx.from): undefined) }); - + if (to != null) { tx.to = to; } if (from != null) { tx.from = from; } @@ -87,9 +87,9 @@ export class BaseWallet extends AbstractSigner { "transaction from address mismatch", "tx.from", tx.from); // delete tx.from; } - - // Build the transaction - const btx = Transaction.from(>tx); + + const btx = QuaiTransaction.from(tx); + btx.signature = this.signingKey.sign(btx.unsignedHash); return btx.serialized; diff --git a/src.ts/wallet/utxohdwallet.ts b/src.ts/wallet/utxohdwallet.ts index 7295ee1e..4177515d 100644 --- a/src.ts/wallet/utxohdwallet.ts +++ b/src.ts/wallet/utxohdwallet.ts @@ -1,7 +1,30 @@ import { N, ShardData } from '../constants'; import { SigningKey, keccak256 as addressKeccak256 } from "../crypto/index.js"; -import { BytesLike, Numeric, Provider, Transaction, TransactionLike, TransactionRequest, Wordlist, assertArgument, assertPrivate, computeAddress, computeHmac, dataSlice, defineProperties, getBytes, getNumber, getShardForAddress, hexlify, isBytesLike, isUTXOAddress, randomBytes, ripemd160, sha256, toBeHex, toBigInt } from '../quais.js'; +import { + BytesLike, + Numeric, + Provider, + TransactionLike, + Wordlist, + assertArgument, + assertPrivate, + computeHmac, + dataSlice, + defineProperties, + getBytes, + getNumber, + getShardForAddress, + hexlify, + isBytesLike, + isUTXOAddress, + randomBytes, + ripemd160, + sha256, + toBeHex, + toBigInt, + computeAddress +} from '../quais.js'; import { Mnemonic } from './mnemonic.js'; import { HardenedBit, derivePath, ser_I } from './utils.js'; import { BaseWallet } from "./base-wallet.js"; @@ -9,6 +32,8 @@ import { MuSigFactory } from "@brandonblack/musig" import { nobleCrypto } from "./musig-crypto.js"; import { schnorr } from "@noble/curves/secp256k1"; import { keccak_256 } from "@noble/hashes/sha3"; +import { QiTransaction } from '../transaction/qi-transaction.js'; +import { QiTransactionRequest } from '../providers/provider.js'; import { TxInput } from "../transaction/utxo.js"; import { getAddress } from "../address/index.js"; @@ -109,7 +134,7 @@ export class UTXOHDWallet extends BaseWallet { } // contains the last BIP44 index derived by the wallet (-1 if none have been derived yet) - #lastDerivedAddressIndex: number = -1; + #lastDerivedAddressIndex: number = -1; /** * Gets the current publicKey @@ -264,7 +289,7 @@ export class UTXOHDWallet extends BaseWallet { currentUtxoAddresses.push({ address, privKey }); this.#lastDerivedAddressIndex = currentIndex; - + // Check if the address has any UTXOs try { // if provider is not set, throw error @@ -320,20 +345,20 @@ export class UTXOHDWallet extends BaseWallet { return newWallet; } - /** * Signs a UTXO transaction and returns the serialized transaction */ - async signTransaction(tx: TransactionRequest): Promise { - const txobj = Transaction.from((>tx)) - if (!txobj.inputsUTXO || !txobj.outputsUTXO) throw new Error('Invalid UTXO transaction, missing inputs or outputs') + + async signTransaction(tx: QiTransactionRequest): Promise { + const txobj = QiTransaction.from((tx)) + if (!txobj.txInputs || !txobj.txOutputs) throw new Error('Invalid UTXO transaction, missing inputs or outputs') const hash = keccak_256(txobj.unsignedSerialized) let signature: string; - if (txobj.inputsUTXO.length == 1){ - signature = this.createSchnorrSignature(txobj.inputsUTXO[0], hash); + if (txobj.txInputs.length == 1){ + signature = this.createSchnorrSignature(txobj.txInputs[0], hash); } else { signature = this.createMuSigSignature(txobj, hash); @@ -354,14 +379,14 @@ export class UTXOHDWallet extends BaseWallet { // create the schnorr signature const signature = schnorr.sign(hash, getBytes(privKey) ); return hexlify(signature); - } - - // createMuSigSignature returns a MuSig signature for the given message + } + + // createMuSigSignature returns a MuSig signature for the given message // and private keys corresponding to the input addresses - private createMuSigSignature(tx: Transaction, hash: Uint8Array): string { + private createMuSigSignature(tx: QiTransaction, hash: Uint8Array): string { const musig = MuSigFactory(nobleCrypto); - const privKeys = tx.inputsUTXO!.map(input => { + const privKeys = tx.txInputs!.map(input => { const address = computeAddress(hexlify(input.pubKey)); const utxoAddrObj = this.utxoAddresses.find(utxoAddr => utxoAddr.address === address); return utxoAddrObj ? utxoAddrObj.privKey : null; @@ -390,7 +415,7 @@ export class UTXOHDWallet extends BaseWallet { // Aggregate the partial signatures into a final aggregated signature const finalSignature = musig.signAgg(partialSignatures, signingSession); - + // const isValid = schnorr.verify(finalSignature, hash, aggPublicKey); return hexlify(finalSignature); }