diff --git a/.docker/docker-entrypoint-initdb.d/init.txt b/.docker/docker-entrypoint-initdb.d/init.txt index 922803f5..b871edfc 100644 --- a/.docker/docker-entrypoint-initdb.d/init.txt +++ b/.docker/docker-entrypoint-initdb.d/init.txt @@ -133,6 +133,17 @@ CREATE TABLE filter ( addresses address[] NULL CHECK (array_length(addresses, 1) > 0), topics jsonb NULL ); +DROP TABLE IF EXISTS subscription CASCADE; + +CREATE TABLE subscription ( + sec_websocket_key varchar COLLATE "default", + id varchar COLLATE "default", + type varchar COLLATE "default", + ip varchar COLLATE "default", + filter jsonb NULL +); + +CREATE UNIQUE INDEX subscription_sec_websocket_key_type_filter_index_idx ON subscription (sec_websocket_key, type, filter); DROP TABLE IF EXISTS transaction CASCADE; CREATE TABLE transaction ( @@ -178,18 +189,6 @@ CREATE TABLE event ( CREATE UNIQUE INDEX event_transaction_index_idx ON event (transaction, index); DROP FUNCTION IF EXISTS eth_blockNumber() RESTRICT; -DROP TABLE IF EXISTS subscription CASCADE; - -CREATE TABLE subscription ( - sec_websocket_key varchar COLLATE "default", - id varchar COLLATE "default", - type varchar COLLATE "default", - ip varchar COLLATE "default", - filter jsonb NULL -); - -CREATE UNIQUE INDEX subscription_sec_websocket_key_type_filter_index_idx ON subscription (sec_websocket_key, type, filter); - CREATE FUNCTION eth_blockNumber() RETURNS blockno AS $$ DECLARE block_id blockno; @@ -505,6 +504,66 @@ BEGIN RETURN result; END; $$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE; +DROP FUNCTION IF EXISTS eth_getTransactionReceiptsByBlockNumber(blockno) RESTRICT; + +CREATE FUNCTION eth_getTransactionReceiptsByBlockNumber(block_id blockno) RETURNS SETOF transaction_receipt AS $$ +DECLARE + result transaction_receipt; +BEGIN + FOR result IN + SELECT + b.id AS "blockNumber", + b.hash AS "blockHash", + t.index AS "transactionIndex", + t.hash AS "transactionHash", + t.from AS "from", + t.to AS "to", + t.gas_used AS "gasUsed", + 0::u256 AS "cumulativeGasUsed", -- TODO: tally? + CASE WHEN (t.to IS NULL OR t.to = '\x0000000000000000000000000000000000000000') AND length(t.output) = 20 THEN t.output + ELSE NULL + END AS "contractAddress", + NULL AS "logs", -- TODO: fetch event.id[] + repeat('\\000', 256)::bytea AS "logsBloom", + CASE WHEN t.status THEN 1 ELSE 0 END AS "status" + FROM transaction t + LEFT JOIN block b ON t.block = b.id + WHERE b.id = block_id + LOOP + RETURN NEXT result; + END LOOP; +END; +$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE; +DROP FUNCTION IF EXISTS eth_getTransactionsByBlockNumber(blockno) RESTRICT; + +CREATE FUNCTION eth_getTransactionsByBlockNumber(block_id blockno) RETURNS SETOF transaction_result AS $$ +DECLARE + result transaction_result; +BEGIN + FOR result IN + SELECT + b.id AS "blockNumber", + b.hash AS "blockHash", + t.index AS "transactionIndex", + t.hash AS "hash", + t.from AS "from", + t.to AS "to", + t.gas_limit AS "gas", + t.gas_price AS "gasPrice", + t.nonce AS "nonce", + t.value AS "value", + coalesce(t.input, '\\x'::bytea) AS "input", + t.v AS "v", + t.r AS "r", + t.s AS "s" + FROM transaction t + LEFT JOIN block b ON t.block = b.id + WHERE b.id = block_id + LOOP + RETURN NEXT result; + END LOOP; +END; +$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE; DROP FUNCTION IF EXISTS eth_getUncleByBlockHashAndIndex(hash, int) RESTRICT; CREATE FUNCTION eth_getUncleByBlockHashAndIndex(block_hash hash, uncle_index int) RETURNS block_result AS $$ diff --git a/etc/methods.yaml b/etc/methods.yaml index 3e720f84..bd2efb1f 100644 --- a/etc/methods.yaml +++ b/etc/methods.yaml @@ -235,6 +235,22 @@ eth_getTransactionReceipt: implementations: - src/servers/skeleton.ts +eth_getTransactionReceiptsByBlockNumber: + status: ok + comment: Non Standard Method + link: + implementations: + - etc/schema/functions/eth_getTransactionReceiptsByBlockNumber.sql + - src/servers/skeleton.ts + +eth_getTransactionsByBlockNumber: + status: ok + comment: Non Standard Method + link: + implementations: + - etc/schema/functions/eth_getTransactionsByBlockNumber.sql + - src/servers/skeleton.ts + eth_getUncleByBlockHashAndIndex: status: ok comment: diff --git a/etc/schema.sql b/etc/schema.sql index 3bfe67e5..065289ad 100644 --- a/etc/schema.sql +++ b/etc/schema.sql @@ -21,6 +21,8 @@ \i etc/schema/functions/eth_getTransactionByHash.sql \i etc/schema/functions/eth_getTransactionCount.sql \i etc/schema/functions/eth_getTransactionReceipt.sql +\i etc/schema/functions/eth_getTransactionReceiptsByBlockNumber.sql +\i etc/schema/functions/eth_getTransactionsByBlockNumber.sql \i etc/schema/functions/eth_getUncleByBlockHashAndIndex.sql \i etc/schema/functions/eth_getUncleByBlockNumberAndIndex.sql \i etc/schema/functions/eth_getUncleCountByBlockHash.sql @@ -29,3 +31,4 @@ \i etc/schema/functions/eth_newFilter.sql \i etc/schema/functions/eth_newPendingTransactionFilter.sql \i etc/schema/functions/eth_uninstallFilter.sql + diff --git a/etc/schema/functions/eth_getTransactionReceiptsByBlockNumber.sql b/etc/schema/functions/eth_getTransactionReceiptsByBlockNumber.sql new file mode 100644 index 00000000..0ec00b1e --- /dev/null +++ b/etc/schema/functions/eth_getTransactionReceiptsByBlockNumber.sql @@ -0,0 +1,30 @@ +DROP FUNCTION IF EXISTS eth_getTransactionReceiptsByBlockNumber(blockno) RESTRICT; + +CREATE FUNCTION eth_getTransactionReceiptsByBlockNumber(block_id blockno) RETURNS SETOF transaction_receipt AS $$ +DECLARE + result transaction_receipt; +BEGIN + FOR result IN + SELECT + b.id AS "blockNumber", + b.hash AS "blockHash", + t.index AS "transactionIndex", + t.hash AS "transactionHash", + t.from AS "from", + t.to AS "to", + t.gas_used AS "gasUsed", + 0::u256 AS "cumulativeGasUsed", -- TODO: tally? + CASE WHEN (t.to IS NULL OR t.to = '\x0000000000000000000000000000000000000000') AND length(t.output) = 20 THEN t.output + ELSE NULL + END AS "contractAddress", + NULL AS "logs", -- TODO: fetch event.id[] + repeat('\\000', 256)::bytea AS "logsBloom", + CASE WHEN t.status THEN 1 ELSE 0 END AS "status" + FROM transaction t + LEFT JOIN block b ON t.block = b.id + WHERE b.id = block_id + LOOP + RETURN NEXT result; + END LOOP; +END; +$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE; diff --git a/etc/schema/functions/eth_getTransactionsByBlockNumber.sql b/etc/schema/functions/eth_getTransactionsByBlockNumber.sql new file mode 100644 index 00000000..ae770f76 --- /dev/null +++ b/etc/schema/functions/eth_getTransactionsByBlockNumber.sql @@ -0,0 +1,30 @@ +DROP FUNCTION IF EXISTS eth_getTransactionsByBlockNumber(blockno) RESTRICT; + +CREATE FUNCTION eth_getTransactionsByBlockNumber(block_id blockno) RETURNS SETOF transaction_result AS $$ +DECLARE + result transaction_result; +BEGIN + FOR result IN + SELECT + b.id AS "blockNumber", + b.hash AS "blockHash", + t.index AS "transactionIndex", + t.hash AS "hash", + t.from AS "from", + t.to AS "to", + t.gas_limit AS "gas", + t.gas_price AS "gasPrice", + t.nonce AS "nonce", + t.value AS "value", + coalesce(t.input, '\\x'::bytea) AS "input", + t.v AS "v", + t.r AS "r", + t.s AS "s" + FROM transaction t + LEFT JOIN block b ON t.block = b.id + WHERE b.id = block_id + LOOP + RETURN NEXT result; + END LOOP; +END; +$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE; diff --git a/lib/servers/database.d.ts b/lib/servers/database.d.ts index deb564ff..7096cd6d 100644 --- a/lib/servers/database.d.ts +++ b/lib/servers/database.d.ts @@ -27,6 +27,8 @@ export declare class DatabaseServer extends SkeletonServer { eth_getTransactionByHash(_request: Request, transactionHash: web3.Data): Promise; eth_getTransactionCount(_request: Request, address: web3.Data, blockNumber: web3.Quantity | web3.Tag): Promise; eth_getTransactionReceipt(_request: Request, transactionHash: string): Promise; + eth_getTransactionReceiptsByBlockNumber(_request: any, blockNumber: web3.Quantity | web3.Tag): Promise; + eth_getTransactionsByBlockNumber(_request: any, blockNumber: web3.Quantity | web3.Tag): Promise; eth_getUncleCountByBlockHash(_request: Request, blockHash: web3.Data): Promise; eth_getUncleCountByBlockNumber(_request: Request, blockNumber: web3.Quantity | web3.Tag): Promise; eth_newBlockFilter(_request: Request): Promise; diff --git a/lib/servers/database.js b/lib/servers/database.js index 0d17893d..8ddb5029 100644 --- a/lib/servers/database.js +++ b/lib/servers/database.js @@ -336,6 +336,39 @@ export class DatabaseServer extends SkeletonServer { return null; } } + async eth_getTransactionReceiptsByBlockNumber(_request, blockNumber) { + const blockNumber_ = parseBlockSpec(blockNumber) != 0 + ? parseBlockSpec(blockNumber) || (await this._fetchCurrentBlockID()) + : parseBlockSpec(blockNumber); + try { + const { rows, } = await this._query('SELECT * FROM eth_getTransactionReceiptsByBlockNumber($1)', [blockNumber_]); + for (let i = 0; i < rows.length; i++) { + rows[i].logs = await this._fetchEvents(hexToBytes(rows[i].transactionHash.toString())); + } + return rows.length == 0 ? [] : exportJSON(rows); + } + catch (error) { + if (this.config.debug) { + console.debug('eth_getTransactionReceiptsByBlockNumber', error); + } + return []; + } + } + async eth_getTransactionsByBlockNumber(_request, blockNumber) { + const blockNumber_ = parseBlockSpec(blockNumber) != 0 + ? parseBlockSpec(blockNumber) || (await this._fetchCurrentBlockID()) + : parseBlockSpec(blockNumber); + try { + const { rows, } = await this._query('SELECT * FROM eth_getTransactionsByBlockNumber($1)', [blockNumber_]); + return rows.length == 0 ? [] : exportJSON(rows); + } + catch (error) { + if (this.config.debug) { + console.debug('eth_getTransactionsByBlockNumber', error); + } + return []; + } + } async eth_getUncleCountByBlockHash(_request, blockHash) { const blockHash_ = blockHash.startsWith('0x') ? hexToBytes(blockHash) diff --git a/lib/servers/skeleton.d.ts b/lib/servers/skeleton.d.ts index 98bfc52b..85165de0 100644 --- a/lib/servers/skeleton.d.ts +++ b/lib/servers/skeleton.d.ts @@ -48,6 +48,8 @@ export declare abstract class SkeletonServer implements web3.Service { eth_getTransactionByHash(_request: Request, _transactionHash: web3.Data): Promise; eth_getTransactionCount(_request: Request, _address: web3.Data, _blockNumber: web3.Quantity | web3.Tag): Promise; eth_getTransactionReceipt(_request: Request, _transactionHash: string): Promise; + eth_getTransactionReceiptsByBlockNumber(_request: any, _blockNumber: web3.Quantity | web3.Tag): Promise; + eth_getTransactionsByBlockNumber(_request: any, _blockNumber: web3.Quantity | web3.Tag): Promise; eth_getUncleByBlockHashAndIndex(_request: Request, _blockHash: web3.Data, _uncleIndex: web3.Quantity): Promise; eth_getUncleByBlockNumberAndIndex(_request: Request, _blockNumber: web3.Quantity | web3.Tag, _uncleIndex: web3.Quantity): Promise; eth_getUncleCountByBlockHash(_request: Request, _blockHash: web3.Data): Promise; diff --git a/lib/servers/skeleton.js b/lib/servers/skeleton.js index 93da9c54..fd3053d9 100644 --- a/lib/servers/skeleton.js +++ b/lib/servers/skeleton.js @@ -185,6 +185,14 @@ export class SkeletonServer { unimplemented('eth_getTransactionReceipt'); return null; } + async eth_getTransactionReceiptsByBlockNumber(_request, _blockNumber) { + unimplemented('eth_getTransactionReceiptsByBlockNumber'); + return []; + } + async eth_getTransactionsByBlockNumber(_request, _blockNumber) { + unimplemented('eth_getTransactionsByBlockNumber'); + return []; + } async eth_getUncleByBlockHashAndIndex(_request, _blockHash, _uncleIndex) { return null; // uncle blocks are never found } diff --git a/lib/web3.d.ts b/lib/web3.d.ts index a67c12a5..c9733e5e 100644 --- a/lib/web3.d.ts +++ b/lib/web3.d.ts @@ -83,6 +83,8 @@ export interface Service { eth_getTransactionByHash(_request: Request, transactionHash: Data): Promise; eth_getTransactionCount(_request: Request, address: Data, blockNumber: Quantity | Tag): Promise; eth_getTransactionReceipt(_request: Request, transactionHash: string): Promise; + eth_getTransactionReceiptsByBlockNumber(_request: any, blockNumber: Quantity | Tag): Promise; + eth_getTransactionsByBlockNumber(_request: any, blockNumber: Quantity | Tag): Promise; eth_getUncleByBlockHashAndIndex(_request: Request, blockHash: Data, uncleIndex: Quantity): Promise; eth_getUncleByBlockNumberAndIndex(_request: Request, blockNumber: Quantity | Tag, uncleIndex: Quantity): Promise; eth_getUncleCountByBlockHash(_request: Request, blockHash: Data): Promise; diff --git a/src/servers/database.ts b/src/servers/database.ts index a1cc9b65..0c516d0c 100644 --- a/src/servers/database.ts +++ b/src/servers/database.ts @@ -549,6 +549,58 @@ export class DatabaseServer extends SkeletonServer { return null; } } + async eth_getTransactionReceiptsByBlockNumber( + _request: any, + blockNumber: web3.Quantity | web3.Tag + ): Promise { + const blockNumber_ = + parseBlockSpec(blockNumber) != 0 + ? parseBlockSpec(blockNumber) || (await this._fetchCurrentBlockID()) + : parseBlockSpec(blockNumber); + try { + const { + rows, + } = await this._query( + 'SELECT * FROM eth_getTransactionReceiptsByBlockNumber($1)', + [blockNumber_] + ); + for (let i = 0; i < rows.length; i++) { + rows[i].logs = await this._fetchEvents( + hexToBytes(rows[i].transactionHash.toString()) + ); + } + return rows.length == 0 ? [] : exportJSON(rows); + } catch (error) { + if (this.config.debug) { + console.debug('eth_getTransactionReceiptsByBlockNumber', error); + } + return []; + } + } + + async eth_getTransactionsByBlockNumber( + _request: any, + blockNumber: web3.Quantity | web3.Tag + ): Promise { + const blockNumber_ = + parseBlockSpec(blockNumber) != 0 + ? parseBlockSpec(blockNumber) || (await this._fetchCurrentBlockID()) + : parseBlockSpec(blockNumber); + try { + const { + rows, + } = await this._query( + 'SELECT * FROM eth_getTransactionsByBlockNumber($1)', + [blockNumber_] + ); + return rows.length == 0 ? [] : exportJSON(rows); + } catch (error) { + if (this.config.debug) { + console.debug('eth_getTransactionsByBlockNumber', error); + } + return []; + } + } async eth_getUncleCountByBlockHash( _request: Request, diff --git a/src/servers/skeleton.ts b/src/servers/skeleton.ts index 044f1381..87151cf0 100644 --- a/src/servers/skeleton.ts +++ b/src/servers/skeleton.ts @@ -316,6 +316,22 @@ export abstract class SkeletonServer implements web3.Service { return null; } + async eth_getTransactionReceiptsByBlockNumber( + _request: any, + _blockNumber: web3.Quantity | web3.Tag + ): Promise { + unimplemented('eth_getTransactionReceiptsByBlockNumber'); + return []; + } + + async eth_getTransactionsByBlockNumber( + _request: any, + _blockNumber: web3.Quantity | web3.Tag + ): Promise { + unimplemented('eth_getTransactionsByBlockNumber'); + return []; + } + async eth_getUncleByBlockHashAndIndex( _request: Request, _blockHash: web3.Data, diff --git a/src/web3.ts b/src/web3.ts index 8518a4c2..a9a051b4 100644 --- a/src/web3.ts +++ b/src/web3.ts @@ -167,6 +167,14 @@ export interface Service { _request: Request, transactionHash: string ): Promise; + eth_getTransactionReceiptsByBlockNumber( + _request: any, + blockNumber: Quantity | Tag + ): Promise; + eth_getTransactionsByBlockNumber( + _request: any, + blockNumber: Quantity | Tag + ): Promise; eth_getUncleByBlockHashAndIndex( _request: Request, blockHash: Data,