Skip to content

Commit 99af64c

Browse files
committed
upload files > 10 MB in chunks
1 parent e3d2dfb commit 99af64c

File tree

7 files changed

+142
-14
lines changed

7 files changed

+142
-14
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ All notable changes starting with v0.1.34 to this project will be documented in
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
# v0.1.55 (2022-01-29
8+
# v0.1.56 (2022-02-07)
9+
- **changed:** increase default bundle size to 100 MB to reduce number of tx and use `chunk/` endpoint instead of `tx/` endpoint used for bundles 10 MB or less.
10+
- **changed:** post transactions in chunks when uploading files greater than 10 MB to allow individual files of unlimited size to be uploaded.
11+
- **added:** include [upload_files](examples/upload_files.rs) example.
12+
13+
# v0.1.55 (2022-01-29)
914
- **added:** include `write-metaplex-items` in `upload-nfts` command.
1015

1116
# v0.1.54 (2022-01-18)

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "arloader"
33
authors = ["calebeverett <[email protected]>"]
44
description = "Command line application and library for uploading files to Arweave."
5-
version = "0.1.55"
5+
version = "0.1.56"
66
edition = "2021"
77
license = "Apache-2.0"
88
repository = "https://github.com/CalebEverett/arloader"

README.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ Upload gigabytes of files with one command. Files are read and posted to [arweav
2020
* [Pricing Comparison](#pricing-comparison)
2121
* [Roadmap](#roadmap)
2222
* [Transactions Prior to v0.1.51](#potential-issue-with-transactions-uploaded-prior-to-version-0.1.51)
23-
* [Tokens of Appreciation](#tokens-of-appreciation)
2423

2524
## Installation
2625

@@ -40,7 +39,7 @@ cargo install arloader
4039
4. If you're going to use SOL, get a [Solana wallet](https://docs.solana.com/wallet-guide/cli) json file and transfer some SOL to it.
4140

4241
## NFT Usage
43-
The single `upload-nfts` command below assumes you have a pair of image and metadata files for each of your NFTs. To learn more about each of the steps to upload your files, check out [upload_nfts_steps](docs/upload_nfts_steps.md). See [multiple_asset_files](docs/multiple_asset_files.md) for an example of how to upload multiple media files.ok
42+
The single `upload-nfts` command below assumes you have a pair of image and metadata files for each of your NFTs. To learn more about each of the steps to upload your files, check out [upload_nfts_steps](docs/upload_nfts_steps.md). See [multiple_asset_files](docs/multiple_asset_files.md) for an example of how to upload multiple media files.
4443

4544
### Create Upload Folder
4645
Put your assets and associated metadata files with `.json` extension in a folder by themselves. You can use any kind of file you want. Arloader automatically adds a content type tag to your upload so that browsers will handle it correctly when accessed from Arweave.
@@ -179,7 +178,7 @@ Updating metadata manifest status...
179178

180179
If you're uploading more than one file, you should pretty much always be using bundles. Bundles take multiple files and packages them together in a single transaction. This is better than uploading multiple individual files because you only have to wait for one transaction to be confirmed. Once the bundle transaction is confirmed, all of your files will be available. Larger transactions with larger rewards are more attractive to miners, which means a larger bundled transaction is more likely to get written quickly than a bunch of smaller individual ones.
181180

182-
Arloader will create as many bundles as necessary to upload all of your files. Your files are read asynchronously, bundled in parallel across multiple threads and then posted to [arweave.net](https://arweave.net). Arloader supports bundle sizes up to 200 MB, but the default bundle size is 10 MB, which makes it possible to post full bundle size payloads to the `/tx` endpoint instead of in 256 KB chunks to the `/chunk` endpoint. This should work fine for individual files up to 10 MB. If your files sizes are bigger than 10 MB (but smaller than 200 MB), you can specify a larger bundle size with the `--bundles-size` argument - `--bundle-size 100` to specify a size of 100 MB, for example.
181+
Arloader will create as many bundles as necessary to upload all of your files. Your files are read asynchronously, bundled in parallel across multiple threads and then posted to [arweave.net](https://arweave.net). Arloader supports bundle sizes up to 200 MB, with a default of 10 MB, which makes it possible to post full bundle size payloads to the `/tx` endpoint instead of in 256 KB chunks to the `/chunk` endpoint. This should work fine for individual files up to 100 MB. If your files sizes are bigger than 100 MB (but smaller than 200 MB), you can specify a larger bundle size with the `--bundles-size` argument - `--bundle-size 200` to specify a size of 200 MB, for example. If you file sizes are bigger than 200 MB, you can upload them as individual files by passing the `--no-bundle` flag.
183182

184183
### Estimate Cost
185184
To get an estimate of the cost of uploading your files run
@@ -197,7 +196,7 @@ To upload your files run
197196
arloader upload <FILE_PATHS>
198197
```
199198

200-
This kicks off the process of uploading a stream of bundles created from your files. The default bundle size is 10 MB. The example output below had a bundle size of 5000 bytes.
199+
This kicks off the process of uploading a stream of bundles created from your files. The example output below had a bundle size of 5000 bytes.
201200

202201
```
203202
bundle txid items KB status confirms
@@ -430,7 +429,4 @@ file size | num files | arweave | bundlr | arweave total | bundlr total | arweav
430429
- [ ] Include duration in completion output.
431430

432431
## Potential Issue with Transactions Uploaded Prior to Version 0.1.51
433-
The way arloader was formatting transactions for upload was not entirely compatible with the Arweave protocol prior to version 1.51. For transactions bigger than 256 KB it is possible that even though your transactions are visible and are showing more than 25 confirmations that they were not written to the Arweave blockchain. If you would like assistance determining whether your transactions were impacted, please open an issue and I will be happy to help, including paying for any necessary re-uploading.
434-
435-
## Tokens of Appreciation
436-
Tokens of appreciation can be sent to `F4B7659xdVcTqQEHShLsxp7w8wckMyBmT9GM8bGDqTUW`.
432+
The way arloader was formatting transactions for upload was not entirely compatible with the Arweave protocol prior to version 1.51. For transactions bigger than 256 KB it is possible that even though your transactions are visible and are showing more than 25 confirmations that they were not written to the Arweave blockchain. If you would like assistance determining whether your transactions were impacted, please open an issue and I will be happy to help, including paying for any necessary re-uploading.

examples/upload_files.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use arloader::{commands::*, error::Error, status::OutputFormat, Arweave};
2+
use rayon::prelude::*;
3+
use std::env;
4+
use std::{fs, path::PathBuf, str::FromStr, time::Instant};
5+
use tempdir::TempDir;
6+
use url::Url;
7+
8+
// For smaller sample sizes, you may have to increase this to have the transactions mined.
9+
const REWARD_MULTIPLIER: f32 = 2.0;
10+
const NUM_FILES: usize = 1;
11+
const FILE_SIZE: usize = 100_000_000;
12+
const BUFFER: usize = 5;
13+
14+
#[tokio::main]
15+
async fn main() -> CommandResult {
16+
let ar_keypair_path = env::var("AR_KEYPAIR_PATH").ok().map(PathBuf::from);
17+
let sol_keypair_path = env::var("SOL_KEYPAIR_PATH").ok().map(PathBuf::from);
18+
19+
let arweave = if let Some(ar_keypair_path) = ar_keypair_path {
20+
Arweave::from_keypair_path(
21+
ar_keypair_path,
22+
Url::from_str("https://arweave.net").unwrap(),
23+
)
24+
.await?
25+
} else {
26+
if sol_keypair_path.is_none() {
27+
println!("Example requires either AR_KEYPAIR_PATH or SOL_KEYPAIR_PATH environment variable to be set.");
28+
return Ok(());
29+
};
30+
Arweave::default()
31+
};
32+
33+
let ext = "bin";
34+
println!("Setting up files...\n");
35+
let temp_dir = files_setup(FILE_SIZE, NUM_FILES, ext)?;
36+
let paths_iter = (0..NUM_FILES).map(|i| temp_dir.path().join(format!("{}.bin", i)));
37+
// let path_chunks = arweave.chunk_file_paths(paths_iter, BUNDLE_SIZE)?;
38+
let log_dir = temp_dir.path().join("status/");
39+
fs::create_dir(log_dir.clone()).unwrap();
40+
let output_format = &OutputFormat::Display;
41+
42+
let start = Instant::now();
43+
if sol_keypair_path.is_none() {
44+
println!("Starting upload with AR...\n");
45+
command_upload(
46+
&arweave,
47+
paths_iter,
48+
Some(log_dir.clone()),
49+
None,
50+
REWARD_MULTIPLIER,
51+
output_format,
52+
BUFFER,
53+
)
54+
.await?;
55+
} else {
56+
println!("Starting upload with SOL...\n");
57+
command_upload_with_sol(
58+
&arweave,
59+
paths_iter,
60+
Some(log_dir.clone()),
61+
None,
62+
REWARD_MULTIPLIER,
63+
output_format,
64+
BUFFER,
65+
sol_keypair_path.unwrap(),
66+
)
67+
.await?;
68+
}
69+
70+
let duration = start.elapsed();
71+
72+
println!(
73+
"\n\nUpload completed in: {:?}\n\nUpdating statuses..\n\n",
74+
duration
75+
);
76+
77+
let paths_iter = (0..NUM_FILES).map(|i| temp_dir.path().join(format!("{}.bin", i)));
78+
command_update_statuses(&arweave, paths_iter, log_dir, output_format, 10).await?;
79+
Ok(())
80+
}
81+
82+
fn files_setup(file_size: usize, num_files: usize, ext: &str) -> Result<TempDir, Error> {
83+
let bytes = vec![0; file_size];
84+
85+
let temp_dir = TempDir::new("tmp")?;
86+
87+
let _ = (0..num_files).into_par_iter().for_each(|i| {
88+
fs::write(
89+
temp_dir.path().join(format!("{}", i)).with_extension(ext),
90+
&bytes,
91+
)
92+
.unwrap();
93+
});
94+
Ok(temp_dir)
95+
}

src/commands.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub async fn command_files(paths: Option<Vec<PathBuf>>) -> CommandResult {
3232
println!("{:?}", paths);
3333
Ok(())
3434
}
35+
3536
/// Gets cost of uploading a list of files.
3637
pub async fn command_get_cost<IP>(
3738
arweave: &Arweave,
@@ -1039,4 +1040,4 @@ pub fn get_manifest_id_from_log_dir(log_dir: &PathBuf) -> String {
10391040
.nth(1)
10401041
.unwrap()
10411042
.to_string()
1042-
}
1043+
}

src/lib.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,12 @@ impl Arweave {
10421042
)
10431043
.await?;
10441044
let signed_transaction = self.sign_transaction(transaction)?;
1045-
let (id, reward) = self.post_transaction(&signed_transaction).await?;
1045+
let (id, reward) = if signed_transaction.data.0.len() > MAX_TX_DATA as usize {
1046+
self.post_transaction_chunks(signed_transaction, 100)
1047+
.await?
1048+
} else {
1049+
self.post_transaction(&signed_transaction).await?
1050+
};
10461051

10471052
let status = Status {
10481053
id,
@@ -1099,7 +1104,12 @@ impl Arweave {
10991104
.sign_transaction_with_sol(transaction, solana_url, sol_ar_url, from_keypair)
11001105
.await?;
11011106

1102-
let (id, reward) = self.post_transaction(&signed_transaction).await?;
1107+
let (id, reward) = if signed_transaction.data.0.len() > MAX_TX_DATA as usize {
1108+
self.post_transaction_chunks(signed_transaction, 100)
1109+
.await?
1110+
} else {
1111+
self.post_transaction(&signed_transaction).await?
1112+
};
11031113

11041114
let mut status = Status {
11051115
file_path: Some(file_path),

src/main.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ fn bundle_size_arg<'a, 'b>() -> Arg<'a, 'b> {
682682
.value_name("BUNDLE_SIZE")
683683
.takes_value(true)
684684
.validator(is_valid_bundle_size)
685-
.default_value("10")
685+
.default_value("100")
686686
.help("Specify the bundle size in megabytes.")
687687
}
688688

@@ -955,6 +955,27 @@ fn is_json_file_path(path_str: String) -> Result<(), String> {
955955
}
956956
}
957957

958+
// fn file_sizes_less_than_bundle_size(paths_vec: Vec<PathBuf>, bundle_size: u64) -> bool {
959+
// if paths_vec.iter().any(|p| {
960+
// let p_len = p.metadata().unwrap().len();
961+
// if p_len > bundle_size {
962+
// println!(
963+
// "{} is {} MB, which is greater than the bundle size of {}. Bundle size must be greater than file.",
964+
// p.display(),
965+
// p_len / 1_000_000,
966+
// bundle_size
967+
// );
968+
// true
969+
// } else {
970+
// false
971+
// }
972+
// }) {
973+
// false
974+
// } else {
975+
// true
976+
// }
977+
// }
978+
958979
// ====================
959980
// Helpers
960981
// ====================

0 commit comments

Comments
 (0)