update monero-serai

This commit is contained in:
Boog900 2024-06-28 22:17:11 +01:00
parent 7e9891de5b
commit 075e9b15cb
No known key found for this signature in database
GPG key ID: 42AB1287CB0041C2
23 changed files with 411 additions and 413 deletions

278
Cargo.lock generated
View file

@ -56,17 +56,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "async-lock"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]] [[package]]
name = "async-stream" name = "async-stream"
version = "0.3.5" version = "0.3.5"
@ -121,28 +110,12 @@ dependencies = [
"rustc-demangle", "rustc-demangle",
] ]
[[package]]
name = "base58-monero"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978e81a45367d2409ecd33369a45dda2e9a3ca516153ec194de1fbda4b9fb79d"
dependencies = [
"thiserror",
"tiny-keccak",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]] [[package]]
name = "bincode" name = "bincode"
version = "1.3.3" version = "1.3.3"
@ -343,15 +316,6 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.4" version = "0.9.4"
@ -433,12 +397,6 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]] [[package]]
name = "crypto-bigint" name = "crypto-bigint"
version = "0.5.5" version = "0.5.5"
@ -523,12 +481,10 @@ dependencies = [
"cuprate-test-utils", "cuprate-test-utils",
"cuprate-types", "cuprate-types",
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group",
"futures", "futures",
"hex", "hex",
"hex-literal", "hex-literal",
"monero-serai", "monero-serai",
"multiexp",
"proptest", "proptest",
"proptest-derive", "proptest-derive",
"randomx-rs", "randomx-rs",
@ -549,11 +505,9 @@ dependencies = [
"cuprate-cryptonight", "cuprate-cryptonight",
"cuprate-helper", "cuprate-helper",
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group",
"hex", "hex",
"hex-literal", "hex-literal",
"monero-serai", "monero-serai",
"multiexp",
"proptest", "proptest",
"proptest-derive", "proptest-derive",
"rand", "rand",
@ -772,7 +726,9 @@ dependencies = [
"futures", "futures",
"hex", "hex",
"hex-literal", "hex-literal",
"monero-rpc",
"monero-serai", "monero-serai",
"monero-simple-request-rpc",
"pretty_assertions", "pretty_assertions",
"serde", "serde",
"serde_json", "serde_json",
@ -838,7 +794,7 @@ dependencies = [
[[package]] [[package]]
name = "dalek-ff-group" name = "dalek-ff-group"
version = "0.4.1" version = "0.4.1"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1" source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [ dependencies = [
"crypto-bigint", "crypto-bigint",
"curve25519-dalek", "curve25519-dalek",
@ -957,27 +913,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "event-listener"
version = "5.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
dependencies = [
"concurrent-queue",
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.1.0" version = "2.1.0"
@ -1004,7 +939,7 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]] [[package]]
name = "flexible-transcript" name = "flexible-transcript"
version = "0.3.2" version = "0.3.2"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1" source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [ dependencies = [
"blake2", "blake2",
"digest", "digest",
@ -1238,15 +1173,6 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]] [[package]]
name = "http" name = "http"
version = "1.1.0" version = "1.1.0"
@ -1283,9 +1209,9 @@ dependencies = [
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.9.3" version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
[[package]] [[package]]
name = "hyper" name = "hyper"
@ -1656,63 +1582,151 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "monero-borromean"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"monero-generators",
"monero-io",
"monero-primitives",
"std-shims",
"zeroize",
]
[[package]]
name = "monero-bulletproofs"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"monero-generators",
"monero-io",
"monero-primitives",
"rand_core",
"std-shims",
"subtle",
"thiserror",
"zeroize",
]
[[package]]
name = "monero-clsag"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"dalek-ff-group",
"flexible-transcript",
"group",
"monero-generators",
"monero-io",
"monero-primitives",
"rand_chacha",
"rand_core",
"std-shims",
"subtle",
"thiserror",
"zeroize",
]
[[package]] [[package]]
name = "monero-generators" name = "monero-generators"
version = "0.4.0" version = "0.4.0"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1" source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group", "dalek-ff-group",
"group", "group",
"monero-io",
"sha3", "sha3",
"std-shims", "std-shims",
"subtle", "subtle",
] ]
[[package]]
name = "monero-io"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"std-shims",
]
[[package]]
name = "monero-mlsag"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"monero-generators",
"monero-io",
"monero-primitives",
"std-shims",
"thiserror",
"zeroize",
]
[[package]]
name = "monero-primitives"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"curve25519-dalek",
"monero-generators",
"monero-io",
"sha3",
"std-shims",
"zeroize",
]
[[package]]
name = "monero-rpc"
version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [
"async-trait",
"curve25519-dalek",
"hex",
"monero-serai",
"serde",
"serde_json",
"std-shims",
"thiserror",
"zeroize",
]
[[package]] [[package]]
name = "monero-serai" name = "monero-serai"
version = "0.1.4-alpha" version = "0.1.4-alpha"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1" source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [ dependencies = [
"async-lock",
"async-trait",
"base58-monero",
"curve25519-dalek", "curve25519-dalek",
"dalek-ff-group",
"digest_auth",
"flexible-transcript",
"group",
"hex",
"hex-literal", "hex-literal",
"monero-borromean",
"monero-bulletproofs",
"monero-clsag",
"monero-generators", "monero-generators",
"multiexp", "monero-io",
"pbkdf2", "monero-mlsag",
"rand", "monero-primitives",
"rand_chacha",
"rand_core", "rand_core",
"rand_distr",
"serde",
"serde_json",
"sha3",
"simple-request",
"std-shims", "std-shims",
"subtle",
"thiserror",
"tokio",
"zeroize", "zeroize",
] ]
[[package]] [[package]]
name = "multiexp" name = "monero-simple-request-rpc"
version = "0.4.0" version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1" source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [ dependencies = [
"ff", "async-trait",
"group", "digest_auth",
"rand_core", "hex",
"rustversion", "monero-rpc",
"std-shims", "simple-request",
"zeroize", "tokio",
] ]
[[package]] [[package]]
@ -1772,12 +1786,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "parking"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.3" version = "0.12.3"
@ -1801,35 +1809,12 @@ dependencies = [
"windows-targets 0.52.5", "windows-targets 0.52.5",
] ]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.15" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
"hmac",
"password-hash",
"sha2",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
@ -2382,7 +2367,7 @@ dependencies = [
[[package]] [[package]]
name = "simple-request" name = "simple-request"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1" source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [ dependencies = [
"http-body-util", "http-body-util",
"hyper", "hyper",
@ -2438,7 +2423,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]] [[package]]
name = "std-shims" name = "std-shims"
version = "0.1.1" version = "0.1.1"
source = "git+https://github.com/Cuprate/serai.git?rev=d27d934#d27d93480aa8a849d84214ad4c71d83ce6fea0c1" source = "git+https://github.com/Cuprate/serai.git?branch=monero-tidy#148137618a72c365b7e846a73292f01f53c0f941"
dependencies = [ dependencies = [
"hashbrown 0.14.5", "hashbrown 0.14.5",
"spin", "spin",
@ -2552,15 +2537,6 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.7.6" version = "0.7.6"

View file

@ -57,15 +57,15 @@ chrono = { version = "0.4.31", default-features = false }
crypto-bigint = { version = "0.5.5", default-features = false } crypto-bigint = { version = "0.5.5", default-features = false }
crossbeam = { version = "0.8.4", default-features = false } crossbeam = { version = "0.8.4", default-features = false }
curve25519-dalek = { version = "4.1.3", default-features = false } curve25519-dalek = { version = "4.1.3", default-features = false }
dalek-ff-group = { git = "https://github.com/Cuprate/serai.git", rev = "d27d934", default-features = false }
dashmap = { version = "5.5.3", default-features = false } dashmap = { version = "5.5.3", default-features = false }
dirs = { version = "5.0.1", default-features = false } dirs = { version = "5.0.1", default-features = false }
futures = { version = "0.3.29", default-features = false } futures = { version = "0.3.29", default-features = false }
hex = { version = "0.4.3", default-features = false } hex = { version = "0.4.3", default-features = false }
hex-literal = { version = "0.4", default-features = false } hex-literal = { version = "0.4", default-features = false }
indexmap = { version = "2.2.5", default-features = false } indexmap = { version = "2.2.5", default-features = false }
monero-serai = { git = "https://github.com/Cuprate/serai.git", rev = "d27d934", default-features = false } monero-rpc = { git = "https://github.com/Cuprate/serai.git", branch = "monero-tidy", default-features = false }
multiexp = { git = "https://github.com/Cuprate/serai.git", rev = "d27d934", default-features = false } monero-simple-request-rpc = { git = "https://github.com/Cuprate/serai.git", branch = "monero-tidy", default-features = false }
monero-serai = { git = "https://github.com/Cuprate/serai.git", branch = "monero-tidy", default-features = false }
paste = { version = "1.0.14", default-features = false } paste = { version = "1.0.14", default-features = false }
pin-project = { version = "1.1.3", default-features = false } pin-project = { version = "1.1.3", default-features = false }
randomx-rs = { git = "https://github.com/Cuprate/randomx-rs.git", rev = "0028464", default-features = false } randomx-rs = { git = "https://github.com/Cuprate/randomx-rs.git", rev = "0028464", default-features = false }

View file

@ -19,8 +19,6 @@ futures = { workspace = true, features = ["std", "async-await"] }
randomx-rs = { workspace = true } randomx-rs = { workspace = true }
monero-serai = { workspace = true, features = ["std"] } monero-serai = { workspace = true, features = ["std"] }
multiexp = { workspace = true }
dalek-ff-group = { workspace = true }
curve25519-dalek = { workspace = true } curve25519-dalek = { workspace = true }
rayon = { workspace = true } rayon = { workspace = true }

View file

@ -244,7 +244,7 @@ where
let block_blob = block.serialize(); let block_blob = block.serialize();
let Some(Input::Gen(height)) = block.miner_tx.prefix.inputs.first() else { let Some(Input::Gen(height)) = block.miner_tx.prefix().inputs.first() else {
return Err(FastSyncError::MinerTx(MinerTxError::InputNotOfTypeGen)); return Err(FastSyncError::MinerTx(MinerTxError::InputNotOfTypeGen));
}; };
if *height != block_chain_ctx.chain_height { if *height != block_chain_ctx.chain_height {
@ -270,7 +270,7 @@ where
let total_fees = verified_txs.iter().map(|tx| tx.fee).sum::<u64>(); let total_fees = verified_txs.iter().map(|tx| tx.fee).sum::<u64>();
let total_outputs = block let total_outputs = block
.miner_tx .miner_tx
.prefix .prefix()
.outputs .outputs
.iter() .iter()
.map(|output| output.amount.unwrap_or(0)) .map(|output| output.amount.unwrap_or(0))

View file

@ -15,8 +15,6 @@ cuprate-helper = { path = "../../helper", default-features = false, features = [
cuprate-cryptonight = {path = "../../cryptonight"} cuprate-cryptonight = {path = "../../cryptonight"}
monero-serai = { workspace = true, features = ["std"] } monero-serai = { workspace = true, features = ["std"] }
multiexp = { workspace = true, features = ["std", "batch"] }
dalek-ff-group = { workspace = true, features = ["std"] }
curve25519-dalek = { workspace = true, features = ["alloc", "zeroize", "precomputed-tables"] } curve25519-dalek = { workspace = true, features = ["alloc", "zeroize", "precomputed-tables"] }
rand = { workspace = true, features = ["std", "std_rng"] } rand = { workspace = true, features = ["std", "std_rng"] }

View file

@ -1,4 +1,4 @@
use multiexp::BatchVerifier as InternalBatchVerifier; use monero_serai::ringct::bulletproofs::BatchVerifier as InternalBatchVerifier;
/// This trait represents a batch verifier. /// This trait represents a batch verifier.
/// ///
@ -12,18 +12,12 @@ pub trait BatchVerifier {
/// # Panics /// # Panics
/// This function may panic if `stmt` contains calls to `rayon`'s parallel iterators, e.g. `par_iter()`. /// This function may panic if `stmt` contains calls to `rayon`'s parallel iterators, e.g. `par_iter()`.
// TODO: remove the panics by adding a generic API upstream. // TODO: remove the panics by adding a generic API upstream.
fn queue_statement<R>( fn queue_statement<R>(&mut self, stmt: impl FnOnce(&mut InternalBatchVerifier) -> R) -> R;
&mut self,
stmt: impl FnOnce(&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>) -> R,
) -> R;
} }
// impl this for a single threaded batch verifier. // impl this for a single threaded batch verifier.
impl BatchVerifier for &'_ mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint> { impl BatchVerifier for &'_ mut InternalBatchVerifier {
fn queue_statement<R>( fn queue_statement<R>(&mut self, stmt: impl FnOnce(&mut InternalBatchVerifier) -> R) -> R {
&mut self,
stmt: impl FnOnce(&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>) -> R,
) -> R {
stmt(self) stmt(self)
} }
} }

View file

@ -29,8 +29,8 @@ fn genesis_miner_tx(network: &Network) -> Transaction {
pub fn generate_genesis_block(network: &Network) -> Block { pub fn generate_genesis_block(network: &Network) -> Block {
Block { Block {
header: BlockHeader { header: BlockHeader {
major_version: 1, hardfork_version: 1,
minor_version: 0, hardfork_signal: 0,
timestamp: 0, timestamp: 0,
previous: [0; 32], previous: [0; 32],
nonce: genesis_nonce(network), nonce: genesis_nonce(network),

View file

@ -202,8 +202,8 @@ impl HardFork {
#[inline] #[inline]
pub fn from_block_header(header: &BlockHeader) -> Result<(HardFork, HardFork), HardForkError> { pub fn from_block_header(header: &BlockHeader) -> Result<(HardFork, HardFork), HardForkError> {
Ok(( Ok((
HardFork::from_version(header.major_version)?, HardFork::from_version(header.hardfork_version)?,
HardFork::from_vote(header.minor_version), HardFork::from_vote(header.hardfork_signal),
)) ))
} }

View file

@ -1,7 +1,4 @@
use monero_serai::{ use monero_serai::transaction::{Input, Output, Timelock, Transaction};
ringct::RctType,
transaction::{Input, Output, Timelock, Transaction},
};
use crate::{is_decomposed_amount, transactions::check_output_types, HardFork, TxVersion}; use crate::{is_decomposed_amount, transactions::check_output_types, HardFork, TxVersion};
@ -188,22 +185,27 @@ pub fn check_miner_tx(
already_generated_coins: u64, already_generated_coins: u64,
hf: &HardFork, hf: &HardFork,
) -> Result<u64, MinerTxError> { ) -> Result<u64, MinerTxError> {
let tx_version = TxVersion::from_raw(tx.prefix.version).ok_or(MinerTxError::VersionInvalid)?; let tx_version = TxVersion::from_raw(tx.version()).ok_or(MinerTxError::VersionInvalid)?;
check_miner_tx_version(&tx_version, hf)?; check_miner_tx_version(&tx_version, hf)?;
// ref: <https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#ringct-type> // ref: <https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#ringct-type>
if hf >= &HardFork::V12 && tx.rct_signatures.rct_type() != RctType::Null { match tx {
return Err(MinerTxError::RCTTypeNotNULL); Transaction::V1 { .. } => (),
Transaction::V2 { proofs, .. } => {
if hf >= &HardFork::V12 && proofs.is_some() {
return Err(MinerTxError::RCTTypeNotNULL);
}
}
} }
check_time_lock(&tx.prefix.timelock, chain_height)?; check_time_lock(&tx.prefix().timelock, chain_height)?;
check_inputs(&tx.prefix.inputs, chain_height)?; check_inputs(&tx.prefix().inputs, chain_height)?;
check_output_types(&tx.prefix.outputs, hf).map_err(|_| MinerTxError::InvalidOutputType)?; check_output_types(&tx.prefix().outputs, hf).map_err(|_| MinerTxError::InvalidOutputType)?;
let reward = calculate_block_reward(block_weight, median_bw, already_generated_coins, hf); let reward = calculate_block_reward(block_weight, median_bw, already_generated_coins, hf);
let total_outs = sum_outputs(&tx.prefix.outputs, hf, &tx_version)?; let total_outs = sum_outputs(&tx.prefix().outputs, hf, &tx_version)?;
check_total_output_amt(total_outs, reward, total_fees, hf) check_total_output_amt(total_outs, reward, total_fees, hf)
} }

View file

@ -91,7 +91,7 @@ impl TxVersion {
/// ///
/// ref: <https://monero-book.cuprate.org/consensus_rules/transactions.html#version> /// ref: <https://monero-book.cuprate.org/consensus_rules/transactions.html#version>
/// && <https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#version> /// && <https://monero-book.cuprate.org/consensus_rules/blocks/miner_tx.html#version>
pub fn from_raw(version: u64) -> Option<TxVersion> { pub fn from_raw(version: u8) -> Option<TxVersion> {
Some(match version { Some(match version {
1 => TxVersion::RingSignatures, 1 => TxVersion::RingSignatures,
2 => TxVersion::RingCT, 2 => TxVersion::RingCT,
@ -205,7 +205,7 @@ fn check_number_of_outputs(
outputs: usize, outputs: usize,
hf: &HardFork, hf: &HardFork,
tx_version: &TxVersion, tx_version: &TxVersion,
rct_type: &RctType, bp_or_bpp: bool,
) -> Result<(), TransactionError> { ) -> Result<(), TransactionError> {
if tx_version == &TxVersion::RingSignatures { if tx_version == &TxVersion::RingSignatures {
return Ok(()); return Ok(());
@ -215,18 +215,10 @@ fn check_number_of_outputs(
return Err(TransactionError::InvalidNumberOfOutputs); return Err(TransactionError::InvalidNumberOfOutputs);
} }
match rct_type { if bp_or_bpp && outputs > MAX_BULLETPROOFS_OUTPUTS {
RctType::Bulletproofs Err(TransactionError::InvalidNumberOfOutputs)
| RctType::BulletproofsCompactAmount } else {
| RctType::Clsag Ok(())
| RctType::BulletproofsPlus => {
if outputs <= MAX_BULLETPROOFS_OUTPUTS {
Ok(())
} else {
Err(TransactionError::InvalidNumberOfOutputs)
}
}
_ => Ok(()),
} }
} }
@ -239,11 +231,11 @@ fn check_outputs_semantics(
outputs: &[Output], outputs: &[Output],
hf: &HardFork, hf: &HardFork,
tx_version: &TxVersion, tx_version: &TxVersion,
rct_type: &RctType, bp_or_bpp: bool,
) -> Result<u64, TransactionError> { ) -> Result<u64, TransactionError> {
check_output_types(outputs, hf)?; check_output_types(outputs, hf)?;
check_output_keys(outputs)?; check_output_keys(outputs)?;
check_number_of_outputs(outputs.len(), hf, tx_version, rct_type)?; check_number_of_outputs(outputs.len(), hf, tx_version, bp_or_bpp)?;
sum_outputs(outputs, hf, tx_version) sum_outputs(outputs, hf, tx_version)
} }
@ -615,28 +607,41 @@ pub fn check_transaction_semantic(
Err(TransactionError::TooBig)?; Err(TransactionError::TooBig)?;
} }
let tx_version = TxVersion::from_raw(tx.prefix.version) let tx_version =
.ok_or(TransactionError::TransactionVersionInvalid)?; TxVersion::from_raw(tx.version()).ok_or(TransactionError::TransactionVersionInvalid)?;
let outputs_sum = check_outputs_semantics( let bp_or_bpp = match tx {
&tx.prefix.outputs, Transaction::V2 {
hf, proofs: Some(proofs),
&tx_version, ..
&tx.rct_signatures.rct_type(), } => match proofs.rct_type() {
)?; RctType::AggregateMlsagBorromean | RctType::MlsagBorromean => false,
let inputs_sum = check_inputs_semantics(&tx.prefix.inputs, hf)?; RctType::MlsagBulletproofs
| RctType::MlsagBulletproofsCompactAmount
| RctType::ClsagBulletproof
| RctType::ClsagBulletproofPlus => true,
},
_ => false,
};
let fee = match tx_version { let outputs_sum = check_outputs_semantics(&tx.prefix().outputs, hf, &tx_version, bp_or_bpp)?;
TxVersion::RingSignatures => { let inputs_sum = check_inputs_semantics(&tx.prefix().inputs, hf)?;
let fee = match tx {
Transaction::V1 { .. } => {
if outputs_sum >= inputs_sum { if outputs_sum >= inputs_sum {
Err(TransactionError::OutputsTooHigh)?; Err(TransactionError::OutputsTooHigh)?;
} }
inputs_sum - outputs_sum inputs_sum - outputs_sum
} }
TxVersion::RingCT => { Transaction::V2 { proofs, .. } => {
ring_ct::ring_ct_semantic_checks(tx, tx_hash, verifier, hf)?; let proofs = proofs
.as_ref()
.ok_or(TransactionError::TransactionVersionInvalid)?;
tx.rct_signatures.base.fee ring_ct::ring_ct_semantic_checks(proofs, tx_hash, verifier, hf)?;
proofs.base.fee
} }
}; };
@ -658,11 +663,11 @@ pub fn check_transaction_contextual(
current_time_lock_timestamp: u64, current_time_lock_timestamp: u64,
hf: &HardFork, hf: &HardFork,
) -> Result<(), TransactionError> { ) -> Result<(), TransactionError> {
let tx_version = TxVersion::from_raw(tx.prefix.version) let tx_version =
.ok_or(TransactionError::TransactionVersionInvalid)?; TxVersion::from_raw(tx.version()).ok_or(TransactionError::TransactionVersionInvalid)?;
check_inputs_contextual( check_inputs_contextual(
&tx.prefix.inputs, &tx.prefix().inputs,
tx_ring_members_info, tx_ring_members_info,
current_chain_height, current_chain_height,
hf, hf,
@ -676,17 +681,22 @@ pub fn check_transaction_contextual(
hf, hf,
)?; )?;
match tx_version { match &tx {
TxVersion::RingSignatures => ring_signatures::check_input_signatures( Transaction::V1 { prefix, signatures } => ring_signatures::check_input_signatures(
&tx.prefix.inputs, &prefix.inputs,
&tx.signatures, signatures,
&tx_ring_members_info.rings, &tx_ring_members_info.rings,
&tx.signature_hash(), // This will only return None on v2 miner txs.
&tx.signature_hash()
.ok_or(TransactionError::TransactionVersionInvalid)?,
), ),
TxVersion::RingCT => Ok(ring_ct::check_input_signatures( Transaction::V2 { prefix, proofs } => Ok(ring_ct::check_input_signatures(
&tx.signature_hash(), &tx.signature_hash()
&tx.prefix.inputs, .ok_or(TransactionError::TransactionVersionInvalid)?,
&tx.rct_signatures, &prefix.inputs,
proofs
.as_ref()
.ok_or(TransactionError::TransactionVersionInvalid)?,
&tx_ring_members_info.rings, &tx_ring_members_info.rings,
)?), )?),
} }

View file

@ -1,13 +1,13 @@
use curve25519_dalek::{EdwardsPoint, Scalar}; use curve25519_dalek::{EdwardsPoint, Scalar};
use hex_literal::hex; use hex_literal::hex;
use monero_serai::{ use monero_serai::{
generators::H,
ringct::{ ringct::{
clsag::ClsagError, clsag::ClsagError,
mlsag::{AggregateRingMatrixBuilder, MlsagError, RingMatrix}, mlsag::{AggregateRingMatrixBuilder, MlsagError, RingMatrix},
RctPrunable, RctSignatures, RctType, RctProofs, RctPrunable, RctType,
}, },
transaction::{Input, Transaction}, transaction::Input,
H,
}; };
use rand::thread_rng; use rand::thread_rng;
#[cfg(feature = "rayon")] #[cfg(feature = "rayon")]
@ -48,12 +48,12 @@ fn check_rct_type(ty: &RctType, hf: HardFork, tx_hash: &[u8; 32]) -> Result<(),
use RctType as T; use RctType as T;
match ty { match ty {
T::MlsagAggregate | T::MlsagIndividual if hf >= F::V4 && hf < F::V9 => Ok(()), T::AggregateMlsagBorromean | T::MlsagBorromean if hf >= F::V4 && hf < F::V9 => Ok(()),
T::Bulletproofs if hf >= F::V8 && hf < F::V11 => Ok(()), T::MlsagBulletproofs if hf >= F::V8 && hf < F::V11 => Ok(()),
T::BulletproofsCompactAmount if hf >= F::V10 && hf < F::V14 => Ok(()), T::MlsagBulletproofsCompactAmount if hf >= F::V10 && hf < F::V14 => Ok(()),
T::BulletproofsCompactAmount if GRANDFATHERED_TRANSACTIONS.contains(tx_hash) => Ok(()), T::MlsagBulletproofsCompactAmount if GRANDFATHERED_TRANSACTIONS.contains(tx_hash) => Ok(()),
T::Clsag if hf >= F::V13 && hf < F::V16 => Ok(()), T::ClsagBulletproof if hf >= F::V13 && hf < F::V16 => Ok(()),
T::BulletproofsPlus if hf >= F::V15 => Ok(()), T::ClsagBulletproofPlus if hf >= F::V15 => Ok(()),
_ => Err(RingCTError::TypeNotAllowed), _ => Err(RingCTError::TypeNotAllowed),
} }
} }
@ -61,14 +61,16 @@ fn check_rct_type(ty: &RctType, hf: HardFork, tx_hash: &[u8; 32]) -> Result<(),
/// Checks that the pseudo-outs sum to the same point as the output commitments. /// Checks that the pseudo-outs sum to the same point as the output commitments.
/// ///
/// <https://monero-book.cuprate.org/consensus_rules/ring_ct.html#pseudo-outs-outpks-balance> /// <https://monero-book.cuprate.org/consensus_rules/ring_ct.html#pseudo-outs-outpks-balance>
fn simple_type_balances(rct_sig: &RctSignatures) -> Result<(), RingCTError> { fn simple_type_balances(rct_sig: &RctProofs) -> Result<(), RingCTError> {
let pseudo_outs = if rct_sig.rct_type() == RctType::MlsagIndividual { let pseudo_outs = if rct_sig.rct_type() == RctType::MlsagBorromean {
&rct_sig.base.pseudo_outs &rct_sig.base.pseudo_outs
} else { } else {
match &rct_sig.prunable { match &rct_sig.prunable {
RctPrunable::Clsag { pseudo_outs, .. } RctPrunable::Clsag { pseudo_outs, .. }
| RctPrunable::MlsagBulletproofsCompactAmount { pseudo_outs, .. }
| RctPrunable::MlsagBulletproofs { pseudo_outs, .. } => pseudo_outs, | RctPrunable::MlsagBulletproofs { pseudo_outs, .. } => pseudo_outs,
_ => panic!("RingCT type is not simple!"), RctPrunable::MlsagBorromean { .. } => &rct_sig.base.pseudo_outs,
RctPrunable::AggregateMlsagBorromean { .. } => panic!("RingCT type is not simple!"),
} }
}; };
@ -89,13 +91,12 @@ fn simple_type_balances(rct_sig: &RctSignatures) -> Result<(), RingCTError> {
/// <https://monero-book.cuprate.org/consensus_rules/ring_ct/bulletproofs.html> /// <https://monero-book.cuprate.org/consensus_rules/ring_ct/bulletproofs.html>
/// <https://monero-book.cuprate.org/consensus_rules/ring_ct/bulletproofs+.html> /// <https://monero-book.cuprate.org/consensus_rules/ring_ct/bulletproofs+.html>
fn check_output_range_proofs( fn check_output_range_proofs(
rct_sig: &RctSignatures, proofs: &RctProofs,
mut verifier: impl BatchVerifier, mut verifier: impl BatchVerifier,
) -> Result<(), RingCTError> { ) -> Result<(), RingCTError> {
let commitments = &rct_sig.base.commitments; let commitments = &proofs.base.commitments;
match &rct_sig.prunable { match &proofs.prunable {
RctPrunable::Null => Err(RingCTError::TypeNotAllowed)?,
RctPrunable::MlsagBorromean { borromean, .. } RctPrunable::MlsagBorromean { borromean, .. }
| RctPrunable::AggregateMlsagBorromean { borromean, .. } => try_par_iter(borromean) | RctPrunable::AggregateMlsagBorromean { borromean, .. } => try_par_iter(borromean)
.zip(commitments) .zip(commitments)
@ -106,10 +107,11 @@ fn check_output_range_proofs(
Err(RingCTError::BorromeanRangeInvalid) Err(RingCTError::BorromeanRangeInvalid)
} }
}), }),
RctPrunable::MlsagBulletproofs { bulletproofs, .. } RctPrunable::MlsagBulletproofs { bulletproof, .. }
| RctPrunable::Clsag { bulletproofs, .. } => { | RctPrunable::MlsagBulletproofsCompactAmount { bulletproof, .. }
| RctPrunable::Clsag { bulletproof, .. } => {
if verifier.queue_statement(|verifier| { if verifier.queue_statement(|verifier| {
bulletproofs.batch_verify(&mut thread_rng(), verifier, (), commitments) bulletproof.batch_verify(&mut thread_rng(), verifier, commitments)
}) { }) {
Ok(()) Ok(())
} else { } else {
@ -120,18 +122,18 @@ fn check_output_range_proofs(
} }
pub(crate) fn ring_ct_semantic_checks( pub(crate) fn ring_ct_semantic_checks(
tx: &Transaction, proofs: &RctProofs,
tx_hash: &[u8; 32], tx_hash: &[u8; 32],
verifier: impl BatchVerifier, verifier: impl BatchVerifier,
hf: &HardFork, hf: &HardFork,
) -> Result<(), RingCTError> { ) -> Result<(), RingCTError> {
let rct_type = tx.rct_signatures.rct_type(); let rct_type = proofs.rct_type();
check_rct_type(&rct_type, *hf, tx_hash)?; check_rct_type(&rct_type, *hf, tx_hash)?;
check_output_range_proofs(&tx.rct_signatures, verifier)?; check_output_range_proofs(&proofs, verifier)?;
if rct_type != RctType::MlsagAggregate { if rct_type != RctType::AggregateMlsagBorromean {
simple_type_balances(&tx.rct_signatures)?; simple_type_balances(&proofs)?;
} }
Ok(()) Ok(())
@ -144,7 +146,7 @@ pub(crate) fn ring_ct_semantic_checks(
pub(crate) fn check_input_signatures( pub(crate) fn check_input_signatures(
msg: &[u8; 32], msg: &[u8; 32],
inputs: &[Input], inputs: &[Input],
rct_sig: &RctSignatures, proofs: &RctProofs,
rings: &Rings, rings: &Rings,
) -> Result<(), RingCTError> { ) -> Result<(), RingCTError> {
let Rings::RingCT(rings) = rings else { let Rings::RingCT(rings) = rings else {
@ -155,15 +157,15 @@ pub(crate) fn check_input_signatures(
Err(RingCTError::RingInvalid)?; Err(RingCTError::RingInvalid)?;
} }
let pseudo_outs = match &rct_sig.prunable { let pseudo_outs = match &proofs.prunable {
RctPrunable::MlsagBulletproofs { pseudo_outs, .. } RctPrunable::MlsagBulletproofs { pseudo_outs, .. }
| RctPrunable::MlsagBulletproofsCompactAmount { pseudo_outs, .. }
| RctPrunable::Clsag { pseudo_outs, .. } => pseudo_outs.as_slice(), | RctPrunable::Clsag { pseudo_outs, .. } => pseudo_outs.as_slice(),
RctPrunable::MlsagBorromean { .. } => rct_sig.base.pseudo_outs.as_slice(), RctPrunable::MlsagBorromean { .. } => proofs.base.pseudo_outs.as_slice(),
RctPrunable::AggregateMlsagBorromean { .. } | RctPrunable::Null => &[], RctPrunable::AggregateMlsagBorromean { .. } => &[],
}; };
match &rct_sig.prunable { match &proofs.prunable {
RctPrunable::Null => Err(RingCTError::TypeNotAllowed)?,
RctPrunable::AggregateMlsagBorromean { mlsag, .. } => { RctPrunable::AggregateMlsagBorromean { mlsag, .. } => {
let key_images = inputs let key_images = inputs
.iter() .iter()
@ -176,11 +178,14 @@ pub(crate) fn check_input_signatures(
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut matrix = let mut matrix =
AggregateRingMatrixBuilder::new(&rct_sig.base.commitments, rct_sig.base.fee); AggregateRingMatrixBuilder::new(&proofs.base.commitments, proofs.base.fee);
rings.iter().try_for_each(|ring| matrix.push_ring(ring))?; rings.iter().try_for_each(|ring| matrix.push_ring(ring))?;
Ok(mlsag.verify(msg, &matrix.build()?, &key_images)?) Ok(mlsag.verify(msg, &matrix.build()?, &key_images)?)
} }
RctPrunable::MlsagBorromean { mlsags, .. } RctPrunable::MlsagBorromean { mlsags, .. }
| RctPrunable::MlsagBulletproofsCompactAmount { mlsags, .. }
| RctPrunable::MlsagBulletproofs { mlsags, .. } => try_par_iter(mlsags) | RctPrunable::MlsagBulletproofs { mlsags, .. } => try_par_iter(mlsags)
.zip(pseudo_outs) .zip(pseudo_outs)
.zip(inputs) .zip(inputs)
@ -216,18 +221,21 @@ mod tests {
#[test] #[test]
fn grandfathered_bulletproofs2() { fn grandfathered_bulletproofs2() {
assert!( assert!(check_rct_type(
check_rct_type(&RctType::BulletproofsCompactAmount, HardFork::V14, &[0; 32]).is_err() &RctType::MlsagBulletproofsCompactAmount,
); HardFork::V14,
&[0; 32]
)
.is_err());
assert!(check_rct_type( assert!(check_rct_type(
&RctType::BulletproofsCompactAmount, &RctType::MlsagBulletproofsCompactAmount,
HardFork::V14, HardFork::V14,
&GRANDFATHERED_TRANSACTIONS[0] &GRANDFATHERED_TRANSACTIONS[0]
) )
.is_ok()); .is_ok());
assert!(check_rct_type( assert!(check_rct_type(
&RctType::BulletproofsCompactAmount, &RctType::MlsagBulletproofsCompactAmount,
HardFork::V14, HardFork::V14,
&GRANDFATHERED_TRANSACTIONS[1] &GRANDFATHERED_TRANSACTIONS[1]
) )

View file

@ -97,31 +97,6 @@ fn test_torsion_ki() {
} }
} }
/// Returns a strategy that resolves to a [`RctType`] that uses
/// BPs(+).
#[allow(unreachable_code)]
#[allow(clippy::diverging_sub_expression)]
fn bulletproof_rct_type() -> BoxedStrategy<RctType> {
return prop_oneof![
Just(RctType::Bulletproofs),
Just(RctType::BulletproofsCompactAmount),
Just(RctType::Clsag),
Just(RctType::BulletproofsPlus),
]
.boxed();
// Here to make sure this is updated when needed.
match unreachable!() {
RctType::Null => {}
RctType::MlsagAggregate => {}
RctType::MlsagIndividual => {}
RctType::Bulletproofs => {}
RctType::BulletproofsCompactAmount => {}
RctType::Clsag => {}
RctType::BulletproofsPlus => {}
};
}
prop_compose! { prop_compose! {
/// Returns a valid prime-order point. /// Returns a valid prime-order point.
fn random_point()(bytes in any::<[u8; 32]>()) -> EdwardsPoint { fn random_point()(bytes in any::<[u8; 32]>()) -> EdwardsPoint {
@ -240,13 +215,13 @@ proptest! {
} }
#[test] #[test]
fn test_valid_number_of_outputs(valid_numb_outs in 2..17_usize, rct_type in bulletproof_rct_type()) { fn test_valid_number_of_outputs(valid_numb_outs in 2..17_usize) {
prop_assert!(check_number_of_outputs(valid_numb_outs, &HardFork::V16, &TxVersion::RingCT, &rct_type).is_ok()); prop_assert!(check_number_of_outputs(valid_numb_outs, &HardFork::V16, &TxVersion::RingCT, true).is_ok());
} }
#[test] #[test]
fn test_invalid_number_of_outputs(numb_outs in 17..usize::MAX, rct_type in bulletproof_rct_type()) { fn test_invalid_number_of_outputs(numb_outs in 17..usize::MAX) {
prop_assert!(check_number_of_outputs(numb_outs, &HardFork::V16, &TxVersion::RingCT, &rct_type).is_err()); prop_assert!(check_number_of_outputs(numb_outs, &HardFork::V16, &TxVersion::RingCT, true).is_err());
} }
#[test] #[test]

View file

@ -1,12 +1,14 @@
use std::{cell::RefCell, ops::DerefMut}; use std::{cell::RefCell, ops::DerefMut};
use multiexp::BatchVerifier as InternalBatchVerifier; use monero_serai::ringct::bulletproofs::BatchVerifier as InternalBatchVerifier;
use rayon::prelude::*; use rayon::prelude::*;
use thread_local::ThreadLocal; use thread_local::ThreadLocal;
use cuprate_consensus_rules::batch_verifier::BatchVerifier;
/// A multithreaded batch verifier. /// A multithreaded batch verifier.
pub struct MultiThreadedBatchVerifier { pub struct MultiThreadedBatchVerifier {
internal: ThreadLocal<RefCell<InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>>>, internal: ThreadLocal<RefCell<InternalBatchVerifier>>,
} }
impl MultiThreadedBatchVerifier { impl MultiThreadedBatchVerifier {
@ -22,19 +24,22 @@ impl MultiThreadedBatchVerifier {
.into_iter() .into_iter()
.map(RefCell::into_inner) .map(RefCell::into_inner)
.par_bridge() .par_bridge()
.find_any(|batch_verifier| !batch_verifier.verify_vartime()) .try_for_each(|batch_verifier| {
.is_none() if batch_verifier.verify() {
Ok(())
} else {
Err(())
}
})
.is_ok()
} }
} }
impl cuprate_consensus_rules::batch_verifier::BatchVerifier for &'_ MultiThreadedBatchVerifier { impl BatchVerifier for &'_ MultiThreadedBatchVerifier {
fn queue_statement<R>( fn queue_statement<R>(&mut self, stmt: impl FnOnce(&mut InternalBatchVerifier) -> R) -> R {
&mut self,
stmt: impl FnOnce(&mut InternalBatchVerifier<(), dalek_ff_group::EdwardsPoint>) -> R,
) -> R {
let mut verifier = self let mut verifier = self
.internal .internal
.get_or(|| RefCell::new(InternalBatchVerifier::new(32))) .get_or(|| RefCell::new(InternalBatchVerifier::new()))
.borrow_mut(); .borrow_mut();
stmt(verifier.deref_mut()) stmt(verifier.deref_mut())

View file

@ -70,7 +70,7 @@ impl PreparedBlockExPow {
let (hf_version, hf_vote) = let (hf_version, hf_vote) =
HardFork::from_block_header(&block.header).map_err(BlockError::HardForkError)?; HardFork::from_block_header(&block.header).map_err(BlockError::HardForkError)?;
let Some(Input::Gen(height)) = block.miner_tx.prefix.inputs.first() else { let Some(Input::Gen(height)) = block.miner_tx.prefix().inputs.first() else {
Err(ConsensusError::Block(BlockError::MinerTxError( Err(ConsensusError::Block(BlockError::MinerTxError(
MinerTxError::InputNotOfTypeGen, MinerTxError::InputNotOfTypeGen,
)))? )))?
@ -124,7 +124,7 @@ impl PreparedBlock {
let (hf_version, hf_vote) = let (hf_version, hf_vote) =
HardFork::from_block_header(&block.header).map_err(BlockError::HardForkError)?; HardFork::from_block_header(&block.header).map_err(BlockError::HardForkError)?;
let Some(Input::Gen(height)) = block.miner_tx.prefix.inputs.first() else { let Some(Input::Gen(height)) = block.miner_tx.prefix().inputs.first() else {
Err(ConsensusError::Block(BlockError::MinerTxError( Err(ConsensusError::Block(BlockError::MinerTxError(
MinerTxError::InputNotOfTypeGen, MinerTxError::InputNotOfTypeGen,
)))? )))?
@ -138,7 +138,7 @@ impl PreparedBlock {
block_hash: block.hash(), block_hash: block.hash(),
pow_hash: calculate_pow_hash( pow_hash: calculate_pow_hash(
randomx_vm, randomx_vm,
&block.serialize_hashable(), &block.serialize_pow_hash(),
*height, *height,
&hf_version, &hf_version,
)?, )?,
@ -168,7 +168,7 @@ impl PreparedBlock {
block_hash: block.block_hash, block_hash: block.block_hash,
pow_hash: calculate_pow_hash( pow_hash: calculate_pow_hash(
randomx_vm, randomx_vm,
&block.block.serialize_hashable(), &block.block.serialize_pow_hash(),
block.height, block.height,
&block.hf_version, &block.hf_version,
)?, )?,
@ -527,9 +527,8 @@ where
); );
// Set up the block and just pass it to [`verify_prepped_main_chain_block`] // Set up the block and just pass it to [`verify_prepped_main_chain_block`]
// We just use the raw `hardfork_version` here, no need to turn it into a `HardFork`.
// We just use the raw `major_version` here, no need to turn it into a `HardFork`. let rx_vms = if block.header.hardfork_version < 12 {
let rx_vms = if block.header.major_version < 12 {
HashMap::new() HashMap::new()
} else { } else {
let BlockChainContextResponse::RxVms(rx_vms) = context_svc let BlockChainContextResponse::RxVms(rx_vms) = context_svc

View file

@ -12,6 +12,7 @@ use std::{
}; };
use futures::FutureExt; use futures::FutureExt;
use monero_serai::ringct::bulletproofs::Bulletproof;
use monero_serai::{ use monero_serai::{
ringct::RctType, ringct::RctType,
transaction::{Input, Timelock, Transaction}, transaction::{Input, Timelock, Transaction},
@ -104,21 +105,62 @@ impl TransactionVerificationData {
let tx_blob = tx.serialize(); let tx_blob = tx.serialize();
// the tx weight is only different from the blobs length for bp(+) txs. // the tx weight is only different from the blobs length for bp(+) txs.
let tx_weight = match tx.rct_signatures.rct_type() { let tx_weight = match &tx {
RctType::Bulletproofs Transaction::V1 { .. } | Transaction::V2 { proofs: None, .. } => tx_blob.len(),
| RctType::BulletproofsCompactAmount Transaction::V2 {
| RctType::Clsag proofs: Some(proofs),
| RctType::BulletproofsPlus => tx.weight(), ..
_ => tx_blob.len(), } => match proofs.rct_type() {
RctType::AggregateMlsagBorromean | RctType::MlsagBorromean => tx_blob.len(),
RctType::MlsagBulletproofs
| RctType::MlsagBulletproofsCompactAmount
| RctType::ClsagBulletproof => {
tx_blob.len()
+ Bulletproof::calculate_bp_clawback(false, tx.prefix().outputs.len()).0
}
RctType::ClsagBulletproofPlus => {
tx_blob.len()
+ Bulletproof::calculate_bp_clawback(true, tx.prefix().outputs.len()).0
}
},
};
let mut fee = 0_u64;
match &tx {
Transaction::V1 { prefix, .. } => {
for input in &prefix.inputs {
match input {
Input::ToKey { amount, .. } => {
fee = fee
.checked_add(amount.unwrap_or(0))
.ok_or(TransactionError::InputsOverflow)?;
}
_ => (),
}
}
for output in &prefix.outputs {
fee.checked_sub(output.amount.unwrap_or(0))
.ok_or(TransactionError::OutputsTooHigh)?;
}
}
Transaction::V2 { proofs, .. } => {
fee = proofs
.as_ref()
.ok_or(TransactionError::TransactionVersionInvalid)?
.base
.fee;
}
}; };
Ok(TransactionVerificationData { Ok(TransactionVerificationData {
tx_hash, tx_hash,
tx_blob, tx_blob,
tx_weight, tx_weight,
fee: tx.rct_signatures.base.fee, fee,
cached_verification_state: StdMutex::new(CachedVerificationState::NotVerified), cached_verification_state: StdMutex::new(CachedVerificationState::NotVerified),
version: TxVersion::from_raw(tx.prefix.version) version: TxVersion::from_raw(tx.version())
.ok_or(TransactionError::TransactionVersionInvalid)?, .ok_or(TransactionError::TransactionVersionInvalid)?,
tx, tx,
}) })
@ -296,7 +338,7 @@ where
let mut spent_kis = HashSet::with_capacity(txs.len()); let mut spent_kis = HashSet::with_capacity(txs.len());
txs.iter().try_for_each(|tx| { txs.iter().try_for_each(|tx| {
tx.tx.prefix.inputs.iter().try_for_each(|input| { tx.tx.prefix().inputs.iter().try_for_each(|input| {
if let Input::ToKey { key_image, .. } = input { if let Input::ToKey { key_image, .. } = input {
if !spent_kis.insert(key_image.compress().0) { if !spent_kis.insert(key_image.compress().0) {
tracing::debug!("Duplicate key image found in batch."); tracing::debug!("Duplicate key image found in batch.");
@ -499,7 +541,7 @@ where
&hf, &hf,
&batch_verifier, &batch_verifier,
)?; )?;
// make sure monero-serai calculated the same fee. // make sure we calculated the right fee.
assert_eq!(fee, tx.fee); assert_eq!(fee, tx.fee);
} }

View file

@ -149,7 +149,7 @@ pub async fn batch_get_ring_member_info<D: Database>(
let mut output_ids = HashMap::new(); let mut output_ids = HashMap::new();
for tx_v_data in txs_verification_data.clone() { for tx_v_data in txs_verification_data.clone() {
insert_ring_member_ids(&tx_v_data.tx.prefix.inputs, &mut output_ids) insert_ring_member_ids(&tx_v_data.tx.prefix().inputs, &mut output_ids)
.map_err(ConsensusError::Transaction)?; .map_err(ConsensusError::Transaction)?;
} }
@ -179,14 +179,14 @@ pub async fn batch_get_ring_member_info<D: Database>(
let ring_members_for_tx = get_ring_members_for_inputs( let ring_members_for_tx = get_ring_members_for_inputs(
|amt, idx| outputs.get(&amt)?.get(&idx).copied(), |amt, idx| outputs.get(&amt)?.get(&idx).copied(),
&tx_v_data.tx.prefix.inputs, &tx_v_data.tx.prefix().inputs,
) )
.map_err(ConsensusError::Transaction)?; .map_err(ConsensusError::Transaction)?;
let decoy_info = if hf != &HardFork::V1 { let decoy_info = if hf != &HardFork::V1 {
// this data is only needed after hard-fork 1. // this data is only needed after hard-fork 1.
Some( Some(
DecoyInfo::new(&tx_v_data.tx.prefix.inputs, numb_outputs, hf) DecoyInfo::new(&tx_v_data.tx.prefix().inputs, numb_outputs, hf)
.map_err(ConsensusError::Transaction)?, .map_err(ConsensusError::Transaction)?,
) )
} else { } else {
@ -222,7 +222,7 @@ pub async fn batch_get_decoy_info<'a, D: Database + Clone + Send + 'static>(
let unique_input_amounts = txs_verification_data let unique_input_amounts = txs_verification_data
.iter() .iter()
.flat_map(|tx_info| { .flat_map(|tx_info| {
tx_info.tx.prefix.inputs.iter().map(|input| match input { tx_info.tx.prefix().inputs.iter().map(|input| match input {
Input::ToKey { amount, .. } => amount.unwrap_or(0), Input::ToKey { amount, .. } => amount.unwrap_or(0),
_ => 0, _ => 0,
}) })
@ -247,7 +247,7 @@ pub async fn batch_get_decoy_info<'a, D: Database + Clone + Send + 'static>(
Ok(txs_verification_data.iter().map(move |tx_v_data| { Ok(txs_verification_data.iter().map(move |tx_v_data| {
DecoyInfo::new( DecoyInfo::new(
&tx_v_data.tx.prefix.inputs, &tx_v_data.tx.prefix().inputs,
|amt| outputs_with_amount.get(&amt).copied().unwrap_or(0), |amt| outputs_with_amount.get(&amt).copied().unwrap_or(0),
&hf, &hf,
) )

View file

@ -11,7 +11,6 @@ use futures::{FutureExt, StreamExt};
use indexmap::IndexMap; use indexmap::IndexMap;
use monero_serai::{ use monero_serai::{
block::{Block, BlockHeader}, block::{Block, BlockHeader},
ringct::{RctBase, RctPrunable, RctSignatures},
transaction::{Input, Timelock, Transaction, TransactionPrefix}, transaction::{Input, Timelock, Transaction, TransactionPrefix},
}; };
use proptest::{collection::vec, prelude::*}; use proptest::{collection::vec, prelude::*};
@ -98,24 +97,14 @@ prop_compose! {
timelock in 0_usize..50_000_000, timelock in 0_usize..50_000_000,
) )
-> Transaction { -> Transaction {
Transaction { Transaction::V1 {
prefix: TransactionPrefix { prefix: TransactionPrefix {
version: 1,
timelock: Timelock::Block(timelock), timelock: Timelock::Block(timelock),
inputs: vec![Input::Gen(height)], inputs: vec![Input::Gen(height)],
outputs: vec![], outputs: vec![],
extra, extra,
}, },
signatures: vec![], signatures: vec![],
rct_signatures: RctSignatures {
base: RctBase {
fee: 0,
pseudo_outs: vec![],
encrypted_amounts: vec![],
commitments: vec![],
},
prunable: RctPrunable::Null
},
} }
} }
} }
@ -134,8 +123,8 @@ prop_compose! {
( (
Block { Block {
header: BlockHeader { header: BlockHeader {
major_version: 0, hardfork_version: 0,
minor_version: 0, hardfork_signal: 0,
timestamp: 0, timestamp: 0,
previous, previous,
nonce: 0, nonce: 0,
@ -169,7 +158,7 @@ prop_compose! {
for (height, mut block) in blocks.into_iter().enumerate() { for (height, mut block) in blocks.into_iter().enumerate() {
if let Some(last) = blockchain.last() { if let Some(last) = blockchain.last() {
block.0.header.previous = *last.0; block.0.header.previous = *last.0;
block.0.miner_tx.prefix.inputs = vec![Input::Gen(height as u64)] block.0.miner_tx.prefix_mut().inputs = vec![Input::Gen(height as u64)]
} }
blockchain.insert(block.0.hash(), block); blockchain.insert(block.0.hash(), block);

View file

@ -200,8 +200,8 @@ pub fn get_block_extended_header_from_height(
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
Ok(ExtendedBlockHeader { Ok(ExtendedBlockHeader {
cumulative_difficulty, cumulative_difficulty,
version: block.header.major_version, version: block.header.hardfork_version,
vote: block.header.minor_version, vote: block.header.hardfork_signal,
timestamp: block.header.timestamp, timestamp: block.header.timestamp,
block_weight: block_info.weight as usize, block_weight: block_info.weight as usize,
long_term_weight: block_info.long_term_weight as usize, long_term_weight: block_info.long_term_weight as usize,
@ -369,8 +369,8 @@ mod test {
let b1 = block_header_from_hash; let b1 = block_header_from_hash;
let b2 = block; let b2 = block;
assert_eq!(b1, block_header_from_height); assert_eq!(b1, block_header_from_height);
assert_eq!(b1.version, b2.block.header.major_version); assert_eq!(b1.version, b2.block.header.hardfork_version);
assert_eq!(b1.vote, b2.block.header.minor_version); assert_eq!(b1.vote, b2.block.header.hardfork_signal);
assert_eq!(b1.timestamp, b2.block.header.timestamp); assert_eq!(b1.timestamp, b2.block.header.timestamp);
assert_eq!(b1.cumulative_difficulty, b2.cumulative_difficulty); assert_eq!(b1.cumulative_difficulty, b2.cumulative_difficulty);
assert_eq!(b1.block_weight, b2.weight); assert_eq!(b1.block_weight, b2.weight);

View file

@ -2,7 +2,7 @@
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::CompressedEdwardsY, Scalar}; use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::CompressedEdwardsY, Scalar};
use monero_serai::{transaction::Timelock, H}; use monero_serai::{generators::H, transaction::Timelock};
use cuprate_database::{ use cuprate_database::{
RuntimeError, {DatabaseRo, DatabaseRw}, RuntimeError, {DatabaseRo, DatabaseRw},

View file

@ -68,7 +68,7 @@ pub fn add_tx(
// so the `u64/usize` is stored without any tag. // so the `u64/usize` is stored without any tag.
// //
// <https://github.com/Cuprate/cuprate/pull/102#discussion_r1558504285> // <https://github.com/Cuprate/cuprate/pull/102#discussion_r1558504285>
match tx.prefix.timelock { match tx.prefix().timelock {
Timelock::None => (), Timelock::None => (),
Timelock::Block(height) => tables.tx_unlock_time_mut().put(&tx_id, &(height as u64))?, Timelock::Block(height) => tables.tx_unlock_time_mut().put(&tx_id, &(height as u64))?,
Timelock::Time(time) => tables.tx_unlock_time_mut().put(&tx_id, &time)?, Timelock::Time(time) => tables.tx_unlock_time_mut().put(&tx_id, &time)?,
@ -92,7 +92,7 @@ pub fn add_tx(
let mut miner_tx = false; let mut miner_tx = false;
// Key images. // Key images.
for inputs in &tx.prefix.inputs { for inputs in &tx.prefix().inputs {
match inputs { match inputs {
// Key images. // Key images.
Input::ToKey { key_image, .. } => { Input::ToKey { key_image, .. } => {
@ -106,70 +106,69 @@ pub fn add_tx(
//------------------------------------------------------ Outputs //------------------------------------------------------ Outputs
// Output bit flags. // Output bit flags.
// Set to a non-zero bit value if the unlock time is non-zero. // Set to a non-zero bit value if the unlock time is non-zero.
let output_flags = match tx.prefix.timelock { let output_flags = match tx.prefix().timelock {
Timelock::None => OutputFlags::empty(), Timelock::None => OutputFlags::empty(),
Timelock::Block(_) | Timelock::Time(_) => OutputFlags::NON_ZERO_UNLOCK_TIME, Timelock::Block(_) | Timelock::Time(_) => OutputFlags::NON_ZERO_UNLOCK_TIME,
}; };
let mut amount_indices = Vec::with_capacity(tx.prefix.outputs.len()); let amount_indices = match &tx {
Transaction::V1 { prefix, .. } => prefix
for (i, output) in tx.prefix.outputs.iter().enumerate() { .outputs
let key = *output.key.as_bytes(); .iter()
.map(|output| {
// Outputs with clear amounts. // Pre-RingCT outputs.
let amount_index = if let Some(amount) = output.amount { Ok(add_output(
// RingCT (v2 transaction) miner outputs. output.amount.unwrap_or(0),
if miner_tx && tx.prefix.version == 2 {
// Create commitment.
// <https://github.com/Cuprate/cuprate/pull/102#discussion_r1559489302>
// FIXME: implement lookup table for common values:
// <https://github.com/monero-project/monero/blob/c8214782fb2a769c57382a999eaf099691c836e7/src/ringct/rctOps.cpp#L322>
let commitment = (ED25519_BASEPOINT_POINT
+ monero_serai::H() * Scalar::from(amount))
.compress()
.to_bytes();
add_rct_output(
&RctOutput {
key,
height,
output_flags,
tx_idx: tx_id,
commitment,
},
tables.rct_outputs_mut(),
)?
// Pre-RingCT outputs.
} else {
add_output(
amount,
&Output { &Output {
key, key: output.key.0,
height, height,
output_flags, output_flags,
tx_idx: tx_id, tx_idx: tx_id,
}, },
tables, tables,
)? )?
.amount_index .amount_index)
} })
// RingCT outputs. .collect::<Result<Vec<_>, RuntimeError>>()?,
} else { Transaction::V2 { prefix, proofs } => prefix
let commitment = tx.rct_signatures.base.commitments[i].compress().to_bytes(); .outputs
add_rct_output( .iter()
&RctOutput { .enumerate()
key, .map(|(i, output)| {
height, // Create commitment.
output_flags, // <https://github.com/Cuprate/cuprate/pull/102#discussion_r1559489302>
tx_idx: tx_id, // FIXME: implement lookup table for common values:
commitment, // <https://github.com/monero-project/monero/blob/c8214782fb2a769c57382a999eaf099691c836e7/src/ringct/rctOps.cpp#L322>
}, let commitment = if miner_tx {
tables.rct_outputs_mut(), ED25519_BASEPOINT_POINT
)? + monero_serai::generators::H() * Scalar::from(output.amount.unwrap_or(0))
}; } else {
let commitment = proofs
.as_ref()
.expect("A V2 transaction with no RCT proofs is a miner tx")
.base
.commitments[i];
amount_indices.push(amount_index); commitment
} // for each output };
(commitment, output.key.0)
})
.map(|(commitment, key)| {
// Add the RCT output.
add_rct_output(
&RctOutput {
key,
height,
output_flags,
tx_idx: tx_id,
commitment: commitment.compress().0,
},
tables.rct_outputs_mut(),
)
})
.collect::<Result<Vec<_>, _>>()?,
};
tables tables
.tx_outputs_mut() .tx_outputs_mut()
@ -227,7 +226,7 @@ pub fn remove_tx(
//------------------------------------------------------ Key Images //------------------------------------------------------ Key Images
// Is this a miner transaction? // Is this a miner transaction?
let mut miner_tx = false; let mut miner_tx = false;
for inputs in &tx.prefix.inputs { for inputs in &tx.prefix().inputs {
match inputs { match inputs {
// Key images. // Key images.
Input::ToKey { key_image, .. } => { Input::ToKey { key_image, .. } => {
@ -240,11 +239,11 @@ pub fn remove_tx(
//------------------------------------------------------ Outputs //------------------------------------------------------ Outputs
// Remove each output in the transaction. // Remove each output in the transaction.
for output in &tx.prefix.outputs { for output in &tx.prefix().outputs {
// Outputs with clear amounts. // Outputs with clear amounts.
if let Some(amount) = output.amount { if let Some(amount) = output.amount {
// RingCT miner outputs. // RingCT miner outputs.
if miner_tx && tx.prefix.version == 2 { if miner_tx && tx.version() == 2 {
let amount_index = get_rct_num_outputs(tables.rct_outputs())? - 1; let amount_index = get_rct_num_outputs(tables.rct_outputs())? - 1;
remove_rct_output(&amount_index, tables.rct_outputs_mut())?; remove_rct_output(&amount_index, tables.rct_outputs_mut())?;
// Pre-RingCT outputs. // Pre-RingCT outputs.

View file

@ -13,7 +13,9 @@ cuprate-p2p-core = { path = "../p2p/p2p-core", features = ["borsh"] }
hex = { workspace = true } hex = { workspace = true }
hex-literal = { workspace = true } hex-literal = { workspace = true }
monero-serai = { workspace = true, features = ["std", "http-rpc"] } monero-serai = { workspace = true, features = ["std"] }
monero-simple-request-rpc = { workspace = true }
monero-rpc = { workspace = true }
futures = { workspace = true, features = ["std"] } futures = { workspace = true, features = ["std"] }
async-trait = { workspace = true } async-trait = { workspace = true }
tokio = { workspace = true, features = ["full"] } tokio = { workspace = true, features = ["full"] }

View file

@ -103,7 +103,8 @@ fn to_tx_verification_data(tx_blob: impl AsRef<[u8]>) -> VerifiedTransactionInfo
let tx = Transaction::read(&mut tx_blob.as_slice()).unwrap(); let tx = Transaction::read(&mut tx_blob.as_slice()).unwrap();
VerifiedTransactionInformation { VerifiedTransactionInformation {
tx_weight: tx.weight(), tx_weight: tx.weight(),
fee: tx.rct_signatures.base.fee, // TODO:
fee: 0,
tx_hash: tx.hash(), tx_hash: tx.hash(),
tx_blob, tx_blob,
tx, tx,
@ -255,7 +256,7 @@ macro_rules! transaction_verification_data_fn {
#[doc = concat!("assert_eq!(tx.tx_blob, ", stringify!($tx_blob), ");")] #[doc = concat!("assert_eq!(tx.tx_blob, ", stringify!($tx_blob), ");")]
#[doc = concat!("assert_eq!(tx.tx_weight, ", $weight, ");")] #[doc = concat!("assert_eq!(tx.tx_weight, ", $weight, ");")]
#[doc = concat!("assert_eq!(tx.tx_hash, hex!(\"", $hash, "\"));")] #[doc = concat!("assert_eq!(tx.tx_hash, hex!(\"", $hash, "\"));")]
#[doc = "assert_eq!(tx.fee, tx.tx.rct_signatures.base.fee);"] // #[doc = "assert_eq!(tx.fee, tx.tx.rct_signatures.base.fee);"]
/// ``` /// ```
pub fn $fn_name() -> &'static VerifiedTransactionInformation { pub fn $fn_name() -> &'static VerifiedTransactionInformation {
static TX: OnceLock<VerifiedTransactionInformation> = OnceLock::new(); static TX: OnceLock<VerifiedTransactionInformation> = OnceLock::new();

View file

@ -5,10 +5,9 @@ use serde::Deserialize;
use serde_json::json; use serde_json::json;
use tokio::task::spawn_blocking; use tokio::task::spawn_blocking;
use monero_serai::{ use monero_rpc::Rpc;
block::Block, use monero_serai::block::Block;
rpc::{HttpRpc, Rpc}, use monero_simple_request_rpc::SimpleRequestRpc;
};
use cuprate_types::{VerifiedBlockInformation, VerifiedTransactionInformation}; use cuprate_types::{VerifiedBlockInformation, VerifiedTransactionInformation};
@ -18,7 +17,7 @@ use crate::rpc::constants::LOCALHOST_RPC_URL;
/// An HTTP RPC client for Monero. /// An HTTP RPC client for Monero.
pub struct HttpRpcClient { pub struct HttpRpcClient {
address: String, address: String,
rpc: Rpc<HttpRpc>, rpc: SimpleRequestRpc,
} }
impl HttpRpcClient { impl HttpRpcClient {
@ -38,7 +37,7 @@ impl HttpRpcClient {
let address = address.unwrap_or_else(|| LOCALHOST_RPC_URL.to_string()); let address = address.unwrap_or_else(|| LOCALHOST_RPC_URL.to_string());
Self { Self {
rpc: HttpRpc::new(address.clone()).await.unwrap(), rpc: SimpleRequestRpc::new(address.clone()).await.unwrap(),
address, address,
} }
} }
@ -51,7 +50,7 @@ impl HttpRpcClient {
/// Access to the inner RPC client for other usage. /// Access to the inner RPC client for other usage.
#[allow(dead_code)] #[allow(dead_code)]
const fn rpc(&self) -> &Rpc<HttpRpc> { const fn rpc(&self) -> &SimpleRequestRpc {
&self.rpc &self.rpc
} }
@ -123,7 +122,7 @@ impl HttpRpcClient {
let total_tx_fees = txs.iter().map(|tx| tx.fee).sum::<u64>(); let total_tx_fees = txs.iter().map(|tx| tx.fee).sum::<u64>();
let generated_coins = block let generated_coins = block
.miner_tx .miner_tx
.prefix .prefix()
.outputs .outputs
.iter() .iter()
.map(|output| output.amount.expect("miner_tx amount was None")) .map(|output| output.amount.expect("miner_tx amount was None"))
@ -171,7 +170,8 @@ impl HttpRpcClient {
tx_blob: tx.serialize(), tx_blob: tx.serialize(),
tx_weight: tx.weight(), tx_weight: tx.weight(),
tx_hash, tx_hash,
fee: tx.rct_signatures.base.fee, // TODO: fix this.
fee: 0,
tx, tx,
} }
}) })