diff --git a/Cargo.lock b/Cargo.lock index 3847987..6388659 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,7 @@ name = "librustzcash" version = "0.1.0" dependencies = [ "bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index f030793..5f56f94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "librustzcash" version = "0.1.0" -authors = ["Sean Bowe "] +authors = [ + "Sean Bowe ", + "Jack Grigg ", + ] [lib] name = "rustzcash" @@ -16,6 +19,10 @@ bellman = "0.1" byteorder = "1" rand = "0.4" +[dependencies.blake2-rfc] +git = "https://github.com/gtank/blake2-rfc" +rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" + [dependencies.sapling-crypto] git = "https://github.com/zcash-hackworks/sapling-crypto" rev = "6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c" diff --git a/include/librustzcash.h b/include/librustzcash.h index 6eb32ea..9033dc2 100644 --- a/include/librustzcash.h +++ b/include/librustzcash.h @@ -14,6 +14,19 @@ extern "C" { const char* sprout_path ); + /// Validates the provided Equihash solution against + /// the given parameters, input and nonce. + bool librustzcash_eh_isvalid( + uint32_t n, + uint32_t k, + const unsigned char* input, + size_t input_len, + const unsigned char* nonce, + size_t nonce_len, + const unsigned char* soln, + size_t soln_len + ); + /// Writes the "uncommitted" note value for empty leaves /// of the merkle tree. `result` must be a valid pointer /// to 32 bytes which will be written. diff --git a/src/equihash.rs b/src/equihash.rs new file mode 100644 index 0000000..b1f9109 --- /dev/null +++ b/src/equihash.rs @@ -0,0 +1,368 @@ +use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; +use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; +use std::io::Cursor; +use std::mem::size_of; + +#[derive(Clone)] +struct Node { + hash: Vec, + indices: Vec, +} + +impl Node { + fn new(a: &mut Node, b: &mut Node, trim: usize) -> Self { + let mut hash = vec![0u8; a.hash.len() - trim]; + let mut indices = Vec::new(); + for i in trim..a.hash.len() { + hash[i - trim] = a.hash[i] ^ b.hash[i]; + } + if a.indices_before(b) { + indices.append(a.indices.as_mut()); + indices.append(b.indices.as_mut()); + } else { + indices.append(b.indices.as_mut()); + indices.append(a.indices.as_mut()); + } + Node { + hash: hash, + indices: indices, + } + } + + fn indices_before(&self, other: &Node) -> bool { + // Indices are serialized in big-endian so that integer + // comparison is equivalent to array comparison + self.indices[0] < other.indices[0] + } + + fn is_zero(&self, len: usize) -> bool { + for i in 0..len { + if self.hash[i] != 0 { + return false; + } + } + return true; + } +} + +fn initialise_state(n: u32, k: u32, digest_len: u8) -> Blake2b { + let mut personalization: Vec = Vec::from("ZcashPoW"); + personalization.write_u32::(n).unwrap(); + personalization.write_u32::(k).unwrap(); + + Blake2b::with_params(digest_len as usize, &[], &[], &personalization) +} + +fn generate_hash(base_state: &Blake2b, i: u32) -> Blake2bResult { + let mut lei = vec![]; + lei.write_u32::(i).unwrap(); + + let mut state = base_state.clone(); + state.update(&lei); + state.finalize() +} + +fn expand_array(vin: &[u8], bit_len: usize, byte_pad: usize) -> Vec { + assert!(bit_len >= 8); + assert!(8 * size_of::() >= 7 + bit_len); + + let out_width = (bit_len + 7) / 8 + byte_pad; + let out_len = 8 * out_width * vin.len() / bit_len; + let mut vout: Vec = vec![0; out_len]; + + let bit_len_mask: u32 = (1 << bit_len) - 1; + + // The acc_bits least-significant bits of acc_value represent a bit sequence + // in big-endian order. + let mut acc_bits = 0; + let mut acc_value: u32 = 0; + + let mut j = 0; + for i in 0..vin.len() { + acc_value = (acc_value << 8) | vin[i] as u32; + acc_bits += 8; + + // When we have bit_len or more bits in the accumulator, write the next + // output element. + if acc_bits >= bit_len { + acc_bits -= bit_len; + for x in 0..byte_pad { + vout[j + x] = 0; + } + for x in byte_pad..out_width { + vout[j + x] = (( + // Big-endian + acc_value >> (acc_bits + (8 * (out_width - x - 1))) + ) + & ( + // Apply bit_len_mask across byte boundaries + (bit_len_mask >> (8 * (out_width - x - 1))) & 0xFF + )) as u8; + } + j += out_width; + } + } + + vout +} + +fn indices_from_minimal(minimal: &[u8], c_bit_len: usize) -> Vec { + assert!(((c_bit_len + 1) + 7) / 8 <= size_of::()); + let len_indices = 8 * size_of::() * minimal.len() / (c_bit_len + 1); + let byte_pad = size_of::() - ((c_bit_len + 1) + 7) / 8; + + let mut csr = Cursor::new(expand_array(minimal, c_bit_len + 1, byte_pad)); + let mut ret = Vec::with_capacity(len_indices); + + // Big-endian so that lexicographic array comparison is equivalent to integer + // comparison + while let Ok(i) = csr.read_u32::() { + ret.push(i); + } + + ret +} + +fn has_collision(a: &Node, b: &Node, len: usize) -> bool { + for i in 0..len { + if a.hash[i] != b.hash[i] { + return false; + } + } + return true; +} + +fn distinct_indices(a: &Node, b: &Node) -> bool { + for i in &(a.indices) { + for j in &(b.indices) { + if i == j { + return false; + } + } + } + return true; +} + +fn is_valid_solution_iterative( + n: u32, + k: u32, + input: &[u8], + nonce: &[u8], + indices: &[u32], +) -> bool { + let IndicesPerHashOutput = 512 / n; + let HashOutput = (IndicesPerHashOutput * n / 8) as u8; + let CollisionBitLength = (n / (k + 1)) as usize; + let CollisionByteLength = (CollisionBitLength + 7) / 8; + let hash_length = ((k as usize) + 1) * CollisionByteLength; + + let mut state = initialise_state(n, k, HashOutput); + state.update(input); + state.update(nonce); + + let mut X = Vec::new(); + for i in indices { + let hash = generate_hash(&state, i / IndicesPerHashOutput); + let start = ((i % IndicesPerHashOutput) * n / 8) as usize; + let end = start + (n as usize) / 8; + X.push(Node { + hash: expand_array(&hash.as_bytes()[start..end], CollisionBitLength, 0), + indices: vec![*i], + }); + } + + let mut hash_len = hash_length; + while X.len() > 1 { + let mut Xc = Vec::new(); + for pair in X.chunks(2) { + let mut a = pair[0].clone(); + let mut b = pair[1].clone(); + if !has_collision(&a, &b, CollisionByteLength) { + // error!("Invalid solution: invalid collision length between StepRows"); + return false; + } + if b.indices_before(&a) { + // error!("Invalid solution: Index tree incorrectly ordered"); + return false; + } + if !distinct_indices(&a, &b) { + // error!("Invalid solution: duplicate indices"); + return false; + } + Xc.push(Node::new(&mut a, &mut b, CollisionByteLength)); + } + X = Xc; + hash_len -= CollisionByteLength; + } + + assert!(X.len() == 1); + return X[0].is_zero(hash_len); +} + +pub fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], soln: &[u8]) -> bool { + let CollisionBitLength = (n / (k + 1)) as usize; + let indices = indices_from_minimal(soln, CollisionBitLength); + + is_valid_solution_iterative(n, k, input, nonce, &indices) +} + +#[cfg(test)] +mod tests { + use super::is_valid_solution_iterative; + + fn is_valid_solution(n: u32, k: u32, input: &[u8], nonce: &[u8], indices: &[u32]) -> bool { + is_valid_solution_iterative(n, k, input, nonce, indices) + } + + #[test] + fn equihash_test_cases() { + let input = b"block header"; + let mut nonce = [0 as u8; 32]; + let mut indices = vec![ + 976, 126621, 100174, 123328, 38477, 105390, 38834, 90500, 6411, 116489, 51107, 129167, + 25557, 92292, 38525, 56514, 1110, 98024, 15426, 74455, 3185, 84007, 24328, 36473, + 17427, 129451, 27556, 119967, 31704, 62448, 110460, 117894, + ]; + assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + + indices = vec![ + 1008, 18280, 34711, 57439, 3903, 104059, 81195, 95931, 58336, 118687, 67931, 123026, + 64235, 95595, 84355, 122946, 8131, 88988, 45130, 58986, 59899, 78278, 94769, 118158, + 25569, 106598, 44224, 96285, 54009, 67246, 85039, 127667, + ]; + assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + + indices = vec![ + 4313, 223176, 448870, 1692641, 214911, 551567, 1696002, 1768726, 500589, 938660, + 724628, 1319625, 632093, 1474613, 665376, 1222606, 244013, 528281, 1741992, 1779660, + 313314, 996273, 435612, 1270863, 337273, 1385279, 1031587, 1147423, 349396, 734528, + 902268, 1678799, 10902, 1231236, 1454381, 1873452, 120530, 2034017, 948243, 1160178, + 198008, 1704079, 1087419, 1734550, 457535, 698704, 649903, 1029510, 75564, 1860165, + 1057819, 1609847, 449808, 527480, 1106201, 1252890, 207200, 390061, 1557573, 1711408, + 396772, 1026145, 652307, 1712346, 10680, 1027631, 232412, 974380, 457702, 1827006, + 1316524, 1400456, 91745, 2032682, 192412, 710106, 556298, 1963798, 1329079, 1504143, + 102455, 974420, 639216, 1647860, 223846, 529637, 425255, 680712, 154734, 541808, + 443572, 798134, 322981, 1728849, 1306504, 1696726, 57884, 913814, 607595, 1882692, + 236616, 1439683, 420968, 943170, 1014827, 1446980, 1468636, 1559477, 1203395, 1760681, + 1439278, 1628494, 195166, 198686, 349906, 1208465, 917335, 1361918, 937682, 1885495, + 494922, 1745948, 1320024, 1826734, 847745, 894084, 1484918, 1523367, 7981, 1450024, + 861459, 1250305, 226676, 329669, 339783, 1935047, 369590, 1564617, 939034, 1908111, + 1147449, 1315880, 1276715, 1428599, 168956, 1442649, 766023, 1171907, 273361, 1902110, + 1169410, 1786006, 413021, 1465354, 707998, 1134076, 977854, 1604295, 1369720, 1486036, + 330340, 1587177, 502224, 1313997, 400402, 1667228, 889478, 946451, 470672, 2019542, + 1023489, 2067426, 658974, 876859, 794443, 1667524, 440815, 1099076, 897391, 1214133, + 953386, 1932936, 1100512, 1362504, 874364, 975669, 1277680, 1412800, 1227580, 1857265, + 1312477, 1514298, 12478, 219890, 534265, 1351062, 65060, 651682, 627900, 1331192, + 123915, 865936, 1218072, 1732445, 429968, 1097946, 947293, 1323447, 157573, 1212459, + 923792, 1943189, 488881, 1697044, 915443, 2095861, 333566, 732311, 336101, 1600549, + 575434, 1978648, 1071114, 1473446, 50017, 54713, 367891, 2055483, 561571, 1714951, + 715652, 1347279, 584549, 1642138, 1002587, 1125289, 1364767, 1382627, 1387373, 2054399, + 97237, 1677265, 707752, 1265819, 121088, 1810711, 1755448, 1858538, 444653, 1130822, + 514258, 1669752, 578843, 729315, 1164894, 1691366, 15609, 1917824, 173620, 587765, + 122779, 2024998, 804857, 1619761, 110829, 1514369, 410197, 493788, 637666, 1765683, + 782619, 1186388, 494761, 1536166, 1582152, 1868968, 825150, 1709404, 1273757, 1657222, + 817285, 1955796, 1014018, 1961262, 873632, 1689675, 985486, 1008905, 130394, 897076, + 419669, 535509, 980696, 1557389, 1244581, 1738170, 197814, 1879515, 297204, 1165124, + 883018, 1677146, 1545438, 2017790, 345577, 1821269, 761785, 1014134, 746829, 751041, + 930466, 1627114, 507500, 588000, 1216514, 1501422, 991142, 1378804, 1797181, 1976685, + 60742, 780804, 383613, 645316, 770302, 952908, 1105447, 1878268, 504292, 1961414, + 693833, 1198221, 906863, 1733938, 1315563, 2049718, 230826, 2064804, 1224594, 1434135, + 897097, 1961763, 993758, 1733428, 306643, 1402222, 532661, 627295, 453009, 973231, + 1746809, 1857154, 263652, 1683026, 1082106, 1840879, 768542, 1056514, 888164, 1529401, + 327387, 1708909, 961310, 1453127, 375204, 878797, 1311831, 1969930, 451358, 1229838, + 583937, 1537472, 467427, 1305086, 812115, 1065593, 532687, 1656280, 954202, 1318066, + 1164182, 1963300, 1232462, 1722064, 17572, 923473, 1715089, 2079204, 761569, 1557392, + 1133336, 1183431, 175157, 1560762, 418801, 927810, 734183, 825783, 1844176, 1951050, + 317246, 336419, 711727, 1630506, 634967, 1595955, 683333, 1461390, 458765, 1834140, + 1114189, 1761250, 459168, 1897513, 1403594, 1478683, 29456, 1420249, 877950, 1371156, + 767300, 1848863, 1607180, 1819984, 96859, 1601334, 171532, 2068307, 980009, 2083421, + 1329455, 2030243, 69434, 1965626, 804515, 1339113, 396271, 1252075, 619032, 2080090, + 84140, 658024, 507836, 772757, 154310, 1580686, 706815, 1024831, 66704, 614858, 256342, + 957013, 1488503, 1615769, 1515550, 1888497, 245610, 1333432, 302279, 776959, 263110, + 1523487, 623933, 2013452, 68977, 122033, 680726, 1849411, 426308, 1292824, 460128, + 1613657, 234271, 971899, 1320730, 1559313, 1312540, 1837403, 1690310, 2040071, 149918, + 380012, 785058, 1675320, 267071, 1095925, 1149690, 1318422, 361557, 1376579, 1587551, + 1715060, 1224593, 1581980, 1354420, 1850496, 151947, 748306, 1987121, 2070676, 273794, + 981619, 683206, 1485056, 766481, 2047708, 930443, 2040726, 1136227, 1945705, 1722044, + 1971986, + ]; + assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); + assert!(is_valid_solution(200, 9, input, &nonce, &indices)); + + nonce[0] = 1; + assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); + assert!(!is_valid_solution(200, 9, input, &nonce, &indices)); + + indices = vec![ + 1911, 96020, 94086, 96830, 7895, 51522, 56142, 62444, 15441, 100732, 48983, 64776, + 27781, 85932, 101138, 114362, 4497, 14199, 36249, 41817, 23995, 93888, 35798, 96337, + 5530, 82377, 66438, 85247, 39332, 78978, 83015, 123505, + ]; + assert!(is_valid_solution(96, 5, input, &nonce, &indices)); + + indices = vec![ + 1505, 1380774, 200806, 1787044, 101056, 1697952, 281464, 374899, 263712, 1532496, + 264180, 637056, 734225, 1882676, 1112004, 2093109, 193394, 1459136, 525171, 657480, + 214528, 1221365, 574444, 594726, 501919, 1309358, 1740268, 1989610, 654491, 1068055, + 919416, 1993208, 17599, 1858176, 1315176, 1901532, 108258, 109600, 1117445, 1936058, + 70247, 1036984, 628234, 1800109, 149791, 365740, 345683, 563554, 21678, 822781, + 1423722, 1644228, 792912, 1409641, 805060, 2041985, 453824, 1003179, 934427, 1068834, + 629003, 1456111, 670049, 1558594, 19016, 1343657, 1698188, 1865216, 45723, 1820952, + 1160970, 1585983, 422549, 1973097, 1296271, 2006382, 650084, 809838, 871727, 1080419, + 28500, 1471829, 384406, 619459, 212041, 1466258, 481435, 866461, 145340, 1403843, + 1339592, 1405761, 163425, 1073771, 285027, 1488210, 167744, 1182267, 1354059, 2089602, + 921700, 2059931, 1704721, 1853088, 585171, 739246, 747551, 1520527, 590255, 1175747, + 705292, 998433, 522014, 1931179, 1629531, 1692879, 588830, 1799457, 963672, 1664237, + 775408, 1926741, 907030, 1466738, 784179, 1972599, 1494787, 1598114, 1736, 1039487, + 88704, 1302687, 579526, 1476728, 1677992, 1854526, 432470, 2062305, 1471132, 1747579, + 1521894, 1917599, 1590975, 1936227, 151871, 1999775, 224664, 461809, 704084, 1306665, + 1316156, 1529628, 876811, 2086004, 1986383, 2012147, 1039505, 1637502, 1432721, + 1565477, 110385, 342650, 659137, 1285167, 367416, 2007586, 445677, 2084877, 285692, + 1144365, 988840, 1990372, 748425, 1617758, 1267712, 1510433, 152291, 1256291, 1722179, + 1995439, 864844, 1623380, 1071853, 1731862, 699978, 1407662, 1048047, 1849702, 962900, + 1083340, 1378752, 1534902, 11843, 115329, 454796, 548919, 148184, 1686936, 862432, + 873854, 60753, 999864, 385959, 1528101, 534420, 678401, 590419, 1962518, 54984, + 1141820, 243305, 1349970, 599681, 1817233, 1632537, 1698724, 580004, 673073, 1403350, + 2026104, 758881, 970056, 1717966, 2062827, 19624, 148580, 609748, 1588928, 456321, + 834920, 700532, 1682606, 20012, 441139, 1591072, 1923394, 194034, 1741063, 1156906, + 1983067, 20703, 1939972, 604581, 963600, 128170, 731716, 606773, 1626824, 139460, + 1386775, 521911, 2043473, 392180, 449532, 895678, 1453340, 7085, 598416, 1514260, + 2061068, 279532, 678363, 943255, 1405306, 119114, 2075865, 592839, 1972064, 254647, + 2078288, 946282, 1567138, 120422, 767626, 213242, 448366, 438457, 1768467, 853790, + 1509505, 735780, 1979631, 1461410, 1462050, 739008, 1572606, 920754, 1507358, 12883, + 1681167, 1308399, 1839490, 85599, 1387522, 703262, 1949514, 18523, 1236125, 669105, + 1464132, 68670, 2085647, 333393, 1731573, 21714, 637827, 985912, 2091029, 84065, + 1688993, 1574405, 1899543, 134032, 179206, 671016, 1118310, 288960, 861994, 622074, + 1738892, 10936, 343910, 598016, 1741971, 586348, 1956071, 851053, 1715626, 531385, + 1213667, 1093995, 1863757, 630365, 1851894, 1328101, 1770446, 31900, 734027, 1078651, + 1701535, 123276, 1916343, 581822, 1681706, 573135, 818091, 1454710, 2052521, 1150284, + 1451159, 1482280, 1811430, 26321, 785837, 877980, 2073103, 107324, 727248, 1785460, + 1840517, 184560, 185640, 364103, 1878753, 518459, 1984029, 964109, 1884200, 74003, + 527272, 516232, 711247, 148582, 209254, 634610, 1534140, 376714, 1573267, 421225, + 1265101, 1078858, 1374310, 1806283, 2091298, 23392, 389637, 413663, 1066737, 226164, + 762552, 1048220, 1583397, 40092, 277435, 775449, 1533894, 202582, 390703, 346741, + 1027320, 523034, 809424, 584882, 1296934, 528062, 733331, 1212771, 1958651, 653372, + 1313962, 1366332, 1784489, 1542466, 1580386, 1628948, 2000957, 57069, 1398636, 1250431, + 1698486, 57289, 596009, 582428, 966130, 167657, 1025537, 1227498, 1630134, 234060, + 1285209, 265623, 1165779, 68485, 632055, 96019, 1854676, 98410, 158575, 168035, + 1296171, 158847, 1243959, 977212, 1113647, 363568, 891940, 954593, 1987111, 90101, + 133251, 1136222, 1255117, 543075, 732768, 749576, 1174878, 422226, 1854657, 1143029, + 1457135, 927105, 1137382, 1566306, 1661926, 103057, 425126, 698089, 1774942, 911019, + 1793511, 1623559, 2002409, 457796, 1196971, 724257, 1811147, 956269, 1165590, 1137531, + 1381215, 201063, 1938529, 986021, 1297857, 921334, 1259083, 1440074, 1939366, 232907, + 747213, 1349009, 1945364, 689906, 1116453, 1904207, 1916192, 229793, 1576982, 1420059, + 1644978, 278248, 2024807, 297914, 419798, 555747, 712605, 1012424, 1428921, 890113, + 1822645, 1082368, 1392894, + ]; + assert!(!is_valid_solution(96, 5, input, &nonce, &indices)); + assert!(is_valid_solution(200, 9, input, &nonce, &indices)); + + let input2 = b"Equihash is an asymmetric PoW based on the Generalised Birthday problem."; + indices = vec![ + 2261, 15185, 36112, 104243, 23779, 118390, 118332, 130041, 32642, 69878, 76925, 80080, + 45858, 116805, 92842, 111026, 15972, 115059, 85191, 90330, 68190, 122819, 81830, 91132, + 23460, 49807, 52426, 80391, 69567, 114474, 104973, 122568, + ]; + assert!(is_valid_solution(96, 5, input2, &nonce, &indices)); + } +} diff --git a/src/rustzcash.rs b/src/rustzcash.rs index a8a0e3c..35c63d3 100644 --- a/src/rustzcash.rs +++ b/src/rustzcash.rs @@ -1,4 +1,5 @@ extern crate bellman; +extern crate blake2_rfc; extern crate byteorder; extern crate libc; extern crate pairing; @@ -25,9 +26,12 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use rand::OsRng; use std::io::BufReader; -use libc::{c_char, c_uchar, size_t, int64_t, uint64_t}; +use libc::{c_char, c_uchar, size_t, int64_t, uint32_t, uint64_t}; use std::ffi::CStr; use std::fs::File; +use std::slice; + +pub mod equihash; #[cfg(test)] mod tests; @@ -189,6 +193,26 @@ pub extern "system" fn librustzcash_xor(a: uint64_t, b: uint64_t) -> uint64_t { a ^ b } +#[no_mangle] +pub extern "system" fn librustzcash_eh_isvalid( + n: uint32_t, + k: uint32_t, + input: *const c_uchar, + input_len: size_t, + nonce: *const c_uchar, + nonce_len: size_t, + soln: *const c_uchar, + soln_len: size_t, +) -> bool { + if (k >= n) || (n % 8 != 0) || (soln_len != (1 << k) * ((n / (k + 1)) as usize + 1) / 8) { + return false; + } + let rs_input = unsafe { slice::from_raw_parts(input, input_len) }; + let rs_nonce = unsafe { slice::from_raw_parts(nonce, nonce_len) }; + let rs_soln = unsafe { slice::from_raw_parts(soln, soln_len) }; + equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln) +} + #[test] fn test_xor() { assert_eq!(