mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 08:51:08 +03:00
Spdupverifypow (#3672)
* speed up cuckatoo verify * speed up cuckaroo verify * speed up cuckarood verify * speed up cuckaroom verify * speed up cuckarooz verify
This commit is contained in:
parent
2f5cfbe4eb
commit
c6f25e9929
5 changed files with 167 additions and 51 deletions
|
@ -62,15 +62,21 @@ impl PoWContext for CuckarooContext {
|
|||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<(), Error> {
|
||||
if proof.proof_size() != global::proofsize() {
|
||||
let size = proof.proof_size();
|
||||
if size != global::proofsize() {
|
||||
return Err(ErrorKind::Verification("wrong cycle length".to_owned()).into());
|
||||
}
|
||||
let nonces = &proof.nonces;
|
||||
let mut uvs = vec![0u64; 2 * proof.proof_size()];
|
||||
let mut uvs = vec![0u64; 2 * size];
|
||||
let mut xor0: u64 = 0;
|
||||
let mut xor1: u64 = 0;
|
||||
let mask = u64::MAX >> size.leading_zeros(); // round size up to 2-power - 1
|
||||
// the next three arrays form a linked list of nodes with matching bits 6..1
|
||||
let mut headu = vec![2 * size; 1 + mask as usize];
|
||||
let mut headv = vec![2 * size; 1 + mask as usize];
|
||||
let mut prev = vec![0usize; 2 * size];
|
||||
|
||||
for n in 0..proof.proof_size() {
|
||||
for n in 0..size {
|
||||
if nonces[n] > self.params.edge_mask {
|
||||
return Err(ErrorKind::Verification("edge too big".to_owned()).into());
|
||||
}
|
||||
|
@ -79,14 +85,36 @@ impl PoWContext for CuckarooContext {
|
|||
}
|
||||
// 21 is standard siphash rotation constant
|
||||
let edge: u64 = siphash_block(&self.params.siphash_keys, nonces[n], 21, false);
|
||||
uvs[2 * n] = edge & self.params.node_mask;
|
||||
xor0 ^= uvs[2 * n];
|
||||
uvs[2 * n + 1] = (edge >> 32) & self.params.node_mask;
|
||||
xor1 ^= uvs[2 * n + 1];
|
||||
let u = edge & self.params.node_mask;
|
||||
let v = (edge >> 32) & self.params.node_mask;
|
||||
|
||||
uvs[2 * n] = u;
|
||||
let ubits = (u & mask) as usize;
|
||||
prev[2 * n] = headu[ubits];
|
||||
headu[ubits] = 2 * n;
|
||||
|
||||
uvs[2 * n + 1] = v;
|
||||
let vbits = (v & mask) as usize;
|
||||
prev[2 * n + 1] = headv[vbits];
|
||||
headv[vbits] = 2 * n + 1;
|
||||
|
||||
xor0 ^= u;
|
||||
xor1 ^= v;
|
||||
}
|
||||
if xor0 | xor1 != 0 {
|
||||
return Err(ErrorKind::Verification("endpoints don't match up".to_owned()).into());
|
||||
}
|
||||
// make prev lists circular
|
||||
for n in 0..size {
|
||||
if prev[2 * n] == 2 * size {
|
||||
let ubits = (uvs[2 * n] & mask) as usize;
|
||||
prev[2 * n] = headu[ubits];
|
||||
}
|
||||
if prev[2 * n + 1] == 2 * size {
|
||||
let vbits = (uvs[2 * n + 1] & mask) as usize;
|
||||
prev[2 * n + 1] = headv[vbits];
|
||||
}
|
||||
}
|
||||
let mut n = 0;
|
||||
let mut i = 0;
|
||||
let mut j;
|
||||
|
@ -95,7 +123,7 @@ impl PoWContext for CuckarooContext {
|
|||
j = i;
|
||||
let mut k = j;
|
||||
loop {
|
||||
k = (k + 2) % (2 * self.params.proof_size);
|
||||
k = prev[k];
|
||||
if k == i {
|
||||
break;
|
||||
}
|
||||
|
@ -116,7 +144,7 @@ impl PoWContext for CuckarooContext {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if n == self.params.proof_size {
|
||||
if n == size {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Verification("cycle too short".to_owned()).into())
|
||||
|
@ -164,7 +192,9 @@ mod test {
|
|||
let mut ctx = new_impl(19, 42);
|
||||
ctx.params.siphash_keys = V1_19_HASH;
|
||||
assert!(ctx.verify(&Proof::new(V1_19_SOL.to_vec())).is_ok());
|
||||
assert!(ctx.verify(&Proof::new(V2_19_SOL.to_vec())).is_err());
|
||||
ctx.params.siphash_keys = V2_19_HASH.clone();
|
||||
assert!(ctx.verify(&Proof::new(V1_19_SOL.to_vec())).is_err());
|
||||
assert!(ctx.verify(&Proof::new(V2_19_SOL.to_vec())).is_ok());
|
||||
assert!(ctx.verify(&Proof::zero(42)).is_err());
|
||||
}
|
||||
|
|
|
@ -56,18 +56,24 @@ impl PoWContext for CuckaroodContext {
|
|||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<(), Error> {
|
||||
if proof.proof_size() != global::proofsize() {
|
||||
let size = proof.proof_size();
|
||||
if size != global::proofsize() {
|
||||
return Err(ErrorKind::Verification("wrong cycle length".to_owned()).into());
|
||||
}
|
||||
let nonces = &proof.nonces;
|
||||
let mut uvs = vec![0u64; 2 * proof.proof_size()];
|
||||
let mut uvs = vec![0u64; 2 * size];
|
||||
let mut ndir = vec![0usize; 2];
|
||||
let mut xor0: u64 = 0;
|
||||
let mut xor1: u64 = 0;
|
||||
let mask = u64::MAX >> size.leading_zeros(); // round size up to 2-power - 1
|
||||
// the next two arrays form a linked list of nodes with matching bits 4..0|dir
|
||||
let mut headu = vec![2 * size; 1 + mask as usize];
|
||||
let mut headv = vec![2 * size; 1 + mask as usize];
|
||||
let mut prev = vec![0usize; 2 * size];
|
||||
|
||||
for n in 0..proof.proof_size() {
|
||||
for n in 0..size {
|
||||
let dir = (nonces[n] & 1) as usize;
|
||||
if ndir[dir] >= proof.proof_size() / 2 {
|
||||
if ndir[dir] >= size / 2 {
|
||||
return Err(ErrorKind::Verification("edges not balanced".to_owned()).into());
|
||||
}
|
||||
if nonces[n] > self.params.edge_mask {
|
||||
|
@ -79,10 +85,21 @@ impl PoWContext for CuckaroodContext {
|
|||
// cuckarood uses a non-standard siphash rotation constant 25 as anti-ASIC tweak
|
||||
let edge: u64 = siphash_block(&self.params.siphash_keys, nonces[n], 25, false);
|
||||
let idx = 4 * ndir[dir] + 2 * dir;
|
||||
uvs[idx] = edge & self.params.node_mask;
|
||||
xor0 ^= uvs[idx];
|
||||
uvs[idx + 1] = (edge >> 32) & self.params.node_mask;
|
||||
xor1 ^= uvs[idx + 1];
|
||||
let u = edge & self.params.node_mask;
|
||||
let v = (edge >> 32) & self.params.node_mask;
|
||||
|
||||
uvs[idx] = u;
|
||||
let ubits = ((u << 1 | dir as u64) & mask) as usize;
|
||||
prev[idx] = headu[ubits];
|
||||
headu[ubits] = idx;
|
||||
|
||||
uvs[idx + 1] = v;
|
||||
let vbits = ((v << 1 | dir as u64) & mask) as usize;
|
||||
prev[idx + 1] = headv[vbits];
|
||||
headv[vbits] = idx + 1;
|
||||
|
||||
xor0 ^= u;
|
||||
xor1 ^= v;
|
||||
ndir[dir] += 1;
|
||||
}
|
||||
if xor0 | xor1 != 0 {
|
||||
|
@ -94,7 +111,12 @@ impl PoWContext for CuckaroodContext {
|
|||
loop {
|
||||
// follow cycle
|
||||
j = i;
|
||||
for k in (((i % 4) ^ 2)..(2 * self.params.proof_size)).step_by(4) {
|
||||
let mut k = if i & 1 == 0 {
|
||||
headu[((uvs[i] << 1 | 1) & mask) as usize]
|
||||
} else {
|
||||
headv[((uvs[i] << 1 | 0) & mask) as usize]
|
||||
};
|
||||
while k != 2 * size {
|
||||
if uvs[k] == uvs[i] {
|
||||
// find reverse edge endpoint identical to one at i
|
||||
if j != i {
|
||||
|
@ -102,6 +124,7 @@ impl PoWContext for CuckaroodContext {
|
|||
}
|
||||
j = k;
|
||||
}
|
||||
k = prev[k];
|
||||
}
|
||||
if j == i {
|
||||
return Err(ErrorKind::Verification("cycle dead ends".to_owned()).into());
|
||||
|
@ -112,7 +135,7 @@ impl PoWContext for CuckaroodContext {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if n == self.params.proof_size {
|
||||
if n == size {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Verification("cycle too short".to_owned()).into())
|
||||
|
|
|
@ -55,17 +55,21 @@ impl PoWContext for CuckaroomContext {
|
|||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<(), Error> {
|
||||
let proofsize = proof.proof_size();
|
||||
if proofsize != global::proofsize() {
|
||||
let size = proof.proof_size();
|
||||
if size != global::proofsize() {
|
||||
return Err(ErrorKind::Verification("wrong cycle length".to_owned()).into());
|
||||
}
|
||||
let nonces = &proof.nonces;
|
||||
let mut from = vec![0u64; proofsize];
|
||||
let mut to = vec![0u64; proofsize];
|
||||
let mut from = vec![0u64; size];
|
||||
let mut to = vec![0u64; size];
|
||||
let mut xor_from: u64 = 0;
|
||||
let mut xor_to: u64 = 0;
|
||||
let mask = u64::MAX >> size.leading_zeros(); // round size up to 2-power - 1
|
||||
// the next two arrays form a linked list of nodes with matching bits 6..1
|
||||
let mut head = vec![size; 1 + mask as usize];
|
||||
let mut prev = vec![0usize; size];
|
||||
|
||||
for n in 0..proofsize {
|
||||
for n in 0..size {
|
||||
if nonces[n] > self.params.edge_mask {
|
||||
return Err(ErrorKind::Verification("edge too big".to_owned()).into());
|
||||
}
|
||||
|
@ -74,15 +78,20 @@ impl PoWContext for CuckaroomContext {
|
|||
}
|
||||
// 21 is standard siphash rotation constant
|
||||
let edge: u64 = siphash_block(&self.params.siphash_keys, nonces[n], 21, true);
|
||||
from[n] = edge & self.params.node_mask;
|
||||
let u = edge & self.params.node_mask;
|
||||
let v = (edge >> 32) & self.params.node_mask;
|
||||
from[n] = u;
|
||||
let bits = (u & mask) as usize;
|
||||
prev[n] = head[bits];
|
||||
head[bits] = n;
|
||||
to[n] = v;
|
||||
xor_from ^= from[n];
|
||||
to[n] = (edge >> 32) & self.params.node_mask;
|
||||
xor_to ^= to[n];
|
||||
}
|
||||
if xor_from != xor_to {
|
||||
return Err(ErrorKind::Verification("endpoints don't match up".to_owned()).into());
|
||||
}
|
||||
let mut visited = vec![false; proofsize];
|
||||
let mut visited = vec![false; size];
|
||||
let mut n = 0;
|
||||
let mut i = 0;
|
||||
loop {
|
||||
|
@ -91,21 +100,24 @@ impl PoWContext for CuckaroomContext {
|
|||
return Err(ErrorKind::Verification("branch in cycle".to_owned()).into());
|
||||
}
|
||||
visited[i] = true;
|
||||
let mut nexti = 0;
|
||||
while from[nexti] != to[i] {
|
||||
nexti += 1;
|
||||
if nexti == proofsize {
|
||||
let mut k = head[(to[i] & mask) as usize];
|
||||
loop {
|
||||
if k == size {
|
||||
return Err(ErrorKind::Verification("cycle dead ends".to_owned()).into());
|
||||
}
|
||||
if from[k] == to[i] {
|
||||
break;
|
||||
}
|
||||
k = prev[k];
|
||||
}
|
||||
i = nexti;
|
||||
i = k;
|
||||
n += 1;
|
||||
if i == 0 {
|
||||
// must cycle back to start or find branch
|
||||
break;
|
||||
}
|
||||
}
|
||||
if n == proofsize {
|
||||
if n == size {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Verification("cycle too short".to_owned()).into())
|
||||
|
|
|
@ -56,14 +56,19 @@ impl PoWContext for CuckaroozContext {
|
|||
}
|
||||
|
||||
fn verify(&self, proof: &Proof) -> Result<(), Error> {
|
||||
if proof.proof_size() != global::proofsize() {
|
||||
let size = proof.proof_size();
|
||||
if size != global::proofsize() {
|
||||
return Err(ErrorKind::Verification("wrong cycle length".to_owned()).into());
|
||||
}
|
||||
let nonces = &proof.nonces;
|
||||
let mut uvs = vec![0u64; 2 * proof.proof_size()];
|
||||
let mut uvs = vec![0u64; 2 * size];
|
||||
let mut xoruv: u64 = 0;
|
||||
let mask = u64::MAX >> size.leading_zeros(); // round size up to 2-power - 1
|
||||
// the next two arrays form a linked list of nodes with matching bits 6..1
|
||||
let mut head = vec![2 * size; 1 + mask as usize];
|
||||
let mut prev = vec![0usize; 2 * size];
|
||||
|
||||
for n in 0..proof.proof_size() {
|
||||
for n in 0..size {
|
||||
if nonces[n] > self.params.edge_mask {
|
||||
return Err(ErrorKind::Verification("edge too big".to_owned()).into());
|
||||
}
|
||||
|
@ -72,13 +77,31 @@ impl PoWContext for CuckaroozContext {
|
|||
}
|
||||
// 21 is standard siphash rotation constant
|
||||
let edge: u64 = siphash_block(&self.params.siphash_keys, nonces[n], 21, true);
|
||||
uvs[2 * n] = edge & self.params.node_mask;
|
||||
uvs[2 * n + 1] = (edge >> 32) & self.params.node_mask;
|
||||
let u = edge & self.params.node_mask;
|
||||
let v = (edge >> 32) & self.params.node_mask;
|
||||
|
||||
uvs[2 * n] = u;
|
||||
let bits = (u & mask) as usize;
|
||||
prev[2 * n] = head[bits];
|
||||
head[bits] = 2 * n;
|
||||
|
||||
uvs[2 * n + 1] = v;
|
||||
let bits = (v & mask) as usize;
|
||||
prev[2 * n + 1] = head[bits];
|
||||
head[bits] = 2 * n + 1;
|
||||
|
||||
xoruv ^= uvs[2 * n] ^ uvs[2 * n + 1];
|
||||
}
|
||||
if xoruv != 0 {
|
||||
return Err(ErrorKind::Verification("endpoints don't match up".to_owned()).into());
|
||||
}
|
||||
// make prev lists circular
|
||||
for n in 0..(2 * size) {
|
||||
if prev[n] == 2 * size {
|
||||
let bits = (uvs[n] & mask) as usize;
|
||||
prev[n] = head[bits];
|
||||
}
|
||||
}
|
||||
let mut n = 0;
|
||||
let mut i = 0;
|
||||
let mut j;
|
||||
|
@ -87,7 +110,7 @@ impl PoWContext for CuckaroozContext {
|
|||
j = i;
|
||||
let mut k = j;
|
||||
loop {
|
||||
k = (k + 1) % (2 * self.params.proof_size);
|
||||
k = prev[k];
|
||||
if k == i {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -255,29 +255,57 @@ impl CuckatooContext {
|
|||
/// Verify that given edges are ascending and form a cycle in a header-generated
|
||||
/// graph
|
||||
pub fn verify_impl(&self, proof: &Proof) -> Result<(), Error> {
|
||||
if proof.proof_size() != global::proofsize() {
|
||||
let size = proof.proof_size();
|
||||
if size != global::proofsize() {
|
||||
return Err(ErrorKind::Verification("wrong cycle length".to_owned()).into());
|
||||
}
|
||||
let nonces = &proof.nonces;
|
||||
let mut uvs = vec![0u64; 2 * proof.proof_size()];
|
||||
let mut xor0: u64 = (self.params.proof_size as u64 / 2) & 1;
|
||||
let mut uvs = vec![0u64; 2 * size];
|
||||
let mask = u64::MAX >> size.leading_zeros(); // round size up to 2-power - 1
|
||||
let mut xor0: u64 = (size as u64 / 2) & 1;
|
||||
let mut xor1: u64 = xor0;
|
||||
// the next two arrays form a linked list of nodes with matching bits 6..1
|
||||
let mut headu = vec![2 * size; 1 + mask as usize];
|
||||
let mut headv = vec![2 * size; 1 + mask as usize];
|
||||
let mut prev = vec![0usize; 2 * size];
|
||||
|
||||
for n in 0..proof.proof_size() {
|
||||
for n in 0..size {
|
||||
if nonces[n] > self.params.edge_mask {
|
||||
return Err(ErrorKind::Verification("edge too big".to_owned()).into());
|
||||
}
|
||||
if n > 0 && nonces[n] <= nonces[n - 1] {
|
||||
return Err(ErrorKind::Verification("edges not ascending".to_owned()).into());
|
||||
}
|
||||
uvs[2 * n] = self.params.sipnode(nonces[n], 0)?;
|
||||
uvs[2 * n + 1] = self.params.sipnode(nonces[n], 1)?;
|
||||
xor0 ^= uvs[2 * n];
|
||||
xor1 ^= uvs[2 * n + 1];
|
||||
let u = self.params.sipnode(nonces[n], 0)?;
|
||||
let v = self.params.sipnode(nonces[n], 1)?;
|
||||
|
||||
uvs[2 * n] = u;
|
||||
let ubits = (u >> 1 & mask) as usize; // larger shifts work too, up to edgebits-6
|
||||
prev[2 * n] = headu[ubits];
|
||||
headu[ubits] = 2 * n;
|
||||
|
||||
uvs[2 * n + 1] = v;
|
||||
let vbits = (v >> 1 & mask) as usize;
|
||||
prev[2 * n + 1] = headv[vbits];
|
||||
headv[vbits] = 2 * n + 1;
|
||||
|
||||
xor0 ^= u;
|
||||
xor1 ^= v;
|
||||
}
|
||||
if xor0 | xor1 != 0 {
|
||||
return Err(ErrorKind::Verification("endpoints don't match up".to_owned()).into());
|
||||
}
|
||||
// make prev lists circular
|
||||
for n in 0..size {
|
||||
if prev[2 * n] == 2 * size {
|
||||
let ubits = (uvs[2 * n] >> 1 & mask) as usize;
|
||||
prev[2 * n] = headu[ubits];
|
||||
}
|
||||
if prev[2 * n + 1] == 2 * size {
|
||||
let vbits = (uvs[2 * n + 1] >> 1 & mask) as usize;
|
||||
prev[2 * n + 1] = headv[vbits];
|
||||
}
|
||||
}
|
||||
let mut n = 0;
|
||||
let mut i = 0;
|
||||
let mut j;
|
||||
|
@ -286,7 +314,7 @@ impl CuckatooContext {
|
|||
j = i;
|
||||
let mut k = j;
|
||||
loop {
|
||||
k = (k + 2) % (2 * self.params.proof_size);
|
||||
k = prev[k];
|
||||
if k == i {
|
||||
break;
|
||||
}
|
||||
|
@ -307,7 +335,7 @@ impl CuckatooContext {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if n == self.params.proof_size {
|
||||
if n == size {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::Verification("cycle too short".to_owned()).into())
|
||||
|
@ -457,13 +485,13 @@ mod test {
|
|||
let mut header = [0u8; 80];
|
||||
header[0] = 1u8;
|
||||
ctx.set_header_nonce(header.to_vec(), Some(20), false)?;
|
||||
assert!(!ctx.verify(&Proof::new(V1_29.to_vec())).is_ok());
|
||||
assert!(ctx.verify(&Proof::new(V1_29.to_vec())).is_err());
|
||||
header[0] = 0u8;
|
||||
ctx.set_header_nonce(header.to_vec(), Some(20), false)?;
|
||||
assert!(ctx.verify(&Proof::new(V1_29.to_vec())).is_ok());
|
||||
let mut bad_proof = V1_29;
|
||||
bad_proof[0] = 0x48a9e1;
|
||||
assert!(!ctx.verify(&Proof::new(bad_proof.to_vec())).is_ok());
|
||||
assert!(ctx.verify(&Proof::new(bad_proof.to_vec())).is_err());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue