diff --git a/.circleci/config.yml b/.circleci/config.yml index 67896e61472..336474a6a7a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -671,17 +671,6 @@ jobs: name: "Build" command: build ethereum - rollup-provider: - machine: - image: ubuntu-2004:202010-01 - resource_class: large - steps: - - *checkout - - *setup_env - - run: - name: "Build" - command: build rollup-provider - e2e-2-rpc-servers: machine: image: ubuntu-2004:202010-01 @@ -1429,7 +1418,6 @@ workflows: - sequencer-client: *yarn_project - types: *yarn_project - circuits-js: *yarn_project - - rollup-provider: *yarn_project - aztec-sandbox-base: *yarn_project - canary: *yarn_project @@ -1470,7 +1458,6 @@ workflows: - sequencer-client - types - circuits-js - - rollup-provider - aztec-sandbox-ecr-manifest - canary <<: *defaults diff --git a/build_manifest.json b/build_manifest.json index 9379e39a965..e142a815006 100644 --- a/build_manifest.json +++ b/build_manifest.json @@ -220,10 +220,6 @@ "buildDir": "yarn-project", "projectDir": "yarn-project/prover-client" }, - "rollup-provider": { - "buildDir": "yarn-project", - "projectDir": "yarn-project/rollup-provider" - }, "aztec-node": { "buildDir": "yarn-project", "projectDir": "yarn-project/aztec-node" @@ -240,4 +236,4 @@ "buildDir": "yarn-project", "projectDir": "yarn-project/world-state" } -} \ No newline at end of file +} diff --git a/yarn-project/aztec-node/src/aztec-node/http-node.test.ts b/yarn-project/aztec-node/src/aztec-node/http-node.test.ts deleted file mode 100644 index fdf5d2b5522..00000000000 --- a/yarn-project/aztec-node/src/aztec-node/http-node.test.ts +++ /dev/null @@ -1,519 +0,0 @@ -import { AztecAddress, CircuitsWasm, EthAddress, Fr, FunctionSelector, HistoricBlockData } from '@aztec/circuits.js'; -import { randomBytes } from '@aztec/foundation/crypto'; -import { Pedersen } from '@aztec/merkle-tree'; -import { - ContractData, - ExtendedContractData, - L1ToL2Message, - L2Block, - L2BlockL2Logs, - LogType, - MerkleTreeId, - SiblingPath, - SimulationError, - TxHash, - mockTx, -} from '@aztec/types'; - -import { jest } from '@jest/globals'; - -import { HttpNode } from './http-node.js'; - -const TEST_URL = 'http://aztec-node-url.com/'; - -const setFetchMock = (response: any, status = 200): void => { - global.fetch = jest - .fn() - .mockImplementation((_input: RequestInfo | URL, _init?: RequestInit | undefined) => { - return Promise.resolve({ - status, - ok: true, - json: () => response, - arrayBuffer: () => response, - } as Response); - }); -}; - -describe('HttpNode', () => { - let httpNode: HttpNode; - let pedersen: Pedersen; - - beforeAll(async () => { - httpNode = new HttpNode(TEST_URL); - pedersen = new Pedersen(await CircuitsWasm.get()); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('isReady', () => { - it.each([true, false])('should return %s when the node is ready', async () => { - const response = { isReady: true }; - setFetchMock(response); - - const result = await httpNode.isReady(); - - expect(fetch).toHaveBeenCalledWith(TEST_URL); - expect(result).toBe(true); - }); - }); - - describe('getBlocks', () => { - it('should fetch and parse blocks', async () => { - const block1 = L2Block.random(1); - const block2 = L2Block.random(2); - const response = { - blocks: [block1.encode(), block2.encode()], - }; - setFetchMock(response); - - const result = await httpNode.getBlocks(0, 3); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-blocks?from=0&limit=3`); - expect(result).toEqual([block1, block2]); - }); - - it('should return an empty array if blocks are not available', async () => { - const response = { blocks: [] }; - setFetchMock(response); - - const result = await httpNode.getBlocks(0, 2); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-blocks?from=0&limit=2`); - expect(result).toEqual([]); - }); - }); - - describe('getBlock', () => { - it('should fetch and parse a block', async () => { - const block1 = L2Block.random(1); - const response = { - block: block1.encode(), - }; - setFetchMock(response); - - const result = await httpNode.getBlock(1); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-block?number=1`); - expect(result).toEqual(block1); - }); - - it('should return undefined if the block is not available', async () => { - const response = { block: undefined }; - setFetchMock(response); - - const result = await httpNode.getBlock(2); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-block?number=2`); - expect(result).toEqual(undefined); - }); - }); - - describe('getBlockNumber', () => { - it('should fetch and return current block number', async () => { - const response = { blockNumber: 100 }; - setFetchMock(response); - - const result = await httpNode.getBlockNumber(); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-block-number`); - expect(result).toBe(100); - }); - }); - - describe('getVersion', () => { - it('should fetch and return the version', async () => { - const response = { version: 5 }; - setFetchMock(response); - - const result = await httpNode.getVersion(); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-version`); - expect(result).toBe(5); - }); - }); - - describe('getChainId', () => { - it('should fetch and return the chain ID', async () => { - const response = { chainId: 1234 }; - setFetchMock(response); - - const result = await httpNode.getChainId(); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-chain-id`); - expect(result).toBe(1234); - }); - }); - - describe('getRollupAddress', () => { - it('should fetch and return the rollup address', async () => { - const addr = EthAddress.random(); - const response = { rollupAddress: addr.toString() }; - setFetchMock(response); - - const result = await httpNode.getRollupAddress(); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-rollup-address`); - expect(result).toEqual(addr); - }); - }); - - describe('getExtendedContractData', () => { - it('should fetch and return contract public data', async () => { - const extendedContractData = ExtendedContractData.random(); - const response = { - contractData: extendedContractData.toBuffer(), - }; - - setFetchMock(response); - - const result = await httpNode.getExtendedContractData(extendedContractData.contractData.contractAddress); - - expect(fetch).toHaveBeenCalledWith( - `${TEST_URL}contract-data-and-bytecode?address=${extendedContractData.contractData.contractAddress.toString()}`, - ); - expect(result).toEqual(extendedContractData); - }); - - it('should return undefined if contract data is not available', async () => { - const response = { - contractData: undefined, - }; - setFetchMock(response); - - const randomAddress = AztecAddress.random(); - - const result = await httpNode.getExtendedContractData(randomAddress); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-data-and-bytecode?address=${randomAddress.toString()}`); - expect(result).toEqual(undefined); - }); - }); - - describe('getLogs', () => { - it.each(['encrypted', 'unencrypted'])('should fetch and return %s logs', async logType => { - const processedLogType = logType === 'encrypted' ? LogType.ENCRYPTED : LogType.UNENCRYPTED; - - const from = 0; - const limit = 3; - const log1 = L2BlockL2Logs.random(2, 3, 4); - const log2 = L2BlockL2Logs.random(1, 5, 2); - const response = { - logs: [log1.toBuffer(), log2.toBuffer()], - }; - setFetchMock(response); - - const result = await httpNode.getLogs(from, limit, processedLogType); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-logs?from=${from}&limit=${limit}&logType=${processedLogType}`); - expect(result).toEqual([log1, log2]); - }); - - it.each(['encrypted', 'unencrypted'])( - 'should return an empty array if %s logs are not available', - async logType => { - const processedLogType = logType === 'encrypted' ? LogType.ENCRYPTED : LogType.UNENCRYPTED; - - const from = 0; - const limit = 2; - const response = {}; - setFetchMock(response); - - const result = await httpNode.getLogs(from, limit, processedLogType); - - expect(fetch).toHaveBeenCalledWith( - `${TEST_URL}get-logs?from=${from}&limit=${limit}&logType=${processedLogType}`, - ); - expect(result).toEqual([]); - }, - ); - }); - - describe('getContractData', () => { - it('should fetch and return contract data', async () => { - const contractData = ContractData.random(); - const response = { - contractData: contractData.toBuffer(), - }; - setFetchMock(response); - - const result = await httpNode.getContractData(contractData.contractAddress); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-data?address=${contractData.contractAddress.toString()}`); - expect(result).toEqual(contractData); - }); - - it('should return undefined if contract data is not available', async () => { - const response = { - contractData: undefined, - }; - setFetchMock(response); - - const randomAddress = AztecAddress.random(); - - const result = await httpNode.getContractData(randomAddress); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-data?address=${randomAddress.toString()}`); - expect(result).toBeUndefined(); - }); - }); - - describe('sendTx', () => { - it('should submit a transaction to the p2p pool', async () => { - const tx = mockTx(); - const irrelevantResponse = {}; - setFetchMock(irrelevantResponse); - - await httpNode.sendTx(tx); - - const init: RequestInit = { - method: 'POST', - body: tx.toBuffer(), - }; - const call = (fetch as jest.Mock).mock.calls[0] as any[]; - expect(call[0].href).toBe(`${TEST_URL}tx`); - expect(call[1]).toStrictEqual(init); - }); - }); - - describe('getPendingTxByHash', () => { - it('should fetch and return a pending tx', async () => { - const txHash = new TxHash(randomBytes(TxHash.SIZE)); - const tx = mockTx(); - const response = tx.toBuffer(); - setFetchMock(response); - - const result = await httpNode.getPendingTxByHash(txHash); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-pending-tx?hash=${txHash}`); - expect(result).toEqual(tx); - }); - - it('should return undefined if the pending tx does not exist', async () => { - const txHash = new TxHash(randomBytes(TxHash.SIZE)); - const response = undefined; - setFetchMock(response, 404); - - const result = await httpNode.getPendingTxByHash(txHash); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-pending-tx?hash=${txHash}`); - expect(result).toBeUndefined(); - }); - }); - - describe('findContractIndex', () => { - it('should fetch and return the index of the given contract', async () => { - const leafValue = Buffer.from('abc123', 'hex'); - const index = '123456'; - const response = { index }; - setFetchMock(response); - - const result = await httpNode.findContractIndex(leafValue); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-index?leaf=${leafValue.toString('hex')}`); - expect(result).toBe(BigInt(index)); - }); - - it('should return undefined if the contract index is not found', async () => { - const leafValue = Buffer.from('def456', 'hex'); - const response = {}; - setFetchMock(response); - - const result = await httpNode.findContractIndex(leafValue); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-index?leaf=${leafValue.toString('hex')}`); - expect(result).toBeUndefined(); - }); - }); - - describe('getContractPath', () => { - it('should fetch and return the sibling path for the given leaf index', async () => { - const leafIndex = BigInt(123456); - const siblingPath = SiblingPath.ZERO(3, Buffer.alloc(32), pedersen); - const response = { path: siblingPath.toBuffer().toString('hex') }; - setFetchMock(response); - - const result = await httpNode.getContractPath(leafIndex); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-path?leaf=${leafIndex.toString()}`); - expect(result).toEqual(siblingPath); - }); - }); - - describe('findCommitmentIndex', () => { - it('should fetch and return the index of the given leaf', async () => { - const leafValue = Buffer.from('0123456789', 'hex'); - const expectedIndex = BigInt(123); - const response = { index: expectedIndex.toString() }; - setFetchMock(response); - - const result = await httpNode.findCommitmentIndex(leafValue); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}commitment-index?leaf=${leafValue.toString('hex')}`); - expect(result).toBe(expectedIndex); - }); - - it('should return undefined if the commitment index is not found', async () => { - const leafValue = Buffer.from('def456', 'hex'); - const response = {}; - setFetchMock(response); - - const result = await httpNode.findCommitmentIndex(leafValue); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}commitment-index?leaf=${leafValue.toString('hex')}`); - expect(result).toBeUndefined(); - }); - }); - - describe('getDataTreePath', () => { - it('should fetch and return the sibling path for the given leaf index', async () => { - const leafIndex = BigInt(123456); - const siblingPath = SiblingPath.ZERO(3, Buffer.alloc(32), pedersen); - const response = { path: siblingPath.toBuffer().toString('hex') }; - setFetchMock(response); - - const result = await httpNode.getDataTreePath(leafIndex); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}data-path?leaf=${leafIndex.toString()}`); - expect(result).toEqual(siblingPath); - }); - }); - - describe('getL1ToL2MessageAndIndex', () => { - it('should fetch and return the L1 to L2 message and index for the given message key', async () => { - const messageKey = new Fr(789); - const expectedMessage = L1ToL2Message.random(); - const expectedIndex = BigInt(123); - const response = { message: expectedMessage.toBuffer().toString('hex'), index: expectedIndex.toString() }; - setFetchMock(response); - - const result = await httpNode.getL1ToL2MessageAndIndex(messageKey); - - expect(fetch).toHaveBeenCalledWith(`${TEST_URL}l1-l2-message?messageKey=${messageKey.toString()}`); - expect(result).toEqual({ - message: expectedMessage, - index: expectedIndex, - }); - }); - }); - - describe('getL1ToL2MessagesTreePath', () => { - it('should fetch and return the sibling path for the given leaf index', async () => { - const leafIndex = BigInt(123456); - const siblingPath = SiblingPath.ZERO(3, Buffer.alloc(32), pedersen); - const response = { path: siblingPath.toBuffer().toString('hex') }; - setFetchMock(response); - - const result = await httpNode.getL1ToL2MessagesTreePath(leafIndex); - - const url = `${TEST_URL}l1-l2-path?leaf=${leafIndex.toString()}`; - expect(fetch).toHaveBeenCalledWith(url); - expect(result).toEqual(siblingPath); - }); - }); - - describe('getPublicStorageAt', () => { - it('should fetch and return the storage value at the given contract slot', async () => { - const contractAddress = AztecAddress.random(); - const slot = BigInt(789); - const storageValue = Buffer.from('0123456789', 'hex'); - const response = { value: storageValue.toString('hex') }; - setFetchMock(response); - - const result = await httpNode.getPublicStorageAt(contractAddress, slot); - - const url = `${TEST_URL}public-storage-at?address=${contractAddress}&slot=${slot.toString()}`; - expect(fetch).toHaveBeenCalledWith(url); - expect(result).toEqual(storageValue); - }); - - it('should return undefined if the storage value is not found', async () => { - const contractAddress = AztecAddress.random(); - const slot = BigInt(987); - const response = {}; - setFetchMock(response); - - const result = await httpNode.getPublicStorageAt(contractAddress, slot); - - const url = `${TEST_URL}public-storage-at?address=${contractAddress}&slot=${slot.toString()}`; - expect(fetch).toHaveBeenCalledWith(url); - expect(result).toBeUndefined(); - }); - }); - - describe('getTreeRoots', () => { - it('should fetch and return the current committed roots for the data trees', async () => { - const roots: Record = { - [MerkleTreeId.CONTRACT_TREE]: Fr.random(), - [MerkleTreeId.PRIVATE_DATA_TREE]: Fr.random(), - [MerkleTreeId.NULLIFIER_TREE]: Fr.random(), - [MerkleTreeId.PUBLIC_DATA_TREE]: Fr.random(), - [MerkleTreeId.L1_TO_L2_MESSAGES_TREE]: Fr.random(), - [MerkleTreeId.BLOCKS_TREE]: Fr.random(), - }; - - const rootsInResponse: Record = Object.fromEntries( - Object.entries(roots).map(([key, value]) => [key, value.toString()]), - ) as Record; - const response = { roots: rootsInResponse }; - - setFetchMock(response); - - const result = await httpNode.getTreeRoots(); - - const url = `${TEST_URL}tree-roots`; - expect(fetch).toHaveBeenCalledWith(url); - expect(result).toEqual(roots); - }); - }); - - describe('getHistoricBlockData', () => { - it('should fetch and return the current committed roots for the data trees', async () => { - const blockData = HistoricBlockData.random(); - - const response = { blockData }; - - setFetchMock(response); - - const result = await httpNode.getHistoricBlockData(); - - const url = `${TEST_URL}historic-block-data`; - expect(fetch).toHaveBeenCalledWith(url); - expect(result).toEqual(blockData); - }); - }); - - describe('simulatePublicCalls', () => { - it('should fetch a successful simulation response', async () => { - const tx = mockTx(); - const response = {}; - setFetchMock(response); - - await httpNode.simulatePublicCalls(tx); - - const init: RequestInit = { - method: 'POST', - body: tx.toBuffer(), - }; - const call = (fetch as jest.Mock).mock.calls[0] as any[]; - expect(call[0].href).toBe(`${TEST_URL}simulate-tx`); - expect(call[1]).toStrictEqual(init); - }); - - it('should fetch a simulation error', async () => { - const tx = mockTx(); - const simulationError = new SimulationError('test error', [ - { contractAddress: AztecAddress.ZERO, functionSelector: FunctionSelector.empty() }, - ]); - - const response = { - simulationError: simulationError.toJSON(), - }; - - setFetchMock(response); - - await expect(httpNode.simulatePublicCalls(tx)).rejects.toThrow(simulationError); - }); - }); -}); diff --git a/yarn-project/aztec-node/src/aztec-node/http-node.ts b/yarn-project/aztec-node/src/aztec-node/http-node.ts deleted file mode 100644 index 52f1d898cfa..00000000000 --- a/yarn-project/aztec-node/src/aztec-node/http-node.ts +++ /dev/null @@ -1,388 +0,0 @@ -import { - AztecAddress, - CONTRACT_TREE_HEIGHT, - EthAddress, - Fr, - HistoricBlockData, - L1_TO_L2_MSG_TREE_HEIGHT, - PRIVATE_DATA_TREE_HEIGHT, -} from '@aztec/circuits.js'; -import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; -import { - AztecNode, - ContractData, - ExtendedContractData, - L1ToL2Message, - L1ToL2MessageAndIndex, - L2Block, - L2BlockL2Logs, - L2Tx, - LogType, - MerkleTreeId, - SiblingPath, - SimulationError, - Tx, - TxHash, -} from '@aztec/types'; - -/** - * A Http client based implementation of Aztec Node. - */ -export class HttpNode implements AztecNode { - private baseUrl: string; - private log: DebugLogger; - - constructor(baseUrl: string, log = createDebugLogger('aztec:http-node')) { - this.baseUrl = baseUrl.toString().replace(/\/$/, ''); - this.log = log; - } - /** - * Method to determine if the node is ready to accept transactions. - * @returns - Flag indicating the readiness for tx submission. - */ - public async isReady(): Promise { - const url = new URL(this.baseUrl); - const response = await fetch(url.toString()); - const respJson = await response.json(); - return respJson.isReady; - } - - /** - * Method to request a block at the provided block number. - * @param number - The block number to request. - * @returns The block requested. Or undefined if it does not exist. - */ - async getBlock(number: number): Promise { - const url = new URL(`${this.baseUrl}/get-block`); - url.searchParams.append('number', number.toString()); - const response = await (await fetch(url.toString())).json(); - const { block } = response; - return Promise.resolve(block ? L2Block.decode(Buffer.from(block, 'hex')) : block); - } - - /** - * Method to request blocks. Will attempt to return all requested blocks but will return only those available. - * @param from - The start of the range of blocks to return. - * @param limit - Maximum number of blocks to obtain. - * @returns The blocks requested. - */ - async getBlocks(from: number, limit: number): Promise { - const url = new URL(`${this.baseUrl}/get-blocks`); - url.searchParams.append('from', from.toString()); - if (limit !== undefined) { - url.searchParams.append('limit', limit.toString()); - } - const response = await (await fetch(url.toString())).json(); - const blocks = response.blocks as string[]; - if (!blocks) { - return Promise.resolve([]); - } - return Promise.resolve(blocks.map(x => L2Block.decode(Buffer.from(x, 'hex')))); - } - - /** - * Method to fetch the current block number. - * @returns The current block number. - */ - async getBlockNumber(): Promise { - const url = new URL(`${this.baseUrl}/get-block-number`); - const response = await fetch(url.toString()); - const respJson = await response.json(); - return respJson.blockNumber; - } - - /** - * Method to fetch the version of the rollup the node is connected to. - * @returns The rollup version. - */ - public async getVersion(): Promise { - const url = new URL(`${this.baseUrl}/get-version`); - const response = await fetch(url.toString()); - const respJson = await response.json(); - return respJson.version; - } - - public async getRollupAddress(): Promise { - const url = new URL(`${this.baseUrl}/get-rollup-address`); - const response = await fetch(url.toString()); - const respJson = await response.json(); - return EthAddress.fromString(respJson.rollupAddress); - } - - /** - * Method to fetch the chain id of the base-layer for the rollup. - * @returns The chain id. - */ - public async getChainId(): Promise { - const url = new URL(`${this.baseUrl}/get-chain-id`); - const response = await fetch(url.toString()); - const respJson = await response.json(); - return respJson.chainId; - } - - /** - * Get the extended contract data for this contract. - * @param contractAddress - The contract data address. - * @returns The extended contract data or undefined if not found. - */ - async getExtendedContractData(contractAddress: AztecAddress): Promise { - const url = new URL(`${this.baseUrl}/contract-data-and-bytecode`); - url.searchParams.append('address', contractAddress.toString()); - const response = await (await fetch(url.toString())).json(); - if (!response || !response.contractData) { - return undefined; - } - const contract = response.contractData as string; - return Promise.resolve(ExtendedContractData.fromBuffer(Buffer.from(contract, 'hex'))); - } - - /** - * Gets up to `limit` amount of logs starting from `from`. - * @param from - Number of the L2 block to which corresponds the first logs to be returned. - * @param limit - The maximum number of logs to return. - * @param logType - Specifies whether to return encrypted or unencrypted logs. - * @returns The requested logs. - */ - public async getLogs(from: number, limit: number, logType: LogType): Promise { - const url = new URL(`${this.baseUrl}/get-logs`); - - url.searchParams.append('from', from.toString()); - url.searchParams.append('limit', limit.toString()); - url.searchParams.append('logType', logType.toString()); - - const response = await (await fetch(url.toString())).json(); - const logs = response.logs as string[]; - - if (!logs) { - return Promise.resolve([]); - } - return Promise.resolve(logs.map(x => L2BlockL2Logs.fromBuffer(Buffer.from(x, 'hex')))); - } - - /** - * Lookup the contract data for this contract. - * Contains the ethereum portal address. - * @param contractAddress - The contract data address. - * @returns The contract's address & portal address. - */ - async getContractData(contractAddress: AztecAddress): Promise { - const url = new URL(`${this.baseUrl}/contract-data`); - url.searchParams.append('address', contractAddress.toString()); - const response = await (await fetch(url.toString())).json(); - if (!response || !response.contractData) { - return undefined; - } - const contract = response.contractData as string; - return Promise.resolve(ContractData.fromBuffer(Buffer.from(contract, 'hex'))); - } - - /** - * Method to submit a transaction to the p2p pool. - * @param tx - The transaction to be submitted. - */ - async sendTx(tx: Tx): Promise { - const url = new URL(`${this.baseUrl}/tx`); - const init: RequestInit = {}; - init['method'] = 'POST'; - init['body'] = tx.toBuffer(); - await fetch(url, init); - } - - /** - * Gets an l2 tx. - * @param txHash - The txHash of the l2 tx. - * @returns The requested L2 tx. - */ - async getTx(txHash: TxHash): Promise { - const url = new URL(`${this.baseUrl}/get-tx`); - url.searchParams.append('hash', txHash.toString()); - const response = await fetch(url.toString()); - if (response.status === 404) { - this.log.info(`Tx ${txHash.toString()} not found`); - return undefined; - } - const txBuffer = Buffer.from(await response.arrayBuffer()); - const tx = L2Tx.fromBuffer(txBuffer); - return Promise.resolve(tx); - } - - /** - * Method to retrieve pending txs. - * @returns - The pending txs. - */ - getPendingTxs(): Promise { - return Promise.resolve([]); - } - - /** - * Method to retrieve a single pending tx. - * @param txHash - The transaction hash to return. - * @returns - The pending tx if it exists. - */ - async getPendingTxByHash(txHash: TxHash): Promise { - const url = new URL(`${this.baseUrl}/get-pending-tx`); - url.searchParams.append('hash', txHash.toString()); - const response = await fetch(url.toString()); - if (response.status === 404) { - this.log.info(`Tx ${txHash.toString()} not found`); - return undefined; - } - const txBuffer = Buffer.from(await response.arrayBuffer()); - const tx = Tx.fromBuffer(txBuffer); - return Promise.resolve(tx); - } - - /** - * Find the index of the given contract. - * @param leafValue - The value to search for. - * @returns The index of the given leaf in the contracts tree or undefined if not found. - */ - async findContractIndex(leafValue: Buffer): Promise { - const url = new URL(`${this.baseUrl}/contract-index`); - url.searchParams.append('leaf', leafValue.toString('hex')); - const response = await (await fetch(url.toString())).json(); - if (!response || !response.index) { - return undefined; - } - const index = response.index as string; - return Promise.resolve(BigInt(index)); - } - - /** - * Returns the sibling path for the given index in the contract tree. - * @param leafIndex - The index of the leaf for which the sibling path is required. - * @returns The sibling path for the leaf index. - */ - async getContractPath(leafIndex: bigint): Promise> { - const url = new URL(`${this.baseUrl}/contract-path`); - url.searchParams.append('leaf', leafIndex.toString()); - const response = await (await fetch(url.toString())).json(); - const path = response.path as string; - return Promise.resolve(SiblingPath.fromString(path)); - } - - /** - * Find the index of the given piece of data. - * @param leafValue - The value to search for. - * @returns The index of the given leaf in the data tree or undefined if not found. - */ - async findCommitmentIndex(leafValue: Buffer): Promise { - const url = new URL(`${this.baseUrl}/commitment-index`); - url.searchParams.append('leaf', leafValue.toString('hex')); - const response = await (await fetch(url.toString())).json(); - if (!response || !response.index) { - return undefined; - } - const index = response.index as string; - return Promise.resolve(BigInt(index)); - } - - /** - * Returns the sibling path for the given index in the data tree. - * @param leafIndex - The index of the leaf for which the sibling path is required. - * @returns The sibling path for the leaf index. - */ - async getDataTreePath(leafIndex: bigint): Promise> { - const url = new URL(`${this.baseUrl}/data-path`); - url.searchParams.append('leaf', leafIndex.toString()); - const response = await (await fetch(url.toString())).json(); - const path = response.path as string; - return Promise.resolve(SiblingPath.fromString(path)); - } - - /** - * Gets a consumed/confirmed L1 to L2 message for the given message key and its index in the merkle tree. - * @param messageKey - The message key. - * @returns the message (or throws if not found) - */ - async getL1ToL2MessageAndIndex(messageKey: Fr): Promise { - const url = new URL(`${this.baseUrl}/l1-l2-message`); - url.searchParams.append('messageKey', messageKey.toString()); - const response = await (await fetch(url.toString())).json(); - return Promise.resolve({ - message: L1ToL2Message.fromBuffer(Buffer.from(response.message as string, 'hex')), - index: BigInt(response.index as string), - }); - } - - /** - * Returns the sibling path for a leaf in the committed l1 to l2 data tree. - * @param leafIndex - Index of the leaf in the tree. - * @returns The sibling path. - */ - async getL1ToL2MessagesTreePath(leafIndex: bigint): Promise> { - const url = new URL(`${this.baseUrl}/l1-l2-path`); - url.searchParams.append('leaf', leafIndex.toString()); - const response = await (await fetch(url.toString())).json(); - const path = response.path as string; - return Promise.resolve(SiblingPath.fromString(path)); - } - - /** - * Gets the storage value at the given contract slot. - * @param contract - Address of the contract to query. - * @param slot - Slot to query. - * @returns Storage value at the given contract slot (or undefined if not found). - * Note: Aztec's version of `eth_getStorageAt`. - */ - async getPublicStorageAt(contract: AztecAddress, slot: bigint): Promise { - const url = new URL(`${this.baseUrl}/public-storage-at`); - url.searchParams.append('address', contract.toString()); - url.searchParams.append('slot', slot.toString()); - const response = await (await fetch(url.toString())).json(); - if (!response || !response.value) { - return undefined; - } - const value = response.value as string; - return Promise.resolve(Buffer.from(value, 'hex')); - } - - /** - * Returns the current committed roots for the data trees. - * @returns The current committed roots for the data trees. - */ - async getTreeRoots(): Promise> { - const url = new URL(`${this.baseUrl}/tree-roots`); - const response = await (await fetch(url.toString())).json(); - - const extractRoot = (treeId: MerkleTreeId) => { - // Buffer.from(...) returns an empty buffer when a hex string is prefixed with "0x" - const rootHexString = response.roots[treeId].replace(/^0x/, ''); - return Fr.fromBuffer(Buffer.from(rootHexString, 'hex')); - }; - - return { - [MerkleTreeId.CONTRACT_TREE]: extractRoot(MerkleTreeId.CONTRACT_TREE), - [MerkleTreeId.PRIVATE_DATA_TREE]: extractRoot(MerkleTreeId.PRIVATE_DATA_TREE), - [MerkleTreeId.NULLIFIER_TREE]: extractRoot(MerkleTreeId.NULLIFIER_TREE), - [MerkleTreeId.PUBLIC_DATA_TREE]: extractRoot(MerkleTreeId.PUBLIC_DATA_TREE), - [MerkleTreeId.L1_TO_L2_MESSAGES_TREE]: extractRoot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE), - [MerkleTreeId.BLOCKS_TREE]: extractRoot(MerkleTreeId.BLOCKS_TREE), - }; - } - - /** - * Returns the currently committed historic block data. - * @returns The current committed block data. - */ - public async getHistoricBlockData(): Promise { - const url = new URL(`${this.baseUrl}/historic-block-data`); - const response = await (await fetch(url.toString())).json(); - return response.blockData; - } - - /** - * Simulates the public part of a transaction with the current state. - * @param tx - The transaction to simulate. - **/ - public async simulatePublicCalls(tx: Tx) { - const url = new URL(`${this.baseUrl}/simulate-tx`); - const init: RequestInit = {}; - init['method'] = 'POST'; - init['body'] = tx.toBuffer(); - const response = await (await fetch(url, init)).json(); - if (response.simulationError) { - throw SimulationError.fromJSON(response.simulationError); - } - } -} diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index f457247f9db..fcf509ff6cb 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -259,7 +259,7 @@ export class AztecNodeService implements AztecNode { * @returns The index of the given leaf in the contracts tree or undefined if not found. */ public async findContractIndex(leafValue: Buffer): Promise { - const committedDb = await this.getWorldState(); + const committedDb = await this.#getWorldState(); return committedDb.findLeafIndex(MerkleTreeId.CONTRACT_TREE, leafValue); } @@ -269,7 +269,7 @@ export class AztecNodeService implements AztecNode { * @returns The sibling path for the leaf index. */ public async getContractPath(leafIndex: bigint): Promise> { - const committedDb = await this.getWorldState(); + const committedDb = await this.#getWorldState(); return committedDb.getSiblingPath(MerkleTreeId.CONTRACT_TREE, leafIndex); } @@ -279,7 +279,7 @@ export class AztecNodeService implements AztecNode { * @returns The index of the given leaf in the private data tree or undefined if not found. */ public async findCommitmentIndex(leafValue: Buffer): Promise { - const committedDb = await this.getWorldState(); + const committedDb = await this.#getWorldState(); return committedDb.findLeafIndex(MerkleTreeId.PRIVATE_DATA_TREE, leafValue); } @@ -289,7 +289,7 @@ export class AztecNodeService implements AztecNode { * @returns The sibling path for the leaf index. */ public async getDataTreePath(leafIndex: bigint): Promise> { - const committedDb = await this.getWorldState(); + const committedDb = await this.#getWorldState(); return committedDb.getSiblingPath(MerkleTreeId.PRIVATE_DATA_TREE, leafIndex); } @@ -301,7 +301,7 @@ export class AztecNodeService implements AztecNode { */ public async getL1ToL2MessageAndIndex(messageKey: Fr): Promise { // todo: #697 - make this one lookup. - const committedDb = await this.getWorldState(); + const committedDb = await this.#getWorldState(); const message = await this.l1ToL2MessageSource.getConfirmedL1ToL2Message(messageKey); const index = (await committedDb.findLeafIndex(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, messageKey.toBuffer()))!; return Promise.resolve({ message, index }); @@ -313,7 +313,7 @@ export class AztecNodeService implements AztecNode { * @returns The sibling path. */ public async getL1ToL2MessagesTreePath(leafIndex: bigint): Promise> { - const committedDb = await this.getWorldState(); + const committedDb = await this.#getWorldState(); return committedDb.getSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, leafIndex); } @@ -325,7 +325,7 @@ export class AztecNodeService implements AztecNode { * Note: Aztec's version of `eth_getStorageAt`. */ public async getPublicStorageAt(contract: AztecAddress, slot: bigint): Promise { - const committedDb = await this.getWorldState(); + const committedDb = await this.#getWorldState(); const leafIndex = computePublicDataTreeLeafIndex(contract, new Fr(slot), await CircuitsWasm.get()); return committedDb.getLeafValue(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex); } @@ -335,7 +335,7 @@ export class AztecNodeService implements AztecNode { * @returns The current committed roots for the data trees. */ public async getTreeRoots(): Promise> { - const committedDb = await this.getWorldState(); + const committedDb = await this.#getWorldState(); const getTreeRoot = async (id: MerkleTreeId) => Fr.fromBuffer((await committedDb.getTreeInfo(id)).root); const [privateDataTree, nullifierTree, contractTree, l1ToL2MessagesTree, blocksTree, publicDataTree] = @@ -363,7 +363,7 @@ export class AztecNodeService implements AztecNode { * @returns The current committed block data. */ public async getHistoricBlockData(): Promise { - const committedDb = await this.getWorldState(); + const committedDb = await this.#getWorldState(); const [roots, globalsHash] = await Promise.all([this.getTreeRoots(), committedDb.getLatestGlobalVariablesHash()]); return new HistoricBlockData( @@ -413,10 +413,10 @@ export class AztecNodeService implements AztecNode { * Returns an instance of MerkleTreeOperations having first ensured the world state is fully synched * @returns An instance of a committed MerkleTreeOperations */ - private async getWorldState() { + async #getWorldState() { try { // Attempt to sync the world state if necessary - await this.syncWorldState(); + await this.#syncWorldState(); } catch (err) { this.log.error(`Error getting world state: ${err}`); } @@ -427,7 +427,7 @@ export class AztecNodeService implements AztecNode { * Ensure we fully sync the world state * @returns A promise that fulfils once the world state is synced */ - private async syncWorldState() { + async #syncWorldState() { const blockSourceHeight = await this.blockSource.getBlockNumber(); await this.worldStateSynchroniser.syncImmediate(blockSourceHeight); } diff --git a/yarn-project/aztec-node/src/index.ts b/yarn-project/aztec-node/src/index.ts index 52ad4e50764..853db6bae96 100644 --- a/yarn-project/aztec-node/src/index.ts +++ b/yarn-project/aztec-node/src/index.ts @@ -1,3 +1,4 @@ export * from './aztec-node/config.js'; export * from './aztec-node/server.js'; -export * from './aztec-node/http-node.js'; +export * from './rpc/http_rpc_server.js'; +export * from './rpc/http_rpc_client.js'; diff --git a/yarn-project/aztec-node/src/rpc/http_rpc_client.ts b/yarn-project/aztec-node/src/rpc/http_rpc_client.ts new file mode 100644 index 00000000000..c19c787a35d --- /dev/null +++ b/yarn-project/aztec-node/src/rpc/http_rpc_client.ts @@ -0,0 +1,23 @@ +import { HistoricBlockData } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { Fr } from '@aztec/foundation/fields'; +import { createJsonRpcClient, defaultFetch } from '@aztec/foundation/json-rpc/client'; +import { AztecNode, ContractData, ExtendedContractData, L2Block, L2BlockL2Logs, L2Tx, Tx, TxHash } from '@aztec/types'; + +/** + * Creates a JSON-RPC client to remotely talk to an AztecNode. + * @param url - The URL of the AztecNode + * @param fetch - The fetch implementation to use + * @returns A JSON-RPC client + */ +export function createAztecNodeRpcClient(url: string, fetch = defaultFetch): AztecNode { + const rpcClient = createJsonRpcClient( + url, + { AztecAddress, EthAddress, ExtendedContractData, ContractData, Fr, HistoricBlockData, L2Block, L2Tx, TxHash }, + { Tx, L2BlockL2Logs }, + false, + fetch, + ); + return rpcClient; +} diff --git a/yarn-project/aztec-node/src/rpc/http_rpc_server.ts b/yarn-project/aztec-node/src/rpc/http_rpc_server.ts new file mode 100644 index 00000000000..e1fb672952b --- /dev/null +++ b/yarn-project/aztec-node/src/rpc/http_rpc_server.ts @@ -0,0 +1,31 @@ +import { HistoricBlockData } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { Fr } from '@aztec/foundation/fields'; +import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; +import { AztecNode, ContractData, ExtendedContractData, L2Block, L2BlockL2Logs, L2Tx, Tx, TxHash } from '@aztec/types'; + +/** + * Wrap an AztecNode instance with a JSON RPC HTTP server. + * @param node - The AztecNode + * @returns An JSON-RPC HTTP server + */ +export function createAztecNodeRpcServer(node: AztecNode) { + const rpc = new JsonRpcServer( + node, + { AztecAddress, EthAddress, ExtendedContractData, ContractData, Fr, HistoricBlockData, L2Block, L2Tx, TxHash }, + { Tx, L2BlockL2Logs }, + false, + // disable methods not part of the AztecNode interface + [ + 'start', + 'stop', + 'findContractIndex', + 'findCommitmentIndex', + 'getDataTreePath', + 'getL1ToL2MessageAndIndex', + 'getL1ToL2MessagesTreePath', + ], + ); + return rpc; +} diff --git a/yarn-project/aztec-rpc/src/aztec_rpc_http/aztec_rpc_http_server.ts b/yarn-project/aztec-rpc/src/aztec_rpc_http/aztec_rpc_http_server.ts index 5947aca09fd..162eb59183d 100644 --- a/yarn-project/aztec-rpc/src/aztec_rpc_http/aztec_rpc_http_server.ts +++ b/yarn-project/aztec-rpc/src/aztec_rpc_http/aztec_rpc_http_server.ts @@ -7,6 +7,7 @@ import { CompleteAddress, ContractData, ExtendedContractData, + L2Block, L2BlockL2Logs, NotePreimage, Tx, @@ -41,6 +42,7 @@ export function getHttpRpcServer(aztecRpcServer: AztecRPC): JsonRpcServer { GrumpkinScalar, NotePreimage, AuthWitness, + L2Block, }, { Tx, TxReceipt, L2BlockL2Logs }, false, diff --git a/yarn-project/circuits.js/src/structs/kernel/historic_block_data.test.ts b/yarn-project/circuits.js/src/structs/kernel/historic_block_data.test.ts new file mode 100644 index 00000000000..0d95c9ad138 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/kernel/historic_block_data.test.ts @@ -0,0 +1,17 @@ +import { HistoricBlockData } from './historic_block_data.js'; + +describe('HistoricBlockData', () => { + it('serialises to buffer and back', () => { + const historicBlockData = HistoricBlockData.random(); + const serialised = historicBlockData.toBuffer(); + const deserialised = HistoricBlockData.fromBuffer(serialised); + expect(deserialised).toEqual(historicBlockData); + }); + + it('serialises to string and back', () => { + const historicBlockData = HistoricBlockData.random(); + const serialised = historicBlockData.toString(); + const deserialised = HistoricBlockData.fromString(serialised); + expect(deserialised).toEqual(historicBlockData); + }); +}); diff --git a/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts b/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts index 13645f2cf8b..1423bee43e5 100644 --- a/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/historic_block_data.ts @@ -4,6 +4,11 @@ import { BufferReader } from '@aztec/foundation/serialize'; import { FieldsOf } from '../../utils/jsUtils.js'; import { serializeToBuffer } from '../../utils/serialize.js'; +/** + * The string encoding used for serialising HistoricBlockData objects. + */ +const STRING_ENCODING: BufferEncoding = 'hex'; + /** * Information about the tree roots used for both public and private kernels. */ @@ -78,7 +83,8 @@ export class HistoricBlockData { } toString() { - return this.toBuffer().toString(); + // originally this was encoding as utf-8 (the default). This caused problems decoding back the data. + return this.toBuffer().toString(STRING_ENCODING); } /** @@ -112,6 +118,10 @@ export class HistoricBlockData { ); } + static fromString(str: string) { + return HistoricBlockData.fromBuffer(Buffer.from(str, STRING_ENCODING)); + } + isEmpty() { return ( this.privateDataTreeRoot.isZero() && diff --git a/yarn-project/package.json b/yarn-project/package.json index 9af19177de9..15e92549986 100644 --- a/yarn-project/package.json +++ b/yarn-project/package.json @@ -39,7 +39,6 @@ "boxes/blank", "boxes/private-token", "prover-client", - "rollup-provider", "aztec-node", "sequencer-client", "types", diff --git a/yarn-project/rollup-provider/.dockerignore b/yarn-project/rollup-provider/.dockerignore deleted file mode 100644 index 2b30eaf4896..00000000000 --- a/yarn-project/rollup-provider/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -data -dest -node_modules -Dockerfile \ No newline at end of file diff --git a/yarn-project/rollup-provider/.eslintrc.cjs b/yarn-project/rollup-provider/.eslintrc.cjs deleted file mode 100644 index e659927475c..00000000000 --- a/yarn-project/rollup-provider/.eslintrc.cjs +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@aztec/foundation/eslint'); diff --git a/yarn-project/rollup-provider/.gitignore b/yarn-project/rollup-provider/.gitignore deleted file mode 100644 index 81efe293f26..00000000000 --- a/yarn-project/rollup-provider/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/data* diff --git a/yarn-project/rollup-provider/Dockerfile b/yarn-project/rollup-provider/Dockerfile deleted file mode 100644 index 4c70de61f2b..00000000000 --- a/yarn-project/rollup-provider/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/yarn-project-base AS builder - -COPY . . - -WORKDIR /usr/src/yarn-project/rollup-provider -RUN yarn build && yarn formatting - -# Prune dev dependencies. See comment in base image. -RUN yarn cache clean -RUN yarn workspaces focus --production > /dev/null - -FROM node:18-alpine -COPY --from=builder /usr/src /usr/src -WORKDIR /usr/src/yarn-project/rollup-provider -ENTRYPOINT ["yarn"] \ No newline at end of file diff --git a/yarn-project/rollup-provider/README.md b/yarn-project/rollup-provider/README.md deleted file mode 100644 index 923ae3b99bb..00000000000 --- a/yarn-project/rollup-provider/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Rollup Provider - -This package contains a standalone instance of the Aztec Node with a simple http interface in front of it. - -To build, simply type `yarn build`. To start, type `yarn start`. diff --git a/yarn-project/rollup-provider/package.json b/yarn-project/rollup-provider/package.json deleted file mode 100644 index abb5e816970..00000000000 --- a/yarn-project/rollup-provider/package.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "@aztec/rollup-provider", - "version": "0.1.0", - "main": "dest/index.js", - "type": "module", - "exports": "./dest/index.js", - "typedocOptions": { - "entryPoints": [ - "./src/index.ts" - ], - "name": "Rollup Provider", - "tsconfig": "./tsconfig.json" - }, - "scripts": { - "build": "yarn clean && tsc -b", - "build:dev": "tsc -b --watch", - "clean": "rm -rf ./dest .tsbuildinfo", - "formatting": "run -T prettier --check ./src && run -T eslint ./src", - "formatting:fix": "run -T prettier -w ./src", - "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --passWithNoTests", - "start": "node ./dest/index.js" - }, - "inherits": [ - "../package.common.json" - ], - "jest": { - "preset": "ts-jest/presets/default-esm", - "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.m?js$": "$1" - }, - "testRegex": "./src/.*\\.test\\.(js|mjs|ts)$", - "rootDir": "./src" - }, - "dependencies": { - "@aztec/aztec-node": "workspace:^", - "@aztec/circuits.js": "workspace:^", - "@aztec/foundation": "workspace:^", - "@aztec/types": "workspace:^", - "dotenv": "^16.0.3", - "koa": "^2.14.2", - "koa-router": "^12.0.0", - "promise-readable": "^6.0.0", - "tslib": "^2.4.0" - }, - "devDependencies": { - "@jest/globals": "^29.5.0", - "@rushstack/eslint-patch": "^1.1.4", - "@types/jest": "^29.5.0", - "@types/koa": "^2.13.6", - "@types/node": "^18.7.23", - "jest": "^29.5.0", - "ts-jest": "^29.1.0", - "ts-node": "^10.9.1", - "typescript": "^5.0.4" - }, - "files": [ - "dest", - "src", - "!*.test.*" - ], - "types": "./dest/index.d.ts", - "engines": { - "node": ">=18" - } -} diff --git a/yarn-project/rollup-provider/src/app.ts b/yarn-project/rollup-provider/src/app.ts deleted file mode 100644 index 7b1cf70ca74..00000000000 --- a/yarn-project/rollup-provider/src/app.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { Fr, HistoricBlockData } from '@aztec/circuits.js'; -import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { createDebugLogger } from '@aztec/foundation/log'; -import { AztecNode, MerkleTreeId, SimulationError, Tx, TxHash } from '@aztec/types'; - -import Koa, { Context, DefaultState } from 'koa'; -import Router from 'koa-router'; -import { PromiseReadable } from 'promise-readable'; - -const logger = createDebugLogger('aztec:http_router'); - -/** - * Factory method for constructing the http service. - * @param node - An instance of Aztec Node into which calls are forwarded. - * @param prefix - A prefix for the http service's api routes - * @returns The constructed http service. - */ -export function appFactory(node: AztecNode, prefix: string) { - const router = new Router({ prefix }); - - const checkReady = async (ctx: Context, next: () => Promise) => { - if (!(await node.isReady())) { - ctx.status = 503; - ctx.body = { error: 'Server not ready. Try again later.' }; - } else { - await next(); - } - }; - - const exceptionHandler = async (ctx: Koa.Context, next: () => Promise) => { - try { - await next(); - } catch (err: any) { - logger.error(err); - ctx.status = 400; - ctx.body = { error: err.message }; - } - }; - - router.get('/', async (ctx: Koa.Context) => { - ctx.body = { - serviceName: 'aztec rollup', - isReady: await node.isReady(), - }; - ctx.set('content-type', 'application/json'); - ctx.status = 200; - }); - - router.get('/get-tx', async (ctx: Koa.Context) => { - const hash = ctx.query.hash!; - const txHash = new TxHash(Buffer.from(hash as string, 'hex')); - const tx = await node.getTx(txHash); - ctx.set('content-type', 'application/octet-stream'); - if (tx == undefined) { - ctx.status = 404; - } else { - ctx.status = 200; - ctx.body = tx.toBuffer(); - } - }); - - router.get('/get-block', async (ctx: Koa.Context) => { - const number = +ctx.query.number!; - const block = await node.getBlock(number); - const str = block?.encode().toString('hex'); - ctx.set('content-type', 'application/json'); - ctx.body = { - block: str, - }; - ctx.status = 200; - }); - - router.get('/get-blocks', async (ctx: Koa.Context) => { - const from = +ctx.query.from!; - const limit = +ctx.query.limit!; - const blocks = await node.getBlocks(from, limit); - const strs = blocks.map(x => x.encode().toString('hex')); - ctx.set('content-type', 'application/json'); - ctx.body = { - blocks: strs, - }; - ctx.status = 200; - }); - - router.get('/get-block-number', async (ctx: Koa.Context) => { - ctx.set('content-type', 'application/json'); - ctx.body = { - blockNumber: await node.getBlockNumber(), - }; - ctx.status = 200; - }); - - router.get('/get-version', async (ctx: Koa.Context) => { - ctx.set('content-type', 'application/json'); - ctx.body = { - version: await node.getVersion(), - }; - ctx.status = 200; - }); - - router.get('/get-chain-id', async (ctx: Koa.Context) => { - ctx.set('content-type', 'application/json'); - ctx.body = { - chainId: await node.getChainId(), - }; - ctx.status = 200; - }); - - router.get('/get-rollup-address', async (ctx: Koa.Context) => { - ctx.set('content-type', 'application/json'); - ctx.body = { - rollupAddress: (await node.getRollupAddress()).toString(), - }; - ctx.status = 200; - }); - - router.get('/contract-data-and-bytecode', async (ctx: Koa.Context) => { - const address = ctx.query.address; - ctx.set('content-type', 'application/json'); - ctx.body = { - contractData: await node.getExtendedContractData(AztecAddress.fromString(address as string)), - }; - ctx.status = 200; - }); - - router.get('/contract-data', async (ctx: Koa.Context) => { - const address = ctx.query.address; - ctx.set('content-type', 'application/json'); - ctx.body = { - contractData: await node.getContractData(AztecAddress.fromString(address as string)), - }; - ctx.status = 200; - }); - - router.get('/tree-roots', async (ctx: Koa.Context) => { - const roots: Record = await node.getTreeRoots(); - const output: { [key: string]: string } = {}; - for (const [key, value] of Object.entries(roots)) { - output[key] = value.toString(); - } - ctx.body = { - roots: output, - }; - ctx.status = 200; - }); - - router.get('/historic-block-data', async (ctx: Koa.Context) => { - const blockData: HistoricBlockData = await node.getHistoricBlockData(); - const output: { [key: string]: string } = {}; - for (const [key, value] of Object.entries(blockData)) { - output[key] = value.toString(); - } - ctx.body = { - blockData: output, - }; - ctx.status = 200; - }); - - router.get('/get-logs', async (ctx: Koa.Context) => { - const from = +ctx.query.from!; - const limit = +ctx.query.limit!; - const logType = Number(ctx.query.logType); - if (logType !== 0 && logType !== 1) { - throw new Error('Invalid log type: ' + ctx.query.logType); - } - - const logs = await node.getLogs(from, limit, logType); - const strs = logs.map(x => x.toBuffer().toString('hex')); - ctx.set('content-type', 'application/json'); - ctx.body = { - logs: strs, - }; - ctx.status = 200; - }); - - router.get('/get-pending-tx', async (ctx: Koa.Context) => { - const hash = ctx.query.hash!; - const txHash = new TxHash(Buffer.from(hash as string, 'hex')); - const tx = await node.getPendingTxByHash(txHash); - ctx.set('content-type', 'application/octet-stream'); - if (tx == undefined) { - ctx.status = 404; - } else { - ctx.status = 200; - ctx.body = tx.toBuffer(); - } - }); - - router.get('/contract-index', async (ctx: Koa.Context) => { - const leaf = ctx.query.leaf!; - const index = await node.findContractIndex(Buffer.from(leaf as string, 'hex')); - ctx.set('content-type', 'application/json'); - ctx.body = { - index, - }; - ctx.status = 200; - }); - - router.get('/contract-path', async (ctx: Koa.Context) => { - const leaf = ctx.query.leaf!; - const path = await node.getContractPath(BigInt(leaf as string)); - ctx.set('content-type', 'application/json'); - ctx.body = { - path: path.toString(), - }; - ctx.status = 200; - }); - - router.get('/commitment-index', async (ctx: Koa.Context) => { - const leaf = ctx.query.leaf!; - const index = await node.findCommitmentIndex(Buffer.from(leaf as string, 'hex')); - ctx.set('content-type', 'application/json'); - ctx.body = { - index, - }; - ctx.status = 200; - }); - - router.get('/data-path', async (ctx: Koa.Context) => { - const leaf = ctx.query.leaf!; - const index = BigInt(leaf as string); - const path = await node.getDataTreePath(index); - ctx.set('content-type', 'application/json'); - const pathAsString = path.toString(); - ctx.body = { - path: pathAsString, - }; - ctx.status = 200; - }); - - router.get('/l1-l2-message', async (ctx: Koa.Context) => { - const key = ctx.query.messageKey!; - const messageAndindex = await node.getL1ToL2MessageAndIndex(Fr.fromString(key as string)); - ctx.set('content-type', 'application/json'); - ctx.body = { - message: messageAndindex.message.toBuffer().toString('hex'), - index: messageAndindex.index, - }; - ctx.status = 200; - }); - - router.get('/l1-l2-path', async (ctx: Koa.Context) => { - const leaf = ctx.query.leaf!; - const path = await node.getL1ToL2MessagesTreePath(BigInt(leaf as string)); - ctx.set('content-type', 'application/json'); - ctx.body = { - path: path.toString(), - }; - ctx.status = 200; - }); - - router.get('/public-storage-at', async (ctx: Koa.Context) => { - logger('public-storage-at'); - const address = ctx.query.address!; - const slot = ctx.query.slot!; - const value = await node.getPublicStorageAt(AztecAddress.fromString(address as string), BigInt(slot as string)); - ctx.set('content-type', 'application/json'); - ctx.body = { - value: value?.toString('hex'), - }; - ctx.status = 200; - }); - - router.post('/tx', checkReady, async (ctx: Koa.Context) => { - const stream = new PromiseReadable(ctx.req); - const postData = (await stream.readAll()) as Buffer; - const tx = Tx.fromBuffer(postData); - await node.sendTx(tx); - ctx.status = 200; - }); - - router.post('/tx-simulate', checkReady, async (ctx: Koa.Context) => { - const stream = new PromiseReadable(ctx.req); - const postData = (await stream.readAll()) as Buffer; - const tx = Tx.fromBuffer(postData); - try { - await node.simulatePublicCalls(tx); - } catch (err) { - if (err instanceof SimulationError) { - ctx.body = { - simulationError: err.toJSON(), - }; - ctx.status = 400; - } else { - throw err; - } - } - }); - - const app = new Koa(); - app.on('error', error => { - logger.error(`KOA app-level error. ${JSON.stringify({ error })}`); - }); - app.proxy = true; - app.use(exceptionHandler); - app.use(router.routes()); - app.use(router.allowedMethods()); - - return app; -} diff --git a/yarn-project/rollup-provider/src/index.ts b/yarn-project/rollup-provider/src/index.ts deleted file mode 100644 index b64dcd6b306..00000000000 --- a/yarn-project/rollup-provider/src/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; -import { createDebugLogger } from '@aztec/foundation/log'; - -import 'dotenv/config'; -import http from 'http'; - -import { appFactory } from './app.js'; - -const logger = createDebugLogger('aztec:rollup_provider'); - -const { SERVER_PORT = 9000 } = process.env; - -/** - * Entrypoint for the rollup provider service - * @returns An empty promise - */ -async function main() { - logger('Server started...'); - const aztecNodeConfig: AztecNodeConfig = getConfigEnvVars(); - const node = await AztecNodeService.createAndSync(aztecNodeConfig); - - const shutdown = async () => { - await node.stop(); - process.exit(0); - }; - - process.once('SIGINT', shutdown); - process.once('SIGTERM', shutdown); - - const app = appFactory(node, ''); - - const httpServer = http.createServer(app.callback()); - httpServer.listen(SERVER_PORT); - logger(`Server listening on port ${SERVER_PORT}.`); -} - -main().catch(err => { - logger.error(err); - process.exit(1); -}); diff --git a/yarn-project/rollup-provider/tsconfig.json b/yarn-project/rollup-provider/tsconfig.json deleted file mode 100644 index d664fee7b6d..00000000000 --- a/yarn-project/rollup-provider/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "..", - "compilerOptions": { - "outDir": "dest", - "rootDir": "src", - "tsBuildInfoFile": ".tsbuildinfo" - }, - "references": [ - { - "path": "../aztec-node" - }, - { - "path": "../circuits.js" - }, - { - "path": "../foundation" - }, - { - "path": "../types" - } - ], - "include": ["src"] -} diff --git a/yarn-project/tsconfig.json b/yarn-project/tsconfig.json index c8682d550f7..5974c470c00 100644 --- a/yarn-project/tsconfig.json +++ b/yarn-project/tsconfig.json @@ -37,7 +37,6 @@ { "path": "p2p/tsconfig.json" }, { "path": "p2p-bootstrap/tsconfig.json" }, { "path": "prover-client/tsconfig.json" }, - { "path": "rollup-provider/tsconfig.json" }, { "path": "sequencer-client/tsconfig.json" }, { "path": "types/tsconfig.json" }, { "path": "world-state/tsconfig.json" } diff --git a/yarn-project/types/src/l2_block.test.ts b/yarn-project/types/src/l2_block.test.ts index 55fd557a68b..99d20880517 100644 --- a/yarn-project/types/src/l2_block.test.ts +++ b/yarn-project/types/src/l2_block.test.ts @@ -11,6 +11,14 @@ describe('L2Block', () => { expect(recovered).toEqual(block); }); + it('can encode a L2 block to string and back', () => { + const block = L2Block.random(42); + const serialised = block.toString(); + const recovered = L2Block.fromString(serialised); + + expect(recovered).toEqual(block); + }); + // TS equivalent of `testComputeKernelLogsIterationWithoutLogs` in `Decoder.t.sol` it('correctly computes kernel logs hash when there are no logs', () => { // The following 2 values are copied from `testComputeKernelLogsIterationWithoutLogs` in `Decoder.t.sol` diff --git a/yarn-project/types/src/l2_block.ts b/yarn-project/types/src/l2_block.ts index a3a7c1e7a20..a0a3950bed6 100644 --- a/yarn-project/types/src/l2_block.ts +++ b/yarn-project/types/src/l2_block.ts @@ -19,6 +19,11 @@ import times from 'lodash.times'; import { ContractData, L2Tx, LogType, PublicDataWrite, TxL2Logs } from './index.js'; import { L2BlockL2Logs } from './logs/l2_block_l2_logs.js'; +/** + * String encoding of serialised L2 block data. + */ +const STRING_ENCODING: BufferEncoding = 'hex'; + /** * The data that makes up the rollup proof, with encoder decoder functions. * TODO: Reuse data types and serialization functions from circuits package. @@ -388,6 +393,14 @@ export class L2Block { return this.encode(); } + /** + * Encodes the block as a hex string + * @returns The encoded L2 block data as a hex string. + */ + toString() { + return this.toBuffer().toString(STRING_ENCODING); + } + /** * Decode the L2 block data from a buffer. * @param encoded - The encoded L2 block data. @@ -447,6 +460,15 @@ export class L2Block { }); } + /** + * Decode the L2 block from a string + * @param str - The serialised L2 block + * @returns An L2 block + */ + static fromString(str: string): L2Block { + return L2Block.decode(Buffer.from(str, STRING_ENCODING)); + } + /** * Helper function to attach logs related to a block. * @param logs - The logs to be attached to a block. diff --git a/yarn-project/types/src/l2_tx.test.ts b/yarn-project/types/src/l2_tx.test.ts index 889ec8e6ee6..460943240cf 100644 --- a/yarn-project/types/src/l2_tx.test.ts +++ b/yarn-project/types/src/l2_tx.test.ts @@ -6,4 +6,10 @@ describe('L2Tx', () => { const buf = tx.toBuffer(); expect(L2Tx.fromBuffer(buf)).toEqual(tx); }); + + it('converts to and from string', () => { + const tx = L2Tx.random(); + const str = tx.toString(); + expect(L2Tx.fromString(str)).toEqual(tx); + }); }); diff --git a/yarn-project/types/src/l2_tx.ts b/yarn-project/types/src/l2_tx.ts index 29452442a65..a101e678038 100644 --- a/yarn-project/types/src/l2_tx.ts +++ b/yarn-project/types/src/l2_tx.ts @@ -15,6 +15,11 @@ import { ContractData } from './contract_data.js'; import { PublicDataWrite } from './public_data_write.js'; import { TxHash } from './tx/tx_hash.js'; +/** + * The string encoding used for serialising L2Tx objects to strings. + */ +const STRING_ENCODING: BufferEncoding = 'hex'; + /** * Represents an L2 transaction. */ @@ -81,6 +86,15 @@ export class L2Tx { ); } + /** + * Deserializes an L2Tx object from a string. + * @param str - String to deserialize. + * @returns An instance of L2Tx. + */ + static fromString(str: string) { + return L2Tx.fromBuffer(Buffer.from(str, STRING_ENCODING)); + } + /** * Serializes the Tx object into a Buffer. * @returns Buffer representation of the Tx object. @@ -98,6 +112,13 @@ export class L2Tx { ]); } + /** + * Returns a string representation of the Tx object. + */ + toString(): string { + return this.toBuffer().toString(STRING_ENCODING); + } + static random() { return new L2Tx( times(MAX_NEW_COMMITMENTS_PER_TX, Fr.random), diff --git a/yarn-project/yarn-project-base/Dockerfile b/yarn-project/yarn-project-base/Dockerfile index 7ab6961d39d..3e3395182de 100644 --- a/yarn-project/yarn-project-base/Dockerfile +++ b/yarn-project/yarn-project-base/Dockerfile @@ -42,7 +42,6 @@ COPY circuits.js/package.json circuits.js/package.json COPY p2p/package.json p2p/package.json COPY p2p-bootstrap/package.json p2p-bootstrap/package.json COPY prover-client/package.json prover-client/package.json -COPY rollup-provider/package.json rollup-provider/package.json COPY sequencer-client/package.json sequencer-client/package.json COPY types/package.json types/package.json COPY world-state/package.json world-state/package.json @@ -96,7 +95,6 @@ COPY p2p/tsconfig.json p2p/tsconfig.json COPY p2p-bootstrap/tsconfig.json p2p-bootstrap/tsconfig.json COPY prover-client/tsconfig.json prover-client/tsconfig.json COPY sequencer-client/tsconfig.json sequencer-client/tsconfig.json -COPY rollup-provider/tsconfig.json rollup-provider/tsconfig.json COPY types/tsconfig.json types/tsconfig.json COPY world-state/tsconfig.json world-state/tsconfig.json diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 3853bf9f7a8..1806b4a0e26 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -685,31 +685,6 @@ __metadata: languageName: unknown linkType: soft -"@aztec/rollup-provider@workspace:rollup-provider": - version: 0.0.0-use.local - resolution: "@aztec/rollup-provider@workspace:rollup-provider" - dependencies: - "@aztec/aztec-node": "workspace:^" - "@aztec/circuits.js": "workspace:^" - "@aztec/foundation": "workspace:^" - "@aztec/types": "workspace:^" - "@jest/globals": ^29.5.0 - "@rushstack/eslint-patch": ^1.1.4 - "@types/jest": ^29.5.0 - "@types/koa": ^2.13.6 - "@types/node": ^18.7.23 - dotenv: ^16.0.3 - jest: ^29.5.0 - koa: ^2.14.2 - koa-router: ^12.0.0 - promise-readable: ^6.0.0 - ts-jest: ^29.1.0 - ts-node: ^10.9.1 - tslib: ^2.4.0 - typescript: ^5.0.4 - languageName: unknown - linkType: soft - "@aztec/sequencer-client@workspace:^, @aztec/sequencer-client@workspace:sequencer-client": version: 0.0.0-use.local resolution: "@aztec/sequencer-client@workspace:sequencer-client" @@ -8039,13 +8014,6 @@ __metadata: languageName: node linkType: hard -"core-js@npm:^3.6.5": - version: 3.32.0 - resolution: "core-js@npm:3.32.0" - checksum: 52921395028550e4c9d21d47b9836439bb5b6b9eefc34d45a3948a68d81fdd093acc0fadf69f9cf632b82f01f95f22f484408a93dd9e940b19119ac204cd2925 - languageName: node - linkType: hard - "core-util-is@npm:^1.0.2, core-util-is@npm:~1.0.0": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -15555,15 +15523,6 @@ __metadata: languageName: node linkType: hard -"promise-readable@npm:^6.0.0": - version: 6.0.0 - resolution: "promise-readable@npm:6.0.0" - dependencies: - core-js: ^3.6.5 - checksum: 2e93f83457011ca721e7a55b9bb5045f2bf75dc1435d1912a2f20db1ba309e3343364eb68525e87b4c8b27baa0ab04619d0a3c064e944584260b620ebcbb1bef - languageName: node - linkType: hard - "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1"