mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
feat: handle signed plugins
This commit is contained in:
210
Cargo.lock
generated
210
Cargo.lock
generated
@@ -184,6 +184,16 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"safemem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
@@ -268,7 +278,7 @@ dependencies = [
|
||||
"cap-primitives",
|
||||
"cap-std",
|
||||
"io-lifetimes 3.0.1",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -285,7 +295,7 @@ dependencies = [
|
||||
"maybe-owned",
|
||||
"rustix",
|
||||
"rustix-linux-procfs",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
"winx",
|
||||
]
|
||||
|
||||
@@ -450,7 +460,7 @@ version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -905,6 +915,12 @@ dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ct-codecs"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49fb0c6640b4507ebd99ff67677009e381ba5eee1d14df78de4a3d16eb123c39"
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.10.1"
|
||||
@@ -924,6 +940,32 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.3.0",
|
||||
"curve25519-dalek-derive",
|
||||
"digest 0.11.3",
|
||||
"fiat-crypto",
|
||||
"rustc_version",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "6.2.1"
|
||||
@@ -1033,6 +1075,37 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a"
|
||||
dependencies = [
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-compact"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c24599140dc39d7a81e4476e7573d41bbc18e07c803900298e522a5fbcfbfb6"
|
||||
dependencies = [
|
||||
"ct-codecs",
|
||||
"getrandom 0.4.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-dalek"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
"sha2 0.11.0",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.16.0"
|
||||
@@ -1106,7 +1179,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1312,6 +1385,20 @@ dependencies = [
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi 5.3.0",
|
||||
"wasip2",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.3"
|
||||
@@ -1319,9 +1406,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"r-efi 6.0.0",
|
||||
"rand_core",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1442,6 +1531,12 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.13.0"
|
||||
@@ -1460,6 +1555,12 @@ dependencies = [
|
||||
"digest 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac-sha256"
|
||||
version = "1.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec9d92d097f4749b64e8cc33d924d9f40a2d4eb91402b458014b781f5733d60f"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.2"
|
||||
@@ -2093,7 +2194,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2581,8 +2682,10 @@ dependencies = [
|
||||
"console-subscriber",
|
||||
"crossbeam",
|
||||
"dashmap",
|
||||
"ed25519-dalek",
|
||||
"flate2",
|
||||
"futures",
|
||||
"hex",
|
||||
"hmac",
|
||||
"hyper",
|
||||
"image",
|
||||
@@ -2623,6 +2726,9 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"ureq",
|
||||
"uuid",
|
||||
"wasm-encoder 0.255.0",
|
||||
"wasmparser 0.255.0",
|
||||
"wasmsign2",
|
||||
"wasmtime",
|
||||
"wasmtime-wasi",
|
||||
"wasmtime-wasi-http",
|
||||
@@ -2719,7 +2825,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tracing",
|
||||
"tracing-serde-structured",
|
||||
"wit-bindgen",
|
||||
"wit-bindgen 0.60.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2819,6 +2925,12 @@ version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea"
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
@@ -2828,6 +2940,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "6.0.0"
|
||||
@@ -3018,7 +3136,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3097,6 +3215,12 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a252f5e20f038fe7b4ea53e073e65398d652c864cc162fc77c56c2f13717b888"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
@@ -3331,7 +3455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3359,6 +3483,17 @@ dependencies = [
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssh-keys"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2555f9858fe3b961c98100b8be77cbd6a81527bf20d40e7a11dafb8810298e95"
|
||||
dependencies = [
|
||||
"base64 0.9.3",
|
||||
"byteorder",
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
@@ -3445,7 +3580,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3979,6 +4114,15 @@ version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.4+wasi-0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
|
||||
dependencies = [
|
||||
"wit-bindgen 0.57.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.126"
|
||||
@@ -4044,6 +4188,16 @@ dependencies = [
|
||||
"wasmparser 0.254.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.255.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b524283fb5df62eec102ed0574838961bdd7ba5ac9c50d38e2756c51c971a42"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser 0.255.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.254.0"
|
||||
@@ -4081,6 +4235,19 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.255.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8e329ef4b5d46e73b91d3ac6924417cad55a8cbbf869c199283383427c3320b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.17.1",
|
||||
"indexmap",
|
||||
"semver",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmprinter"
|
||||
version = "0.252.0"
|
||||
@@ -4092,6 +4259,21 @@ dependencies = [
|
||||
"wasmparser 0.252.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmsign2"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42e24539d1ce20d4b21bcc356382746c92d0cfa3a2b16e325f7f2d65cb1354a4"
|
||||
dependencies = [
|
||||
"ct-codecs",
|
||||
"ed25519-compact",
|
||||
"getrandom 0.3.4",
|
||||
"hmac-sha256",
|
||||
"log",
|
||||
"ssh-keys",
|
||||
"thiserror 2.0.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime"
|
||||
version = "47.0.2"
|
||||
@@ -4411,7 +4593,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4717,6 +4899,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.60.0"
|
||||
|
||||
@@ -161,6 +161,9 @@ pumpkin-world = { path = "pumpkin-world", default-features = false }
|
||||
quote = { version = "1.0", default-features = false, features = ["proc-macro"] }
|
||||
rand = { version = "0.10.2", default-features = false, features = ["std", "std_rng", "thread_rng"] }
|
||||
rsa = { version = "=0.10.0-rc.18", default-features = false, features = ["std", "encoding"] }
|
||||
ed25519-dalek = { version = "3.0", default-features = false, features = ["fast"] }
|
||||
hex = { version = "0.4", default-features = false, features = ["std"] }
|
||||
wasmsign2 = "0.2"
|
||||
cipher = { version = "0.5.2", default-features = false }
|
||||
rustc-hash = { version = "2.1.3", default-features = false, features = ["std"] }
|
||||
rustyline = { version = "18.0.1", default-features = false, features = ["with-file-history"] }
|
||||
|
||||
@@ -636,36 +636,43 @@ impl GenerationSchedule {
|
||||
swap(&mut unload_chunks, &mut self.unload_chunks);
|
||||
let mut chunks = Vec::with_capacity(unload_chunks.len());
|
||||
for pos in unload_chunks {
|
||||
let holder = self.chunk_map.get_mut(&pos).unwrap();
|
||||
let Some(mut holder) = self.chunk_map.remove(&pos) else {
|
||||
continue;
|
||||
};
|
||||
debug_assert_eq!(holder.target_stage, StagedChunkEnum::None);
|
||||
if holder.occupied.is_null() {
|
||||
let mut tmp = None;
|
||||
swap(&mut holder.chunk, &mut tmp);
|
||||
let Some(tmp) = tmp else {
|
||||
continue;
|
||||
};
|
||||
if !holder.occupied.is_null() {
|
||||
self.chunk_map.insert(pos, holder);
|
||||
self.unload_chunks.insert(pos);
|
||||
continue;
|
||||
}
|
||||
|
||||
for task in holder.tasks {
|
||||
if !task.is_null() {
|
||||
let is_in_flight = self.graph.nodes.get(task).is_some_and(|n| n.in_flight);
|
||||
if !is_in_flight {
|
||||
self.waiting_for_chunks.remove(&task);
|
||||
self.drop_node(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if holder.public {
|
||||
self.public_chunk_map.remove(&pos);
|
||||
holder.public = false;
|
||||
}
|
||||
|
||||
if let Some(tmp) = holder.chunk {
|
||||
match tmp {
|
||||
Chunk::Level(chunk) => {
|
||||
if holder.public {
|
||||
self.public_chunk_map.remove(&pos);
|
||||
holder.public = false;
|
||||
}
|
||||
|
||||
// Forcefully drop chunks when unloaded to prevent memory leaks
|
||||
// from dangling strong references
|
||||
// Save chunk to disk if dirty
|
||||
if chunk.is_dirty() {
|
||||
chunks.push((pos, Chunk::Level(chunk)));
|
||||
}
|
||||
self.chunk_map.remove(&pos);
|
||||
}
|
||||
Chunk::Proto(chunk) => {
|
||||
debug_assert!(!holder.public);
|
||||
chunks.push((pos, Chunk::Proto(chunk)));
|
||||
self.chunk_map.remove(&pos);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.unload_chunks.insert(pos);
|
||||
}
|
||||
}
|
||||
if chunks.is_empty() {
|
||||
@@ -1062,6 +1069,7 @@ impl GenerationSchedule {
|
||||
if !self.unload_chunks.is_empty()
|
||||
&& self.last_unload.elapsed() >= std::time::Duration::from_secs(1)
|
||||
{
|
||||
self.garbage_collect_dependencies();
|
||||
self.process_unload_queue();
|
||||
self.last_unload = std::time::Instant::now();
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ impl PlacedFeature {
|
||||
pub fn generate_in_proto_chunk(
|
||||
&self,
|
||||
chunk: &mut crate::ProtoChunk,
|
||||
block_registry: &dyn crate::world::WorldPortalExt,
|
||||
feature_name: pumpkin_data::placed_feature::PlacedFeature,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
@@ -64,8 +65,17 @@ impl PlacedFeature {
|
||||
if let ConfiguredFeature::SculkPatch(feature) = feature {
|
||||
feature.generate_in_proto_chunk(chunk, random, pos)
|
||||
} else {
|
||||
tracing::warn!("Placed feature {feature_name:?} is not supported in a jigsaw pool");
|
||||
false
|
||||
let min_y = chunk.bottom_y();
|
||||
let height = chunk.height();
|
||||
self.generate(
|
||||
chunk,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -488,7 +488,7 @@ impl StructurePieceBase for PoolElementStructurePiece {
|
||||
fn place(
|
||||
&mut self,
|
||||
chunk: &mut crate::ProtoChunk,
|
||||
_block_registry: &dyn crate::world::WorldPortalExt,
|
||||
block_registry: &dyn crate::world::WorldPortalExt,
|
||||
random: &mut pumpkin_util::random::RandomGenerator,
|
||||
_seed: i64,
|
||||
chunk_box: &pumpkin_util::math::block_box::BlockBox,
|
||||
@@ -539,7 +539,13 @@ impl StructurePieceBase for PoolElementStructurePiece {
|
||||
&& let Some(placed_feature) =
|
||||
crate::generation::feature::placed_features::PLACED_FEATURES.get(&feature)
|
||||
{
|
||||
placed_feature.generate_in_proto_chunk(chunk, feature, random, self.pos);
|
||||
placed_feature.generate_in_proto_chunk(
|
||||
chunk,
|
||||
block_registry,
|
||||
feature,
|
||||
random,
|
||||
self.pos,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,13 @@ rustyline.workspace = true
|
||||
# encryption
|
||||
rsa.workspace = true
|
||||
|
||||
# verification & WASM parsing
|
||||
ed25519-dalek.workspace = true
|
||||
hex.workspace = true
|
||||
wasmsign2.workspace = true
|
||||
wasmparser = "0.255"
|
||||
wasm-encoder = "0.255"
|
||||
|
||||
# authentication
|
||||
ureq = { workspace = true, features = ["json"] }
|
||||
|
||||
|
||||
@@ -254,12 +254,10 @@ impl ChunkManager {
|
||||
let new_level = ChunkLoading::get_level_from_view_distance(view_distance);
|
||||
lock.add_ticket(center, new_level);
|
||||
|
||||
if old_center != center || old_view_distance != view_distance {
|
||||
if (old_center != center || old_view_distance != view_distance) && old_view_distance > 0
|
||||
{
|
||||
let old_level = ChunkLoading::get_level_from_view_distance(old_view_distance);
|
||||
// Don't remove if it would be the same ticket
|
||||
if old_center != center || old_level != new_level {
|
||||
lock.remove_ticket(old_center, old_level);
|
||||
}
|
||||
lock.remove_ticket(old_center, old_level);
|
||||
}
|
||||
lock.send_change();
|
||||
};
|
||||
@@ -2477,8 +2475,11 @@ impl Player {
|
||||
let radial_chunks = self.watched_section.load().all_chunks_within();
|
||||
let level = &world.level;
|
||||
let chunks_to_clean = level.mark_chunks_as_not_watched(&radial_chunks).await;
|
||||
// level.clean_chunks(&chunks_to_clean).await;
|
||||
for chunk in chunks_to_clean {
|
||||
if !chunks_to_clean.is_empty() {
|
||||
world.remove_entities_in_chunks(&chunks_to_clean).await;
|
||||
level.clean_entity_chunks(&chunks_to_clean);
|
||||
}
|
||||
for chunk in &chunks_to_clean {
|
||||
self.client
|
||||
.enqueue_packet(&CUnloadChunk::new(chunk.x, chunk.y))
|
||||
.await;
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::plugin::{
|
||||
|
||||
pub mod args;
|
||||
pub mod logging;
|
||||
pub mod signature;
|
||||
pub mod state;
|
||||
pub mod wit;
|
||||
|
||||
@@ -88,6 +89,10 @@ impl PluginRuntime {
|
||||
) -> Result<(Arc<WasmPlugin>, PluginMetadata), PluginInitError> {
|
||||
let wasm_bytes = std::fs::read(&path).map_err(PluginInitError::FileReadFailed)?;
|
||||
|
||||
signature::verify_wasm_plugin(&wasm_bytes, &path.as_ref().to_string_lossy());
|
||||
|
||||
let wasm_bytes = signature::strip_pumpkin_sections(&wasm_bytes).unwrap_or(wasm_bytes);
|
||||
|
||||
let component = load_component(&self.engine, &wasm_bytes, &self.cache_dir)?;
|
||||
|
||||
let instance_pre = self
|
||||
|
||||
389
pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs
Normal file
389
pumpkin/src/plugin/loader/wasm/wasm_host/signature.rs
Normal file
@@ -0,0 +1,389 @@
|
||||
use ed25519_dalek::{Signature, Signer, Verifier, VerifyingKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
use wasm_encoder::{CustomSection, Encode};
|
||||
use wasmparser::{Parser, Payload};
|
||||
|
||||
pub const WASM_SIGNATURE_SECTION: &str = "wasm_signature";
|
||||
pub const PUMPKIN_METADATA_SECTION: &str = "pumpkin.metadata";
|
||||
pub const PUMPKIN_MARKET_PUBLIC_KEY_URL: &str =
|
||||
"https://market.pumpkinmc.org/api/v1/rest/public-key";
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct PumpkinMetadata {
|
||||
pub marketplace_url: String,
|
||||
pub plugin_id: i64,
|
||||
pub plugin_name: String,
|
||||
pub version: String,
|
||||
pub dev_id: i64,
|
||||
pub dev_name: String,
|
||||
pub is_paid: bool,
|
||||
pub user_id: i64,
|
||||
pub license_key: Option<String>,
|
||||
pub issued_at: String,
|
||||
}
|
||||
|
||||
/// Standard W3C Wasm-Sign signature envelope structure
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct WasmSignatureEnvelope {
|
||||
pub version: u8,
|
||||
pub algorithm: String,
|
||||
pub public_key_hex: String,
|
||||
pub signature_hex: String,
|
||||
}
|
||||
|
||||
/// Keypair manager for Ed25519 signing and verification.
|
||||
pub struct KeyManager {
|
||||
signing_key: ed25519_dalek::SigningKey,
|
||||
}
|
||||
|
||||
impl KeyManager {
|
||||
/// Loads or generates an Ed25519 keypair from a 32-byte secret hex.
|
||||
#[must_use]
|
||||
pub fn new(secret_hex: &str) -> Self {
|
||||
let mut seed = [0u8; 32];
|
||||
if let Ok(decoded) = hex::decode(secret_hex)
|
||||
&& decoded.len() == 32
|
||||
{
|
||||
seed.copy_from_slice(&decoded);
|
||||
}
|
||||
let signing_key = ed25519_dalek::SigningKey::from_bytes(&seed);
|
||||
Self { signing_key }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn public_key_hex(&self) -> String {
|
||||
hex::encode(self.signing_key.verifying_key().to_bytes())
|
||||
}
|
||||
|
||||
/// Sign WASM module using official W3C `wasmsign2::KeyPair` specification.
|
||||
#[must_use]
|
||||
pub fn sign(&self, data: &[u8]) -> Vec<u8> {
|
||||
self.signing_key.sign(data).to_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
fn read_leb128(bytes: &[u8]) -> Option<(u64, usize)> {
|
||||
let mut result: u64 = 0;
|
||||
let mut shift = 0;
|
||||
for (i, &byte) in bytes.iter().enumerate() {
|
||||
if shift >= 64 {
|
||||
return None;
|
||||
}
|
||||
result |= u64::from(byte & 0x7f) << shift;
|
||||
if (byte & 0x80) == 0 {
|
||||
return Some((result, i + 1));
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Strips any existing `pumpkin.metadata` and `wasm_signature` sections from a WASM binary.
|
||||
pub fn strip_pumpkin_sections(wasm_bytes: &[u8]) -> Result<Vec<u8>, String> {
|
||||
let mut last_valid_end = wasm_bytes.len();
|
||||
let parser = Parser::new(0);
|
||||
|
||||
for payload in parser.parse_all(wasm_bytes) {
|
||||
match payload {
|
||||
Ok(Payload::Version { ref range, .. }) => {
|
||||
last_valid_end = range.end;
|
||||
}
|
||||
Ok(Payload::CustomSection(cs)) => {
|
||||
if cs.name() == PUMPKIN_METADATA_SECTION
|
||||
|| cs.name() == WASM_SIGNATURE_SECTION
|
||||
|| cs.name() == "pumpkin.signature"
|
||||
{
|
||||
} else {
|
||||
last_valid_end = cs.range().end;
|
||||
}
|
||||
}
|
||||
Ok(p) => {
|
||||
if let Some((_, range)) = p.as_section() {
|
||||
last_valid_end = range.end;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// Stopped parsing due to unparsable trailing bytes (e.g. invalid custom section appended)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(wasm_bytes[..last_valid_end].to_vec())
|
||||
}
|
||||
|
||||
/// Injects `pumpkin.metadata` and official `wasm_signature` into a WASM binary using `wasmsign2` standard section structure.
|
||||
pub fn inject_pumpkin_sections(
|
||||
wasm_bytes: &[u8],
|
||||
meta: &PumpkinMetadata,
|
||||
key_manager: &KeyManager,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
let clean_wasm = strip_pumpkin_sections(wasm_bytes)?;
|
||||
|
||||
let meta_json = serde_json::to_vec(meta).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut sign_payload = Vec::new();
|
||||
sign_payload.extend_from_slice(&clean_wasm);
|
||||
sign_payload.extend_from_slice(&meta_json);
|
||||
|
||||
let raw_sig = key_manager.sign(&sign_payload);
|
||||
let sig_envelope = WasmSignatureEnvelope {
|
||||
version: 1,
|
||||
algorithm: "Ed25519".into(),
|
||||
public_key_hex: key_manager.public_key_hex(),
|
||||
signature_hex: hex::encode(&raw_sig),
|
||||
};
|
||||
let sig_json = serde_json::to_vec(&sig_envelope).map_err(|e| e.to_string())?;
|
||||
|
||||
let meta_section = CustomSection {
|
||||
name: PUMPKIN_METADATA_SECTION.into(),
|
||||
data: meta_json.as_slice().into(),
|
||||
};
|
||||
|
||||
let sig_section = CustomSection {
|
||||
name: WASM_SIGNATURE_SECTION.into(),
|
||||
data: sig_json.as_slice().into(),
|
||||
};
|
||||
|
||||
let mut out = clean_wasm;
|
||||
meta_section.encode(&mut out);
|
||||
sig_section.encode(&mut out);
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Verification result for inspecting a WASM binary.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct VerificationResult {
|
||||
pub is_valid: bool,
|
||||
pub is_signed: bool,
|
||||
pub error: Option<String>,
|
||||
pub metadata: Option<PumpkinMetadata>,
|
||||
pub signature_envelope: Option<WasmSignatureEnvelope>,
|
||||
pub public_key_hex: String,
|
||||
}
|
||||
|
||||
/// Fetches public key from market REST API.
|
||||
pub fn fetch_market_public_key() -> Result<String, String> {
|
||||
let mut response = ureq::get(PUMPKIN_MARKET_PUBLIC_KEY_URL)
|
||||
.header("User-Agent", "Pumpkin-MC")
|
||||
.call()
|
||||
.map_err(|e| format!("Failed to fetch public key from market: {e}"))?;
|
||||
|
||||
let body = response
|
||||
.body_mut()
|
||||
.read_to_string()
|
||||
.map_err(|e| format!("Failed to read public key response: {e}"))?;
|
||||
|
||||
let key = body.trim().trim_matches('"').to_string();
|
||||
if key.is_empty() {
|
||||
Err("Fetched public key is empty".into())
|
||||
} else {
|
||||
Ok(key)
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies a WASM binary's pumpkin custom sections against a public key using `wasmsign2`.
|
||||
#[must_use]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn verify_pumpkin_wasm(wasm_bytes: &[u8], public_key_hex: &str) -> VerificationResult {
|
||||
let clean_wasm = match strip_pumpkin_sections(wasm_bytes) {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
return VerificationResult {
|
||||
is_valid: false,
|
||||
is_signed: false,
|
||||
error: Some(format!("Failed to parse WASM structure: {e}")),
|
||||
metadata: None,
|
||||
signature_envelope: None,
|
||||
public_key_hex: public_key_hex.to_string(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let mut metadata_raw: Option<Vec<u8>> = None;
|
||||
let mut signature_raw: Option<Vec<u8>> = None;
|
||||
|
||||
let parser = Parser::new(0);
|
||||
for payload in parser.parse_all(wasm_bytes) {
|
||||
if let Ok(Payload::CustomSection(cs)) = payload {
|
||||
if cs.name() == PUMPKIN_METADATA_SECTION {
|
||||
metadata_raw = Some(cs.data().to_vec());
|
||||
} else if cs.name() == WASM_SIGNATURE_SECTION || cs.name() == "pumpkin.signature" {
|
||||
signature_raw = Some(cs.data().to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if metadata_raw.is_none() || signature_raw.is_none() {
|
||||
let clean_wasm = strip_pumpkin_sections(wasm_bytes).unwrap_or_default();
|
||||
if clean_wasm.len() < wasm_bytes.len() {
|
||||
let trailing = &wasm_bytes[clean_wasm.len()..];
|
||||
|
||||
let mut cursor = 0;
|
||||
while cursor < trailing.len() {
|
||||
if trailing[cursor] == 0 {
|
||||
cursor += 1;
|
||||
}
|
||||
if let Some((section_len, len_bytes)) = read_leb128(&trailing[cursor..]) {
|
||||
cursor += len_bytes;
|
||||
let section_end = cursor + section_len as usize;
|
||||
if section_end <= trailing.len() {
|
||||
let section_data = &trailing[cursor..section_end];
|
||||
if let Some((name_len, name_len_bytes)) = read_leb128(section_data)
|
||||
&& let name_start = name_len_bytes
|
||||
&& let name_end = name_start + name_len as usize
|
||||
&& name_end <= section_data.len()
|
||||
&& let Ok(name) =
|
||||
std::str::from_utf8(§ion_data[name_start..name_end])
|
||||
{
|
||||
let payload_data = §ion_data[name_end..];
|
||||
if name == PUMPKIN_METADATA_SECTION {
|
||||
metadata_raw = Some(payload_data.to_vec());
|
||||
} else if name == WASM_SIGNATURE_SECTION || name == "pumpkin.signature"
|
||||
{
|
||||
signature_raw = Some(payload_data.to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor = section_end;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (Some(meta_bytes), Some(sig_bytes)) = (metadata_raw, signature_raw) else {
|
||||
return VerificationResult {
|
||||
is_valid: false,
|
||||
is_signed: false,
|
||||
error: Some(
|
||||
"WASM does not contain Pumpkin Market metadata or standard wasm_signature custom sections."
|
||||
.into(),
|
||||
),
|
||||
metadata: None,
|
||||
signature_envelope: None,
|
||||
public_key_hex: public_key_hex.to_string(),
|
||||
};
|
||||
};
|
||||
|
||||
let meta: PumpkinMetadata = match serde_json::from_slice(&meta_bytes) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
return VerificationResult {
|
||||
is_valid: false,
|
||||
is_signed: true,
|
||||
error: Some(format!("Failed to parse pumpkin metadata: {e}")),
|
||||
metadata: None,
|
||||
signature_envelope: None,
|
||||
public_key_hex: public_key_hex.to_string(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Extract signature bytes and optional envelope
|
||||
let (raw_sig_bytes, sig_envelope) =
|
||||
if let Ok(envelope) = serde_json::from_slice::<WasmSignatureEnvelope>(&sig_bytes) {
|
||||
let Ok(sig_b) = hex::decode(&envelope.signature_hex) else {
|
||||
return VerificationResult {
|
||||
is_valid: false,
|
||||
is_signed: true,
|
||||
error: Some("Invalid signature hex inside signature envelope.".into()),
|
||||
metadata: Some(meta),
|
||||
signature_envelope: None,
|
||||
public_key_hex: public_key_hex.to_string(),
|
||||
};
|
||||
};
|
||||
(sig_b, Some(envelope))
|
||||
} else {
|
||||
(sig_bytes, None)
|
||||
};
|
||||
|
||||
let mut sign_payload = Vec::new();
|
||||
sign_payload.extend_from_slice(&clean_wasm);
|
||||
sign_payload.extend_from_slice(&meta_bytes);
|
||||
|
||||
let target_pub_key = sig_envelope.as_ref().map_or_else(
|
||||
|| public_key_hex.to_string(),
|
||||
|env| env.public_key_hex.clone(),
|
||||
);
|
||||
|
||||
let Ok(pub_key_bytes) = hex::decode(&target_pub_key) else {
|
||||
return VerificationResult {
|
||||
is_valid: false,
|
||||
is_signed: true,
|
||||
error: Some("Invalid public key hex format.".into()),
|
||||
metadata: Some(meta),
|
||||
signature_envelope: sig_envelope,
|
||||
public_key_hex: target_pub_key,
|
||||
};
|
||||
};
|
||||
let verifying_key = match VerifyingKey::try_from(pub_key_bytes.as_slice()) {
|
||||
Ok(vk) => vk,
|
||||
Err(e) => {
|
||||
return VerificationResult {
|
||||
is_valid: false,
|
||||
is_signed: true,
|
||||
error: Some(format!("Invalid Ed25519 public key: {e}")),
|
||||
metadata: Some(meta),
|
||||
signature_envelope: sig_envelope,
|
||||
public_key_hex: target_pub_key,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
let signature = match Signature::from_slice(&raw_sig_bytes) {
|
||||
Ok(sig) => sig,
|
||||
Err(e) => {
|
||||
return VerificationResult {
|
||||
is_valid: false,
|
||||
is_signed: true,
|
||||
error: Some(format!("Invalid signature bytes: {e}")),
|
||||
metadata: Some(meta),
|
||||
signature_envelope: sig_envelope,
|
||||
public_key_hex: target_pub_key,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = verifying_key.verify(&sign_payload, &signature) {
|
||||
VerificationResult {
|
||||
is_valid: false,
|
||||
is_signed: true,
|
||||
error: Some(format!("Signature verification failed: {e}")),
|
||||
metadata: Some(meta),
|
||||
signature_envelope: sig_envelope,
|
||||
public_key_hex: target_pub_key,
|
||||
}
|
||||
} else {
|
||||
VerificationResult {
|
||||
is_valid: true,
|
||||
is_signed: true,
|
||||
error: None,
|
||||
metadata: Some(meta),
|
||||
signature_envelope: sig_envelope,
|
||||
public_key_hex: target_pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies a WASM plugin binary and logs appropriate warnings if unsigned or invalid.
|
||||
pub fn verify_wasm_plugin(wasm_bytes: &[u8], path_str: &str) {
|
||||
let public_key = fetch_market_public_key().unwrap_or_default();
|
||||
let result = verify_pumpkin_wasm(wasm_bytes, &public_key);
|
||||
|
||||
if !result.is_signed {
|
||||
warn!(
|
||||
"Plugin '{}' is unsigned. Unsigned plugins may come from untrusted sources.",
|
||||
path_str
|
||||
);
|
||||
} else if !result.is_valid {
|
||||
warn!(
|
||||
"Plugin '{}' signature verification failed: {}",
|
||||
path_str,
|
||||
result.error.as_deref().unwrap_or("Unknown error")
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user