├── .DS_Store ├── LICENSE ├── README.md ├── chapter_01 ├── implementation_02 │ ├── .gitignore │ ├── .tool-versions │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ └── src │ │ ├── field_element.rs │ │ └── lib.rs └── implementation_03 │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ ├── errors.rs │ ├── finite_field_element.rs │ └── lib.rs ├── chapter_02 ├── implementation_02 │ ├── .gitignore │ ├── .tool-versions │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ └── src │ │ ├── lib.rs │ │ └── point.rs └── implementation_03 │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ ├── lib.rs │ └── point.rs └── src ├── chapter_01 └── implementation_01 │ └── main.rs └── chapter_02 └── implementation_01 └── point.rs /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/programming_bitcoin_in_rust/401817a72d649f190191c6746d330e5cd6ad4696/.DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Lambdaclass 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Programming Bitcoin in Rust 2 | 3 | 4 | This is a work in progress. We're implementing the book "Programming Bitcoin" in Rust as an exercise to learn both about Rust and the inner workings of Bitcoin. 5 | 6 | Each chapter may have different implementations, produced by different developer teams. 7 | 8 | The code might not be perfect, as for many this is their first contact with Rust. 9 | 10 | Enjoy! 11 | 12 | -------------------------------------------------------------------------------- /chapter_01/implementation_02/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode 3 | .env 4 | -------------------------------------------------------------------------------- /chapter_01/implementation_02/.tool-versions: -------------------------------------------------------------------------------- 1 | rust 1.59.0 2 | -------------------------------------------------------------------------------- /chapter_01/implementation_02/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "num-bigint" 13 | version = "0.4.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" 16 | dependencies = [ 17 | "autocfg", 18 | "num-integer", 19 | "num-traits", 20 | ] 21 | 22 | [[package]] 23 | name = "num-integer" 24 | version = "0.1.45" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 27 | dependencies = [ 28 | "autocfg", 29 | "num-traits", 30 | ] 31 | 32 | [[package]] 33 | name = "num-traits" 34 | version = "0.2.15" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 37 | dependencies = [ 38 | "autocfg", 39 | ] 40 | 41 | [[package]] 42 | name = "programming_bitcoin" 43 | version = "0.1.0" 44 | dependencies = [ 45 | "num-bigint", 46 | ] 47 | -------------------------------------------------------------------------------- /chapter_01/implementation_02/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "programming_bitcoin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | num-bigint = "0.4" 10 | -------------------------------------------------------------------------------- /chapter_01/implementation_02/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Gabriel Bosio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /chapter_01/implementation_02/README.md: -------------------------------------------------------------------------------- 1 | # Programming Bitcoin 2 | Rust implementation of the exercises from [the Programming Bitcoin book](https://www.oreilly.com/library/view/programming-bitcoin/9781492031482/). 3 | -------------------------------------------------------------------------------- /chapter_01/implementation_02/src/field_element.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigInt; 2 | use num_bigint::ToBigInt; 3 | use std::fmt; 4 | use std::ops; 5 | 6 | #[derive(Debug, Eq)] 7 | pub struct FieldElement { 8 | num: BigInt, 9 | prime: BigInt, 10 | } 11 | 12 | impl FieldElement { 13 | pub fn new(num: BigInt, prime: BigInt) -> Self { 14 | if num >= prime || num < 0_i32.to_bigint().unwrap() { 15 | panic!("Num {} not in field range 0 to {}", num, prime - 1); 16 | } 17 | Self { num, prime } 18 | } 19 | 20 | pub fn pow(&self, exponent: BigInt) -> Self { 21 | let positive_exponent = exponent.rem_euclid(self.prime.clone() - 1); 22 | let num = self.num.modpow(&positive_exponent, &self.prime); 23 | Self { 24 | num, 25 | prime: self.prime.clone(), 26 | } 27 | } 28 | } 29 | 30 | impl fmt::Display for FieldElement { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 32 | write!(f, "FieldElement_{}({})", self.prime, self.num) 33 | } 34 | } 35 | 36 | impl PartialEq for FieldElement { 37 | fn eq(&self, other: &Self) -> bool { 38 | self.num == other.num && self.prime == other.prime 39 | } 40 | } 41 | 42 | impl ops::Add for FieldElement { 43 | type Output = Self; 44 | 45 | fn add(self, rhs: Self) -> Self::Output { 46 | if self.prime != rhs.prime { 47 | panic!("Cannot add two numbers in different Fields"); 48 | } 49 | let num = (self.num + rhs.num).rem_euclid(self.prime.clone()); 50 | Self { 51 | num, 52 | prime: self.prime, 53 | } 54 | } 55 | } 56 | 57 | impl ops::Sub for FieldElement { 58 | type Output = Self; 59 | 60 | fn sub(self, rhs: Self) -> Self::Output { 61 | if self.prime != rhs.prime { 62 | panic!("Cannot subtract two numbers in different Fields"); 63 | } 64 | let num = (self.num - rhs.num).rem_euclid(self.prime.clone()); 65 | Self { 66 | num, 67 | prime: self.prime, 68 | } 69 | } 70 | } 71 | 72 | impl ops::Mul for FieldElement { 73 | type Output = Self; 74 | 75 | fn mul(self, rhs: Self) -> Self::Output { 76 | if self.prime != rhs.prime { 77 | panic!("Cannot multiply two numbers in different Fields"); 78 | } 79 | let num = (self.num * rhs.num).rem_euclid(self.prime.clone()); 80 | Self { 81 | num, 82 | prime: self.prime, 83 | } 84 | } 85 | } 86 | 87 | impl ops::Div for FieldElement { 88 | type Output = Self; 89 | 90 | fn div(self, rhs: Self) -> Self::Output { 91 | if self.prime != rhs.prime { 92 | panic!("Cannot divide two numbers in different Fields"); 93 | } 94 | // a / b == a * b.pow(p - 2) 95 | let exponent = self.prime.clone() - 2_i32.to_bigint().unwrap(); 96 | let rhs_factor = rhs.num.modpow(&exponent, &self.prime); 97 | let num = (self.num * rhs_factor) % self.prime.clone(); 98 | Self { 99 | num, 100 | prime: self.prime, 101 | } 102 | } 103 | } 104 | 105 | trait RemEuclid { 106 | fn rem_euclid(&self, rhs: Self) -> Self; 107 | } 108 | 109 | impl RemEuclid for BigInt { 110 | fn rem_euclid(&self, rhs: Self) -> Self { 111 | self.modpow(&1_i32.to_bigint().unwrap(), &rhs) 112 | } 113 | } 114 | 115 | #[cfg(test)] 116 | mod tests { 117 | use super::*; 118 | 119 | #[test] 120 | fn add_two_field_elements() { 121 | let prime = 13_i32.to_bigint().unwrap(); 122 | let a_num = 7_i32.to_bigint().unwrap(); 123 | let b_num = 12_i32.to_bigint().unwrap(); 124 | let c_num = 6_i32.to_bigint().unwrap(); 125 | let a = FieldElement::new(a_num, prime.clone()); 126 | let b = FieldElement::new(b_num, prime.clone()); 127 | let c = FieldElement::new(c_num, prime); 128 | 129 | assert_eq!(a + b, c); 130 | } 131 | 132 | #[test] 133 | fn substract_two_field_elements() { 134 | let prime = 19_i32.to_bigint().unwrap(); 135 | let a_num = 6_i32.to_bigint().unwrap(); 136 | let b_num = 13_i32.to_bigint().unwrap(); 137 | let c_num = 12_i32.to_bigint().unwrap(); 138 | let a = FieldElement::new(a_num, prime.clone()); 139 | let b = FieldElement::new(b_num, prime.clone()); 140 | let c = FieldElement::new(c_num, prime); 141 | 142 | assert_eq!(a - b, c); 143 | } 144 | 145 | #[test] 146 | fn multiply_two_field_elements() { 147 | let prime = 13_i32.to_bigint().unwrap(); 148 | let a_num = 3_i32.to_bigint().unwrap(); 149 | let b_num = 12_i32.to_bigint().unwrap(); 150 | let c_num = 10_i32.to_bigint().unwrap(); 151 | let a = FieldElement::new(a_num, prime.clone()); 152 | let b = FieldElement::new(b_num, prime.clone()); 153 | let c = FieldElement::new(c_num, prime); 154 | 155 | assert_eq!(a * b, c); 156 | } 157 | 158 | #[test] 159 | fn power_a_field_element_to_a_positive_exponent() { 160 | let prime = 13_i32.to_bigint().unwrap(); 161 | let a_num = 3_i32.to_bigint().unwrap(); 162 | let b_num = 1_i32.to_bigint().unwrap(); 163 | let a = FieldElement::new(a_num, prime.clone()); 164 | let b = FieldElement::new(b_num, prime); 165 | let exponent = 3_i32.to_bigint().unwrap(); 166 | 167 | assert_eq!(a.pow(exponent), b); 168 | } 169 | 170 | #[test] 171 | fn divide_two_field_elements() { 172 | let prime = 19_i32.to_bigint().unwrap(); 173 | let a_num = 2_i32.to_bigint().unwrap(); 174 | let b_num = 7_i32.to_bigint().unwrap(); 175 | let c_num = 3_i32.to_bigint().unwrap(); 176 | let a = FieldElement::new(a_num, prime.clone()); 177 | let b = FieldElement::new(b_num, prime.clone()); 178 | let c = FieldElement::new(c_num, prime); 179 | 180 | assert_eq!(a / b, c); 181 | } 182 | 183 | #[test] 184 | fn power_a_field_element_to_a_negative_exponent() { 185 | let prime = 13_i32.to_bigint().unwrap(); 186 | let a_num = 7_i32.to_bigint().unwrap(); 187 | let b_num = 8_i32.to_bigint().unwrap(); 188 | let a = FieldElement::new(a_num, prime.clone()); 189 | let b = FieldElement::new(b_num, prime); 190 | let exponent = -3_i32.to_bigint().unwrap(); 191 | 192 | assert_eq!(a.pow(exponent), b); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /chapter_01/implementation_02/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod field_element; 2 | -------------------------------------------------------------------------------- /chapter_01/implementation_03/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /chapter_01/implementation_03/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.57" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" 10 | 11 | [[package]] 12 | name = "programming_bitcoin_rs" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "anyhow", 16 | ] 17 | -------------------------------------------------------------------------------- /chapter_01/implementation_03/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "programming_bitcoin_rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [dependencies.anyhow] 8 | version = "1.0" 9 | -------------------------------------------------------------------------------- /chapter_01/implementation_03/src/errors.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct NotPrimeError; 5 | 6 | impl fmt::Display for NotPrimeError { 7 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 8 | write!(f, "The order of the field must be a prime number") 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /chapter_01/implementation_03/src/finite_field_element.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::NotPrimeError; 2 | use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign}; 3 | 4 | fn is_prime(number_to_check: u128) -> bool { 5 | if number_to_check == 1 { 6 | return false; 7 | } 8 | 9 | let mut aux = 2; 10 | while aux * aux <= number_to_check { 11 | if number_to_check % aux == 0 { 12 | return false; 13 | } 14 | aux += 1; 15 | } 16 | 17 | true 18 | } 19 | 20 | #[derive(Debug, Clone, PartialEq, Eq)] 21 | pub struct FiniteFieldElement { 22 | pub value: i128, 23 | } 24 | 25 | impl FiniteFieldElement

{ 26 | pub fn new(value: i128) -> Result { 27 | if !is_prime(P) { 28 | return Err(NotPrimeError); 29 | } 30 | Ok(Self { value }) 31 | } 32 | } 33 | 34 | impl Add> for FiniteFieldElement

{ 35 | type Output = Self; 36 | 37 | fn add(self, other_number: Self) -> Self { 38 | Self { 39 | value: (self.value + other_number.value).rem_euclid(P as i128), 40 | } 41 | } 42 | } 43 | 44 | impl Sub> for FiniteFieldElement

{ 45 | type Output = Self; 46 | 47 | fn sub(self, other_number: Self) -> Self { 48 | Self { 49 | value: (self.value - other_number.value).rem_euclid(P as i128), 50 | } 51 | } 52 | } 53 | 54 | impl Mul> for FiniteFieldElement

{ 55 | type Output = Self; 56 | 57 | fn mul(self, other_number: Self) -> Self { 58 | Self { 59 | value: (self.value * other_number.value).rem_euclid(P as i128), 60 | } 61 | } 62 | } 63 | 64 | impl FiniteFieldElement

{ 65 | pub fn pow(&self, n: i128) -> Self { 66 | let exp = n.rem_euclid((P - 1_u128) as i128); 67 | let mut value = self.value; 68 | for _ in 1..exp { 69 | value *= self.value; 70 | } 71 | Self { 72 | value: value.rem_euclid(P as i128), 73 | } 74 | } 75 | } 76 | 77 | impl Div> for FiniteFieldElement

{ 78 | type Output = Self; 79 | 80 | fn div(self, other_number: Self) -> Self { 81 | self * other_number.pow((P - 2_u128) as i128) 82 | } 83 | } 84 | 85 | impl MulAssign> for FiniteFieldElement

{ 86 | fn mul_assign(&mut self, other_number: Self) { 87 | self.value = (self.value * other_number.value).rem_euclid(P as i128); 88 | } 89 | } 90 | 91 | impl AddAssign> for FiniteFieldElement

{ 92 | fn add_assign(&mut self, other_number: Self) { 93 | self.value = (self.value + other_number.value).rem_euclid(P as i128); 94 | } 95 | } 96 | 97 | impl SubAssign> for FiniteFieldElement

{ 98 | fn sub_assign(&mut self, other_number: Self) { 99 | self.value = (self.value - other_number.value).rem_euclid(P as i128); 100 | } 101 | } 102 | 103 | #[cfg(test)] 104 | mod tests { 105 | use super::*; 106 | 107 | #[should_panic] 108 | #[test] 109 | fn test_can_not_create_field_with_no_prime_order() { 110 | let _ = FiniteFieldElement::<10>::new(1).unwrap(); 111 | } 112 | 113 | #[test] 114 | fn test_add_two_finite_field_elements() { 115 | let first_field_element = FiniteFieldElement::<11>::new(1).unwrap(); 116 | let second_field_element = FiniteFieldElement::<11>::new(20).unwrap(); 117 | 118 | assert_eq!( 119 | first_field_element + second_field_element, 120 | FiniteFieldElement::<11>::new(10).unwrap() 121 | ); 122 | } 123 | 124 | #[test] 125 | fn test_sub_two_finite_field_elements() { 126 | let first_field_element = FiniteFieldElement::<11>::new(1).unwrap(); 127 | let second_field_element = FiniteFieldElement::<11>::new(20).unwrap(); 128 | 129 | assert_eq!( 130 | first_field_element - second_field_element, 131 | FiniteFieldElement::<11>::new(3).unwrap() 132 | ); 133 | } 134 | 135 | #[test] 136 | fn test_mul_two_finite_field_elements() { 137 | let first_field_element = FiniteFieldElement::<11>::new(1).unwrap(); 138 | let second_field_element = FiniteFieldElement::<11>::new(20).unwrap(); 139 | 140 | assert_eq!( 141 | first_field_element * second_field_element, 142 | FiniteFieldElement::<11>::new(9).unwrap() 143 | ); 144 | } 145 | 146 | #[test] 147 | fn test_pow_a_finite_field_with_a_number() { 148 | let first_field_element = FiniteFieldElement::<11>::new(3).unwrap(); 149 | 150 | assert_eq!( 151 | first_field_element.pow(3), 152 | FiniteFieldElement::<11>::new(5).unwrap() 153 | ); 154 | } 155 | 156 | #[test] 157 | fn test_div_two_finite_field_elements() { 158 | let first_field_element = FiniteFieldElement::<11>::new(1).unwrap(); 159 | let second_field_element = FiniteFieldElement::<11>::new(20).unwrap(); 160 | 161 | assert_eq!( 162 | first_field_element / second_field_element, 163 | FiniteFieldElement::<11>::new(5).unwrap() 164 | ); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /chapter_01/implementation_03/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod errors; 2 | mod finite_field_element; 3 | -------------------------------------------------------------------------------- /chapter_02/implementation_02/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode 3 | .env 4 | -------------------------------------------------------------------------------- /chapter_02/implementation_02/.tool-versions: -------------------------------------------------------------------------------- 1 | rust 1.59.0 2 | -------------------------------------------------------------------------------- /chapter_02/implementation_02/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "num-bigint" 13 | version = "0.4.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" 16 | dependencies = [ 17 | "autocfg", 18 | "num-integer", 19 | "num-traits", 20 | ] 21 | 22 | [[package]] 23 | name = "num-integer" 24 | version = "0.1.45" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 27 | dependencies = [ 28 | "autocfg", 29 | "num-traits", 30 | ] 31 | 32 | [[package]] 33 | name = "num-traits" 34 | version = "0.2.15" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 37 | dependencies = [ 38 | "autocfg", 39 | ] 40 | 41 | [[package]] 42 | name = "programming_bitcoin" 43 | version = "0.1.0" 44 | dependencies = [ 45 | "num-bigint", 46 | ] 47 | -------------------------------------------------------------------------------- /chapter_02/implementation_02/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "programming_bitcoin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | num-bigint = "0.4" 10 | -------------------------------------------------------------------------------- /chapter_02/implementation_02/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Gabriel Bosio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /chapter_02/implementation_02/README.md: -------------------------------------------------------------------------------- 1 | # Programming Bitcoin 2 | Rust implementation of the exercises from [the Programming Bitcoin book](https://www.oreilly.com/library/view/programming-bitcoin/9781492031482/). 3 | -------------------------------------------------------------------------------- /chapter_02/implementation_02/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod point; 2 | -------------------------------------------------------------------------------- /chapter_02/implementation_02/src/point.rs: -------------------------------------------------------------------------------- 1 | use num_bigint::BigInt; 2 | use std::fmt; 3 | use std::ops; 4 | 5 | #[derive(Clone, Debug, Eq)] 6 | pub struct Point { 7 | x: Option, 8 | y: Option, 9 | a: BigInt, 10 | b: BigInt, 11 | } 12 | 13 | impl Point { 14 | pub fn new(x: Option, y: Option, a: BigInt, b: BigInt) -> Self { 15 | match (x.clone(), y.clone()) { 16 | (Some(x_num), Some(y_num)) => { 17 | if y_num.pow(2) != x_num.pow(3) + a.clone() * x_num.clone() + b.clone() { 18 | panic!("({}, {}) is not on the curve", x_num, y_num); 19 | } 20 | } 21 | (Some(x_num), None) => { 22 | panic!("({}, None) is not valid", x_num); 23 | } 24 | (None, Some(y_num)) => { 25 | panic!("(None, {}) is not valid", y_num); 26 | } 27 | (None, None) => {} 28 | } 29 | Self { x, y, a, b } 30 | } 31 | } 32 | 33 | impl fmt::Display for Point { 34 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 35 | match (self.x.clone(), self.y.clone()) { 36 | (Some(x_num), Some(y_num)) => { 37 | write!(f, "Point({},{})_{}_{}", x_num, y_num, self.a, self.b) 38 | } 39 | (None, None) => write!(f, "Point(infinity)_{}_{}", self.a, self.b), 40 | _ => { 41 | panic!("This shouldn't happen"); 42 | } 43 | } 44 | } 45 | } 46 | 47 | impl PartialEq for Point { 48 | fn eq(&self, other: &Self) -> bool { 49 | self.x == other.x && self.y == other.y && self.a == other.a && self.b == other.b 50 | } 51 | } 52 | 53 | impl ops::Add for Point { 54 | type Output = Self; 55 | 56 | fn add(self, rhs: Self) -> Self::Output { 57 | if self.a != rhs.a || self.b != rhs.b { 58 | panic!("Points {}, {} are not on the same curve", self, rhs); 59 | } 60 | match ( 61 | (self.x.clone(), self.y.clone()), 62 | (rhs.x.clone(), rhs.y.clone()), 63 | ) { 64 | ((None, _), (Some(_), _)) => rhs, 65 | ((Some(_), _), (None, _)) => self, 66 | ((Some(self_x), Some(self_y)), (Some(rhs_x), Some(rhs_y))) 67 | if self_x == rhs_x && self_y == -rhs_y.clone() => 68 | { 69 | Self { 70 | x: None, 71 | y: None, 72 | a: self.a, 73 | b: self.b, 74 | } 75 | } 76 | ((Some(self_x), Some(self_y)), (Some(rhs_x), Some(rhs_y))) if self_x != rhs_x => { 77 | let slope = (rhs_y - self_y.clone()) / (rhs_x.clone() - self_x.clone()); 78 | println!("slope: {}", slope.clone()); 79 | let result_x = slope.clone() * slope.clone() - self_x.clone() - rhs_x; 80 | let result_y = slope * (self_x - result_x.clone()) - self_y; 81 | 82 | Self { 83 | x: Some(result_x), 84 | y: Some(result_y), 85 | a: self.a, 86 | b: self.b, 87 | } 88 | } 89 | // Other cases, TODO 90 | _ => self, 91 | } 92 | } 93 | } 94 | 95 | #[cfg(test)] 96 | mod tests { 97 | use num_bigint::ToBigInt; 98 | 99 | use super::*; 100 | 101 | #[test] 102 | #[should_panic] 103 | fn create_a_point_that_is_not_in_the_curve() { 104 | let p1_x = Some(-1_i32.to_bigint().unwrap()); 105 | let p1_y = Some(-1_i32.to_bigint().unwrap()); 106 | let a = 5_i32.to_bigint().unwrap(); 107 | let b = 7_i32.to_bigint().unwrap(); 108 | let p2_x = Some(-1_i32.to_bigint().unwrap()); 109 | let p2_y = Some(-2_i32.to_bigint().unwrap()); 110 | Point::new(p1_x, p1_y, a.clone(), b.clone()); 111 | Point::new(p2_x, p2_y, a.clone(), b.clone()); 112 | } 113 | #[test] 114 | fn compare_two_points() { 115 | let p1_x = Some(-1_i32.to_bigint().unwrap()); 116 | let p1_y = Some(-1_i32.to_bigint().unwrap()); 117 | let a = 5_i32.to_bigint().unwrap(); 118 | let b = 7_i32.to_bigint().unwrap(); 119 | let p2_x = Some(-1_i32.to_bigint().unwrap()); 120 | let p2_y = Some(-1_i32.to_bigint().unwrap()); 121 | let p1 = Point::new(p1_x, p1_y, a.clone(), b.clone()); 122 | let p2 = Point::new(p2_x, p2_y, a.clone(), b.clone()); 123 | 124 | assert_eq!(p1, p2); 125 | } 126 | 127 | #[test] 128 | fn add_two_points_with_the_same_x() { 129 | let p1_x = Some(-1_i32.to_bigint().unwrap()); 130 | let p1_y = Some(-1_i32.to_bigint().unwrap()); 131 | let a = 5_i32.to_bigint().unwrap(); 132 | let b = 7_i32.to_bigint().unwrap(); 133 | let p2_x = Some(-1_i32.to_bigint().unwrap()); 134 | let p2_y = Some(1_i32.to_bigint().unwrap()); 135 | let p1 = Point::new(p1_x, p1_y, a.clone(), b.clone()); 136 | let p2 = Point::new(p2_x, p2_y, a.clone(), b.clone()); 137 | let inf = Point::new(None, None, a.clone(), b.clone()); 138 | 139 | assert_eq!(p1.clone() + inf.clone(), p1); 140 | assert_eq!(inf.clone() + p2.clone(), p2); 141 | assert_eq!(p1 + p2, inf); 142 | } 143 | #[test] 144 | fn add_two_points_with_different_x() { 145 | let p1_x = Some(2_i32.to_bigint().unwrap()); 146 | let p1_y = Some(5_i32.to_bigint().unwrap()); 147 | let a = 5_i32.to_bigint().unwrap(); 148 | let b = 7_i32.to_bigint().unwrap(); 149 | let p2_x = Some(-1_i32.to_bigint().unwrap()); 150 | let p2_y = Some(-1_i32.to_bigint().unwrap()); 151 | let p3_x = Some(3_i32.to_bigint().unwrap()); 152 | let p3_y = Some(-7_i32.to_bigint().unwrap()); 153 | let p1 = Point::new(p1_x, p1_y, a.clone(), b.clone()); 154 | let p2 = Point::new(p2_x, p2_y, a.clone(), b.clone()); 155 | let p3 = Point::new(p3_x, p3_y, a.clone(), b.clone()); 156 | 157 | assert_eq!(p1 + p2, p3); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /chapter_02/implementation_03/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /chapter_02/implementation_03/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.57" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" 10 | 11 | [[package]] 12 | name = "programming_bitcoin_rs" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "anyhow", 16 | ] 17 | -------------------------------------------------------------------------------- /chapter_02/implementation_03/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "programming_bitcoin_rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [dependencies.anyhow] 8 | version = "1.0" 9 | -------------------------------------------------------------------------------- /chapter_02/implementation_03/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod point; 2 | -------------------------------------------------------------------------------- /chapter_02/implementation_03/src/point.rs: -------------------------------------------------------------------------------- 1 | use anyhow::ensure; 2 | use std::ops::Add; 3 | 4 | #[derive(Clone, Copy, Debug)] 5 | pub enum Point { 6 | Point(i64, i64), 7 | Infinity 8 | } 9 | 10 | impl Point { 11 | pub fn new_point(x: i64, y: i64) -> Result { 12 | ensure!(y.pow(2) == (x.pow(3) + A * x + B), "Point is not on the curve"); 13 | Ok(Point::::Point(x, y)) 14 | } 15 | 16 | pub fn new_infinity() -> Self { 17 | Point::::Infinity 18 | } 19 | } 20 | 21 | impl PartialEq for Point { 22 | fn eq(&self, other: &Self) -> bool { 23 | match (self, other) { 24 | (Self::Infinity, Self::Infinity) => true, 25 | (Self::Point(x1, y1), Self::Point(x2, y2)) => x1 == x2 && y1 == y2, 26 | _ => false, 27 | } 28 | } 29 | } 30 | 31 | impl Add> for Point { 32 | type Output = Self; 33 | fn add(self, other: Point) -> Self { 34 | match (self, other) { 35 | (Self::Infinity, Self::Infinity) => Self::new_infinity(), 36 | (Self::Infinity, Self::Point(_, _)) => other, 37 | (Self::Point(_, _), Self::Infinity) => self, 38 | (Self::Point(x1, y1), Self::Point(x2, y2)) if x1 == x2 && y1 != y2 => Self::new_infinity(), 39 | (Self::Point(x1, y1), Self::Point(x2, y2)) if x1 == x2 && (y1 == 0 || y2 == 0) => Self::new_infinity(), 40 | (Self::Point(x1, y1), Self::Point(x2, y2)) if x1 == x2 && y1 == y2 => { 41 | let slope = (3 * x1.pow(2) + A) / (2 * y1); 42 | let x3 = slope.pow(2) - (2 * x1); 43 | let y3 = slope * (x1 - x3) - y1; 44 | Self::new_point(x3, y3).unwrap() 45 | }, 46 | (Self::Point(x1, y1), Self::Point(x2, y2)) if x1 != x2 => { 47 | let slope = (y2 - y1) / (x2 - x1); 48 | let x3 = slope.pow(2) - (x1 + x2); 49 | let y3 = slope * (x1 - x3) - y1; 50 | Self::new_point(x3, y3).unwrap() 51 | }, 52 | _ => todo!(), 53 | } 54 | } 55 | } 56 | 57 | #[cfg(test)] 58 | mod point_tests { 59 | use super::*; 60 | 61 | #[test] 62 | fn test00_create_valid_point() { 63 | assert!(Point::<5, 7>::new_point(-1, -1).is_ok()); 64 | } 65 | 66 | #[test] 67 | fn test01_create_invalid_point() { 68 | assert!(Point::<5, 7>::new_point(-1, -2).is_err()); 69 | } 70 | 71 | #[test] 72 | fn test02_points_with_same_cords_are_equal() { 73 | let point1 = Point::<5, 7>::new_point(-1, -1).unwrap(); 74 | let point2 = Point::<5, 7>::new_point(-1, -1).unwrap(); 75 | 76 | assert_eq!(point1, point2); 77 | } 78 | 79 | #[test] 80 | fn test03_points_with_different_cords_are_different() { 81 | let point1 = Point::<5, 7>::new_point(-1, -1).unwrap(); 82 | let point2 = Point::<5, 7>::new_point(18, 77).unwrap(); 83 | 84 | assert_ne!(point1, point2); 85 | } 86 | 87 | #[test] 88 | fn test04_sum_inifity_and_another_point_returns_the_point() { 89 | let infinity = Point::<5, 7>::new_infinity(); 90 | let point = Point::<5, 7>::new_point(-1, -1).unwrap(); 91 | 92 | assert_eq!(point + infinity, point); 93 | assert_eq!(infinity + point, point); 94 | } 95 | 96 | #[test] 97 | fn test05_two_different_points_from_the_same_curve_can_be_added() { 98 | let point1 = Point::<5, 7>::new_point(2, 5).unwrap(); 99 | let point2 = Point::<5, 7>::new_point(-1, -1).unwrap(); 100 | let expected_point3 = Point::<5, 7>::new_point(3, -7).unwrap(); 101 | 102 | assert_eq!(point1 + point2, expected_point3); 103 | assert_eq!(point2 + point1, expected_point3); 104 | } 105 | 106 | #[test] 107 | fn test06_adding_the_same_point_results_infinity() { 108 | let point1 = Point::<5, 7>::new_point(-1, -1).unwrap(); 109 | let point2 = Point::<5, 7>::new_point(-1, 1).unwrap(); 110 | 111 | let expected_point3 = Point::<5, 7>::new_infinity(); 112 | 113 | assert_eq!(point1 + point2, expected_point3); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/chapter_01/implementation_01/main.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::ops::{Add, Div, Mul}; 3 | 4 | #[derive(Debug)] 5 | pub struct FieldElement { 6 | num: u32, 7 | prime: u32, 8 | } 9 | 10 | impl FieldElement { 11 | pub fn new(num: u32, prime: u32) -> Self { 12 | if num >= prime { 13 | panic!("Num {} not in field range 0 to {}", num, prime); 14 | } 15 | 16 | Self { num, prime } 17 | } 18 | 19 | pub fn pow(self, power: i32) -> Self { 20 | let exponent: i32 = power.rem_euclid((self.prime - 1) as i32); 21 | let result = u32::pow(self.num, exponent as u32) % self.prime; 22 | FieldElement::new(result, self.prime) 23 | } 24 | } 25 | 26 | impl Add for FieldElement { 27 | type Output = Self; 28 | 29 | fn add(self, other: Self) -> Self { 30 | if self.prime != other.prime { 31 | panic!("Cannot add two numbers in different field"); 32 | } 33 | let num: u32 = (self.num + other.num) % self.prime; 34 | let prime: u32 = self.prime; 35 | Self { num, prime } 36 | } 37 | } 38 | 39 | impl Mul for FieldElement { 40 | type Output = Self; 41 | 42 | fn mul(self, other: Self) -> Self { 43 | if self.prime != other.prime { 44 | panic!("Cannot multiplicate two numbers in different field"); 45 | } 46 | let num: u32 = (self.num * other.num) % self.prime; 47 | let prime: u32 = self.prime; 48 | Self { num, prime } 49 | } 50 | } 51 | 52 | impl Div for FieldElement { 53 | type Output = Self; 54 | 55 | fn div(self, other: Self) -> Self { 56 | if self.prime != other.prime { 57 | panic!("Cannot divide two numbers of different Fields"); 58 | } 59 | 60 | if other.num == 0 { 61 | panic!("Cannot divide by zero-valued FieldElement!"); 62 | } 63 | 64 | let other_inverse = other.pow((self.prime - 2) as i32); 65 | self * other_inverse 66 | } 67 | } 68 | 69 | impl PartialEq for FieldElement { 70 | fn eq(&self, other: &Self) -> bool { 71 | self.num == other.num && self.prime == other.prime 72 | } 73 | } 74 | 75 | impl fmt::Display for FieldElement { 76 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 77 | write!(f, "number: {}, prime: {}", self.num, self.prime) 78 | } 79 | } 80 | 81 | fn main() { 82 | let field: FieldElement = FieldElement::new(10, 11); 83 | let other_field: FieldElement = FieldElement::new(10, 11); 84 | let an_other_field: FieldElement = FieldElement::new(20, 21); 85 | 86 | assert_eq!(field, other_field); 87 | assert_ne!(field, an_other_field); 88 | 89 | let sum_elem1: FieldElement = FieldElement::new(1, 10); 90 | let sum_elem2: FieldElement = FieldElement::new(1, 10); 91 | let sum_total_hardcoded: FieldElement = FieldElement::new(2, 10); 92 | let sum_total: FieldElement = sum_elem1.add(sum_elem2); 93 | 94 | assert_eq!(sum_total, sum_total_hardcoded); 95 | 96 | let mul_elem1: FieldElement = FieldElement::new(2, 10); 97 | let mul_elem2: FieldElement = FieldElement::new(2, 10); 98 | let mul_total_hardcoded: FieldElement = FieldElement::new(4, 10); 99 | let mul_total: FieldElement = mul_elem1.mul(mul_elem2); 100 | 101 | assert_eq!(mul_total, mul_total_hardcoded); 102 | 103 | let pow_elem1: FieldElement = FieldElement::new(2, 10); 104 | let power: i32 = 4; 105 | let pow_total_hardcoded: FieldElement = FieldElement::new(6, 10); 106 | let pow_total: FieldElement = pow_elem1.pow(power); 107 | assert_eq!(pow_total, pow_total_hardcoded); 108 | 109 | let div_elem1: FieldElement = FieldElement::new(2, 7); 110 | let div_elem2: FieldElement = FieldElement::new(3, 7); 111 | let div_total_hardcoded: FieldElement = FieldElement::new(3, 7); 112 | let div_total: FieldElement = div_elem1 / div_elem2; 113 | 114 | assert_eq!(div_total, div_total_hardcoded); 115 | 116 | let pow_elem1: FieldElement = FieldElement::new(2, 7); 117 | let power: i32 = -1; 118 | let pow_total_hardcoded: FieldElement = FieldElement::new(4, 7); 119 | let pow_total: FieldElement = pow_elem1.pow(power); 120 | 121 | assert_eq!(pow_total, pow_total_hardcoded); 122 | } 123 | -------------------------------------------------------------------------------- /src/chapter_02/implementation_01/point.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Add; 2 | 3 | #[derive(Copy, Clone, Debug)] 4 | pub struct Point { 5 | x: Option, 6 | y: Option, 7 | a: i32, 8 | b: i32, 9 | } 10 | 11 | impl Point { 12 | pub fn new(x: Option, y: Option, a: i32, b: i32) -> Point { 13 | if let (Some(x_some), Some(y_some)) = (x, y) { 14 | if i32::pow(y_some, 2) != i32::pow(x_some, 3) + (a * x_some) + b { 15 | panic!("The point is not in the curve.") 16 | } else { 17 | return Point {x, y, a, b} 18 | } 19 | } 20 | Point{ x: None, y: None, a, b } 21 | } 22 | } 23 | 24 | impl PartialEq for Point { 25 | fn eq(self: &Self, other: &Self) -> bool { 26 | self.x == other.x && self.y == other.y && self.a == other.a && self.b == other.b 27 | } 28 | } 29 | 30 | impl Add for Point { 31 | type Output = Self; 32 | fn add(self: Self, other: Self) -> Self { 33 | if self.a != other.a || self.b != other.b { 34 | panic!("Not the same elliptic curve") 35 | } if let None = self.x { 36 | return Point{x: None, y: None, a: self.a, b: self.b}; 37 | } if let None = other.x { 38 | return Point{x: None, y: None, a: self.a, b: self.b}; 39 | } 40 | if let None = self.y { 41 | return Point{x: None, y: None, a: self.a, b: self.b}; 42 | } if let None = other.y { 43 | return Point{x: None, y: None, a: self.a, b: self.b}; 44 | } 45 | 46 | let x1: i32 = self.x.unwrap(); 47 | let x2: i32 = other.x.unwrap(); 48 | 49 | let y1: i32 = self.y.unwrap(); 50 | let y2: i32 = other.y.unwrap(); 51 | if x1 != x2 { 52 | let s: i32 = (y2 - y1) / (x2 - x1); 53 | let x3 = i32::pow(s, 2) - x1 - x2; 54 | let y3: i32 = s*(x1 - x3) - y1; 55 | return Point::new(Some(x3), Some(y3), self.a, self.b) 56 | } 57 | if self == other && y1 == 0 * x1 { 58 | return Point::new(None, None, self.a, self.b) 59 | } 60 | if self == other { 61 | let s: i32 = (3 * i32::pow(x1, 2) + self.a) / ( 2 * y1); 62 | let x3: i32 = i32::pow(s, 2) - 2 * x1; 63 | let y3: i32 = s * (x1 - x3) - y1; 64 | return Point::new(Some(x3), Some(y3), self.a, self.b) 65 | } 66 | Point {x: None,y: None,a: self.a, b: self.b} 67 | } 68 | } 69 | 70 | 71 | #[cfg(test)] 72 | mod point_test { 73 | use super::*; 74 | #[test] 75 | fn point_inside_curve() { 76 | let pairs = [(-1, 1), (-1, -1), (2, -5), (3, -7), (3, 7), (18, -77)]; 77 | for pair in pairs { 78 | let (x, y) = pair; 79 | let _point = Point::new(Some(x), Some(y), 5, 7); 80 | } 81 | } 82 | #[test] 83 | #[should_panic] 84 | fn point_outside_curve() { 85 | let pairs = [(-4, 1), (-3, -1), (55, -5), (1, -7), (3, 9), (111, -77)]; 86 | for pair in pairs { 87 | let (x, y) = pair; 88 | let _point = Point::new(Some(x), Some(y), 5, 7); 89 | } 90 | let _point = Point::new(Some(-1), Some(-2), 5, 7); 91 | } 92 | #[test] 93 | fn add_same_x_different_y() { 94 | let point_0 = Point::new(Some(-1), Some(1), 5, 7); 95 | let point_1 = Point::new(Some(-1), Some(-1), 5, 7); 96 | let infinity_5_7 = Point::new(None, None, 5, 7); 97 | let must_be_infinity = point_0 + point_1; 98 | assert_eq!(must_be_infinity, infinity_5_7); 99 | } 100 | #[test] 101 | fn add_infinite() { 102 | let inf = None; 103 | let point_0 = Point::new(inf, inf, 5, 7); 104 | let point_1 = Point::new(Some(2), Some(5), 5, 7); 105 | assert_eq!(point_0, (point_0.clone() + point_1)); 106 | } 107 | #[test] 108 | fn add_different_x() { 109 | let point_0 = Point::new(Some(3), Some(7), 5, 7); 110 | let point_1 = Point::new(Some(-1), Some(-1), 5, 7); 111 | let result = point_0.clone() + point_1.clone(); 112 | assert_eq!( 113 | result, 114 | Point::new(Some(2), Some(-5), 5, 7) 115 | ); 116 | } 117 | // Tests taken from the books' repo. 118 | #[test] 119 | fn add_equal() { 120 | let a = Point::new(Some(-1), Some(-1), 5, 7); 121 | let b = a.clone(); 122 | let result = a + b; 123 | let expected = Point::new(Some(18), Some(77), 5, 7); 124 | assert_eq!(expected, result); 125 | } 126 | #[test] 127 | fn not_equal() { 128 | let a = Point::new(Some(3), Some(-7), 5, 7); 129 | let b = Point::new(Some(18), Some(77), 5, 7); 130 | assert!(a != b); 131 | assert!(a == a); 132 | } 133 | } 134 | --------------------------------------------------------------------------------