-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
R2 upload for
LightBlock
in single file (#54)
* R2 upload for wallet server chunks * handle upload head * updating upload mechanism to split functionality * refactoring LightBlockUpload and writing tests * gzip by stream, writes tests validating streaming unzip * add .env.test * upload lightblocks * test for gzip, manifest, and lightblock decoding * pr comment rework, upload to new directory and replace latest.json * add example script for downloading block * pr comment address about exception catching bug fixes: uint32 not capturing block timestamp correctly head hash was updated sometimes before block was added, on premature exit this was causing empty block * bug fix * db fixture cleanup * db fixture readme
- Loading branch information
Showing
26 changed files
with
1,707 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
NODE_HOST=node.domain.com | ||
NODE_PORT=8020 | ||
NODE_AUTH_TOKEN=your-auth-token | ||
BUILD_CACHE=true | ||
CACHE_PATH=./block-cache | ||
BLOCK_RANGE_MAX=1000 | ||
|
||
UPLOAD_BLOCKS=true | ||
BUCKET_ENDPOINT=https://foo.r2.cloudflarestorage.com | ||
BUCKET_NAME=ironfish-light-blocks-testnet | ||
BUCKET_ACCESS_KEY_ID=your-access-key-id | ||
BUCKET_SECRET_ACCESS_KEY=your-secret-access-key | ||
UPLOAD_CHUNK_SIZE_MB=1 | ||
MAX_UPLOAD_LAG_MS=86400000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,6 @@ node_modules/ | |
*-cache* | ||
*testdb* | ||
*yarn-error.log | ||
test/* | ||
test/* | ||
blocks* | ||
latest.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import axios from "axios"; | ||
import fs from "fs"; | ||
import zlib from "zlib"; | ||
import readline from "readline"; | ||
import { LightBlock } from "../../src/models/lightstreamer"; | ||
import { promisify } from "util"; | ||
import { Readable, finished } from "stream"; | ||
|
||
const R2_URL = "https://pub-a64f19884be64edaa4f3326fa5c9a39a.r2.dev/"; | ||
const BLOCK_NUMBER = 420; | ||
|
||
const finishedPromise = promisify(finished); // Convert callback to promise | ||
|
||
async function downloadFile(url: string, path: string) { | ||
const response = await axios.get(url, { responseType: "stream" }); | ||
const writer = fs.createWriteStream(path); | ||
response.data.pipe(writer); | ||
return new Promise((resolve, reject) => { | ||
writer.on("finish", resolve); | ||
writer.on("error", reject); | ||
}); | ||
} | ||
|
||
async function decompressGzip(inputPath: string, outputPath: string) { | ||
const gzip = zlib.createGunzip(); | ||
const input = fs.createReadStream(inputPath); | ||
const output = fs.createWriteStream(outputPath); | ||
input.pipe(gzip).pipe(output); | ||
await finishedPromise(output); | ||
} | ||
|
||
async function findBlockRange(manifestPath: string, blockNumber: number) { | ||
const fileStream = fs.createReadStream(manifestPath); | ||
const rl = readline.createInterface({ input: fileStream }); | ||
for await (const line of rl) { | ||
const [block, start, end] = line.split(","); | ||
if (parseInt(block) === blockNumber) { | ||
return { start: parseInt(start), end: parseInt(end) }; | ||
} | ||
} | ||
throw new Error(`Block ${blockNumber} not found in manifest`); | ||
} | ||
|
||
async function downloadBlock( | ||
url: string, | ||
range: { start: number; end: number }, | ||
) { | ||
const response = await axios.get(url, { | ||
headers: { Range: `bytes=${range.start}-${range.end}` }, | ||
responseType: "stream", | ||
}); | ||
const data = await streamToBuffer(response.data); | ||
return data; | ||
} | ||
|
||
async function streamToBuffer(readableStream: Readable): Promise<Buffer> { | ||
const chunks: Buffer[] = []; | ||
for await (const chunk of readableStream) { | ||
chunks.push(chunk); | ||
} | ||
return Buffer.concat(chunks); | ||
} | ||
|
||
async function main() { | ||
await downloadFile(R2_URL + "latest.json", "latest.json"); | ||
const latest = JSON.parse(fs.readFileSync("latest.json", "utf-8")); | ||
await downloadFile(R2_URL + latest.manifest, "blocks.manifest.gz"); | ||
await decompressGzip("blocks.manifest.gz", "blocks.manifest"); | ||
const range = await findBlockRange("blocks.manifest", BLOCK_NUMBER); | ||
const block = await downloadBlock(R2_URL + latest.blocks, range); | ||
console.log(JSON.stringify(LightBlock.decode(block), null, 2)); | ||
} | ||
|
||
main().catch(console.error); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
require("dotenv").config({ path: ".env.test" }); | ||
|
||
// Create working copy of test database | ||
const sourceDir = path.join(__dirname, "test", "dbfixture", "cache"); | ||
const targetDir = path.join(__dirname, "test", "cache"); | ||
|
||
function copyDirectory(source, target) { | ||
return new Promise((resolve, reject) => { | ||
fs.readdir(source, (err, files) => { | ||
if (err) reject(err); | ||
|
||
const promises = files.map((file) => { | ||
return new Promise((resolve, reject) => { | ||
const sourceFile = path.join(source, file); | ||
const targetFile = path.join(target, file); | ||
|
||
fs.stat(sourceFile, (err, stats) => { | ||
if (err) reject(err); | ||
|
||
if (stats.isDirectory()) { | ||
fs.mkdir(targetFile, { recursive: true }, (err) => { | ||
if (err) reject(err); | ||
resolve(copyDirectory(sourceFile, targetFile)); | ||
}); | ||
} else { | ||
fs.copyFile(sourceFile, targetFile, (err) => { | ||
if (err) reject(err); | ||
resolve(); | ||
}); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
Promise.all(promises) | ||
.then(() => resolve()) | ||
.catch((error) => reject(error)); | ||
}); | ||
}); | ||
} | ||
|
||
module.exports = async () => { | ||
await copyDirectory(sourceDir, targetDir); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.