diff --git a/.all-contributorsrc b/.all-contributorsrc index be74b079..0974e3de 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -144,6 +144,60 @@ "contributions": [ "code" ] + }, + { + "login": "Gerson2102", + "name": "Gerson", + "avatar_url": "https://avatars.githubusercontent.com/u/71728860?v=4", + "profile": "https://github.com/Gerson2102", + "contributions": [ + "code" + ] + }, + { + "login": "PavitraAgarwal21", + "name": "PavitraAgarwal21", + "avatar_url": "https://avatars.githubusercontent.com/u/85789615?v=4", + "profile": "https://github.com/PavitraAgarwal21", + "contributions": [ + "code" + ] + }, + { + "login": "bloomingpeach", + "name": "Nguyen Dao", + "avatar_url": "https://avatars.githubusercontent.com/u/177087057?v=4", + "profile": "https://github.com/bloomingpeach", + "contributions": [ + "code" + ] + }, + { + "login": "od-hunter", + "name": "Hunter001", + "avatar_url": "https://avatars.githubusercontent.com/u/146340502?v=4", + "profile": "https://github.com/od-hunter", + "contributions": [ + "code" + ] + }, + { + "login": "feltroidprime", + "name": "feltroid Prime", + "avatar_url": "https://avatars.githubusercontent.com/u/96737978?v=4", + "profile": "https://github.com/feltroidprime", + "contributions": [ + "code" + ] + }, + { + "login": "mexes20", + "name": "Mexes", + "avatar_url": "https://avatars.githubusercontent.com/u/127276944?v=4", + "profile": "https://github.com/mexes20", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/python-lint.yml b/.github/workflows/python-lint.yml new file mode 100644 index 00000000..120c8f8e --- /dev/null +++ b/.github/workflows/python-lint.yml @@ -0,0 +1,31 @@ +name: Python Lint and Formatting Check + +on: [push, pull_request] + +jobs: + lint: + name: Lint and Format Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 black + + - name: Lint with flake8 + run: | + flake8 scripts/ --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 scripts/ --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics + + - name: Check code formatting with black + run: | + black --check scripts/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index b2c7f1ca..2fb090f2 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ Cargo.lock .python-version __pycache__ -.raito \ No newline at end of file +.client_cache/ \ No newline at end of file diff --git a/README.md b/README.md index a9e15c56..76b31c63 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Raito is a zero-knowledge Bitcoin client implemented in Cairo. It aims to provid ```mermaid flowchart TB -Pnm1(STARK proof of the chain state
up to the block n - 1,
including utxo accumulator) --> Vp(zk verifier) +Pnm1(STARK proof of the chain state up to the block n - 1, including utxo accumulator) --> Vp(zk verifier) Bn(blocks n..m) ----> Vb subgraph Cairo @@ -26,7 +26,7 @@ subgraph Cairo Vb --> ChS end -Vb --> Pn(STARK proof of the chain state
up to the block m,
including utxo accumulator) +Vb --> Pn(STARK proof of the chain state up to the block m,
including utxo accumulator) style Bn fill:pink style Pn fill:lightgreen @@ -49,6 +49,10 @@ Although this is a highly experimental project without immediate plans for deplo ## Roadmap +

+ components +

+ ### Milestone 1 - Block header validation Implement a reduced light client that can verify a range of blocks starting at genesis. @@ -70,7 +74,7 @@ Extend light client with partial transaction validation, but without UTXO checks Tasks: -* [ ] reassess validation check list (analyze Bitcoin core codebase) +* [x] reassess validation check list (analyze Bitcoin core codebase) * [x] generate & run integration tests e2e instead of Cairo codegen * [x] transaction ID calculation * [x] transaction root computation @@ -131,40 +135,18 @@ Raito is a reference to Light Yagami (夜神月, Yagami Raito) from the manga/an ## Usage -This will compile all the components: +This will compile all the packages: ```bash scarb build ``` -This will run unit and integration tests: +This will run tests for all the packages: ```bash scarb test ``` -For integration tests ony: - -```bash -scarb run integration_tests -``` - -Run for specific test file(s): - -```bash -scarb run integration_tests tests/data/light_481823.json -``` - -Re-generate integration test data: - -```base -scarb run regenerate_tests --force -``` - -* Without `--force` flag only non-existent files will be created -* Files are located in [tests/data/](https://github.com/keep-starknet-strange/raito/blob/main/tests/data) -* If you want to add a new test case, edit [scripts/data/regenerate_tests.sh](https://github.com/keep-starknet-strange/raito/blob/main/scripts/data/regenerate_tests.sh) - ## Build dependencies Install necessary packages required by Python scripts: @@ -213,6 +195,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Gerson
Gerson

💻 PavitraAgarwal21
PavitraAgarwal21

💻 bloomingpeach
Nguyen Dao

💻 + od-hunter
Hunter001

💻 + feltroidprime
feltroid Prime

💻 + mexes20
Mexes

💻 diff --git a/Scarb.lock b/Scarb.lock index a0e0284f..8beea944 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -2,5 +2,19 @@ version = 1 [[package]] -name = "raito" +name = "client" +version = "0.1.0" +dependencies = [ + "consensus", +] + +[[package]] +name = "consensus" +version = "0.1.0" +dependencies = [ + "utils", +] + +[[package]] +name = "utils" version = "0.1.0" diff --git a/Scarb.toml b/Scarb.toml index 8f063046..95012dcc 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -1,15 +1,13 @@ -[package] -name = "raito" -version = "0.1.0" -edition = "2024_07" - -[scripts] -regenerate_tests= "./scripts/data/regenerate_tests.sh" -integration_tests = "scarb build && ./scripts/data/integration_tests.sh" -client= "scarb build && ./scripts/data/client.sh" -test = "scarb cairo-test && scarb run integration_tests" +[workspace] +members = ["packages/*"] -[dependencies] +[workspace.package] +description = "Bitcoin ZK client." +cairo-version = "2.8.2" +version = "0.1.0" +readme = "README.md" +repository = "https://github.com/keep-starknet-strange/raito" +license-file = "LICENSE" -[dev-dependencies] +[workspace.dependencies] cairo_test = "2.8.0" diff --git a/docs/img/components.excalidraw b/docs/img/components.excalidraw new file mode 100644 index 00000000..a1d08fd7 --- /dev/null +++ b/docs/img/components.excalidraw @@ -0,0 +1,2079 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "vFRNXFX39m9q9jMcfiUTs", + "type": "rectangle", + "x": 773.8748840617886, + "y": -364.6578263794893, + "width": 443.20001220703125, + "height": 762.3999633789062, + "angle": 0, + "strokeColor": "#ffd43b", + "backgroundColor": "#fff9db", + "fillStyle": "cross-hatch", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "Zn", + "roundness": null, + "seed": 1196178576, + "version": 681, + "versionNonce": 1570713200, + "isDeleted": false, + "boundElements": [], + "updated": 1726051252954, + "link": null, + "locked": false + }, + { + "id": "OlQhyNzC4xYaxYiMVIpNp", + "type": "rectangle", + "x": 819.0748962688199, + "y": -66.05769820566115, + "width": 358.4000244140625, + "height": 441.60003662109375, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e3fafc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "Zo", + "roundness": null, + "seed": 253553776, + "version": 819, + "versionNonce": 1755594384, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "4IRy5JWO9w3bjNd3vAALY" + }, + { + "id": "0mlUBxdk542WH_plyugqH", + "type": "arrow" + }, + { + "id": "zap4kyaz0rdo0-hRwtXJT", + "type": "arrow" + }, + { + "id": "3FaeA6gbXhf3lFDHI-1by", + "type": "arrow" + }, + { + "id": "adQ0gG4ND7sWaO3NgRvCg", + "type": "arrow" + } + ], + "updated": 1726051226631, + "link": null, + "locked": false + }, + { + "id": "4IRy5JWO9w3bjNd3vAALY", + "type": "text", + "x": 898.805021696066, + "y": -61.057698205661154, + "width": 198.9397735595703, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "Zp", + "roundness": null, + "seed": 258474640, + "version": 695, + "versionNonce": 2127389328, + "isDeleted": false, + "boundElements": null, + "updated": 1726046483406, + "link": null, + "locked": false, + "text": "consensus validation", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "OlQhyNzC4xYaxYiMVIpNp", + "originalText": "consensus validation", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "p1SkBd3U9KUtb31g2b5og", + "type": "rectangle", + "x": 864.8750366496793, + "y": -11.657978967379904, + "width": 288.0000305175781, + "height": 367.199951171875, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#99e9f2", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "Zs", + "roundness": null, + "seed": 1190119568, + "version": 702, + "versionNonce": 707434128, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "VTu4_oVkyOEMO4CjPRMhW" + }, + { + "id": "0mlUBxdk542WH_plyugqH", + "type": "arrow" + }, + { + "id": "3FaeA6gbXhf3lFDHI-1by", + "type": "arrow" + } + ], + "updated": 1726047632268, + "link": null, + "locked": false + }, + { + "id": "VTu4_oVkyOEMO4CjPRMhW", + "type": "text", + "x": 933.6051391887418, + "y": -6.657978967379904, + "width": 150.53982543945312, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "Zt", + "roundness": null, + "seed": 945675408, + "version": 600, + "versionNonce": 767674000, + "isDeleted": false, + "boundElements": null, + "updated": 1726046477932, + "link": null, + "locked": false, + "text": "block validation", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "p1SkBd3U9KUtb31g2b5og", + "originalText": "block validation", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 906, + "versionNonce": 109459600, + "index": "Zu", + "isDeleted": false, + "id": "46TXE340NfJVz-ExR_Ntg", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 889.0748352336636, + "y": 123.74195389394822, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 246.3999633789062, + "height": 222.400146484375, + "seed": 779618448, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "OT5Pjyf0K1R184eX3jIFY" + }, + { + "id": "3FaeA6gbXhf3lFDHI-1by", + "type": "arrow" + } + ], + "updated": 1726050163287, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 830, + "versionNonce": 1501684336, + "index": "Zv", + "isDeleted": false, + "id": "OT5Pjyf0K1R184eX3jIFY", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 903.354940824484, + "y": 128.74195389394822, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 217.83975219726562, + "height": 25, + "seed": 1367897744, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1726046374765, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "transaction validation", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "46TXE340NfJVz-ExR_Ntg", + "originalText": "transaction validation", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Jq37eLzJFtAphIByXXJ8J", + "type": "rectangle", + "x": 883.8749450969449, + "y": 31.542155309963846, + "width": 244.7999877929688, + "height": 64.00006103515625, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0", + "roundness": { + "type": 3 + }, + "seed": 2100957808, + "version": 543, + "versionNonce": 68680848, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "KxKd8R05CEMH2kYjmvG1K" + } + ], + "updated": 1726046848776, + "link": null, + "locked": false + }, + { + "id": "KxKd8R05CEMH2kYjmvG1K", + "type": "text", + "x": 921.6050400066129, + "y": 51.04218582754197, + "width": 169.3397979736328, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0V", + "roundness": null, + "seed": 1007192208, + "version": 446, + "versionNonce": 591404656, + "isDeleted": false, + "boundElements": null, + "updated": 1726045175596, + "link": null, + "locked": false, + "text": "header validation", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Jq37eLzJFtAphIByXXJ8J", + "originalText": "header validation", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "6d5F4qlkBsianjtDW-DFS", + "type": "rectangle", + "x": 515.675024442648, + "y": -33.85789962167678, + "width": 165.5999755859375, + "height": 93.60000610351562, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aB", + "roundness": { + "type": 3 + }, + "seed": 2066883696, + "version": 728, + "versionNonce": 812569200, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "iiOg1LCjOHC5ozMm407bS" + }, + { + "id": "UgL2Tuek8HSxxStjQYB0q", + "type": "arrow" + }, + { + "id": "0mlUBxdk542WH_plyugqH", + "type": "arrow" + } + ], + "updated": 1726051262809, + "link": null, + "locked": false + }, + { + "id": "iiOg1LCjOHC5ozMm407bS", + "type": "text", + "x": 555.2750534343472, + "y": 0.44210343008103337, + "width": 86.39991760253906, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aC", + "roundness": null, + "seed": 1322088560, + "version": 688, + "versionNonce": 1716037744, + "isDeleted": false, + "boundElements": null, + "updated": 1726051262811, + "link": null, + "locked": false, + "text": "full node", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6d5F4qlkBsianjtDW-DFS", + "originalText": "full node", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ia3NpRXin76YPLtFP4Yl3", + "type": "rectangle", + "x": 919.6749939250699, + "y": 284.94208817129197, + "width": 195.20001220703125, + "height": 41.600067138671875, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aC8", + "roundness": { + "type": 3 + }, + "seed": 379577488, + "version": 617, + "versionNonce": 1085809296, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "WnFHta4VWF5MQyUmBCxW8" + } + ], + "updated": 1726046864018, + "link": null, + "locked": false + }, + { + "id": "WnFHta4VWF5MQyUmBCxW8", + "type": "text", + "x": 937.6250976848355, + "y": 293.2421217406279, + "width": 159.2998046875, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aCG", + "roundness": null, + "seed": 726540944, + "version": 588, + "versionNonce": 129134224, + "isDeleted": false, + "boundElements": null, + "updated": 1726046408098, + "link": null, + "locked": false, + "text": "script validation", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ia3NpRXin76YPLtFP4Yl3", + "originalText": "script validation", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 904, + "versionNonce": 260106864, + "index": "aCd", + "isDeleted": false, + "id": "Ss1_PWLNhrSWpBTmOopDV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 916.6747192668668, + "y": 226.94225601797166, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 195.20001220703125, + "height": 44.000091552734375, + "seed": 1266475120, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "cQiXWAbergoCUDlg7BMCc" + }, + { + "id": "adQ0gG4ND7sWaO3NgRvCg", + "type": "arrow" + } + ], + "updated": 1726046859355, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 893, + "versionNonce": 1380229232, + "index": "aCl", + "isDeleted": false, + "id": "cQiXWAbergoCUDlg7BMCc", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 926.8748230266324, + "y": 236.44230179433885, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 174.7998046875, + "height": 25, + "seed": 1273451120, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1726046394358, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "UTXO verification", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Ss1_PWLNhrSWpBTmOopDV", + "originalText": "UTXO verification", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1127, + "versionNonce": 107678352, + "index": "aD", + "isDeleted": false, + "id": "Y-atAe5CEQudIM5qB3LGl", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 510.67520754811676, + "y": 198.34206680898728, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 165.5999755859375, + "height": 93.60000610351562, + "seed": 879980688, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "puxQis3GvrmQmj6Ewshuy" + }, + { + "id": "adQ0gG4ND7sWaO3NgRvCg", + "type": "arrow" + }, + { + "id": "UgL2Tuek8HSxxStjQYB0q", + "type": "arrow" + } + ], + "updated": 1726051280505, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1122, + "versionNonce": 1721914512, + "index": "aE", + "isDeleted": false, + "id": "puxQis3GvrmQmj6Ewshuy", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 538.8352493571988, + "y": 220.1420698607451, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 109.27989196777344, + "height": 50, + "seed": 1406590608, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1726051280506, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "utreexo\nbridge node", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Y-atAe5CEQudIM5qB3LGl", + "originalText": "utreexo\nbridge node", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "adQ0gG4ND7sWaO3NgRvCg", + "type": "arrow", + "x": 677.2751831340543, + "y": 249.72548691501987, + "width": 138.79955291748047, + "height": 0.9433040090914915, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aF", + "roundness": { + "type": 2 + }, + "seed": 274845296, + "version": 2082, + "versionNonce": 2009806992, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "0FIupR64RF-AbNbrvCDwP" + } + ], + "updated": 1726051280506, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 138.79955291748047, + -0.9433040090914915 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "Y-atAe5CEQudIM5qB3LGl", + "focus": 0.10879727040177857, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "OlQhyNzC4xYaxYiMVIpNp", + "focus": -0.41810056413000074, + "gap": 3.0001602172851562, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "0FIupR64RF-AbNbrvCDwP", + "type": "text", + "x": 1230.2597323995374, + "y": 173.66878046063067, + "width": 76.0399169921875, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aG", + "roundness": null, + "seed": 452208272, + "version": 32, + "versionNonce": 1394634864, + "isDeleted": false, + "boundElements": null, + "updated": 1726045282081, + "link": null, + "locked": false, + "text": "utxo\ninclusion\nproofs", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "adQ0gG4ND7sWaO3NgRvCg", + "originalText": "utxo\ninclusion\nproofs", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "UgL2Tuek8HSxxStjQYB0q", + "type": "arrow", + "x": 602.359464326293, + "y": 60.742106481838846, + "width": 3.252438008386207, + "height": 135.59994506835938, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aH", + "roundness": { + "type": 2 + }, + "seed": 232236176, + "version": 1675, + "versionNonce": 1760929424, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "lzW_E-Dknz3Cjp3-7IVvd" + } + ], + "updated": 1726051280506, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -3.252438008386207, + 135.59994506835938 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "6d5F4qlkBsianjtDW-DFS", + "focus": -0.0848461298687917, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "Y-atAe5CEQudIM5qB3LGl", + "focus": 0.053160184157585534, + "gap": 2.0000152587890625, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "lzW_E-Dknz3Cjp3-7IVvd", + "type": "text", + "x": 1339.8186975423196, + "y": 63.542170568752894, + "width": 57.27995300292969, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aHV", + "roundness": null, + "seed": 1272586896, + "version": 8, + "versionNonce": 480416400, + "isDeleted": false, + "boundElements": null, + "updated": 1726043762970, + "link": null, + "locked": false, + "text": "blocks", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "UgL2Tuek8HSxxStjQYB0q", + "originalText": "blocks", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "0mlUBxdk542WH_plyugqH", + "type": "arrow", + "x": 682.2750000285855, + "y": 12.953114543529589, + "width": 136.4517173655363, + "height": 1.7043832129644851, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aI", + "roundness": { + "type": 2 + }, + "seed": 1104431216, + "version": 1752, + "versionNonce": 1020327536, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "vZTRp78c_rn0-LAvcVhkn" + } + ], + "updated": 1726051262810, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 136.4517173655363, + -1.7043832129644851 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "6d5F4qlkBsianjtDW-DFS", + "focus": 0.022112512524973975, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "OlQhyNzC4xYaxYiMVIpNp", + "focus": 0.6359857359815234, + "gap": 1, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "vZTRp78c_rn0-LAvcVhkn", + "type": "text", + "x": 1138.8349441814175, + "y": -64.95781417245803, + "width": 57.27995300292969, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aJ", + "roundness": null, + "seed": 349960816, + "version": 8, + "versionNonce": 1181125232, + "isDeleted": false, + "boundElements": null, + "updated": 1726043727503, + "link": null, + "locked": false, + "text": "blocks", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "0mlUBxdk542WH_plyugqH", + "originalText": "blocks", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "3FaeA6gbXhf3lFDHI-1by", + "type": "arrow", + "x": 1181.1332952764878, + "y": 267.5422163451201, + "width": 71.54169864858204, + "height": 1.567668477541588, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ah", + "roundness": { + "type": 2 + }, + "seed": 2089958032, + "version": 934, + "versionNonce": 137755792, + "isDeleted": false, + "boundElements": null, + "updated": 1726051175901, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 71.54169864858204, + -1.567668477541588 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "p1SkBd3U9KUtb31g2b5og", + "focus": 0.5321123566425469, + "gap": 28.25822810923046, + "fixedPoint": null + }, + "endBinding": { + "elementId": "meCDK-TD6HL11SHWrQCBW", + "focus": -0.0881613568239664, + "gap": 1, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "Sx7Xvkm8n1gPTX2MebqV0", + "type": "rectangle", + "x": 896.6748413371793, + "y": -334.25786300058303, + "width": 180.800048828125, + "height": 56.000030517578125, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffd43b", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ai", + "roundness": { + "type": 3 + }, + "seed": 1513587312, + "version": 391, + "versionNonce": 474861200, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "BNjgoH0KcjIxXt9pYsPgf" + }, + { + "id": "IwaIfRpdALQBDgV0u-ATE", + "type": "arrow" + }, + { + "id": "zr6A3arcQYrb0Em0BgkHX", + "type": "arrow" + } + ], + "updated": 1726047038018, + "link": null, + "locked": false + }, + { + "id": "BNjgoH0KcjIxXt9pYsPgf", + "type": "text", + "x": 920.4449447917691, + "y": -318.75784774179397, + "width": 133.2598419189453, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aiV", + "roundness": null, + "seed": 94841456, + "version": 273, + "versionNonce": 1286962320, + "isDeleted": false, + "boundElements": null, + "updated": 1726047038018, + "link": null, + "locked": false, + "text": "stark verifier", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Sx7Xvkm8n1gPTX2MebqV0", + "originalText": "stark verifier", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 870, + "versionNonce": 957720720, + "index": "as", + "isDeleted": false, + "id": "A7N4kLQMptJOkqU5E4DfL", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 821.6749328899136, + "y": -232.65779586191115, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 335.19995117187506, + "height": 118.39990234374997, + "seed": 1604022928, + "groupIds": [ + "t-XPndLuq3N4YvRhUk6XX" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "W-a8bRzcGmPEjXd4jALkn" + }, + { + "id": "zr6A3arcQYrb0Em0BgkHX", + "type": "arrow" + }, + { + "id": "zap4kyaz0rdo0-hRwtXJT", + "type": "arrow" + } + ], + "updated": 1726047035918, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 689, + "versionNonce": 2003965584, + "index": "at", + "isDeleted": false, + "id": "W-a8bRzcGmPEjXd4jALkn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 953.0649551677457, + "y": -227.65779586191115, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 72.41990661621094, + "height": 25, + "seed": 514760848, + "groupIds": [ + "t-XPndLuq3N4YvRhUk6XX" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1726047035918, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "verified", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "A7N4kLQMptJOkqU5E4DfL", + "originalText": "verified", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 999, + "versionNonce": 938795152, + "index": "au", + "isDeleted": false, + "id": "xiLjXbKFxuf582YFoRlCJ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 838.8151754298465, + "y": -195.95775313730178, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 143.31571977535447, + "height": 63.19996643066406, + "seed": 791416464, + "groupIds": [ + "t-XPndLuq3N4YvRhUk6XX" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "Qn4GW_gGfSRHpCZs3FwEn", + "type": "text" + } + ], + "updated": 1726047035918, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 827, + "versionNonce": 1168789136, + "index": "av", + "isDeleted": false, + "id": "Qn4GW_gGfSRHpCZs3FwEn", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 854.5530905543401, + "y": -176.85776992196975, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 111.83988952636719, + "height": 25, + "seed": 1774959760, + "groupIds": [ + "t-XPndLuq3N4YvRhUk6XX" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1726047035918, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "chain state", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "xiLjXbKFxuf582YFoRlCJ", + "originalText": "chain state", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1392, + "versionNonce": 377617552, + "index": "aw", + "isDeleted": false, + "id": "wDJ5UcgOitmQVb6wFc-BV", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 996.8661636121401, + "y": -195.55771346445022, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 145.49011817706395, + "height": 64, + "seed": 1346742928, + "groupIds": [ + "t-XPndLuq3N4YvRhUk6XX" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "axqPpvy7aOZU_TExH_LMz", + "type": "text" + } + ], + "updated": 1726047035918, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1142, + "versionNonce": 204728976, + "index": "ax", + "isDeleted": false, + "id": "axqPpvy7aOZU_TExH_LMz", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1009.6212706132698, + "y": -188.55771346445022, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "width": 119.97990417480469, + "height": 50, + "seed": 1834080400, + "groupIds": [ + "t-XPndLuq3N4YvRhUk6XX" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1726047035918, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "utxo merkle \naccumulator", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "wDJ5UcgOitmQVb6wFc-BV", + "originalText": "utxo merkle accumulator", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1265, + "versionNonce": 1064197776, + "index": "azG", + "isDeleted": false, + "id": "meCDK-TD6HL11SHWrQCBW", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1252.0749573039761, + "y": 150.14237503652635, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ebfbee", + "width": 187.99987792968759, + "height": 208.7998046875, + "seed": 732126832, + "groupIds": [ + "FzZYbLV792S3RtinrAqt8" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "wJiOgag0fhKyd-7ulo941" + }, + { + "id": "3FaeA6gbXhf3lFDHI-1by", + "type": "arrow" + } + ], + "updated": 1726051167308, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1068, + "versionNonce": 1345583760, + "index": "azV", + "isDeleted": false, + "id": "wJiOgag0fhKyd-7ulo941", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1319.084921293234, + "y": 155.14237503652635, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ebfbee", + "width": 53.979949951171875, + "height": 25, + "seed": 512626800, + "groupIds": [ + "FzZYbLV792S3RtinrAqt8" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1726051167309, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "proof", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "meCDK-TD6HL11SHWrQCBW", + "originalText": "proof", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1284, + "versionNonce": 2062952592, + "index": "azl", + "isDeleted": false, + "id": "6MJcYEo6mHeF_YEA28iJO", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1274.815297500159, + "y": 195.24244217519822, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 143.31571977535447, + "height": 63.19996643066406, + "seed": 89293424, + "groupIds": [ + "FzZYbLV792S3RtinrAqt8" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "zjsEKV9bbiImLOha8078C", + "type": "text" + } + ], + "updated": 1726051167309, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1112, + "versionNonce": 692229776, + "index": "b00", + "isDeleted": false, + "id": "zjsEKV9bbiImLOha8078C", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1290.5532126246526, + "y": 214.34242539053025, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ebfbee", + "width": 111.83988952636719, + "height": 25, + "seed": 1052292208, + "groupIds": [ + "FzZYbLV792S3RtinrAqt8" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1726051167309, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "chain state", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6MJcYEo6mHeF_YEA28iJO", + "originalText": "chain state", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1723, + "versionNonce": 1875985552, + "index": "b00V", + "isDeleted": false, + "id": "jOgsVLQFNDDfjLwkM1oUr", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1275.8662856824526, + "y": 273.8424330199248, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 145.49011817706395, + "height": 64, + "seed": 277390960, + "groupIds": [ + "FzZYbLV792S3RtinrAqt8" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "Gtil3CQd9GWRJfyo0XBFJ", + "type": "text" + } + ], + "updated": 1726051167309, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1474, + "versionNonce": 207237776, + "index": "b01", + "isDeleted": false, + "id": "Gtil3CQd9GWRJfyo0XBFJ", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1288.6213926835821, + "y": 280.8424330199248, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ebfbee", + "width": 119.97990417480469, + "height": 50, + "seed": 1200248944, + "groupIds": [ + "FzZYbLV792S3RtinrAqt8" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1726051167309, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "utxo merkle \naccumulator", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "jOgsVLQFNDDfjLwkM1oUr", + "originalText": "utxo merkle accumulator", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "IwaIfRpdALQBDgV0u-ATE", + "type": "arrow", + "x": 696.0747131633511, + "y": -304.11763979415537, + "width": 199.02465149538898, + "height": 1.5402171029120382, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b06", + "roundness": { + "type": 2 + }, + "seed": 904018576, + "version": 686, + "versionNonce": 1950726256, + "isDeleted": false, + "boundElements": null, + "updated": 1726051209826, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 199.02465149538898, + -1.5402171029120382 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "cwLLnN-1jTMdIZNDEow_n", + "focus": -0.45026786372359473, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "Sx7Xvkm8n1gPTX2MebqV0", + "focus": 0.0038952289644372906, + "gap": 1.575476678439145, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "zr6A3arcQYrb0Em0BgkHX", + "type": "arrow", + "x": 982.5732480335655, + "y": -277.2578095948213, + "width": 0.48651040507729704, + "height": 38.999977111816406, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b07", + "roundness": { + "type": 2 + }, + "seed": 723379312, + "version": 293, + "versionNonce": 587904144, + "isDeleted": false, + "boundElements": null, + "updated": 1726047038040, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.48651040507729704, + 38.999977111816406 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "Sx7Xvkm8n1gPTX2MebqV0", + "focus": 0.04561855825192419, + "gap": 1.0000228881835938, + "fixedPoint": null + }, + "endBinding": { + "elementId": "A7N4kLQMptJOkqU5E4DfL", + "focus": -0.037986458919341964, + "gap": 5.600036621093764, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "zap4kyaz0rdo0-hRwtXJT", + "type": "arrow", + "x": 983.6910781344301, + "y": -113.25789351816118, + "width": 3.017949999096004, + "height": 42.20007324218753, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b08", + "roundness": { + "type": 2 + }, + "seed": 1327634032, + "version": 173, + "versionNonce": 2047248016, + "isDeleted": false, + "boundElements": null, + "updated": 1726047035985, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 3.017949999096004, + 42.20007324218753 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "A7N4kLQMptJOkqU5E4DfL", + "focus": 0.05755010789097818, + "gap": 1, + "fixedPoint": null + }, + "endBinding": { + "elementId": "OlQhyNzC4xYaxYiMVIpNp", + "focus": 0.015207863669545869, + "gap": 5.0001220703125, + "fixedPoint": null + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "type": "rectangle", + "version": 675, + "versionNonce": 294525072, + "index": "b09", + "isDeleted": false, + "id": "-kMJn4t02d3BEj1iHbvsI", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 914.6749023723355, + "y": 169.74218277578416, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 195.20001220703125, + "height": 41.600067138671875, + "seed": 713915024, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "type": "text", + "id": "qODkEh4lm-W3hsbAvtR-i" + } + ], + "updated": 1726046854154, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 663, + "versionNonce": 50382448, + "index": "b0A", + "isDeleted": false, + "id": "qODkEh4lm-W3hsbAvtR-i", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 936.3549866008511, + "y": 178.0422163451201, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 151.83984375, + "height": 25, + "seed": 1310697616, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1726046421370, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 1, + "text": "consensus rules", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "-kMJn4t02d3BEj1iHbvsI", + "originalText": "consensus rules", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "6VvMekiLDQkruhQDgnoct", + "type": "text", + "x": 1139.4749206828824, + "y": -340.65779586191115, + "width": 49.21995544433594, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#fff9db", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b0C", + "roundness": null, + "seed": 380584560, + "version": 33, + "versionNonce": 1305454192, + "isDeleted": false, + "boundElements": null, + "updated": 1726047161439, + "link": null, + "locked": false, + "text": "Cairo", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Cairo", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1419, + "versionNonce": 1876825744, + "index": "b0D", + "isDeleted": false, + "id": "cwLLnN-1jTMdIZNDEow_n", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 508.47510378835113, + "y": -360.45772261972365, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ebfbee", + "width": 187.99987792968759, + "height": 208.7998046875, + "seed": 1914053264, + "groupIds": [ + "gKhb0zfr_4XXWP_0mOYtd" + ], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "Hc1h69tP3l0GVZB4vPvCg" + }, + { + "id": "IwaIfRpdALQBDgV0u-ATE", + "type": "arrow" + } + ], + "updated": 1726051204516, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1221, + "versionNonce": 1459405968, + "index": "b0E", + "isDeleted": false, + "id": "Hc1h69tP3l0GVZB4vPvCg", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 575.485067777609, + "y": -355.45772261972365, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ebfbee", + "width": 53.979949951171875, + "height": 25, + "seed": 1850953872, + "groupIds": [ + "gKhb0zfr_4XXWP_0mOYtd" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1726051204516, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "proof", + "textAlign": "center", + "verticalAlign": "top", + "containerId": "cwLLnN-1jTMdIZNDEow_n", + "originalText": "proof", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1437, + "versionNonce": 2011226256, + "index": "b0F", + "isDeleted": false, + "id": "aNm6PI-DtRQWT2Sj0bi7s", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 531.215443984534, + "y": -315.3576554810518, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 143.31571977535447, + "height": 63.19996643066406, + "seed": 112901776, + "groupIds": [ + "gKhb0zfr_4XXWP_0mOYtd" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "uziA6oySDKPMBkhclY7Na", + "type": "text" + } + ], + "updated": 1726051204516, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1265, + "versionNonce": 672550544, + "index": "b0G", + "isDeleted": false, + "id": "uziA6oySDKPMBkhclY7Na", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 546.9533591090276, + "y": -296.25767226571975, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ebfbee", + "width": 111.83988952636719, + "height": 25, + "seed": 1006922896, + "groupIds": [ + "gKhb0zfr_4XXWP_0mOYtd" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1726051204516, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "chain state", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aNm6PI-DtRQWT2Sj0bi7s", + "originalText": "chain state", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1876, + "versionNonce": 1265499280, + "index": "b0H", + "isDeleted": false, + "id": "ydKJB4ugsrqufvEqVHRk5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 532.2664321668276, + "y": -236.75766463632522, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffffff", + "width": 145.49011817706395, + "height": 64, + "seed": 937974416, + "groupIds": [ + "gKhb0zfr_4XXWP_0mOYtd" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "I1QEZkbQosDrXZjKyV4O5", + "type": "text" + } + ], + "updated": 1726051204516, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 1627, + "versionNonce": 1134553744, + "index": "b0I", + "isDeleted": false, + "id": "I1QEZkbQosDrXZjKyV4O5", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 545.0215391679571, + "y": -229.75766463632522, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ebfbee", + "width": 119.97990417480469, + "height": 50, + "seed": 756975760, + "groupIds": [ + "gKhb0zfr_4XXWP_0mOYtd" + ], + "frameId": null, + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1726051204516, + "link": null, + "locked": false, + "fontSize": 20, + "fontFamily": 5, + "text": "utxo merkle \naccumulator", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ydKJB4ugsrqufvEqVHRk5", + "originalText": "utxo merkle accumulator", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/docs/img/components.svg b/docs/img/components.svg new file mode 100644 index 00000000..0f42a0c9 --- /dev/null +++ b/docs/img/components.svg @@ -0,0 +1,17 @@ + + + + + + + + consensus validationblock validationtransaction validationheader validationfull nodescript validationUTXO verificationutreexobridge nodeutxoinclusionproofsblocksblocksstark verifierverifiedchain stateutxo merkle accumulatorproofchain stateutxo merkle accumulatorconsensus rulesCairoproofchain stateutxo merkle accumulator \ No newline at end of file diff --git a/packages/client/README.md b/packages/client/README.md new file mode 100644 index 00000000..f99b53d9 --- /dev/null +++ b/packages/client/README.md @@ -0,0 +1,45 @@ +# Bitcoin client in Cairo + +This package is a standalone Cairo program (outside of Starknet context) that implements a Bitcoin client which can work in two modes: +- Light mode: block header validation only +- Full mode: full Bitcoin consensus validation + +## Usage + +```sh +# You have to be in the "packages/client" directory +scarb run client START_HEIGHT END_HEIGHT BATCH_SIZE MODE STRATEGY +``` + +Client expects the following arguments: +* `START_HEIGHT` height of the initial chain state +* `END_HEIGHT` height of the final (resulting) chain state +* `BATCH_SIZE` number of blocks applied per single program run +* `MODE` either `light` or `full` (default is light) +* `STRATEGY` either `sequential` or `random` (default is sequential) + +## Integration tests + +In order to run integration tests: + +```sh +scarb test +``` + +Run a specific test file (or several files): + +```sh +# You have to be in the "packages/client" directory +scarb test tests/data/light_481823.json +``` + +Re-generate integration test data: + +```sh +# You have to be in the "packages/client" directory +scarb run regenerate_tests --force +``` + +If you want to just add a new test case, edit `scripts/data/regenerate_tests.sh` and run without `--force` flag. + +You can also add/remove ignored scenarios, check out `scripts/data/regenerate_tests.sh` as well. diff --git a/packages/client/Scarb.toml b/packages/client/Scarb.toml new file mode 100644 index 00000000..b2189723 --- /dev/null +++ b/packages/client/Scarb.toml @@ -0,0 +1,16 @@ +[package] +name = "client" +version = "0.1.0" +edition = "2024_07" + +[dependencies] +consensus = { path = "../consensus" } + +[dev-dependencies] +cairo_test.workspace = true + +[scripts] +test = "scarb build && ../../scripts/data/integration_tests.sh" +regenerate_tests= "../../scripts/data/regenerate_tests.sh" +client = "scarb build && ../../scripts/data/client.sh" +lint = "flake8 scripts/ && black --check scripts/ && scarb fmt" diff --git a/packages/client/src/lib.cairo b/packages/client/src/lib.cairo new file mode 100644 index 00000000..c1bd37d3 --- /dev/null +++ b/packages/client/src/lib.cairo @@ -0,0 +1,4 @@ +mod main; +// TODO: scarb cairo-run should support "features" argument +// so that we can conditionally compile this module +mod test; diff --git a/src/main.cairo b/packages/client/src/main.cairo similarity index 88% rename from src/main.cairo rename to packages/client/src/main.cairo index 714279e5..34c65fc2 100644 --- a/src/main.cairo +++ b/packages/client/src/main.cairo @@ -1,5 +1,5 @@ -use crate::types::block::Block; -use crate::types::chain_state::{ChainState, BlockValidator}; +use consensus::types::block::Block; +use consensus::types::chain_state::{ChainState, BlockValidator}; /// Raito program arguments. #[derive(Serde)] diff --git a/src/test.cairo b/packages/client/src/test.cairo similarity index 94% rename from src/test.cairo rename to packages/client/src/test.cairo index da9f7d80..a8d03acf 100644 --- a/src/test.cairo +++ b/packages/client/src/test.cairo @@ -1,5 +1,5 @@ -use crate::types::block::Block; -use crate::types::chain_state::{ChainState, BlockValidator}; +use consensus::types::block::Block; +use consensus::types::chain_state::{ChainState, BlockValidator}; use core::testing::get_available_gas; /// Integration testing program arguments. diff --git a/tests/data/full_169.json b/packages/client/tests/data/full_169.json similarity index 100% rename from tests/data/full_169.json rename to packages/client/tests/data/full_169.json diff --git a/tests/data/full_757738.json b/packages/client/tests/data/full_757738.json similarity index 100% rename from tests/data/full_757738.json rename to packages/client/tests/data/full_757738.json diff --git a/tests/data/light_150012.json b/packages/client/tests/data/light_150012.json similarity index 100% rename from tests/data/light_150012.json rename to packages/client/tests/data/light_150012.json diff --git a/tests/data/light_169.json b/packages/client/tests/data/light_169.json similarity index 100% rename from tests/data/light_169.json rename to packages/client/tests/data/light_169.json diff --git a/tests/data/light_2015.json b/packages/client/tests/data/light_2015.json similarity index 100% rename from tests/data/light_2015.json rename to packages/client/tests/data/light_2015.json diff --git a/tests/data/light_209999.json b/packages/client/tests/data/light_209999.json similarity index 100% rename from tests/data/light_209999.json rename to packages/client/tests/data/light_209999.json diff --git a/tests/data/light_24834.json b/packages/client/tests/data/light_24834.json similarity index 100% rename from tests/data/light_24834.json rename to packages/client/tests/data/light_24834.json diff --git a/tests/data/light_32255.json b/packages/client/tests/data/light_32255.json similarity index 100% rename from tests/data/light_32255.json rename to packages/client/tests/data/light_32255.json diff --git a/tests/data/light_478557.json b/packages/client/tests/data/light_478557.json similarity index 100% rename from tests/data/light_478557.json rename to packages/client/tests/data/light_478557.json diff --git a/tests/data/light_481823.json b/packages/client/tests/data/light_481823.json similarity index 100% rename from tests/data/light_481823.json rename to packages/client/tests/data/light_481823.json diff --git a/tests/data/light_491406.json b/packages/client/tests/data/light_491406.json similarity index 100% rename from tests/data/light_491406.json rename to packages/client/tests/data/light_491406.json diff --git a/tests/data/light_57042.json b/packages/client/tests/data/light_57042.json similarity index 100% rename from tests/data/light_57042.json rename to packages/client/tests/data/light_57042.json diff --git a/tests/data/light_629999.json b/packages/client/tests/data/light_629999.json similarity index 100% rename from tests/data/light_629999.json rename to packages/client/tests/data/light_629999.json diff --git a/tests/data/light_709631.json b/packages/client/tests/data/light_709631.json similarity index 100% rename from tests/data/light_709631.json rename to packages/client/tests/data/light_709631.json diff --git a/tests/data/light_757738.json b/packages/client/tests/data/light_757738.json similarity index 100% rename from tests/data/light_757738.json rename to packages/client/tests/data/light_757738.json diff --git a/tests/data/light_757752.json b/packages/client/tests/data/light_757752.json similarity index 100% rename from tests/data/light_757752.json rename to packages/client/tests/data/light_757752.json diff --git a/tests/data/light_774627.json b/packages/client/tests/data/light_774627.json similarity index 100% rename from tests/data/light_774627.json rename to packages/client/tests/data/light_774627.json diff --git a/tests/data/light_839999.json b/packages/client/tests/data/light_839999.json similarity index 100% rename from tests/data/light_839999.json rename to packages/client/tests/data/light_839999.json diff --git a/packages/consensus/README.md b/packages/consensus/README.md new file mode 100644 index 00000000..1c89183f --- /dev/null +++ b/packages/consensus/README.md @@ -0,0 +1,8 @@ +# Bitcoin consensus in Cairo + +This package is a Cairo library providing primitives for validating Bitcoin consensus. + +It is structured as follows: +* `types` module contains all Bitcoin specific entities (start your codebase tour with this folder) adapted for recursive verification; +* `validation` module contains most of the consensus validation logic; +* `codec` module contains implementation of Bitcoin binary codec for transaction types. diff --git a/packages/consensus/Scarb.toml b/packages/consensus/Scarb.toml new file mode 100644 index 00000000..7ca44ee0 --- /dev/null +++ b/packages/consensus/Scarb.toml @@ -0,0 +1,14 @@ +[package] +name = "consensus" +version = "0.1.0" +edition = "2024_07" + +[dependencies] +utils = { path = "../utils" } + +[dev-dependencies] +cairo_test.workspace = true + +[scripts] +# TODO: cairo lint +lint = "scarb fmt" diff --git a/src/codec.cairo b/packages/consensus/src/codec.cairo similarity index 99% rename from src/codec.cairo rename to packages/consensus/src/codec.cairo index d967b12e..ac5820d4 100644 --- a/src/codec.cairo +++ b/packages/consensus/src/codec.cairo @@ -1,7 +1,7 @@ //! Bitcoin binary codec traits, implementations, and helpers. use super::types::transaction::{Transaction, TxIn, TxOut, OutPoint}; -use raito::utils::hash::Digest; +use utils::hash::Digest; pub trait Encode { /// Encode using Bitcoin codec and append to the buffer. @@ -133,8 +133,8 @@ pub fn encode_compact_size(len: usize, ref dest: ByteArray) { } #[cfg(test)] mod tests { - use raito::types::transaction::{Transaction, TxIn, TxOut, OutPoint}; - use raito::utils::hex::{from_hex, hex_to_hash_rev}; + use utils::hex::{from_hex, hex_to_hash_rev}; + use crate::types::transaction::{Transaction, TxIn, TxOut, OutPoint}; use super::{Encode, TransactionCodec, encode_compact_size}; #[test] diff --git a/packages/consensus/src/lib.cairo b/packages/consensus/src/lib.cairo new file mode 100644 index 00000000..238e5741 --- /dev/null +++ b/packages/consensus/src/lib.cairo @@ -0,0 +1,17 @@ +pub mod validation { + pub mod difficulty; + pub mod coinbase; + pub mod locktime; + pub mod timestamp; + pub mod transaction; + pub mod work; + pub mod block; +} +pub mod codec; +pub mod types { + pub mod utreexo; + pub mod chain_state; + pub mod block; + pub mod transaction; + pub mod utxo_set; +} diff --git a/src/types/block.cairo b/packages/consensus/src/types/block.cairo similarity index 78% rename from src/types/block.cairo rename to packages/consensus/src/types/block.cairo index a404b19e..19f63b4a 100644 --- a/src/types/block.cairo +++ b/packages/consensus/src/types/block.cairo @@ -2,10 +2,11 @@ //! //! The data is expected to be prepared in advance and passed as program arguments. -use crate::utils::hash::Digest; -use crate::utils::sha256::double_sha256_u32_array; -use crate::utils::numeric::u32_byte_reverse; +use utils::hash::Digest; +use utils::sha256::double_sha256_u32_array; +use utils::numeric::u32_byte_reverse; use super::transaction::Transaction; +use core::fmt::{Display, Formatter, Error}; /// Represents a block in the blockchain. #[derive(Drop, Copy, Debug, PartialEq, Default, Serde)] @@ -76,11 +77,49 @@ pub impl TransactionDataDefault of Default { } } +impl BlockDisplay of Display { + fn fmt(self: @Block, ref f: Formatter) -> Result<(), Error> { + let data = match *self.data { + TransactionData::MerkleRoot(root) => format!("{}", root), + TransactionData::Transactions(txs) => format!("{}", txs.len()) + }; + let str: ByteArray = format!(" Block {{ header: {}, data: {} }}", *self.header, @data); + f.buffer.append(@str); + Result::Ok(()) + } +} + +impl HeaderDisplay of Display
{ + fn fmt(self: @Header, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Header {{ version: {}, time: {}, bits: {}, nonce: {}}}", + *self.version, + *self.time, + *self.bits, + *self.nonce + ); + f.buffer.append(@str); + Result::Ok(()) + } +} + +impl TransactionDataDisplay of Display { + fn fmt(self: @TransactionData, ref f: Formatter) -> Result<(), Error> { + match *self { + TransactionData::MerkleRoot(root) => f.buffer.append(@format!("MerkleRoot: {}", root)), + TransactionData::Transactions(txs) => f + .buffer + .append(@format!("Transactions: {}", txs.len())) + }; + Result::Ok(()) + } +} + #[cfg(test)] mod tests { use super::{Header, BlockHash}; - use raito::types::chain_state::ChainState; - use raito::utils::hash::Digest; + use crate::types::chain_state::ChainState; + use utils::hash::Digest; #[test] fn test_block_hash() { diff --git a/src/types/chain_state.cairo b/packages/consensus/src/types/chain_state.cairo similarity index 83% rename from src/types/chain_state.cairo rename to packages/consensus/src/types/chain_state.cairo index f1a50a11..37b01eee 100644 --- a/src/types/chain_state.cairo +++ b/packages/consensus/src/types/chain_state.cairo @@ -4,13 +4,14 @@ //! Chain state alone is not enough to do full block validation, however //! it is sufficient to validate block headers. -use crate::utils::hash::Digest; +use utils::hash::Digest; use crate::validation::{ difficulty::{validate_bits, adjust_difficulty}, coinbase::validate_coinbase, timestamp::{validate_timestamp, next_prev_timestamps}, work::{validate_proof_of_work, compute_total_work}, block::{compute_and_validate_tx_data}, }; use super::block::{BlockHash, Block, TransactionData}; +use core::fmt::{Display, Formatter, Error}; /// Represents the state of the blockchain. #[derive(Drop, Copy, Debug, PartialEq, Serde)] @@ -97,6 +98,33 @@ pub impl BlockValidatorImpl of BlockValidator { ) } } + +impl ChainStateDisplay of Display { + fn fmt(self: @ChainState, ref f: Formatter) -> Result<(), Error> { + let mut prev_ts: ByteArray = Default::default(); + for ts in *self.prev_timestamps { + prev_ts.append(@format!("{},", ts)); + }; + let str: ByteArray = format!( + " + block_height: {} + total_work: {} + best_block_hash: {} + current_target: {} + epoch_start_time: {} + prev_timestamps: [{}] +}}", + *self.block_height, + *self.total_work, + *self.best_block_hash, + *self.current_target, + *self.epoch_start_time, + @prev_ts + ); + f.buffer.append(@str); + Result::Ok(()) + } +} // TODO: implement Digest trait for ChainState diff --git a/src/types/transaction.cairo b/packages/consensus/src/types/transaction.cairo similarity index 78% rename from src/types/transaction.cairo rename to packages/consensus/src/types/transaction.cairo index 41471ca0..e5ae7d11 100644 --- a/src/types/transaction.cairo +++ b/packages/consensus/src/types/transaction.cairo @@ -3,7 +3,8 @@ //! Types are extended with extra information required for validation. //! The data is expected to be prepared in advance and passed as program arguments. -use crate::utils::{hash::Digest, bytearray::{ByteArraySnapHash, ByteArraySnapSerde}}; +use utils::{hash::Digest, bytearray::{ByteArraySnapHash, ByteArraySnapSerde}}; +use core::fmt::{Display, Formatter, Error}; /// Represents a transaction. /// https://learnmeabitcoin.com/technical/transaction/ @@ -128,13 +129,78 @@ impl TxOutDefault of Default { } } +impl TransactionDisplay of Display { + fn fmt(self: @Transaction, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "Transaction {{ version: {}, is_segwit: {}, inputs: {}, outputs: {}, lock_time: {} }}", + *self.version, + *self.is_segwit, + (*self.inputs).len(), + (*self.outputs).len(), + *self.lock_time + ); + f.buffer.append(@str); + Result::Ok(()) + } +} + +impl TxInDisplay of Display { + fn fmt(self: @TxIn, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "TxIn {{ script: {}, sequence: {}, previous_output: {}, witness: {} }}", + *self.script, + *self.sequence, + *self.previous_output.txid, + (*self.witness).len() + ); + f.buffer.append(@str); + Result::Ok(()) + } +} + +impl OutPointDisplay of Display { + fn fmt(self: @OutPoint, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "OutPoint {{ + txid: {}, + vout: {}, + data: {}, + block_height: {}, + block_time: {}, + is_coinbase: {}, + }}", + *self.txid, + *self.vout, + *self.data, + *self.block_height, + *self.block_time, + *self.is_coinbase + ); + f.buffer.append(@str); + Result::Ok(()) + } +} + +impl TxOutDisplay of Display { + fn fmt(self: @TxOut, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "TxOut {{ value: {}, pk_script: {}, cached: {} }}", + *self.value, + *self.pk_script, + *self.cached + ); + f.buffer.append(@str); + Result::Ok(()) + } +} + #[cfg(test)] mod tests { use core::hash::HashStateTrait; use core::hash::HashStateExTrait; use core::poseidon::PoseidonTrait; use super::{OutPoint, TxOut}; - use crate::utils::{hash::{DigestTrait}}; + use utils::hash::{DigestTrait}; #[test] pub fn test_outpoint_poseidon_hash() { diff --git a/src/types/utreexo.cairo b/packages/consensus/src/types/utreexo.cairo similarity index 73% rename from src/types/utreexo.cairo rename to packages/consensus/src/types/utreexo.cairo index 9ba075c7..5ae1468e 100644 --- a/src/types/utreexo.cairo +++ b/packages/consensus/src/types/utreexo.cairo @@ -28,10 +28,11 @@ //! Read more about utreexo: https://eprint.iacr.org/2019/611.pdf use super::transaction::OutPoint; +use core::fmt::{Display, Formatter, Error}; /// Accumulator representation of the state aka "Compact State Node". /// Part of the chain state. -#[derive(Drop, Copy)] +#[derive(Drop, Copy, PartialEq, Serde, Debug)] pub struct UtreexoState { /// Roots of the Merkle tree forest. /// Index is the root height, None means a gap. @@ -99,3 +100,45 @@ pub impl UtreexoStateDefault of Default { UtreexoState { roots: array![].span(), num_leaves: 0, } } } + +impl UtreexoStateDisplay of Display { + fn fmt(self: @UtreexoState, ref f: Formatter) -> Result<(), Error> { + let str: ByteArray = format!( + "UtreexoState {{ roots: {}, num_leaves: {}, }}", (*self.roots).len(), *self.num_leaves + ); + f.buffer.append(@str); + Result::Ok(()) + } +} + +impl UtreexoProofDisplay of Display { + fn fmt(self: @UtreexoProof, ref f: Formatter) -> Result<(), Error> { + let mut proofs: ByteArray = Default::default(); + for proof in *self.proof { + proofs.append(@format!("{},", proof)); + }; + let str: ByteArray = format!( + "UtreexoProof {{ leaf_index: {}, proof: {}, }}", *self.leaf_index, @proofs + ); + f.buffer.append(@str); + Result::Ok(()) + } +} + +impl UtreexoBatchProofDisplay of Display { + fn fmt(self: @UtreexoBatchProof, ref f: Formatter) -> Result<(), Error> { + let mut targets: ByteArray = Default::default(); + let mut proofs: ByteArray = Default::default(); + for target in *self.targets { + targets.append(@format!("{},", target)); + }; + for proof in *self.proof { + proofs.append(@format!("{},", proof)); + }; + let str: ByteArray = format!( + "UtreexoBatchProof {{ leaf_index: [{}], proof: [{}] }}", @targets, @proofs + ); + f.buffer.append(@str); + Result::Ok(()) + } +} diff --git a/src/types/utxo_set.cairo b/packages/consensus/src/types/utxo_set.cairo similarity index 100% rename from src/types/utxo_set.cairo rename to packages/consensus/src/types/utxo_set.cairo diff --git a/src/validation/block.cairo b/packages/consensus/src/validation/block.cairo similarity index 96% rename from src/validation/block.cairo rename to packages/consensus/src/validation/block.cairo index f329336a..425be5bd 100644 --- a/src/validation/block.cairo +++ b/packages/consensus/src/validation/block.cairo @@ -1,7 +1,7 @@ //! Block validation helpers. use crate::types::transaction::{Transaction}; use crate::codec::{Encode, TransactionCodec}; -use crate::utils::{hash::Digest, merkle_tree::merkle_root, sha256::double_sha256_byte_array}; +use utils::{hash::Digest, merkle_tree::merkle_root, sha256::double_sha256_byte_array}; use super::transaction::validate_transaction; const MAX_BLOCK_WEIGHT_LEGACY: usize = 1_000_000; diff --git a/src/validation/coinbase.cairo b/packages/consensus/src/validation/coinbase.cairo similarity index 99% rename from src/validation/coinbase.cairo rename to packages/consensus/src/validation/coinbase.cairo index 7ab82405..90addf81 100644 --- a/src/validation/coinbase.cairo +++ b/packages/consensus/src/validation/coinbase.cairo @@ -3,7 +3,7 @@ //! https://learnmeabitcoin.com/technical/mining/coinbase-transaction/ use crate::types::transaction::{Transaction, TxIn, TxOut}; -use crate::utils::{ +use utils::{ bit_shifts::shr, hash::{Digest, DigestIntoByteArray}, sha256::{double_sha256_byte_array} }; @@ -84,7 +84,6 @@ fn validate_coinbase_input(input: @TxIn, block_height: u32) -> Result<(), ByteAr Result::Ok(()) } -#[inline] /// Validate coinbase sig script (BIP-34) fn validate_coinbase_sig_script(script: @ByteArray, block_height: u32) -> Result<(), ByteArray> { let script_len = script.len(); @@ -199,7 +198,7 @@ mod tests { validate_coinbase_sig_script, validate_coinbase_witness, validate_segwit_output, calculate_wtxid_commitment }; - use crate::utils::{hex::{from_hex, hex_to_hash_rev}, hash::Digest}; + use utils::{hex::{from_hex, hex_to_hash_rev}, hash::Digest}; // Ref implementation here: // https://github.com/bitcoin/bitcoin/blob/0f68a05c084bef3e53e3f549c403bc90b1db319c/src/test/validation_tests.cpp#L24 diff --git a/src/validation/difficulty.cairo b/packages/consensus/src/validation/difficulty.cairo similarity index 99% rename from src/validation/difficulty.cairo rename to packages/consensus/src/validation/difficulty.cairo index 3e894691..1f4bf558 100644 --- a/src/validation/difficulty.cairo +++ b/packages/consensus/src/validation/difficulty.cairo @@ -4,7 +4,7 @@ //! - https://learnmeabitcoin.com/technical/mining/target/ //! - https://learnmeabitcoin.com/technical/block/bits/ -use crate::utils::{bit_shifts::{shl, shr}}; +use utils::{bit_shifts::{shl, shr}}; /// Maximum difficulty target allowed const MAX_TARGET: u256 = 0x00000000FFFF0000000000000000000000000000000000000000000000000000; diff --git a/src/validation/locktime.cairo b/packages/consensus/src/validation/locktime.cairo similarity index 99% rename from src/validation/locktime.cairo rename to packages/consensus/src/validation/locktime.cairo index a12c5a60..d930702c 100644 --- a/src/validation/locktime.cairo +++ b/packages/consensus/src/validation/locktime.cairo @@ -116,7 +116,7 @@ pub fn validate_relative_locktime( #[cfg(test)] mod tests { use crate::types::transaction::{TxIn, OutPoint, TxOut}; - use crate::utils::hex::{from_hex, hex_to_hash_rev}; + use utils::hex::{from_hex, hex_to_hash_rev}; use super::{validate_absolute_locktime, validate_relative_locktime}; // TODO: tests for invalid relative locktime diff --git a/src/validation/timestamp.cairo b/packages/consensus/src/validation/timestamp.cairo similarity index 100% rename from src/validation/timestamp.cairo rename to packages/consensus/src/validation/timestamp.cairo diff --git a/src/validation/transaction.cairo b/packages/consensus/src/validation/transaction.cairo similarity index 99% rename from src/validation/transaction.cairo rename to packages/consensus/src/validation/transaction.cairo index a0e5c2bf..d21a13d4 100644 --- a/src/validation/transaction.cairo +++ b/packages/consensus/src/validation/transaction.cairo @@ -67,7 +67,6 @@ pub fn validate_transaction( return compute_transaction_fee(total_input_amount, total_output_amount); } -#[inline] /// Ensure transaction fee is not negative. fn compute_transaction_fee( total_input_amount: u64, total_output_amount: u64 @@ -80,7 +79,6 @@ fn compute_transaction_fee( return Result::Ok(total_input_amount - total_output_amount); } -#[inline] /// Ensure than coinbase output is old enough to be spent. fn validate_coinbase_maturity(output_height: u32, block_height: u32) -> Result<(), ByteArray> { if block_height <= output_height + 100 { @@ -99,7 +97,7 @@ fn validate_coinbase_maturity(output_height: u32, block_height: u32) -> Result<( #[cfg(test)] mod tests { use crate::types::transaction::{Transaction, TxIn, TxOut, OutPoint}; - use crate::utils::hex::{from_hex, hex_to_hash_rev}; + use utils::hex::{from_hex, hex_to_hash_rev}; use super::validate_transaction; // TODO: tests for coinbase maturity diff --git a/src/validation/work.cairo b/packages/consensus/src/validation/work.cairo similarity index 99% rename from src/validation/work.cairo rename to packages/consensus/src/validation/work.cairo index c9b272d8..cfaeaf9c 100644 --- a/src/validation/work.cairo +++ b/packages/consensus/src/validation/work.cairo @@ -1,6 +1,6 @@ //! Proof-of-work validation helpers. -use crate::utils::hash::Digest; +use utils::hash::Digest; /// Check if the work done (by calculating the block hash) satisfies the difficulty target. pub fn validate_proof_of_work(target: u256, block_hash: Digest) -> Result<(), ByteArray> { diff --git a/packages/utils/README.md b/packages/utils/README.md new file mode 100644 index 00000000..8d47ad55 --- /dev/null +++ b/packages/utils/README.md @@ -0,0 +1,3 @@ +# Common utilities + +This package contains common helpers that are not Bitcoin-specific. diff --git a/packages/utils/Scarb.toml b/packages/utils/Scarb.toml new file mode 100644 index 00000000..81475f48 --- /dev/null +++ b/packages/utils/Scarb.toml @@ -0,0 +1,11 @@ +[package] +name = "utils" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test.workspace = true + +[scripts] +# TODO: cairo lint +lint = "scarb fmt" diff --git a/src/utils/bit_shifts.cairo b/packages/utils/src/bit_shifts.cairo similarity index 100% rename from src/utils/bit_shifts.cairo rename to packages/utils/src/bit_shifts.cairo diff --git a/src/utils/bytearray.cairo b/packages/utils/src/bytearray.cairo similarity index 98% rename from src/utils/bytearray.cairo rename to packages/utils/src/bytearray.cairo index 064f50dd..e123e30f 100644 --- a/src/utils/bytearray.cairo +++ b/packages/utils/src/bytearray.cairo @@ -16,7 +16,6 @@ pub impl ByteArraySnapSerde of Serde<@ByteArray> { } pub impl ByteArraySnapHash, +Drop> of Hash<@ByteArray, S> { - #[inline] fn update_state(mut state: S, value: @ByteArray) -> S { let mut serialized_bytearray: Array = array![]; value.serialize(ref serialized_bytearray); diff --git a/src/utils/hash.cairo b/packages/utils/src/hash.cairo similarity index 99% rename from src/utils/hash.cairo rename to packages/utils/src/hash.cairo index ceed2390..bfb4eed4 100644 --- a/src/utils/hash.cairo +++ b/packages/utils/src/hash.cairo @@ -112,7 +112,7 @@ pub impl DigestHash, +Drop> of Hash { #[cfg(test)] mod tests { - use crate::utils::hex::from_hex; + use crate::hex::from_hex; use super::Digest; #[test] diff --git a/src/utils/hex.cairo b/packages/utils/src/hex.cairo similarity index 97% rename from src/utils/hex.cairo rename to packages/utils/src/hex.cairo index 52830eed..736a403c 100644 --- a/src/utils/hex.cairo +++ b/packages/utils/src/hex.cairo @@ -1,5 +1,5 @@ //! Hex helpers -use raito::utils::hash::Digest; +use crate::hash::Digest; /// Get bytes from hex (base16) pub fn from_hex(hex_string: ByteArray) -> ByteArray { @@ -86,7 +86,7 @@ fn hex_char_to_nibble(hex_char: u8) -> u8 { #[cfg(test)] mod tests { use super::{from_hex, to_hex, hex_to_hash_rev}; - use raito::utils::hash::Digest; + use crate::hash::Digest; #[test] fn test_bytes_from_hex() { diff --git a/packages/utils/src/lib.cairo b/packages/utils/src/lib.cairo new file mode 100644 index 00000000..e85c50e3 --- /dev/null +++ b/packages/utils/src/lib.cairo @@ -0,0 +1,9 @@ +pub mod bytearray; +pub mod sha256; +pub mod hash; +pub mod bit_shifts; +pub mod merkle_tree; +pub mod numeric; + +#[cfg(target: 'test')] +pub mod hex; diff --git a/src/utils/merkle_tree.cairo b/packages/utils/src/merkle_tree.cairo similarity index 99% rename from src/utils/merkle_tree.cairo rename to packages/utils/src/merkle_tree.cairo index 18c967dd..026dd4b7 100644 --- a/src/utils/merkle_tree.cairo +++ b/packages/utils/src/merkle_tree.cairo @@ -29,7 +29,7 @@ pub fn merkle_root(ref hashes: Array) -> Digest { #[cfg(test)] mod tests { - use crate::utils::{hash::{Digest, U256IntoDigest}, hex::hex_to_hash_rev}; + use crate::{hash::{Digest, U256IntoDigest}, hex::hex_to_hash_rev}; use super::{merkle_root}; #[test] diff --git a/src/utils/numeric.cairo b/packages/utils/src/numeric.cairo similarity index 100% rename from src/utils/numeric.cairo rename to packages/utils/src/numeric.cairo diff --git a/src/utils/sha256.cairo b/packages/utils/src/sha256.cairo similarity index 97% rename from src/utils/sha256.cairo rename to packages/utils/src/sha256.cairo index 57d83951..707c69af 100644 --- a/src/utils/sha256.cairo +++ b/packages/utils/src/sha256.cairo @@ -36,7 +36,7 @@ pub fn double_sha256_u32_array(words: Array) -> Digest { #[cfg(test)] mod tests { - use crate::utils::{hex::from_hex, hash::Digest}; + use crate::{hex::from_hex, hash::Digest}; use super::{double_sha256_byte_array, double_sha256_u32_array, double_sha256_parent}; #[test] diff --git a/scripts/data/client.sh b/scripts/data/client.sh index a8235da9..5b39d1a2 100644 --- a/scripts/data/client.sh +++ b/scripts/data/client.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash -set -e; -set -o pipefail; +#set -e; +#set -o pipefail; -base_dir=".raito" +base_dir=".client_cache" start=${1:-0} end=${2:-100} @@ -23,11 +23,11 @@ run_client() { batch_file=${base_dir}/${mode}_${initial_height}_${num_blocks}.json if [ ! -f "$batch_file" ]; then - python scripts/data/generate_data.py $mode $initial_height $num_blocks true $batch_file + python ../../scripts/data/generate_data.py $mode $initial_height $num_blocks true $batch_file fi - arguments=$(python scripts/data/format_args.py $batch_file) - output=$(scarb cairo-run --no-build --function test "$arguments") + arguments=$(python ../../scripts/data/format_args.py $batch_file) + output=$(scarb cairo-run --no-build --package client --function test "$arguments") if [[ "$output" == *"FAIL"* ]]; then echo " fail" echo $output diff --git a/scripts/data/format_args.py b/scripts/data/format_args.py index 9d3c18cd..9d054e8e 100644 --- a/scripts/data/format_args.py +++ b/scripts/data/format_args.py @@ -20,7 +20,7 @@ def serialize(obj): return 1 if obj else 0 elif isinstance(obj, int): # This covers u8, u16, u32, u64, u128, felt252 - assert(obj >= 0 and obj < 2 ** 252) + assert obj >= 0 and obj < 2**252 return obj elif isinstance(obj, str): if obj == "0" * 64: @@ -30,25 +30,27 @@ def serialize(obj): # TODO: there might still be collisions with hashes # Try to cast to int and then to low/high parts num = int(obj) - assert(num >= 0 and num < 2 ** 256) - lo = num % 2 ** 128 - hi = num // 2 ** 128 + assert num >= 0 and num < 2**256 + lo = num % 2**128 + hi = num // 2**128 return (lo, hi) - elif obj.startswith('0x'): + elif obj.startswith("0x"): # Split into 31-byte chunks and save the remainder src = bytes.fromhex(obj[2:]) num_chunks = len(src) // 31 main_len = num_chunks * 31 rem_len = len(src) - main_len - main = [int.from_bytes(src[i:i+31], 'big') for i in range(0, main_len, 31)] + main = [ + int.from_bytes(src[i : i + 31], "big") for i in range(0, main_len, 31) + ] # TODO: check if this is how byte31 is implemented - rem = int.from_bytes(src[main_len:].rjust(31, b'\x00'), 'big') + rem = int.from_bytes(src[main_len:].rjust(31, b"\x00"), "big") return tuple([len(main)] + main + [rem, rem_len]) else: # Reversed hex string into 4-byte words then into BE u32 - assert(len(obj) == 64) + assert len(obj) == 64 rev = list(reversed(bytes.fromhex(obj))) - return tuple(int.from_bytes(rev[i:i+4], 'big') for i in range(0, 32, 4)) + return tuple(int.from_bytes(rev[i : i + 4], "big") for i in range(0, 32, 4)) elif isinstance(obj, list): arr = list(map(serialize, obj)) return tuple([len(arr)] + arr) @@ -68,6 +70,7 @@ def flatten_tuples(src): :return: an object that can only contain integers and lists, top-level tuple converts to a list. """ res = [] + def append_obj(obj, to): if isinstance(obj, int): to.append(obj) @@ -81,6 +84,7 @@ def append_obj(obj, to): append_obj(item, to) else: raise NotImplementedError(obj) + append_obj(src, res) return res @@ -92,12 +96,14 @@ def format_cairo1_run(args: list) -> str: :param args: Python object containing already processed arguments. :return: Returns string with removed commas. """ + def format_item(item): if isinstance(item, list): arr = " ".join(map(format_item, item)) - return f'[{arr}]' + return f"[{arr}]" else: return str(item) + return format_item(args) @@ -106,12 +112,12 @@ def format_args(): Expects a single CLI argument containing file path. Output is compatible with the Scarb runner arguments format. """ - if (len(sys.argv) != 2): + if len(sys.argv) != 2: raise TypeError("Expected single argument") args = json.loads(Path(sys.argv[1]).read_text()) res = flatten_tuples(serialize(args)) print([res]) -if __name__ == '__main__': +if __name__ == "__main__": format_args() diff --git a/scripts/data/generate_data.py b/scripts/data/generate_data.py index aa4855f2..691a42e6 100755 --- a/scripts/data/generate_data.py +++ b/scripts/data/generate_data.py @@ -190,13 +190,15 @@ def format_coinbase_input(input: dict): "block_time": 0, "is_coinbase": False, }, - "witness": ["0x0000000000000000000000000000000000000000000000000000000000000000"], + "witness": [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ], } def format_output(output: dict): """Formats transaction output according to the Cairo type.""" - value = (Decimal(str(output["value"])) * Decimal('100000000')).to_integral_value() + value = (Decimal(str(output["value"])) * Decimal("100000000")).to_integral_value() return { "value": int(value), "pk_script": f'0x{output["scriptPubKey"]["hex"]}', diff --git a/scripts/data/integration_tests.sh b/scripts/data/integration_tests.sh index 4f40ad80..bd51c13d 100755 --- a/scripts/data/integration_tests.sh +++ b/scripts/data/integration_tests.sh @@ -21,15 +21,17 @@ if [ $# -gt 0 ]; then test_files="${args[@]}" fi +echo "running integration tests ..." + for test_file in $test_files; do if [ -f "$test_file" ]; then - echo -n "test e2e:$test_file ..." + echo -n "test $test_file ..." if [[ "$ignored" =~ "$test_file" ]]; then echo " ignored" num_ignored=$((num_ignored + 1)) else - arguments=$(python scripts/data/format_args.py ${test_file}) + arguments=$(python ../../scripts/data/format_args.py ${test_file}) output=$(scarb cairo-run --no-build --function test "$arguments") gas_spent=$(echo $output | grep -o 'gas_spent=[0-9]*' | sed 's/gas_spent=//') @@ -37,8 +39,7 @@ for test_file in $test_files; do echo -e "${RED} fail ${RESET}(gas usage est.: $gas_spent)" num_fail=$((num_fail + 1)) error=$(echo $output | grep -o "error='[^']*'" | sed "s/error=//") - failures+="\te2e:$test_file — Panicked with $error:\n" - failures+="\te2e:$test_file — output:\n$output\n" + failures+="\t$test_file — Panicked with $error\n" elif [[ "$output" == *"OK"* ]]; then echo -e "${GREEN} ok ${RESET}(gas usage est.: $gas_spent)" num_ok=$((num_ok + 1)) @@ -46,8 +47,7 @@ for test_file in $test_files; do echo -e "${RED} fail ${RESET}(gas usage est.: 0)" num_fail=$((num_fail + 1)) error=$(echo "$output" | sed '1d') - failures+="\te2e:$test_file — $error\n" - failures+="\te2e:$test_file — output:\n$output\n" + failures+="\t$test_file — $error\n" fi fi fi diff --git a/scripts/data/regenerate_tests.sh b/scripts/data/regenerate_tests.sh index 3a314723..fabc833e 100755 --- a/scripts/data/regenerate_tests.sh +++ b/scripts/data/regenerate_tests.sh @@ -9,6 +9,7 @@ then fi force=0 +data_dir="tests/data" if [[ "$1" == "--force" ]]; then force=1 @@ -25,8 +26,6 @@ light_test_cases=( 478557 # Bitcoin Cash hard fork block (478558) 481823 # Segwit soft fork block (481824) 491406 # Bitcoin Gold hard fork block (491407) - 542212 # (542213) - 553723 # (553724) 629999 # Third halving block (630000) 709631 # Taproot soft fort block (709632) 757738 # Block with witness (757739) @@ -37,20 +36,18 @@ light_test_cases=( full_test_cases=( 169 # Block containing first P2P tx to Hal Finney (170) - 542212 - 553723 # (553724) 757738 # Block with witness (757739) ) -mkdir tests/data || true +mkdir $data_dir || true # Generate test file if it does not exist yet or if "force" flag is set generate_test() { local mode=$1 local height=$2 - test_file="tests/data/${mode}_${test_case}.json" + test_file="${data_dir}/${mode}_${test_case}.json" if [[ ! -f "$test_file" || $force -eq 1 ]]; then - python scripts/data/generate_data.py $mode $height 1 true $test_file + python ../../scripts/data/generate_data.py $mode $height 1 true $test_file fi } diff --git a/scripts/data/requirements.txt b/scripts/data/requirements.txt index ef487e06..4ce787d1 100644 --- a/scripts/data/requirements.txt +++ b/scripts/data/requirements.txt @@ -1 +1,4 @@ -requests==2.32.3 \ No newline at end of file +requests==2.32.3 +black==24.8.0 +flake8==7.1.1 +flake8-black==0.3.6 \ No newline at end of file diff --git a/scripts/misc/create_issues.py b/scripts/misc/create_issues.py index 4cd113ff..a6b88330 100644 --- a/scripts/misc/create_issues.py +++ b/scripts/misc/create_issues.py @@ -3,14 +3,20 @@ import sys import argparse + def create_issue(title, body, labels): cmd = [ - "gh", "issue", "create", - "--title", title, - "--body", body, - "--label", ",".join(labels) + "gh", + "issue", + "create", + "--title", + title, + "--body", + body, + "--label", + ",".join(labels), ] - + try: subprocess.run(cmd, check=True) print(f"Successfully created issue: {title}") @@ -19,8 +25,11 @@ def create_issue(title, body, labels): print(f"Error message: {e}") sys.exit(1) + def main(): - parser = argparse.ArgumentParser(description="Create GitHub issues from a JSON file.") + parser = argparse.ArgumentParser( + description="Create GitHub issues from a JSON file." + ) parser.add_argument("json_file", help="Path to the JSON file containing issue data") args = parser.parse_args() @@ -41,5 +50,6 @@ def main(): for issue in data["issues"]: create_issue(issue["title"], issue["body"], issue["labels"]) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/lib.cairo b/src/lib.cairo deleted file mode 100644 index 7767f8d3..00000000 --- a/src/lib.cairo +++ /dev/null @@ -1,35 +0,0 @@ -pub mod utils { - pub mod bytearray; - pub mod sha256; - pub mod hash; - pub mod bit_shifts; - pub mod merkle_tree; - pub mod numeric; - - // #[cfg(target: 'test')] - pub mod hex; -} -pub mod validation { - pub mod difficulty; - pub mod coinbase; - pub mod locktime; - pub mod timestamp; - pub mod transaction; - pub mod work; - pub mod block; -} -pub mod codec; -pub mod types { - pub mod utreexo; - pub mod chain_state; - pub mod block; - pub mod transaction; - pub mod utxo_set; -} - -mod main; - -// TODO: move this module to a separate package -// Scarb does not support features when using cairo-run -// neither it allows to run function from the "tests" folder -mod test; diff --git a/tests/data/full_542212.json b/tests/data/full_542212.json deleted file mode 100644 index f57eb2c5..00000000 --- a/tests/data/full_542212.json +++ /dev/null @@ -1,359 +0,0 @@ -{ - "chain_state": { - "block_height": 542212, - "total_work": "1005317431382460840667183195", - "best_block_hash": "000000000000000000085a38ccf9c046c51b96add547c466ccba3612b1eb8089", - "current_target": "3840827764407250199942201944063224491938810378873470976", - "epoch_start_time": 1536290079, - "prev_timestamps": [ - 1537424869, - 1537425316, - 1537425370, - 1537425379, - 1537425770, - 1537425986, - 1537427160, - 1537428220, - 1537428564, - 1537429315, - 1537429661 - ] - }, - "blocks": [ - { - "header": { - "version": 536870912, - "time": 1537429727, - "bits": 388503969, - "nonce": 31692307 - }, - "data": { - "variant_id": 1, - "transactions": [ - { - "version": 1, - "is_segwit": true, - "inputs": [ - { - "script": "0x03054608174d696e656420627920416e74506f6f6c393b205ba350dffabe6d6d3a3e92d9efff857664de632e89fa3182f1e793d00be2e71a117b273145945a810400000000000000db250000acba0500", - "sequence": 4294967295, - "previous_output": { - "txid": "0000000000000000000000000000000000000000000000000000000000000000", - "vout": 4294967295, - "data": { - "value": 0, - "pk_script": "0x", - "cached": false - }, - "block_height": 0, - "block_time": 0, - "is_coinbase": false - }, - "witness": [ - "0x0000000000000000000000000000000000000000000000000000000000000000" - ] - } - ], - "outputs": [ - { - "value": 1250004874, - "pk_script": "0x76a914edf10a7fac6b32e24daa5305c723f3de58db1bc888ac", - "cached": false - }, - { - "value": 0, - "pk_script": "0x6a24aa21a9ed4a657fcaa2149342376247e2e283a55a6b92dcc35d4d89e4ac7b74488cb63be2", - "cached": false - } - ], - "lock_time": 0 - }, - { - "version": 1, - "is_segwit": false, - "inputs": [ - { - "script": "0x483045022100e2e9bc1f6bae2deed086e935bb49fd6ac1e13dc3a44c36cd8b9a6f4257efb70d022076537c7021f12d761e1202796029f13798503bc22ab8c2ee8cb98207cbfeb414012102071c2c88e4560b47a03c033c736149a2ddd6071aea54ab85c5169cee156712f8", - "sequence": 4294967295, - "previous_output": { - "txid": "db636bb7d1c79b38b496766dcf37fbc215edb1fd4f19b5ec9993d4983f2798b8", - "vout": 1, - "data": { - "value": 20000000, - "pk_script": "0x76a914682d27c2f705e69a7c291aa4860d1fac01ff3cb388ac", - "cached": false - }, - "block_height": 542034, - "block_time": 1537329301, - "is_coinbase": false - }, - "witness": [] - } - ], - "outputs": [ - { - "value": 750000, - "pk_script": "0x76a914849a95fc65eeaa2ac47b6b6fc1f1883edb2c6c9788ac", - "cached": false - }, - { - "value": 19248870, - "pk_script": "0x76a9142964198f7ae9f7b920a2ab7c0b96b90e4ec9b14d88ac", - "cached": false - } - ], - "lock_time": 0 - }, - { - "version": 1, - "is_segwit": true, - "inputs": [ - { - "script": "0x160014dde2f1a9a4bfda011ba9ec4062990c7e1a531585", - "sequence": 4294967295, - "previous_output": { - "txid": "88a7846ff8f618cd70c9ee0cf1be9bbbb7b3b0a12434f640355b0560265e4052", - "vout": 0, - "data": { - "value": 1629016, - "pk_script": "0xa914c460985af9c3b4213ac834a91cc22b8dd6ef82f987", - "cached": false - }, - "block_height": 542212, - "block_time": 1537429661, - "is_coinbase": false - }, - "witness": [ - "0x3045022100f93bd5d3529418f60dddd477f169c32ea5ab340c99573438ee7c40d705db9ba20220323f1b4d8840098b1271c95365cdc7e8f81d0bf1fcc568c68fc7de8b871b4bee01", - "0x03211d047d92547bca4aa116bc51ec4b09188e5991f69f0432fe1b5dfd89478597" - ] - } - ], - "outputs": [ - { - "value": 1627238, - "pk_script": "0xa914adc5ec550548f087371e645047170864d5fbdc0387", - "cached": false - }, - { - "value": 1150, - "pk_script": "0x001441f6746110cc0fec102e83053d4c0ae56fab1bdd", - "cached": false - } - ], - "lock_time": 0 - }, - { - "version": 1, - "is_segwit": false, - "inputs": [ - { - "script": "0x483045022100fa00a6651015ac807b03d8d54559831db40d80329f46a886b93ca6b3996daf9b02201d0fafccc88c4654a9c3d205ef0828e32376ecb42f79587615203f23fc8f2d42012102a2cda42f6954605e40cbc5601f65673621c057f8c12f16149fbbf632e8be8ed8", - "sequence": 4294967295, - "previous_output": { - "txid": "21e78cdeb8e642c3617104a9517cb56f01fcf7b98ed096ae9227b9e55498a1c1", - "vout": 0, - "data": { - "value": 4262994, - "pk_script": "0x76a9140a1f8d3cff7cdcd57e0b9a6f18021e22cf6db4c388ac", - "cached": false - }, - "block_height": 542093, - "block_time": 1537364214, - "is_coinbase": false - }, - "witness": [] - }, - { - "script": "0x4730440220167b4777a23db304f50ac75febfae5db0b1578c90d85769bc76e0f5458484319022023f763e95ea7771c15ea0df91c17519014b2223cd726a3b0a8b1a6f67f99b2bf012103b9492a823f03b70a1750e0fac44f58f5c81e09f4c18d0b28755b44c911ffab5f", - "sequence": 4294967295, - "previous_output": { - "txid": "5fc2e2941b3da3c969f53df56c42dfe98d1640a7d4e5d5168ff724734aff16da", - "vout": 0, - "data": { - "value": 4228088, - "pk_script": "0x76a914ebc43e4d992d3f9c07d439b6e8fb0bd6dacf056a88ac", - "cached": false - }, - "block_height": 540089, - "block_time": 1536180961, - "is_coinbase": false - }, - "witness": [] - }, - { - "script": "0x483045022100be0b8dc174f4136e3fb4f3ccf1a2be67a44d2086179c5726c61a1af010d5232f02205c0fb4cdd7c9cb8698ceed05e688e10a0cbdd0ee5db1a0a2ef92f322e1a60e9f0121020b9f404317cc6ab5a699f607a9bb0acd0bf5588777672f8f7f4c1a13304e9f76", - "sequence": 4294967295, - "previous_output": { - "txid": "701be2ca7a438771484515919c38d45a10d65968bb5be02b9d854ba32e3f53f1", - "vout": 0, - "data": { - "value": 4371107, - "pk_script": "0x76a914a7ec934b55c0b849f185f3efd786e227a7f6fc2188ac", - "cached": false - }, - "block_height": 538892, - "block_time": 1535461055, - "is_coinbase": false - }, - "witness": [] - }, - { - "script": "0x483045022100ef3c61b5ba155b94fd02a96bf788e9a9be2de0ee53e3fe1fa6c24dd9d9aaee12022044c07be54a9c59fed96dfb778da0079f6753ab634d1920bf0fac25ebf7ac49d0012103f93e29be8b393773228f704151964662a91df4c1a3e15364e4cfb38a8cacbda9", - "sequence": 4294967295, - "previous_output": { - "txid": "72951146c456f5857a0f5a798a8e197dda6b6d9da7c24cfa1c5baa3718199513", - "vout": 0, - "data": { - "value": 4111308, - "pk_script": "0x76a914a970862cc2755b0b682dddb587148f9a3786a2f888ac", - "cached": false - }, - "block_height": 539663, - "block_time": 1535905599, - "is_coinbase": false - }, - "witness": [] - }, - { - "script": "0x4730440220421aef290bbd39a18d1281ecfbb420b43daf2cc1c315b6bb44c895f88cb3cfcc022007a512c65b7768b1505f789b6d78479063d04ffb243072f2061eeb66fb1ade81012102712eea19c72fd644b2698c5480ebe77ce6face0bed64238a34a32085567f2f8e", - "sequence": 4294967295, - "previous_output": { - "txid": "7cfdf540f7d74367f36b908fdc409fc26fe434d78c19d05f84bb5f4d680f1255", - "vout": 0, - "data": { - "value": 4410420, - "pk_script": "0x76a9141bbf3fbab89d6dc4f54a296cc5e43c03fdd5030a88ac", - "cached": false - }, - "block_height": 539312, - "block_time": 1535707474, - "is_coinbase": false - }, - "witness": [] - }, - { - "script": "0x483045022100a29aea775d2c46028f40dceb1707b23e720bb314cef455854313569200c83162022009d0cdcef2e1f0a9217794991fa838fe6ce2bdc6e3c04ad490343c7e4a0ee7d3012102ed59ec6d98f9c2a4dd1324d46d74c56ff7e15935925f69799e547969a523ea98", - "sequence": 4294967295, - "previous_output": { - "txid": "a62b4710ba33b2aaebe2d6ca6cb1019d9d0f26a506fa9c4e465631ce193d55f3", - "vout": 0, - "data": { - "value": 4676554, - "pk_script": "0x76a914465215552ce0ef787217718e4e73f9b44c6dd2c888ac", - "cached": false - }, - "block_height": 541513, - "block_time": 1537012975, - "is_coinbase": false - }, - "witness": [] - }, - { - "script": "0x483045022100fad9f10989ab4ab019da4f6e430f9fe9ebe76941a9200d7346447358e93b1dce0220756c864b029a64ed9d6eedc13cf8b7d602572fcd7a3d3cb528350bb032d7e3ec012102e3e0c78d034627b1616cf196aa69bfaf20009ba9ebf09cf069453cc0423239e2", - "sequence": 4294967295, - "previous_output": { - "txid": "b63dfef28758e3a02bce39512538a66cbefe83e9cb12fb9b66830d5fd9b60a53", - "vout": 0, - "data": { - "value": 4570694, - "pk_script": "0x76a914a3666f0e4a31d9be5ee063ea5df05424cb49d73d88ac", - "cached": false - }, - "block_height": 538129, - "block_time": 1535042216, - "is_coinbase": false - }, - "witness": [] - }, - { - "script": "0x48304502210095f37fb2700c9f96d5e5c02d4b043a7cd804f79dd071d8b221b7ae781f0f5b4e02200ae3135b8bebf813449956ae19e7c02db5eff2472da023afa8b8d4e9baba0cdd01210226cc53dfc0a41cc0cf7117dfd406db2b87161e89e2bab9908a2382173fc6dbe1", - "sequence": 4294967295, - "previous_output": { - "txid": "d8e47e4abae931f98f529e0240c09bf7bcfaadac3c7099df65d9414982708e4b", - "vout": 0, - "data": { - "value": 4320873, - "pk_script": "0x76a914268b4fd271aff252177751747505e5c504f43cc788ac", - "cached": false - }, - "block_height": 539037, - "block_time": 1535546708, - "is_coinbase": false - }, - "witness": [] - }, - { - "script": "0x473044022032361f724fe006079cf37b3df61cb6c51cb0c5fd77f29b61a318134ca4948d0e02202fbe6d484a78730899230244f3662fd5578b87e9eaaccdf9bf8f05d23917de8301210254e84223b3d7f7cfd14315be8fa0c7d7eb1a1a34a672f08e2b8ec134472a66d4", - "sequence": 4294967295, - "previous_output": { - "txid": "dfab9fae87944c4947c69b3be2d953ee832bed82487e7f212b7281989e126252", - "vout": 0, - "data": { - "value": 4068982, - "pk_script": "0x76a91473c603a415a4e845f5f71724e9642388a27a3e4488ac", - "cached": false - }, - "block_height": 538932, - "block_time": 1535482136, - "is_coinbase": false - }, - "witness": [] - }, - { - "script": "0x483045022100b78b9c24f5f3e950aba637b827d5b11615c17ae2f43105941d172cc4a8b73c80022064998c17becd06abbcfde47c91b9d87da5ac67873ed9a412010b08b954e0b5cd01210329a0acc2b0d60dd243eef46073a672ed0caf467c92f63ef7293f2036a3851a1e", - "sequence": 4294967295, - "previous_output": { - "txid": "f4b9d1b9075f753ccaa73eb1584498c949081305b7b75dbe2e288832e0407006", - "vout": 0, - "data": { - "value": 4768471, - "pk_script": "0x76a914daf4983be7452bb38d0d75fcc44115dce5bc990a88ac", - "cached": false - }, - "block_height": 540164, - "block_time": 1536220801, - "is_coinbase": false - }, - "witness": [] - } - ], - "outputs": [ - { - "value": 32514, - "pk_script": "0x76a914c6a396ae979670eeaa6929df3dd1c2d8fba31c3d88ac", - "cached": false - }, - { - "value": 43753861, - "pk_script": "0xa914409dbd0e9a1ab27853186367130e6aab2509e47f87", - "cached": false - } - ], - "lock_time": 0 - } - ] - } - } - ], - "expected": { - "block_height": 542213, - "total_work": "1005347579073620115088156986", - "best_block_hash": "000000000000000000143a2c56c0214236dadfd30df41d4a0345492ad6d861ec", - "current_target": "3840827764407250199942201944063224491938810378873470976", - "epoch_start_time": 1536290079, - "prev_timestamps": [ - 1537425316, - 1537425370, - 1537425379, - 1537425770, - 1537425986, - 1537427160, - 1537428220, - 1537428564, - 1537429315, - 1537429661, - 1537429727 - ] - } -} \ No newline at end of file diff --git a/tests/data/full_553723.json b/tests/data/full_553723.json deleted file mode 100644 index 26aa73a8..00000000 --- a/tests/data/full_553723.json +++ /dev/null @@ -1,214 +0,0 @@ -{ - "chain_state": { - "block_height": 553723, - "total_work": "1349054508381181651674186396", - "best_block_hash": "0000000000000000000567a96e76bb24f8e954d24a981af937fb566f9624d70e", - "current_target": "4774638159061819979596346127394133648234752261950013440", - "epoch_start_time": 1543838368, - "prev_timestamps": [ - 1544748510, - 1544748579, - 1544748881, - 1544749065, - 1544749190, - 1544749434, - 1544750432, - 1544750860, - 1544750935, - 1544751014, - 1544751144 - ] - }, - "blocks": [ - { - "header": { - "version": 536870912, - "time": 1544751216, - "bits": 389142908, - "nonce": 430846328 - }, - "data": { - "variant_id": 1, - "transactions": [ - { - "version": 1, - "is_segwit": true, - "inputs": [ - { - "script": "0x03fc72081b4d696e656420627920416e74506f6f6c31384e00a103205c130870fabe6d6d0782f935d81e4948c75d613779fe7c53eab4c35616d073755df5fe49f3cdb16d0400000000000000cc050000d38d0200", - "sequence": 4294967295, - "previous_output": { - "txid": "0000000000000000000000000000000000000000000000000000000000000000", - "vout": 4294967295, - "data": { - "value": 0, - "pk_script": "0x", - "cached": false - }, - "block_height": 0, - "block_time": 0, - "is_coinbase": false - }, - "witness": [ - "0x0000000000000000000000000000000000000000000000000000000000000000" - ] - } - ], - "outputs": [ - { - "value": 1250005141, - "pk_script": "0x76a914edf10a7fac6b32e24daa5305c723f3de58db1bc888ac", - "cached": false - }, - { - "value": 0, - "pk_script": "0x6a24aa21a9ed6502e8637ba29cd8a820021915339c7341223d571e5e8d66edd83786d387e715", - "cached": false - }, - { - "value": 0, - "pk_script": "0x52534b424c4f434b3abcb062d40bf073e3eac877f2b1bad4eddc4f8d882ab014a8d2278563f832e95b", - "cached": false - } - ], - "lock_time": 0 - }, - { - "version": 1, - "is_segwit": true, - "inputs": [ - { - "script": "0x16001473e8ab17ce626fc190105f583f0073c76a6b37d9", - "sequence": 4294967295, - "previous_output": { - "txid": "bca6b51a545e7ba7c8c65c93a496b38c52d2dd17318e1ee515928514cd52e9d5", - "vout": 0, - "data": { - "value": 7520877, - "pk_script": "0xa914f351628496269cf29ce89a61a0b549641f38731e87", - "cached": false - }, - "block_height": 553723, - "block_time": 1544751144, - "is_coinbase": false - }, - "witness": [ - "0x304402200fd489534abd79ef87d0c2a3e5db3d3e77b91c2c19d76421f7b61ce6cf3789e202206fa79d2e723462694880b37a93e4155a11b4a533c3a2c82f5d917f9cb30b134101", - "0x034d12143057d8154b0a0044548861a29fb93ec1e1da607c9661c46a1dfef6bf6f" - ] - } - ], - "outputs": [ - { - "value": 218247, - "pk_script": "0x0014d3421512c645bcfa2af9743a18731001f0173904", - "cached": false - }, - { - "value": 7300000, - "pk_script": "0xa914379056a3f6d10caa54ee8ef27131c348a7ccf31f87", - "cached": false - } - ], - "lock_time": 0 - }, - { - "version": 1, - "is_segwit": true, - "inputs": [ - { - "script": "0x", - "sequence": 4294967295, - "previous_output": { - "txid": "34c5844262f47603acc025d9cba6a34bae74120e4bb7b2c874471671a3662589", - "vout": 0, - "data": { - "value": 14467222, - "pk_script": "0x0014957970e18969de99dd4b9aed13b77811006a0b40", - "cached": false - }, - "block_height": 553720, - "block_time": 1544750860, - "is_coinbase": false - }, - "witness": [ - "0x30440220212ed58e54c2fbf219fb6db8c4ea4854e62d56667ff1a74da91cd27fc62e10c4022018923a537ba525f68e64ce0b70a36b4839b55aecd791dab8d85052c69594f1f501", - "0x034e6622f089ad175ab1aca77487577e350fb00413a7420250d60422a2e0cc40a9" - ] - } - ], - "outputs": [ - { - "value": 2427272, - "pk_script": "0x0014bed752ca565818fee0c7be61713dec3b9b4841e2", - "cached": false - }, - { - "value": 12037687, - "pk_script": "0xa9144c13003630a952f25cfc24728b69e5b65dc7fc6787", - "cached": false - } - ], - "lock_time": 0 - }, - { - "version": 2, - "is_segwit": false, - "inputs": [ - { - "script": "0x483045022100da31f58b0e2ec9da2e8fadaa38e7b1d0084433fc62160e02c7b7016e6e31477f02206a0462df44e82869798337f5523286972c93d66d523f137c0db5a4baa6e6930d0121034b11147333a38bfb9466c60bec7ce3dc608a4fd2315227fae928a7c10556e9a5", - "sequence": 4294967294, - "previous_output": { - "txid": "13cc7b4ba4dc602ecb4c2580bcceb7276ef68d82807790952ef1206651fbec6f", - "vout": 0, - "data": { - "value": 1383599, - "pk_script": "0x76a9146c5e8d9e82ea507e34746a36471a66f6e2e1329e88ac", - "cached": false - }, - "block_height": 549207, - "block_time": 1541643907, - "is_coinbase": false - }, - "witness": [] - } - ], - "outputs": [ - { - "value": 120000, - "pk_script": "0xa91401f7b90f5653e8c2a45093c3b2168cfa7e46811e87", - "cached": false - }, - { - "value": 1263351, - "pk_script": "0x76a914b95067af2d4ebef77ddeae544963fa04df66bb0a88ac", - "cached": false - } - ], - "lock_time": 553722 - } - ] - } - } - ], - "expected": { - "block_height": 553724, - "total_work": "1349078759871112371043869813", - "best_block_hash": "0000000000000000002849bd7ea6df81fa2f07652af0600ffa0f2b0bc47d736c", - "current_target": "4774638159061819979596346127394133648234752261950013440", - "epoch_start_time": 1543838368, - "prev_timestamps": [ - 1544748579, - 1544748881, - 1544749065, - 1544749190, - 1544749434, - 1544750432, - 1544750860, - 1544750935, - 1544751014, - 1544751144, - 1544751216 - ] - } -} \ No newline at end of file diff --git a/tests/data/light_542212.json b/tests/data/light_542212.json deleted file mode 100644 index 597da2f8..00000000 --- a/tests/data/light_542212.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "chain_state": { - "block_height": 542212, - "total_work": "1005317431382460840667183195", - "best_block_hash": "000000000000000000085a38ccf9c046c51b96add547c466ccba3612b1eb8089", - "current_target": "3840827764407250199942201944063224491938810378873470976", - "epoch_start_time": 1536290079, - "prev_timestamps": [ - 1537424869, - 1537425316, - 1537425370, - 1537425379, - 1537425770, - 1537425986, - 1537427160, - 1537428220, - 1537428564, - 1537429315, - 1537429661 - ] - }, - "blocks": [ - { - "header": { - "version": 536870912, - "time": 1537429727, - "bits": 388503969, - "nonce": 31692307 - }, - "data": { - "variant_id": 0, - "merkle_root": "64a8b69cb7100430aec35825d54a48f4434f3db52dff8a02709aee3a659b5e13" - } - } - ], - "expected": { - "block_height": 542213, - "total_work": "1005347579073620115088156986", - "best_block_hash": "000000000000000000143a2c56c0214236dadfd30df41d4a0345492ad6d861ec", - "current_target": "3840827764407250199942201944063224491938810378873470976", - "epoch_start_time": 1536290079, - "prev_timestamps": [ - 1537425316, - 1537425370, - 1537425379, - 1537425770, - 1537425986, - 1537427160, - 1537428220, - 1537428564, - 1537429315, - 1537429661, - 1537429727 - ] - } -} \ No newline at end of file diff --git a/tests/data/light_553723.json b/tests/data/light_553723.json deleted file mode 100644 index 8786a1b6..00000000 --- a/tests/data/light_553723.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "chain_state": { - "block_height": 553723, - "total_work": "1349054508381181651674186396", - "best_block_hash": "0000000000000000000567a96e76bb24f8e954d24a981af937fb566f9624d70e", - "current_target": "4774638159061819979596346127394133648234752261950013440", - "epoch_start_time": 1543838368, - "prev_timestamps": [ - 1544748510, - 1544748579, - 1544748881, - 1544749065, - 1544749190, - 1544749434, - 1544750432, - 1544750860, - 1544750935, - 1544751014, - 1544751144 - ] - }, - "blocks": [ - { - "header": { - "version": 536870912, - "time": 1544751216, - "bits": 389142908, - "nonce": 430846328 - }, - "data": { - "variant_id": 0, - "merkle_root": "0e5b14f4c65043f1776b0f5940d7bd8d414d1e9de4aafb0984dc2273467da307" - } - } - ], - "expected": { - "block_height": 553724, - "total_work": "1349078759871112371043869813", - "best_block_hash": "0000000000000000002849bd7ea6df81fa2f07652af0600ffa0f2b0bc47d736c", - "current_target": "4774638159061819979596346127394133648234752261950013440", - "epoch_start_time": 1543838368, - "prev_timestamps": [ - 1544748579, - 1544748881, - 1544749065, - 1544749190, - 1544749434, - 1544750432, - 1544750860, - 1544750935, - 1544751014, - 1544751144, - 1544751216 - ] - } -} \ No newline at end of file