forked from crosschain-alliance/dendreth-proofs-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(global): adds first implementation
- Loading branch information
1 parent
172ad41
commit 0708099
Showing
14 changed files
with
1,804 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
PORT= | ||
SOURCE_RPC="" | ||
TARGET_RPC="" | ||
SOURCE_CHAIN_ID="" | ||
TARGET_CHAIN_ID="" | ||
LC_ADDRESS="" | ||
SOURCE_BEACON_API_URL= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
node_modules/ | ||
.env | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
v20 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"tabWidth": 2, | ||
"semi": false, | ||
"singleQuote": true, | ||
"bracketSpacing": true, | ||
"printWidth": 120, | ||
"trailingComma": "none" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,55 @@ | ||
# dendreth-proofs-api | ||
# Dendreth Proofs API | ||
|
||
Dendreth Proofs API is a Node.js-based API that provides functionality for generating Dendreth proofs to use with Hashi | ||
|
||
## Prerequisites | ||
|
||
Before running the application, ensure you have the following installed: | ||
|
||
- [Node.js](https://nodejs.org/) (version 20.x or higher recommended) | ||
- [Yarn](https://yarnpkg.com/) (version 1.x or higher) | ||
|
||
## Installation | ||
|
||
Clone the repository and navigate to the project directory: | ||
|
||
```bash | ||
git clone https://github.com/crosschain-alliance/dendreth-proofs-api | ||
cd dendreth-proofs-api | ||
``` | ||
|
||
Install the dependencies using Yarn: | ||
|
||
```bash | ||
yarn install | ||
``` | ||
|
||
## Running the Application | ||
|
||
### Development Mode | ||
|
||
To start the application in development mode with hot-reloading, use the following command: | ||
|
||
```bash | ||
yarn start:dev | ||
``` | ||
|
||
This command typically runs the server using a development configuration, where any code changes automatically restart the server. | ||
|
||
### Production Mode | ||
|
||
To start the application in production mode, use: | ||
|
||
```bash | ||
yarn start | ||
``` | ||
|
||
This command runs the server with the production configuration, optimized for performance. | ||
|
||
## Contributing | ||
|
||
Feel free to submit issues and pull requests. For major changes, please open an issue first to discuss what you would like to change. | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"watch": ["src"], | ||
"exec": "node ./src/server.js | npx pino-pretty" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"name": "dendreth-proofs-api", | ||
"version": "1.0.0", | ||
"description": "", | ||
"type": "module", | ||
"scripts": { | ||
"start": "node ./src/server.js | npx pino-pretty", | ||
"format": "prettier --config ./.prettierrc --write \"./**/*.+(js|ts|json)\"", | ||
"start:dev": "nodemon" | ||
}, | ||
"author": "", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@chainsafe/persistent-merkle-tree": "^0.8.0", | ||
"@ethereumjs/rlp": "^5.0.2", | ||
"@ethereumjs/trie": "^6.2.1", | ||
"@ethereumjs/tx": "^5.4.0", | ||
"@lodestar/types": "^1.21.0", | ||
"axios": "^1.6.8", | ||
"dotenv": "^16.4.5", | ||
"fastify": "^4.26.2", | ||
"node-fetch": "^3.3.2", | ||
"pino": "^8.19.0", | ||
"pino-pretty": "^11.0.0", | ||
"viem": "^2.9.9" | ||
}, | ||
"devDependencies": { | ||
"nodemon": "^3.1.0", | ||
"prettier": "^3.2.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import 'dotenv/config' | ||
import { createPublicClient, http } from 'viem' | ||
import * as chains from 'viem/chains' | ||
import axios from 'axios' | ||
import { RLP } from '@ethereumjs/rlp' | ||
|
||
import logger from '../../utils/logger.js' | ||
import dendrethAbi from '../../utils/abi/dendreth.js' | ||
import { getReceiptProof, getReceiptsRootProof } from '../../utils/proofs.js' | ||
import sleep from '../../utils/sleep.js' | ||
|
||
const MESSAGE_DISPATCHED_TOPIC = '0x218247aabc759e65b5bb92ccc074f9d62cd187259f2a0984c3c9cf91f67ff7cf' | ||
|
||
const getMessageDispatchedProof = async (_request, _reply) => { | ||
const { transactionHash } = _request.params | ||
|
||
const sourceChain = Object.values(chains).find((_chain) => _chain.id === parseInt(process.env.SOURCE_CHAIN_ID)) | ||
const targetChain = Object.values(chains).find((_chain) => _chain.id === parseInt(process.env.TARGET_CHAIN_ID)) | ||
|
||
const sourceClient = createPublicClient({ | ||
chain: sourceChain, | ||
transport: http(process.env.SOURCE_RPC) | ||
}) | ||
|
||
const targetClient = createPublicClient({ | ||
chain: targetChain, | ||
transport: http(process.env.TARGET_RPC) | ||
}) | ||
|
||
const receipt = await sourceClient.getTransactionReceipt({ | ||
hash: transactionHash | ||
}) | ||
|
||
logger.info('Checking finality ...') | ||
let txSlot = null | ||
const { | ||
data: { data } | ||
} = await axios.get(`https://sepolia.beaconcha.in/api/v1/execution/block/${receipt.blockNumber}`) | ||
const [ | ||
{ | ||
posConsensus: { slot, finalized } | ||
} | ||
] = data | ||
if (!finalized) { | ||
return _reply.code(400).send({ error: 'Block not finalized' }) | ||
} | ||
txSlot = slot | ||
|
||
logger.info('Calculating receipt proof ...') | ||
const { receiptProof, receiptsRoot } = await getReceiptProof(transactionHash, sourceClient) | ||
|
||
logger.info('Getting the correct light client slot ...') | ||
// NOTE: find the first slot > txSlot | ||
const initialIndex = await targetClient.readContract({ | ||
address: process.env.LC_ADDRESS, | ||
abi: dendrethAbi, | ||
functionName: 'currentIndex' | ||
}) | ||
let currentIndex = initialIndex | ||
let inverted = false | ||
let lastLightClientSlot = null | ||
try { | ||
while (true) { | ||
const lightClientSlot = await targetClient.readContract({ | ||
address: process.env.LC_ADDRESS, | ||
abi: dendrethAbi, | ||
functionName: 'optimisticSlots', | ||
args: [currentIndex] | ||
}) | ||
|
||
if (txSlot > lightClientSlot) { | ||
break | ||
} | ||
lastLightClientSlot = lightClientSlot | ||
|
||
if (currentIndex === 0n) { | ||
inverted = true | ||
currentIndex = initialIndex | ||
} | ||
currentIndex = inverted ? (currentIndex += 1n) : currentIndex - 1n | ||
await sleep(1000) | ||
} | ||
} catch (_err) { | ||
return _reply.code(404).send({ | ||
error: | ||
'The slot containing the block where the transaction emitted the MessageDispatched event has not been stored within the Light Client' | ||
}) | ||
} | ||
|
||
logger.info('Getting receipts root proof ...') | ||
const { receiptsRootProof } = await getReceiptsRootProof( | ||
Number(lastLightClientSlot), | ||
Number(txSlot), | ||
axios.create({ | ||
baseURL: process.env.SOURCE_BEACON_API_URL, | ||
responseType: 'json', | ||
headers: { 'Content-Type': 'application/json' } | ||
}) | ||
) | ||
|
||
logger.info('Getting log index ...') | ||
const logIndex = receipt.logs.findIndex(({ topics }) => topics[0] === MESSAGE_DISPATCHED_TOPIC) | ||
if (logIndex < 0) { | ||
return _reply.code(404).send({ error: 'Log not found' }) | ||
} | ||
|
||
const proof = [ | ||
parseInt(lastLightClientSlot), | ||
parseInt(txSlot), | ||
receiptsRootProof, | ||
receiptsRoot, | ||
receiptProof, | ||
'0x' + Buffer.from(RLP.encode(receipt.transactionIndex)).toString('hex'), | ||
logIndex | ||
] | ||
|
||
_reply.send({ | ||
proof | ||
}) | ||
} | ||
|
||
const handler = (_fastify, _opts, _done) => { | ||
_fastify.get('/get-message-dispatched-proof/:transactionHash', getMessageDispatchedProof) | ||
_done() | ||
} | ||
|
||
export default handler |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import dotenv from 'dotenv' | ||
dotenv.config() | ||
import Fastify from 'fastify' | ||
|
||
import routes from './routes/v1/index.js' | ||
|
||
const fastify = Fastify({ | ||
logger: true, | ||
requestTimeout: 30000, | ||
exposeHeadRoutes: true | ||
}) | ||
|
||
const port = process.env.PORT || 3002 | ||
|
||
fastify.route({ | ||
method: 'OPTIONS', | ||
url: '/*', | ||
handler: async (request, reply) => { | ||
var reqAllowedHeaders = request.headers['access-control-request-headers'] | ||
if (reqAllowedHeaders !== undefined) { | ||
reply.header('Access-Control-Allow-Headers', reqAllowedHeaders) | ||
} | ||
reply | ||
.code(204) | ||
.header('Content-Length', '0') | ||
.header('Access-Control-Allow-Origin', '*') | ||
.header('Access-Control-Allow-Credentials', true) | ||
.header('Access-Control-Allow-Methods', 'GET,HEAD,PUT,PATCH,POST,DELETE') | ||
.send() | ||
} | ||
}) | ||
|
||
fastify.addHook('onRequest', function (request, reply, next) { | ||
reply.header('Access-Control-Allow-Origin', '*') | ||
reply.header('Access-Control-Allow-Credentials', true) | ||
next() | ||
}) | ||
|
||
fastify.register(routes, { prefix: '/v1' }) | ||
|
||
fastify.listen({ port }, (_err, _address) => { | ||
if (_err) { | ||
fastify.log._error(_err) | ||
} | ||
fastify.log.info(`Fastify is listening on port: ${_address}`) | ||
}) |
Oops, something went wrong.