Skip to content

Commit 818885b

Browse files
committed
masp: check size and hash of params in node
1 parent 3b38092 commit 818885b

2 files changed

Lines changed: 134 additions & 62 deletions

File tree

shared/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ wasmer-engine-universal = {version = "=2.2.0", optional = true}
128128
wasmer-vm = {version = "2.2.0", optional = true}
129129
wasmparser.workspace = true
130130
masp_primitives.workspace = true
131-
masp_proofs.workspace = true
131+
masp_proofs = { workspace = true, features = ["download-params"] }
132132
rand = {version = "0.8", default-features = false, optional = true}
133133
rand_core = {version = "0.6", default-features = false, optional = true}
134134
zeroize.workspace = true

shared/src/ledger/masp.rs

Lines changed: 133 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use std::collections::hash_map::Entry;
44
use std::collections::{BTreeMap, HashMap, HashSet};
55
use std::env;
66
use std::fmt::Debug;
7-
use std::fs::File;
87
#[cfg(feature = "masp-tx-gen")]
98
use std::ops::Deref;
109
use std::path::PathBuf;
@@ -44,9 +43,7 @@ use masp_primitives::transaction::{
4443
TransparentAddress, Unauthorized,
4544
};
4645
use masp_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey};
47-
use masp_proofs::bellman::groth16::{
48-
prepare_verifying_key, PreparedVerifyingKey,
49-
};
46+
use masp_proofs::bellman::groth16::PreparedVerifyingKey;
5047
use masp_proofs::bls12_381::Bls12;
5148
use masp_proofs::prover::LocalTxProver;
5249
use masp_proofs::sapling::SaplingVerificationContext;
@@ -90,62 +87,37 @@ pub const OUTPUT_NAME: &str = "masp-output.params";
9087
/// Convert circuit name
9188
pub const CONVERT_NAME: &str = "masp-convert.params";
9289

93-
enum MaspParams {
94-
Spend,
95-
Convert,
96-
Output,
97-
}
98-
99-
/// Generic helper to load Sapling params.
100-
fn load_params(name: MaspParams) -> (
101-
masp_proofs::bellman::groth16::Parameters<Bls12>,
102-
masp_proofs::bellman::groth16::PreparedVerifyingKey<Bls12>,
90+
fn load_pvks() -> (
91+
PreparedVerifyingKey<Bls12>,
92+
PreparedVerifyingKey<Bls12>,
93+
PreparedVerifyingKey<Bls12>,
10394
) {
104-
let params_name = match name {
105-
MaspParams::Spend => SPEND_NAME,
106-
MaspParams::Convert => CONVERT_NAME,
107-
MaspParams::Output => OUTPUT_NAME,
108-
};
109-
11095
let params_dir = get_params_dir();
111-
let params_path = params_dir.join(params_name);
112-
if !params_path.exists() {
113-
#[cfg(feature = "masp_proofs/download-params")]
114-
masp_proofs::download_parameters()
115-
.expect("MASP parameters not present or downloadable");
116-
#[cfg(not(feature = "masp_proofs/download-params"))]
117-
panic!("MASP parameters not present or downloadable");
118-
}
119-
let param_f = File::open(params_path).unwrap();
120-
let params =
121-
masp_proofs::bellman::groth16::Parameters::read(&param_f, false)
122-
.unwrap();
123-
let vk = prepare_verifying_key(&params.vk);
124-
(params, vk)
125-
}
126-
127-
/// Load Sapling spend params.
128-
pub fn load_spend_params() -> (
129-
masp_proofs::bellman::groth16::Parameters<Bls12>,
130-
masp_proofs::bellman::groth16::PreparedVerifyingKey<Bls12>,
131-
) {
132-
load_params(MaspParams::Spend)
133-
}
96+
let [spend_path, convert_path, output_path] =
97+
[SPEND_NAME, CONVERT_NAME, OUTPUT_NAME].map(|p| params_dir.join(p));
13498

135-
/// Load Sapling convert params.
136-
pub fn load_convert_params() -> (
137-
masp_proofs::bellman::groth16::Parameters<Bls12>,
138-
masp_proofs::bellman::groth16::PreparedVerifyingKey<Bls12>,
139-
) {
140-
load_params(MaspParams::Convert)
141-
}
142-
143-
/// Load Sapling output params.
144-
pub fn load_output_params() -> (
145-
masp_proofs::bellman::groth16::Parameters<Bls12>,
146-
masp_proofs::bellman::groth16::PreparedVerifyingKey<Bls12>,
147-
) {
148-
load_params(MaspParams::Output)
99+
if !spend_path.exists() || !convert_path.exists() || !output_path.exists() {
100+
let paths = masp_proofs::download_masp_parameters(None).expect(
101+
"MASP parameters were not present, expected the download to \
102+
succeed",
103+
);
104+
if paths.spend != spend_path
105+
|| paths.convert != convert_path
106+
|| paths.output != output_path
107+
{
108+
panic!(
109+
"unrecoverable: downloaded missing masp params, but to an \
110+
unfamiliar path"
111+
)
112+
}
113+
}
114+
// size and blake2b checked here
115+
let params = masp_proofs::load_parameters(
116+
spend_path.as_path(),
117+
output_path.as_path(),
118+
convert_path.as_path(),
119+
);
120+
(params.spend_vk, params.convert_vk, params.output_vk)
149121
}
150122

151123
/// check_spend wrapper
@@ -277,9 +249,7 @@ pub fn verify_shielded_tx(transaction: &Transaction) -> bool {
277249

278250
tracing::info!("sighash computed");
279251

280-
let (_, spend_pvk) = load_spend_params();
281-
let (_, convert_pvk) = load_convert_params();
282-
let (_, output_pvk) = load_output_params();
252+
let (spend_pvk, convert_pvk, output_pvk) = load_pvks();
283253

284254
let mut ctx = SaplingVerificationContext::new(true);
285255
let spends_valid = sapling_bundle.shielded_spends.iter().all(|spend| {
@@ -365,7 +335,7 @@ impl<P1, R1, N1>
365335

366336
/// Abstracts platform specific details away from the logic of shielded pool
367337
/// operations.
368-
#[async_trait(?Send)]
338+
#[async_trait(? Send)]
369339
pub trait ShieldedUtils:
370340
Sized + BorshDeserialize + BorshSerialize + Default + Clone
371341
{
@@ -1546,3 +1516,105 @@ fn convert_amount(
15461516
.expect("invalid value for amount");
15471517
(asset_type, amount)
15481518
}
1519+
1520+
mod tests {
1521+
/// quick and dirty test. will fail on size check
1522+
#[test]
1523+
#[should_panic(expected = "parameter file size is not correct")]
1524+
fn test_wrong_masp_params() {
1525+
use std::io::Write;
1526+
1527+
use super::{CONVERT_NAME, OUTPUT_NAME, SPEND_NAME};
1528+
1529+
let tempdir = tempfile::tempdir()
1530+
.expect("expected a temp dir")
1531+
.into_path();
1532+
let fake_params_paths =
1533+
[SPEND_NAME, CONVERT_NAME, OUTPUT_NAME].map(|p| tempdir.join(p));
1534+
for path in fake_params_paths {
1535+
let mut f =
1536+
std::fs::File::create(path).expect("expected a temp file");
1537+
f.write_all(b"fake params")
1538+
.expect("expected a writable temp file");
1539+
f.sync_all()
1540+
.expect("expected a writable temp file (on sync)");
1541+
}
1542+
1543+
std::env::set_var(super::ENV_VAR_MASP_PARAMS_DIR, tempdir.as_os_str());
1544+
// should panic here
1545+
super::load_pvks();
1546+
}
1547+
1548+
/// a more involved test, using dummy parameters with the right
1549+
/// size but the wrong hash.
1550+
#[test]
1551+
#[should_panic(expected = "parameter file is not correct")]
1552+
fn test_wrong_masp_params_hash() {
1553+
use masp_primitives::ff::PrimeField;
1554+
use masp_proofs::bellman::groth16::{
1555+
generate_random_parameters, Parameters,
1556+
};
1557+
use masp_proofs::bellman::{Circuit, ConstraintSystem, SynthesisError};
1558+
use masp_proofs::bls12_381::{Bls12, Scalar};
1559+
1560+
use super::{CONVERT_NAME, OUTPUT_NAME, SPEND_NAME};
1561+
1562+
struct FakeCircuit<E: PrimeField> {
1563+
x: E,
1564+
}
1565+
1566+
impl<E: PrimeField> Circuit<E> for FakeCircuit<E> {
1567+
fn synthesize<CS: ConstraintSystem<E>>(
1568+
self,
1569+
cs: &mut CS,
1570+
) -> Result<(), SynthesisError> {
1571+
let x = cs.alloc(|| "x", || Ok(self.x)).unwrap();
1572+
cs.enforce(
1573+
|| {
1574+
"this is an extra long constraint name so that rustfmt \
1575+
is ok with wrapping the params of enforce()"
1576+
},
1577+
|lc| lc + x,
1578+
|lc| lc + x,
1579+
|lc| lc + x,
1580+
);
1581+
Ok(())
1582+
}
1583+
}
1584+
1585+
let dummy_circuit = FakeCircuit { x: Scalar::zero() };
1586+
let mut rng = rand::thread_rng();
1587+
let fake_params: Parameters<Bls12> =
1588+
generate_random_parameters(dummy_circuit, &mut rng)
1589+
.expect("expected to generate fake params");
1590+
1591+
let tempdir = tempfile::tempdir()
1592+
.expect("expected a temp dir")
1593+
.into_path();
1594+
// TODO: get masp to export these consts
1595+
let fake_params_paths = [
1596+
(SPEND_NAME, 49848572u64),
1597+
(CONVERT_NAME, 22570940u64),
1598+
(OUTPUT_NAME, 16398620u64),
1599+
]
1600+
.map(|(p, s)| (tempdir.join(p), s));
1601+
for (path, size) in fake_params_paths {
1602+
let mut f =
1603+
std::fs::File::create(path).expect("expected a temp file");
1604+
fake_params
1605+
.write(&mut f)
1606+
.expect("expected a writable temp file");
1607+
// the dummy circuit has one constraint, and therefore its
1608+
// params should always be smaller than the large masp
1609+
// circuit params. so this truncate extends the file, and
1610+
// extra bytes at the end do not make it invalid.
1611+
f.set_len(size).expect("expected to truncate the temp file");
1612+
f.sync_all()
1613+
.expect("expected a writable temp file (on sync)");
1614+
}
1615+
1616+
std::env::set_var(super::ENV_VAR_MASP_PARAMS_DIR, tempdir.as_os_str());
1617+
// should panic here
1618+
super::load_pvks();
1619+
}
1620+
}

0 commit comments

Comments
 (0)