diff --git a/scripts/build.js b/scripts/build.js index 639081f7e..d081a93d3 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -30,6 +30,18 @@ methodFiles.forEach(file => { ]; }); +methodsBase = "src/address/methods/"; +methodFiles = fs.readdirSync(methodsBase); +methodFiles.forEach(file => { + console.log(file); + let raw = fs.readFileSync(methodsBase + file); + let parsed = yaml.load(raw); + methods = [ + ...methods, + ...parsed, + ]; +}); + methodsBase = "src/debug/"; methodFiles = fs.readdirSync(methodsBase); methodFiles.forEach(file => { @@ -55,6 +67,7 @@ methodFiles.forEach(file => { }); let schemas = {}; + let schemasBase = "src/schemas/" let schemaFiles = fs.readdirSync(schemasBase); schemaFiles.forEach(file => { @@ -67,6 +80,18 @@ schemaFiles.forEach(file => { }; }); +schemasBase = "src/address/schemas/" +schemaFiles = fs.readdirSync(schemasBase); +schemaFiles.forEach(file => { + console.log(file); + let raw = fs.readFileSync(schemasBase + file); + let parsed = yaml.load(raw); + schemas = { + ...schemas, + ...parsed, + }; +}); + schemasBase = "src/engine/openrpc/schemas/" schemaFiles = fs.readdirSync(schemasBase); schemaFiles.forEach(file => { diff --git a/src/address/README.md b/src/address/README.md new file mode 100644 index 000000000..395b6b835 --- /dev/null +++ b/src/address/README.md @@ -0,0 +1,84 @@ +# Address JSON-RPC API + +## Table of Contents + +- [Address JSON-RPC API](#address-json-rpc-api) + - [Table of Contents](#table-of-contents) + - [Overview](#overview) + - [Separate `address_` namespace](#separate-address_-namespace) + - [Database requirement](#database-requirement) + - [Rationale for the index existing inside the node](#rationale-for-the-index-existing-inside-the-node) + - ["Address appearances" concept](#address-appearances-concept) + - [Security considerations](#security-considerations) + - [Increased resource use](#increased-resource-use) + +## Overview + +The Address JSON-RPC API is a collection of methods that Ethereum archival execution clients MAY implement. + +This interface allows the use of an archive node from the perspective of an end user. +It provides information about which transactions are important for a given address. +This includes any address, including an externally owned account, a smart wallet, an account abstraction wallet or an application contract. + +The API is desinged around an index mapping addresses to transaction identifiers. This index is created in advance so that queries are responded to quickly and do not require block re-execution. + +The API allows introspection into otherwise opaque history. An example is an externally +owned account that received ether via a `CALL` opcode. +The API can return this transaction id, which can then be used with other methods (e.g., `debug_traceTransaction`) to identify the meaning of the transaction, +in this case an ether transfer. + +## Separate `address_` namespace + +A separace `address_` namespace exists because the method(s) within the namespace +require additional considerations that the `eth_` namespace does not require. +Clients that support the `address_` therefore have a clear way to toggle on this feature +to explicitly opt in to these requirements. + +The requirements are: +- Archival node (store history for all blocks) +- Additional database requirement (see below) + +## Database requirement + +A node that supports the `address_` must store an index consisting of a mapping of address +to transaction identifiers. Each block must be parsed and addresses detected. Then for each +address the block number and transaction index must be stored. + +The space required for a basic implementation is estimated at 80GB. Client implementations +may be able to reduce this amount significantly. + +## Rationale for the index existing inside the node + +The rationale for storing the index alongside other chain data is to standardise a new pattern +of data access. Archive nodes that support the `address_` namespace will be able to provide +users with the ability to introspect on particular addresses out of the box. This +means that a frontend can connect to a node and have historical data access for an +arbitrary combination of addresses. + +This provides application developers with a new tool for decentralised front ends that +include historical information. + +As the `address_` namespace methods apply to all addresses, this is a generalised solution +that makes inclusion inside the node an immediate utility for every user and protocol. + +## "Address appearances" concept + +`address_getAppearances` is the basic method to discover which transactions are relevant to a given address. + +For a given address, this method returns an array of transaction identifiers in which the +address "appears". An "address appearance" is defined in [../../specs/appearance](../../specs/appearance.md) + +## Security considerations + +See [../../specs/appearance](../../specs/appearance.md) for additional security +considerations. + +### Increased resource use + +The provision of the `address_` namespace will result in the index being created by +the client. This can cause a temporary high resource use (CPU/disk), and then an +additional amount of work as the chain grows. + +The amount of new work for a new block may impact a resource constrained node. The +task involves the equivalent of calling `debug_traceTransaction` with a `callTracer`, +parsing for addresses, and then adding these to a local database. diff --git a/src/address/methods/appearances.yaml b/src/address/methods/appearances.yaml new file mode 100644 index 000000000..508f2f9d6 --- /dev/null +++ b/src/address/methods/appearances.yaml @@ -0,0 +1,17 @@ +- name: address_getAppearances + summary: Returns transaction identifiers relevant to an address. + params: + - name: Address + required: true + schema: + $ref: '#/components/schemas/address' + - name: Block range + required: false + schema: + $ref: '#/components/schemas/BlockRange' + result: + name: Address appearances + schema: + oneOf: + - $ref: '#/components/schemas/notFound' + - $ref: '#/components/schemas/AddressAppearances' diff --git a/src/address/schemas/appearances.yaml b/src/address/schemas/appearances.yaml new file mode 100644 index 000000000..c1130fc75 --- /dev/null +++ b/src/address/schemas/appearances.yaml @@ -0,0 +1,38 @@ +AddressAppearances: + title: Transaction identifiers relevant to an address + type: array + additionalProperties: false + $ref: '#/components/schemas/RelevantTransaction' +RelevantTransaction: + title: Relevant Transaction + type: object + description: Identifier of a transaction relevant to an address + required: + - blockNumber + - location + additionalProperties: false + properties: + blockNumber: + title: Block number + description: Block containing the relevant transactions + $ref: '#/components/schemas/uint' + location: + title: Location in block + $ref: '#/components/schemas/Location' +BlockRange: + title: Block range + type: object + description: Inclusive range of blocks + required: + - firstBlock + - lastBlock + additionalProperties: false + properties: + firstBlock: + title: First block + description: Lowest block in the range + $ref: '#/components/schemas/BlockNumberOrTag' + lastBlock: + title: Last block + description: Highest block in the range + $ref: '#/components/schemas/BlockNumberOrTag' diff --git a/src/schemas/address.yaml b/src/schemas/address.yaml new file mode 100644 index 000000000..e9259fd6e --- /dev/null +++ b/src/schemas/address.yaml @@ -0,0 +1,17 @@ +Location: + title: Location in a block + description: A place an address appears within a block + oneOf: + - title: Transaction index + $ref: '#/components/schemas/uint64' + - title: Block field + $ref: '#/components/schemas/BlockField' +BlockField: + title: Block field + description: A block field. Alloc is a genesis block field. + type: string + enum: + - alloc + - miner + - uncles + - withdrawals \ No newline at end of file diff --git a/tests/address_getAppearances/get-appearances-all-blocks-none.io b/tests/address_getAppearances/get-appearances-all-blocks-none.io new file mode 100644 index 000000000..a4ad78694 --- /dev/null +++ b/tests/address_getAppearances/get-appearances-all-blocks-none.io @@ -0,0 +1,2 @@ +>> {"jsonrpc":"2.0","id":1,"method":"address_getAppearances","params":["0x0000639850b3ddeaaca4f06280aa751682f11382"]} +<< {"jsonrpc":"2.0","id":1,"result":[]} diff --git a/tests/address_getAppearances/get-appearances-all-blocks-some.io b/tests/address_getAppearances/get-appearances-all-blocks-some.io new file mode 100644 index 000000000..cea0a7d95 --- /dev/null +++ b/tests/address_getAppearances/get-appearances-all-blocks-some.io @@ -0,0 +1,2 @@ +>> {"jsonrpc":"2.0","id":1,"method":"address_getAppearances","params":["0x30a4639850b3ddeaaca4f06280aa751682f11382"]} +<< {"id":1,"jsonrpc":"2.0","result":[{"blockNumber":"0xd63f51","location":"0xe6"},{"blockNumber":"0xd63f5a","location":"0x11b"},{"blockNumber":"0xd68154","location":"0x6"},{"blockNumber":"0xd69d4b","location":"0x3d"},{"blockNumber":"0xd69db2","location":"0x4b"},{"blockNumber":"0xd6de5e","location":"0x25"},{"blockNumber":"0xd6dedb","location":"0x73"},{"blockNumber":"0xd6ecfe","location":"0x66"},{"blockNumber":"0xe00b8c","location":"0x6b"},{"blockNumber":"0xe00b96","location":"0x78"},{"blockNumber":"0xe00b96","location":"0x79"},{"blockNumber":"0xe0ae21","location":"0x126"},{"blockNumber":"0xe2e9b7","location":"0x81"},{"blockNumber":"0xe2e9bd","location":"0x13c"},{"blockNumber":"0xec5518","location":"0xaf"},{"blockNumber":"0xec5569","location":"0x113"},{"blockNumber":"0xec5569","location":"0x11c"},{"blockNumber":"0xec556b","location":"0xa5"},{"blockNumber":"0xec5576","location":"0x125"},{"blockNumber":"0xec557b","location":"0x109"},{"blockNumber":"0xec5584","location":"0x72"},{"blockNumber":"0xec5612","location":"0x1a"},{"blockNumber":"0xf2ea24","location":"0x22"},{"blockNumber":"0xf2ea59","location":"0x95"},{"blockNumber":"0x1064b72","location":"0x54"},{"blockNumber":"0x1064b7a","location":"0x9e"},{"blockNumber":"0x1064f19","location":"0x67"},{"blockNumber":"0x1064f22","location":"0x88"},{"blockNumber":"0x1064fd9","location":"0x5f"},{"blockNumber":"0x1064fe9","location":"0x59"},{"blockNumber":"0x1065d79","location":"0x7d"},{"blockNumber":"0x1065da3","location":"0xa3"},{"blockNumber":"0x106c13f","location":"0x74"},{"blockNumber":"0x10c2127","location":"0x2b"}]} diff --git a/tests/address_getAppearances/get-appearances-block-range-miner.io b/tests/address_getAppearances/get-appearances-block-range-miner.io new file mode 100644 index 000000000..1adc693dc --- /dev/null +++ b/tests/address_getAppearances/get-appearances-block-range-miner.io @@ -0,0 +1,2 @@ +>> {"jsonrpc":"2.0","id":1,"method":"address_getAppearances","params":["0xd2090025857b9c7b24387741f120538e928a3a59",{"firstBlock":"0x1064fd9","lastBlock":"0x1064fe9"}]} +<< {"id":1,"jsonrpc":"2.0","result":[{"blockNumber":"0x1064fd9","location":"0x8"},{"blockNumber":"0x1064fd9","location":"0xcd"},{"blockNumber":"0x1064fd9","location":"miner"}]} diff --git a/tests/address_getAppearances/get-appearances-block-range-single.io b/tests/address_getAppearances/get-appearances-block-range-single.io new file mode 100644 index 000000000..809e41fa6 --- /dev/null +++ b/tests/address_getAppearances/get-appearances-block-range-single.io @@ -0,0 +1,2 @@ +>> {"jsonrpc":"2.0","id":1,"method":"address_getAppearances","params":["0x30a4639850b3ddeaaca4f06280aa751682f11382",{"firstBlock":"0x1064fd9","lastBlock":"0x1064fd9"}]} +<< {"id":1,"jsonrpc":"2.0","result":[{"blockNumber":"0x1064fd9","location":"0x5f"}]} diff --git a/tests/address_getAppearances/get-appearances-block-range.io b/tests/address_getAppearances/get-appearances-block-range.io new file mode 100644 index 000000000..dc8b82ea3 --- /dev/null +++ b/tests/address_getAppearances/get-appearances-block-range.io @@ -0,0 +1,2 @@ +>> {"jsonrpc":"2.0","id":1,"method":"address_getAppearances","params":["0x30a4639850b3ddeaaca4f06280aa751682f11382",{"firstBlock":"0x1064fd9","lastBlock":"0x1064fe9"}]} +<< {"id":1,"jsonrpc":"2.0","result":[{"blockNumber":"0x1064fd9","location":"0x5f"},{"blockNumber":"0x1064fe9","location":"0x59"}]} diff --git a/tests/address_getAppearances/get-appearances-one-block-multiple-transactions.io b/tests/address_getAppearances/get-appearances-one-block-multiple-transactions.io new file mode 100644 index 000000000..cc6912069 --- /dev/null +++ b/tests/address_getAppearances/get-appearances-one-block-multiple-transactions.io @@ -0,0 +1,2 @@ +>> {"jsonrpc":"2.0","id":1,"method":"address_getAppearances","params":["0x2d7C6B69175c2939173F2fd470538835336Df92b",{"firstBlock":"0x1064fd9","lastBlock":"0x1064fd9"}]} +<< {"id":1,"jsonrpc":"2.0","result":[{"blockNumber":"0x1064fd9","location":"0x67"},{"blockNumber":"0x1064fd9","location":"0xa3"},{"blockNumber":"0x1064fd9","location":"0xa9"},{"blockNumber":"0x1064fd9","location":"0xb3"},{"blockNumber":"0x1064fd9","location":"0xb6"}]} diff --git a/wordlist.txt b/wordlist.txt index fef7a4af8..ece805892 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -1,3 +1,4 @@ +alloc apis attributesv bodyv