Cargo registry service for deploying programs (#33570)
This commit is contained in:
parent
2d84c1dbba
commit
4c664a8d31
|
@ -2209,6 +2209,21 @@ version = "0.27.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
|
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "git2"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.3.3",
|
||||||
|
"libc",
|
||||||
|
"libgit2-sys",
|
||||||
|
"log",
|
||||||
|
"openssl-probe",
|
||||||
|
"openssl-sys",
|
||||||
|
"url 2.4.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -2447,6 +2462,12 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-range"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -2521,6 +2542,25 @@ dependencies = [
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-staticfile"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "318ca89e4827e7fe4ddd2824f52337239796ae8ecc761a663324407dc3d8d7e7"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-range",
|
||||||
|
"httpdate",
|
||||||
|
"hyper",
|
||||||
|
"mime_guess",
|
||||||
|
"percent-encoding 2.3.0",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"tokio",
|
||||||
|
"url 2.4.1",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-timeout"
|
name = "hyper-timeout"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -2872,6 +2912,20 @@ version = "0.2.148"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libgit2-sys"
|
||||||
|
version = "0.16.1+1.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"libssh2-sys",
|
||||||
|
"libz-sys",
|
||||||
|
"openssl-sys",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
|
@ -2951,6 +3005,20 @@ dependencies = [
|
||||||
"libsecp256k1-core",
|
"libsecp256k1-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libssh2-sys"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"libz-sys",
|
||||||
|
"openssl-sys",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libz-sys"
|
name = "libz-sys"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
@ -2958,6 +3026,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
|
checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
|
"libc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
@ -3113,6 +3182,16 @@ version = "0.3.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mime_guess"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
|
||||||
|
dependencies = [
|
||||||
|
"mime",
|
||||||
|
"unicase",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "min-max-heap"
|
name = "min-max-heap"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
@ -3939,7 +4018,7 @@ version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
|
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"toml",
|
"toml 0.5.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3949,7 +4028,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
|
checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml",
|
"toml 0.5.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4784,6 +4863,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -5471,6 +5559,35 @@ dependencies = [
|
||||||
"tar",
|
"tar",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "solana-cargo-registry"
|
||||||
|
version = "1.18.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap 2.33.3",
|
||||||
|
"flate2",
|
||||||
|
"git2",
|
||||||
|
"hyper",
|
||||||
|
"hyper-staticfile",
|
||||||
|
"log",
|
||||||
|
"rustc_version 0.4.0",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"solana-clap-utils",
|
||||||
|
"solana-cli",
|
||||||
|
"solana-cli-config",
|
||||||
|
"solana-cli-output",
|
||||||
|
"solana-logger",
|
||||||
|
"solana-remote-wallet",
|
||||||
|
"solana-rpc-client",
|
||||||
|
"solana-rpc-client-api",
|
||||||
|
"solana-sdk",
|
||||||
|
"solana-version",
|
||||||
|
"tar",
|
||||||
|
"tempfile",
|
||||||
|
"tokio",
|
||||||
|
"toml 0.8.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "solana-cargo-test-bpf"
|
name = "solana-cargo-test-bpf"
|
||||||
version = "1.18.0"
|
version = "1.18.0"
|
||||||
|
@ -8371,6 +8488,40 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap 2.0.2",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tonic"
|
name = "tonic"
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
|
@ -9057,6 +9208,15 @@ version = "0.48.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.5.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "037711d82167854aff2018dfd193aa0fef5370f456732f0d5a0c59b0f1b4b907"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.50.0"
|
version = "0.50.0"
|
||||||
|
|
|
@ -17,6 +17,7 @@ members = [
|
||||||
"bench-tps",
|
"bench-tps",
|
||||||
"bloom",
|
"bloom",
|
||||||
"bucket_map",
|
"bucket_map",
|
||||||
|
"cargo-registry",
|
||||||
"clap-utils",
|
"clap-utils",
|
||||||
"clap-v3-utils",
|
"clap-v3-utils",
|
||||||
"cli",
|
"cli",
|
||||||
|
@ -205,6 +206,7 @@ gag = "1.0.0"
|
||||||
generic-array = { version = "0.14.7", default-features = false }
|
generic-array = { version = "0.14.7", default-features = false }
|
||||||
gethostname = "0.2.3"
|
gethostname = "0.2.3"
|
||||||
getrandom = "0.2.10"
|
getrandom = "0.2.10"
|
||||||
|
git2 = "0.18.1"
|
||||||
goauth = "0.13.1"
|
goauth = "0.13.1"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
hidapi = { version = "2.4.1", default-features = false }
|
hidapi = { version = "2.4.1", default-features = false }
|
||||||
|
@ -214,6 +216,7 @@ http = "0.2.9"
|
||||||
humantime = "2.0.1"
|
humantime = "2.0.1"
|
||||||
hyper = "0.14.27"
|
hyper = "0.14.27"
|
||||||
hyper-proxy = "0.9.1"
|
hyper-proxy = "0.9.1"
|
||||||
|
hyper-staticfile = "0.9.5"
|
||||||
im = "15.1.0"
|
im = "15.1.0"
|
||||||
index_list = "0.2.7"
|
index_list = "0.2.7"
|
||||||
indexmap = "2.0.2"
|
indexmap = "2.0.2"
|
||||||
|
@ -311,6 +314,7 @@ solana-bench-tps = { path = "bench-tps", version = "=1.18.0" }
|
||||||
solana-bloom = { path = "bloom", version = "=1.18.0" }
|
solana-bloom = { path = "bloom", version = "=1.18.0" }
|
||||||
solana-bpf-loader-program = { path = "programs/bpf_loader", version = "=1.18.0" }
|
solana-bpf-loader-program = { path = "programs/bpf_loader", version = "=1.18.0" }
|
||||||
solana-bucket-map = { path = "bucket_map", version = "=1.18.0" }
|
solana-bucket-map = { path = "bucket_map", version = "=1.18.0" }
|
||||||
|
solana-cargo-registry = { path = "cargo-registry", version = "=1.18.0" }
|
||||||
solana-connection-cache = { path = "connection-cache", version = "=1.18.0", default-features = false }
|
solana-connection-cache = { path = "connection-cache", version = "=1.18.0", default-features = false }
|
||||||
solana-clap-utils = { path = "clap-utils", version = "=1.18.0" }
|
solana-clap-utils = { path = "clap-utils", version = "=1.18.0" }
|
||||||
solana-clap-v3-utils = { path = "clap-v3-utils", version = "=1.18.0" }
|
solana-clap-v3-utils = { path = "clap-v3-utils", version = "=1.18.0" }
|
||||||
|
@ -405,6 +409,7 @@ tokio-serde = "0.8"
|
||||||
tokio-stream = "0.1.14"
|
tokio-stream = "0.1.14"
|
||||||
tokio-tungstenite = "0.20.1"
|
tokio-tungstenite = "0.20.1"
|
||||||
tokio-util = "0.6"
|
tokio-util = "0.6"
|
||||||
|
toml = "0.8.0"
|
||||||
tonic = "0.9.2"
|
tonic = "0.9.2"
|
||||||
tonic-build = "0.9.2"
|
tonic-build = "0.9.2"
|
||||||
trees = "0.4.2"
|
trees = "0.4.2"
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
[package]
|
||||||
|
name = "solana-cargo-registry"
|
||||||
|
description = "Solana cargo registry"
|
||||||
|
documentation = "https://docs.rs/solana-cargo-registry"
|
||||||
|
version = { workspace = true }
|
||||||
|
authors = { workspace = true }
|
||||||
|
repository = { workspace = true }
|
||||||
|
homepage = { workspace = true }
|
||||||
|
license = { workspace = true }
|
||||||
|
edition = { workspace = true }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { workspace = true }
|
||||||
|
flate2 = { workspace = true }
|
||||||
|
git2 = { workspace = true }
|
||||||
|
hyper = { workspace = true, features = ["full"] }
|
||||||
|
hyper-staticfile = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
solana-clap-utils = { workspace = true }
|
||||||
|
solana-cli = { workspace = true }
|
||||||
|
solana-cli-config = { workspace = true }
|
||||||
|
solana-cli-output = { workspace = true }
|
||||||
|
solana-logger = { workspace = true }
|
||||||
|
solana-remote-wallet = { workspace = true, features = ["default"] }
|
||||||
|
solana-rpc-client = { workspace = true, features = ["default"] }
|
||||||
|
solana-rpc-client-api = { workspace = true }
|
||||||
|
solana-sdk = { workspace = true }
|
||||||
|
solana-version = { workspace = true }
|
||||||
|
tar = { workspace = true }
|
||||||
|
tempfile = { workspace = true }
|
||||||
|
tokio = { workspace = true, features = ["full"] }
|
||||||
|
toml = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
targets = ["x86_64-unknown-linux-gnu"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
rustc_version = { workspace = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dev-context-only-utils = []
|
|
@ -0,0 +1,209 @@
|
||||||
|
use {
|
||||||
|
clap::{crate_description, crate_name, value_t_or_exit, App, Arg, ArgMatches},
|
||||||
|
solana_clap_utils::{
|
||||||
|
hidden_unless_forced,
|
||||||
|
input_validators::is_url_or_moniker,
|
||||||
|
keypair::{DefaultSigner, SignerIndex},
|
||||||
|
},
|
||||||
|
solana_cli::{
|
||||||
|
cli::{DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS, DEFAULT_RPC_TIMEOUT_SECONDS},
|
||||||
|
program_v4::ProgramV4CommandConfig,
|
||||||
|
},
|
||||||
|
solana_cli_config::{Config, ConfigInput},
|
||||||
|
solana_cli_output::OutputFormat,
|
||||||
|
solana_rpc_client::rpc_client::RpcClient,
|
||||||
|
solana_sdk::{
|
||||||
|
commitment_config,
|
||||||
|
signature::{read_keypair_file, Keypair},
|
||||||
|
},
|
||||||
|
std::{error, sync::Arc, time::Duration},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ClientConfig<'a>(pub ProgramV4CommandConfig<'a>);
|
||||||
|
|
||||||
|
impl<'a> ClientConfig<'a> {
|
||||||
|
pub fn new(client: &'a Client) -> Self {
|
||||||
|
Self(ProgramV4CommandConfig {
|
||||||
|
websocket_url: &client.websocket_url,
|
||||||
|
commitment: client.commitment,
|
||||||
|
payer: &client.cli_signers[0],
|
||||||
|
authority: &client.cli_signers[client.authority_signer_index],
|
||||||
|
output_format: &OutputFormat::Display,
|
||||||
|
use_quic: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Client {
|
||||||
|
pub rpc_client: Arc<RpcClient>,
|
||||||
|
pub port: u16,
|
||||||
|
websocket_url: String,
|
||||||
|
commitment: commitment_config::CommitmentConfig,
|
||||||
|
cli_signers: Vec<Keypair>,
|
||||||
|
authority_signer_index: SignerIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
fn get_keypair(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
config_path: &str,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<Keypair, Box<dyn error::Error>> {
|
||||||
|
let (_, default_signer_path) = ConfigInput::compute_keypair_path_setting(
|
||||||
|
matches.value_of(name).unwrap_or(""),
|
||||||
|
config_path,
|
||||||
|
);
|
||||||
|
|
||||||
|
let default_signer = DefaultSigner::new(name, default_signer_path);
|
||||||
|
|
||||||
|
read_keypair_file(default_signer.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_clap_app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, 'v> {
|
||||||
|
App::new(name)
|
||||||
|
.about(about)
|
||||||
|
.version(version)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("config_file")
|
||||||
|
.short("C")
|
||||||
|
.long("config")
|
||||||
|
.value_name("FILEPATH")
|
||||||
|
.takes_value(true)
|
||||||
|
.global(true)
|
||||||
|
.help("Configuration file to use"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("json_rpc_url")
|
||||||
|
.short("u")
|
||||||
|
.long("url")
|
||||||
|
.value_name("URL_OR_MONIKER")
|
||||||
|
.takes_value(true)
|
||||||
|
.global(true)
|
||||||
|
.validator(is_url_or_moniker)
|
||||||
|
.help(
|
||||||
|
"URL for Solana's JSON RPC or moniker (or their first letter): \
|
||||||
|
[mainnet-beta, testnet, devnet, localhost]",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("keypair")
|
||||||
|
.short("k")
|
||||||
|
.long("keypair")
|
||||||
|
.value_name("KEYPAIR")
|
||||||
|
.global(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Filepath or URL to a keypair"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("authority")
|
||||||
|
.short("a")
|
||||||
|
.long("authority")
|
||||||
|
.value_name("KEYPAIR")
|
||||||
|
.global(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Filepath or URL to program authority keypair"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("port")
|
||||||
|
.short("p")
|
||||||
|
.long("port")
|
||||||
|
.value_name("PORT")
|
||||||
|
.global(true)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Cargo registry's local TCP port. The server will bind to this port and wait for requests."),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("commitment")
|
||||||
|
.long("commitment")
|
||||||
|
.takes_value(true)
|
||||||
|
.possible_values(&[
|
||||||
|
"processed",
|
||||||
|
"confirmed",
|
||||||
|
"finalized",
|
||||||
|
])
|
||||||
|
.value_name("COMMITMENT_LEVEL")
|
||||||
|
.hide_possible_values(true)
|
||||||
|
.global(true)
|
||||||
|
.help("Return information at the selected commitment level [possible values: processed, confirmed, finalized]"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("rpc_timeout")
|
||||||
|
.long("rpc-timeout")
|
||||||
|
.value_name("SECONDS")
|
||||||
|
.takes_value(true)
|
||||||
|
.default_value(DEFAULT_RPC_TIMEOUT_SECONDS)
|
||||||
|
.global(true)
|
||||||
|
.hidden(hidden_unless_forced())
|
||||||
|
.help("Timeout value for RPC requests"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("confirm_transaction_initial_timeout")
|
||||||
|
.long("confirm-timeout")
|
||||||
|
.value_name("SECONDS")
|
||||||
|
.takes_value(true)
|
||||||
|
.default_value(DEFAULT_CONFIRM_TX_TIMEOUT_SECONDS)
|
||||||
|
.global(true)
|
||||||
|
.hidden(hidden_unless_forced())
|
||||||
|
.help("Timeout value for initial transaction status"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Result<Client, Box<dyn error::Error>> {
|
||||||
|
let matches = Self::get_clap_app(
|
||||||
|
crate_name!(),
|
||||||
|
crate_description!(),
|
||||||
|
solana_version::version!(),
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let config = if let Some(config_file) = matches.value_of("config_file") {
|
||||||
|
Config::load(config_file).unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
Config::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_, json_rpc_url) = ConfigInput::compute_json_rpc_url_setting(
|
||||||
|
matches.value_of("json_rpc_url").unwrap_or(""),
|
||||||
|
&config.json_rpc_url,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (_, websocket_url) = ConfigInput::compute_websocket_url_setting(
|
||||||
|
matches.value_of("websocket_url").unwrap_or(""),
|
||||||
|
&config.websocket_url,
|
||||||
|
matches.value_of("json_rpc_url").unwrap_or(""),
|
||||||
|
&config.json_rpc_url,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (_, commitment) = ConfigInput::compute_commitment_config(
|
||||||
|
matches.value_of("commitment").unwrap_or(""),
|
||||||
|
&config.commitment,
|
||||||
|
);
|
||||||
|
|
||||||
|
let rpc_timeout = value_t_or_exit!(matches, "rpc_timeout", u64);
|
||||||
|
let rpc_timeout = Duration::from_secs(rpc_timeout);
|
||||||
|
|
||||||
|
let confirm_transaction_initial_timeout =
|
||||||
|
value_t_or_exit!(matches, "confirm_transaction_initial_timeout", u64);
|
||||||
|
let confirm_transaction_initial_timeout =
|
||||||
|
Duration::from_secs(confirm_transaction_initial_timeout);
|
||||||
|
|
||||||
|
let payer_keypair = Self::get_keypair(&matches, &config.keypair_path, "keypair")?;
|
||||||
|
let authority_keypair = Self::get_keypair(&matches, &config.keypair_path, "authority")?;
|
||||||
|
|
||||||
|
let port = value_t_or_exit!(matches, "port", u16);
|
||||||
|
|
||||||
|
Ok(Client {
|
||||||
|
rpc_client: Arc::new(RpcClient::new_with_timeouts_and_commitment(
|
||||||
|
json_rpc_url.to_string(),
|
||||||
|
rpc_timeout,
|
||||||
|
commitment,
|
||||||
|
confirm_transaction_initial_timeout,
|
||||||
|
)),
|
||||||
|
port,
|
||||||
|
websocket_url,
|
||||||
|
commitment,
|
||||||
|
cli_signers: vec![payer_keypair, authority_keypair],
|
||||||
|
authority_signer_index: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
use {
|
||||||
|
git2::{IndexAddOption, Repository},
|
||||||
|
serde::{Deserialize, Serialize},
|
||||||
|
std::{
|
||||||
|
fs::{self, create_dir_all},
|
||||||
|
io::ErrorKind,
|
||||||
|
net::SocketAddr,
|
||||||
|
path::PathBuf,
|
||||||
|
process::Command,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||||
|
struct RegistryConfig {
|
||||||
|
dl: String,
|
||||||
|
api: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DummyGitIndex {}
|
||||||
|
|
||||||
|
impl DummyGitIndex {
|
||||||
|
pub fn create_or_update_git_repo(root_dir: PathBuf, server_addr: &SocketAddr) {
|
||||||
|
create_dir_all(&root_dir).expect("Failed to create root directory");
|
||||||
|
|
||||||
|
let expected_config = serde_json::to_string(&RegistryConfig {
|
||||||
|
dl: format!(
|
||||||
|
"http://{}/api/v1/crates/{{crate}}/{{version}}/download",
|
||||||
|
server_addr
|
||||||
|
),
|
||||||
|
api: Some(format!("http://{}", server_addr)),
|
||||||
|
})
|
||||||
|
.expect("Failed to create expected config");
|
||||||
|
|
||||||
|
let config_path = root_dir.join("config.json");
|
||||||
|
let config_written = if let Ok(config) = fs::read_to_string(&config_path) {
|
||||||
|
if config != expected_config {
|
||||||
|
fs::write(config_path, expected_config).expect("Failed to update config");
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fs::write(config_path, expected_config).expect("Failed to write config");
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::symlink;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::os::windows::fs::symlink_dir as symlink;
|
||||||
|
|
||||||
|
let new_symlink = match symlink(".", root_dir.join("index")) {
|
||||||
|
Ok(()) => true,
|
||||||
|
Err(ref err) if err.kind() == ErrorKind::AlreadyExists => false,
|
||||||
|
Err(err) => panic!("Failed to create a symlink: {}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_git_symlink = match symlink(".git", root_dir.join("git")) {
|
||||||
|
Ok(()) => true,
|
||||||
|
Err(ref err) if err.kind() == ErrorKind::AlreadyExists => false,
|
||||||
|
Err(err) => panic!("Failed to create git symlink: {}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let repository = Repository::init(&root_dir).expect("Failed to GIT init");
|
||||||
|
|
||||||
|
let empty = repository
|
||||||
|
.is_empty()
|
||||||
|
.expect("Failed to check if GIT repo is empty");
|
||||||
|
|
||||||
|
if empty || config_written || new_symlink || new_git_symlink {
|
||||||
|
let mut index = repository.index().expect("cannot get the Index file");
|
||||||
|
index
|
||||||
|
.add_all(["*"].iter(), IndexAddOption::DEFAULT, None)
|
||||||
|
.expect("Failed to add modified files to git index");
|
||||||
|
index.write().expect("Failed to update the git index");
|
||||||
|
|
||||||
|
let tree = index
|
||||||
|
.write_tree()
|
||||||
|
.and_then(|tree_id| repository.find_tree(tree_id))
|
||||||
|
.expect("Failed to get tree");
|
||||||
|
|
||||||
|
let signature = repository.signature().expect("Failed to get signature");
|
||||||
|
|
||||||
|
if empty {
|
||||||
|
repository.commit(
|
||||||
|
Some("HEAD"),
|
||||||
|
&signature,
|
||||||
|
&signature,
|
||||||
|
"Created new repo",
|
||||||
|
&tree,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let oid = repository
|
||||||
|
.refname_to_id("HEAD")
|
||||||
|
.expect("Failed to get HEAD ref");
|
||||||
|
let parent = repository
|
||||||
|
.find_commit(oid)
|
||||||
|
.expect("Failed to find parent commit");
|
||||||
|
|
||||||
|
repository.commit(
|
||||||
|
Some("HEAD"),
|
||||||
|
&signature,
|
||||||
|
&signature,
|
||||||
|
"Updated GIT repo",
|
||||||
|
&tree,
|
||||||
|
&[&parent],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.expect("Failed to commit the changes");
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::new("git")
|
||||||
|
.current_dir(&root_dir)
|
||||||
|
.arg("update-server-info")
|
||||||
|
.status()
|
||||||
|
.expect("git update-server-info failed");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,327 @@
|
||||||
|
//! The `registry_service` module implements the Solana cargo registry service.
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::Client,
|
||||||
|
dummy_git_index::DummyGitIndex,
|
||||||
|
publisher::{Error, Publisher},
|
||||||
|
},
|
||||||
|
hyper::{
|
||||||
|
body,
|
||||||
|
service::{make_service_fn, service_fn},
|
||||||
|
Method, Server,
|
||||||
|
},
|
||||||
|
hyper_staticfile::Static,
|
||||||
|
log::*,
|
||||||
|
std::{
|
||||||
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
|
path::PathBuf,
|
||||||
|
sync::Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod client;
|
||||||
|
mod dummy_git_index;
|
||||||
|
mod publisher;
|
||||||
|
|
||||||
|
const PATH_PREFIX: &str = "/api/v1/crates";
|
||||||
|
|
||||||
|
pub struct CargoRegistryService {}
|
||||||
|
|
||||||
|
impl CargoRegistryService {
|
||||||
|
fn error_response(status: hyper::StatusCode, msg: &str) -> hyper::Response<hyper::Body> {
|
||||||
|
error!("{}", msg);
|
||||||
|
hyper::Response::builder()
|
||||||
|
.status(status)
|
||||||
|
.body(hyper::Body::from(
|
||||||
|
serde_json::json!({
|
||||||
|
"errors" : [
|
||||||
|
{"details": msg}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn success_response() -> hyper::Response<hyper::Body> {
|
||||||
|
hyper::Response::builder()
|
||||||
|
.status(hyper::StatusCode::OK)
|
||||||
|
.body(hyper::Body::from(""))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_publish_request(
|
||||||
|
request: hyper::Request<hyper::Body>,
|
||||||
|
client: Arc<Client>,
|
||||||
|
) -> hyper::Response<hyper::Body> {
|
||||||
|
info!("Handling request to publish the crate");
|
||||||
|
let bytes = body::to_bytes(request.into_body()).await;
|
||||||
|
|
||||||
|
match bytes {
|
||||||
|
Ok(data) => {
|
||||||
|
let Ok(result) =
|
||||||
|
tokio::task::spawn_blocking(move || Publisher::publish_crate(data, client))
|
||||||
|
.await
|
||||||
|
else {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"Internal error. Failed to wait for program deployment",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if result.is_ok() {
|
||||||
|
info!("Published the crate successfully. {:?}", result);
|
||||||
|
Self::success_response()
|
||||||
|
} else {
|
||||||
|
Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
format!("Failed to publish the crate. {:?}", result).as_str(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Failed to receive the crate data from the client.",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_crate_name_and_version(path: &str) -> Option<(&str, &str, &str)> {
|
||||||
|
path.rsplit_once('/').and_then(|(remainder, version)| {
|
||||||
|
remainder
|
||||||
|
.rsplit_once('/')
|
||||||
|
.map(|(remainder, name)| (remainder, name, version))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_yank_request(
|
||||||
|
path: &str,
|
||||||
|
_request: &hyper::Request<hyper::Body>,
|
||||||
|
) -> hyper::Response<hyper::Body> {
|
||||||
|
let Some((path, _crate_name, _version)) = Self::get_crate_name_and_version(path) else {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Failed to parse the request.",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.len() != PATH_PREFIX.len() {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Request length is incorrect",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::error_response(
|
||||||
|
hyper::StatusCode::NOT_IMPLEMENTED,
|
||||||
|
"This command is not implemented yet",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_unyank_request(
|
||||||
|
path: &str,
|
||||||
|
_request: &hyper::Request<hyper::Body>,
|
||||||
|
) -> hyper::Response<hyper::Body> {
|
||||||
|
let Some((path, _crate_name, _version)) = Self::get_crate_name_and_version(path) else {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Failed to parse the request.",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.len() != PATH_PREFIX.len() {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Request length is incorrect",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::error_response(
|
||||||
|
hyper::StatusCode::NOT_IMPLEMENTED,
|
||||||
|
"This command is not implemented yet",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_crate_name(path: &str) -> Option<(&str, &str)> {
|
||||||
|
path.rsplit_once('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_get_owners_request(
|
||||||
|
path: &str,
|
||||||
|
_request: &hyper::Request<hyper::Body>,
|
||||||
|
) -> hyper::Response<hyper::Body> {
|
||||||
|
let Some((path, _crate_name)) = Self::get_crate_name(path) else {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Failed to parse the request.",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.len() != PATH_PREFIX.len() {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Request length is incorrect",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::error_response(
|
||||||
|
hyper::StatusCode::NOT_IMPLEMENTED,
|
||||||
|
"This command is not implemented yet",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_add_owners_request(
|
||||||
|
path: &str,
|
||||||
|
_request: &hyper::Request<hyper::Body>,
|
||||||
|
) -> hyper::Response<hyper::Body> {
|
||||||
|
let Some((path, _crate_name)) = Self::get_crate_name(path) else {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Failed to parse the request.",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.len() != PATH_PREFIX.len() {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Request length is incorrect",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::error_response(
|
||||||
|
hyper::StatusCode::NOT_IMPLEMENTED,
|
||||||
|
"This command is not implemented yet",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_delete_owners_request(
|
||||||
|
path: &str,
|
||||||
|
_request: &hyper::Request<hyper::Body>,
|
||||||
|
) -> hyper::Response<hyper::Body> {
|
||||||
|
let Some((path, _crate_name)) = Self::get_crate_name(path) else {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Failed to parse the request.",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.len() != PATH_PREFIX.len() {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Request length is incorrect",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::error_response(
|
||||||
|
hyper::StatusCode::NOT_IMPLEMENTED,
|
||||||
|
"This command is not implemented yet",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_get_crates_request(
|
||||||
|
path: &str,
|
||||||
|
_request: &hyper::Request<hyper::Body>,
|
||||||
|
) -> hyper::Response<hyper::Body> {
|
||||||
|
// The endpoint for this type of request is `/api/v1/crates` (same as PATH_PREFIX).
|
||||||
|
// The `crates` substring has already been extracted out of the endpoint string.
|
||||||
|
// So the path should only contain `/api/v1". The caller already checked that the
|
||||||
|
// full path started with PATH_PREFIX. So it's sufficient to check that provided
|
||||||
|
// path is smaller than PATH_PREFIX.
|
||||||
|
if path.len() >= PATH_PREFIX.len() {
|
||||||
|
return Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Request length is incorrect",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::error_response(
|
||||||
|
hyper::StatusCode::NOT_IMPLEMENTED,
|
||||||
|
"This command is not implemented yet",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handler(
|
||||||
|
request: hyper::Request<hyper::Body>,
|
||||||
|
client: Arc<Client>,
|
||||||
|
) -> Result<hyper::Response<hyper::Body>, Error> {
|
||||||
|
let path = request.uri().path();
|
||||||
|
if path.starts_with("/git") {
|
||||||
|
return Static::new("/tmp/dummy-git")
|
||||||
|
.serve(request)
|
||||||
|
.await
|
||||||
|
.or_else(|_| {
|
||||||
|
Ok(Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Failed to serve git index",
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if !path.starts_with(PATH_PREFIX) {
|
||||||
|
return Ok(Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Invalid path for the request",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some((path, endpoint)) = path.rsplit_once('/') else {
|
||||||
|
return Ok(Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Invalid endpoint in the path",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(match *request.method() {
|
||||||
|
Method::PUT => match endpoint {
|
||||||
|
"new" => {
|
||||||
|
if path.len() != PATH_PREFIX.len() {
|
||||||
|
Self::error_response(
|
||||||
|
hyper::StatusCode::BAD_REQUEST,
|
||||||
|
"Invalid length of the request.",
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Self::handle_publish_request(request, client.clone()).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"unyank" => Self::handle_unyank_request(path, &request),
|
||||||
|
"owners" => Self::handle_add_owners_request(path, &request),
|
||||||
|
_ => Self::error_response(hyper::StatusCode::METHOD_NOT_ALLOWED, "Unknown request"),
|
||||||
|
},
|
||||||
|
Method::GET => match endpoint {
|
||||||
|
"crates" => Self::handle_get_crates_request(path, &request),
|
||||||
|
"owners" => Self::handle_get_owners_request(path, &request),
|
||||||
|
_ => Self::error_response(hyper::StatusCode::METHOD_NOT_ALLOWED, "Unknown request"),
|
||||||
|
},
|
||||||
|
Method::DELETE => match endpoint {
|
||||||
|
"yank" => Self::handle_yank_request(path, &request),
|
||||||
|
"owners" => Self::handle_delete_owners_request(path, &request),
|
||||||
|
_ => Self::error_response(hyper::StatusCode::METHOD_NOT_ALLOWED, "Unknown request"),
|
||||||
|
},
|
||||||
|
_ => Self::error_response(hyper::StatusCode::METHOD_NOT_ALLOWED, "Unknown request"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
solana_logger::setup_with_default("solana=info");
|
||||||
|
let client = Arc::new(Client::new().expect("Failed to get RPC Client instance"));
|
||||||
|
let port = client.port;
|
||||||
|
|
||||||
|
let registry_service = make_service_fn(move |_| {
|
||||||
|
let client_inner = client.clone();
|
||||||
|
async move {
|
||||||
|
Ok::<_, Error>(service_fn(move |request| {
|
||||||
|
CargoRegistryService::handler(request, client_inner.clone())
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port);
|
||||||
|
DummyGitIndex::create_or_update_git_repo(PathBuf::from("/tmp/dummy-git"), &addr);
|
||||||
|
|
||||||
|
let server = Server::bind(&addr).serve(registry_service);
|
||||||
|
info!("Server running on on http://{}", addr);
|
||||||
|
|
||||||
|
let _ = server.await;
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
use {
|
||||||
|
crate::client::{Client, ClientConfig},
|
||||||
|
flate2::read::GzDecoder,
|
||||||
|
hyper::body::Bytes,
|
||||||
|
log::*,
|
||||||
|
serde::{Deserialize, Serialize},
|
||||||
|
serde_json::from_slice,
|
||||||
|
solana_cli::program_v4::{process_deploy_program, read_and_verify_elf},
|
||||||
|
solana_sdk::{
|
||||||
|
signature::{Keypair, Signer},
|
||||||
|
signer::EncodableKey,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
fs,
|
||||||
|
mem::size_of,
|
||||||
|
ops::Deref,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
},
|
||||||
|
tar::Archive,
|
||||||
|
tempfile::{tempdir, TempDir},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
enum DependencyType {
|
||||||
|
Dev,
|
||||||
|
Build,
|
||||||
|
Normal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Dependency {
|
||||||
|
name: String,
|
||||||
|
version_req: String,
|
||||||
|
features: Vec<String>,
|
||||||
|
optional: bool,
|
||||||
|
default_features: bool,
|
||||||
|
target: Option<String>,
|
||||||
|
kind: DependencyType,
|
||||||
|
registry: Option<String>,
|
||||||
|
explicit_name_in_toml: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[allow(unused)]
|
||||||
|
struct PackageMetaData {
|
||||||
|
name: String,
|
||||||
|
vers: String,
|
||||||
|
deps: Vec<Dependency>,
|
||||||
|
features: BTreeMap<String, Vec<String>>,
|
||||||
|
authors: Vec<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
documentation: Option<String>,
|
||||||
|
homepage: Option<String>,
|
||||||
|
readme: Option<String>,
|
||||||
|
readme_file: Option<String>,
|
||||||
|
keywords: Vec<String>,
|
||||||
|
categories: Vec<String>,
|
||||||
|
license: Option<String>,
|
||||||
|
license_file: Option<String>,
|
||||||
|
repository: Option<String>,
|
||||||
|
badges: BTreeMap<String, BTreeMap<String, String>>,
|
||||||
|
links: Option<String>,
|
||||||
|
rust_version: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackageMetaData {
|
||||||
|
fn new(bytes: &Bytes) -> serde_json::Result<(PackageMetaData, usize)> {
|
||||||
|
let (json_length, sizeof_length) = Self::read_u32_length(bytes)?;
|
||||||
|
let end_of_meta_data = sizeof_length.saturating_add(json_length as usize);
|
||||||
|
let json_body = bytes.slice(sizeof_length..end_of_meta_data);
|
||||||
|
from_slice::<PackageMetaData>(json_body.deref()).map(|data| (data, end_of_meta_data))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_u32_length(bytes: &Bytes) -> serde_json::Result<(u32, usize)> {
|
||||||
|
let sizeof_length = size_of::<u32>();
|
||||||
|
let length_le = bytes.slice(0..sizeof_length);
|
||||||
|
let length =
|
||||||
|
u32::from_le_bytes(length_le.deref().try_into().expect("Failed to read length"));
|
||||||
|
Ok((length, sizeof_length))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Publisher {}
|
||||||
|
|
||||||
|
impl Publisher {
|
||||||
|
fn make_path<P: AsRef<Path>>(tempdir: &TempDir, meta: &PackageMetaData, append: P) -> PathBuf {
|
||||||
|
let mut path = tempdir.path().to_path_buf();
|
||||||
|
path.push(format!("{}-{}/", meta.name, meta.vers));
|
||||||
|
path.push(append);
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_library_name(tempdir: &TempDir, meta: &PackageMetaData) -> Result<String, Error> {
|
||||||
|
let toml_content = fs::read_to_string(Self::make_path(tempdir, meta, "Cargo.toml.orig"))?;
|
||||||
|
let toml = toml_content.parse::<toml::Table>()?;
|
||||||
|
let library_name = toml
|
||||||
|
.get("lib")
|
||||||
|
.and_then(|v| v.get("name"))
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.ok_or("Failed to get module name")?;
|
||||||
|
Ok(library_name.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn publish_crate(bytes: Bytes, client: Arc<Client>) -> Result<(), Error> {
|
||||||
|
let (meta_data, offset) = PackageMetaData::new(&bytes)?;
|
||||||
|
|
||||||
|
let (_crate_file_length, length_size) =
|
||||||
|
PackageMetaData::read_u32_length(&bytes.slice(offset..))?;
|
||||||
|
let crate_bytes = bytes.slice(offset.saturating_add(length_size)..);
|
||||||
|
|
||||||
|
let decoder = GzDecoder::new(crate_bytes.as_ref());
|
||||||
|
let mut archive = Archive::new(decoder);
|
||||||
|
|
||||||
|
let tempdir = tempdir()?;
|
||||||
|
archive.unpack(tempdir.path())?;
|
||||||
|
|
||||||
|
let config = ClientConfig::new(client.as_ref());
|
||||||
|
|
||||||
|
let lib_name = Self::program_library_name(&tempdir, &meta_data)?;
|
||||||
|
|
||||||
|
let program_path = Self::make_path(&tempdir, &meta_data, format!("out/{}.so", lib_name))
|
||||||
|
.into_os_string()
|
||||||
|
.into_string()
|
||||||
|
.map_err(|_| "Failed to get program file path")?;
|
||||||
|
|
||||||
|
let program_data = read_and_verify_elf(program_path.as_ref())
|
||||||
|
.map_err(|e| format!("failed to read the program: {}", e))?;
|
||||||
|
|
||||||
|
let program_keypair = Keypair::read_from_file(Self::make_path(
|
||||||
|
&tempdir,
|
||||||
|
&meta_data,
|
||||||
|
format!("out/{}-keypair.json", lib_name),
|
||||||
|
))
|
||||||
|
.map_err(|e| format!("Failed to get keypair from the file: {}", e))?;
|
||||||
|
|
||||||
|
info!("Deploying program at {:?}", program_keypair.pubkey());
|
||||||
|
|
||||||
|
process_deploy_program(
|
||||||
|
client.rpc_client.clone(),
|
||||||
|
&config.0,
|
||||||
|
&program_data,
|
||||||
|
program_data.len() as u32,
|
||||||
|
&program_keypair.pubkey(),
|
||||||
|
Some(&program_keypair),
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("Failed to deploy the program: {}", e);
|
||||||
|
format!("Failed to deploy the program: {}", e)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
info!("Successfully deployed the program");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -311,7 +311,7 @@ pub fn parse_program_v4_subcommand(
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
pub fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||||
let mut file = File::open(program_location)
|
let mut file = File::open(program_location)
|
||||||
.map_err(|err| format!("Unable to open program file: {err}"))?;
|
.map_err(|err| format!("Unable to open program file: {err}"))?;
|
||||||
let mut program_data = Vec::new();
|
let mut program_data = Vec::new();
|
||||||
|
@ -427,7 +427,7 @@ pub fn process_program_v4_subcommand(
|
||||||
// * Redeploy a program using a buffer account
|
// * Redeploy a program using a buffer account
|
||||||
// - buffer_signer argument must contain the temporary buffer account information
|
// - buffer_signer argument must contain the temporary buffer account information
|
||||||
// (program_address must contain program ID and must NOT be same as buffer_signer.pubkey())
|
// (program_address must contain program ID and must NOT be same as buffer_signer.pubkey())
|
||||||
fn process_deploy_program(
|
pub fn process_deploy_program(
|
||||||
rpc_client: Arc<RpcClient>,
|
rpc_client: Arc<RpcClient>,
|
||||||
config: &ProgramV4CommandConfig,
|
config: &ProgramV4CommandConfig,
|
||||||
program_data: &[u8],
|
program_data: &[u8],
|
||||||
|
|
Loading…
Reference in New Issue