Skip to content

Commit dff2733

Browse files
committed
CompactSize should allow for 0
1 parent 11fdeab commit dff2733

File tree

13 files changed

+21
-153
lines changed

13 files changed

+21
-153
lines changed

Diff for: 10_compact_size_unsigned_integers.md

+8-14
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
2121
let mut compact_size = [0; 1];
2222
transaction_bytes.read(&mut compact_size).unwrap();
2323

24-
if (1..253).contains(&compact_size[0]) {
24+
if (0..253).contains(&compact_size[0]) {
2525
compact_size[0] as u64
2626
} else if compact_size[0] == 253 {
2727
let mut buffer = [0; 2];
@@ -31,22 +31,19 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
3131
let mut buffer = [0; 4];
3232
transaction_bytes.read(&mut buffer).unwrap();
3333
u32::from_le_bytes(buffer) as u64
34-
} else if compact_size[0] == 255 {
34+
} else { // only other possible value for a u8 here is 255
3535
let mut buffer = [0; 8];
3636
transaction_bytes.read(&mut buffer).unwrap();
3737
u64::from_le_bytes(buffer)
38-
} else {
39-
panic!("invalid compact size");
4038
}
4139
}
4240
```
4341

4442
A few things to point out here:
45-
1. `1..253` syntax is a [range type](https://doc.rust-lang.org/std/ops/struct.Range.html#), which has a method called `contains` to check if a value is in the given range.
43+
1. `0..253` syntax is a [range type](https://doc.rust-lang.org/std/ops/struct.Range.html#), which has a method called `contains` to check if a value is in the given range.
4644
2. The number of bytes read match the integer type. For example, 2 bytes give us a `u16` type. 4 bytes give us a `u32` type.
4745
3. We **cast** each type into a `u64`. We can convert between primitive types in Rust using the [`as` keyword](https://doc.rust-lang.org/std/keyword.as.html).
4846
4. Notice how there are are no semicolons for each ending line, such as `u32::from_le_bytes(buffer) as u64`. This is the equivalent of returning that value from the function. We could also write it as `return u32::from_le_bytes(buffer) as u64;` but implicit return without semicolon is more idiomatic.
49-
5. We will call `panic!` and crash our program if the compact size number does not match the specification. This is exactly what happens when `unwrap` is called on a failed result. Technically, the only way this code will be reached is if the size is `0`. All other `u8` values are captured and anything that is not a valid `u8` will not enter this function.
5047

5148
We're going to make one more change. While standard if/else statements work fine, Rust provides pattern matching via the `match` keyword and this is a good opportunity to use it as it is commonly used in Rust codebases. https://doc.rust-lang.org/book/ch06-02-match.html
5249

@@ -56,9 +53,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
5653
transaction_bytes.read(&mut compact_size).unwrap();
5754

5855
match compact_size[0] {
59-
1..=252 => {
60-
compact_size[0] as u64
61-
},
56+
0..=252 => compact_size[0] as u64,
6257
253 => {
6358
let mut buffer = [0; 2];
6459
transaction_bytes.read(&mut buffer).unwrap();
@@ -73,15 +68,14 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
7368
let mut buffer = [0; 8];
7469
transaction_bytes.read(&mut buffer).unwrap();
7570
u64::from_le_bytes(buffer)
76-
},
77-
_ => {
78-
panic!("invalid compact size");
7971
}
8072
}
8173
}
8274
```
8375

84-
What do you think? The `match` looks nicer doesn't it? Take a moment to get familiar with the syntax. Each of the `arm`'s has a pattern to match followed by `=>` and then some code to return for that given pattern. The `_` at the end is a catchall for any pattern not specified above. It's also nice that we don't have to repeat calling `compact_size[0]` for each scenario.
76+
What do you think? The `match` looks nicer doesn't it? Take a moment to get familiar with the syntax. Each of the `arm`'s has a pattern to match followed by `=>` and then some code to return for that given pattern.
77+
78+
We sometimes see an arm with the underscore symbol (`_` ) as the pattern to match. This represents a catchall pattern that will capture any value not already covered by the previous arms. However, in our case, this is not needed since the previous arms are exhaustive and capture all the possible scenarios. Remember a `u8` can only have a value between `0` and `255`.
8579

8680
Now all we have to do is update our `main` function to call this and return the number of inputs.
8781

@@ -105,7 +99,7 @@ Version: 1
10599
Input Length: 2
106100
```
107101

108-
Pretty neat! We're making good progress. But even though our code compiles, how can we be sure we've written it correctly and that this function will return the appropriate number of inputs for different transactions? We want to test it with different inputs and ensure it is returning the appropriate outputs. We can do this with unit testing. So let's look into setting up our first unit test in the next section.
102+
Pretty neat! We're making good progress. But even though our code compiles, how can we be sure we've written it correctly and that this function will return the appropriate number of inputs for different transactions? We want to test it with different arguments and ensure it is returning the correct compactSize. We can do this with unit testing. So let's look into setting up our first unit test in the next section.
109103

110104
### Quiz
111105
*How do nodes know whether the transaction is a legacy or a segwit transaction as they read it? How do they know whether to view the next field after the version as an input length encoded as compactSize or as the marker and flag for a Segwit transaction?*

Diff for: 11_unit_testing.md

-15
Original file line numberDiff line numberDiff line change
@@ -200,27 +200,12 @@ fn test_reading_compact_size() {
200200
}
201201
```
202202

203-
That checks out! Neat. Let's add another test to account for the scenario in which the program `panic!`s. Since a `panic!` will cause the program to crash, we need a way of catching that when it happens and then checking the result.
204-
205-
One way we can do this is with `std::panic::catch_unwind` which will take the code we want to run as a `closure` and return an `Err` `Result` if a `panic!` is reached. We'll talk more about the `Result` enum later on. For now just know that there's a method, `is_err`, that will check if the `Result` is the `Err` type. And if you're not familiar with closures, you can read more about them [here](https://doc.rust-lang.org/book/ch13-01-closures.html). These are essentially anonymous functions that we can pass into another function as an argument.
206-
207-
```rust
208-
let result = std::panic::catch_unwind(|| {
209-
let mut bytes = [0_u8].as_slice();
210-
read_compact_size(&mut bytes);
211-
});
212-
assert!(result.is_err());
213-
```
214-
215-
This is the only scenario in which the code will panic. We won't be able to pass in a number greater than 255 because we've ensured that the input type is a `u8` and the maximum number for a `u8` is 255. So the only value that will actually cause the program to crash is 0.
216-
217203
Run this with `cargo test` and all the tests should pass!
218204

219205
Great! We've learned about unit testing. We'll keep this in mind as we write more functions with complex logic. Let's keep it moving and keep reading the transaction.
220206

221207
### Additional Reading
222208
* Test Organization: https://doc.rust-lang.org/book/ch11-03-test-organization.html
223-
* Closures: https://doc.rust-lang.org/book/ch13-01-closures.html
224209

225210
<hr/>
226211

Diff for: code/transaction_decoder_10/src/main.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
1313
transaction_bytes.read(&mut compact_size).unwrap();
1414

1515
match compact_size[0] {
16-
1..=252 => {
17-
compact_size[0] as u64
18-
},
16+
0..=252 => compact_size[0] as u64,
1917
253 => {
2018
let mut buffer = [0; 2];
2119
transaction_bytes.read(&mut buffer).unwrap();
@@ -30,9 +28,6 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
3028
let mut buffer = [0; 8];
3129
transaction_bytes.read(&mut buffer).unwrap();
3230
u64::from_le_bytes(buffer)
33-
},
34-
_ => {
35-
panic!("invalid compact size");
3631
}
3732
}
3833
}

Diff for: code/transaction_decoder_11/src/main.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
1212
transaction_bytes.read(&mut compact_size).unwrap();
1313

1414
match compact_size[0] {
15-
1..=252 => {
16-
compact_size[0] as u64
17-
},
15+
0..=252 => compact_size[0] as u64,
1816
253 => {
1917
let mut buffer = [0; 2];
2018
transaction_bytes.read(&mut buffer).unwrap();
@@ -29,9 +27,6 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
2927
let mut buffer = [0; 8];
3028
transaction_bytes.read(&mut buffer).unwrap();
3129
u64::from_le_bytes(buffer)
32-
},
33-
_ => {
34-
panic!("invalid compact size");
3530
}
3631
}
3732
}
@@ -78,11 +73,5 @@ mod unit_tests {
7873
let length = read_compact_size(&mut bytes);
7974
let expected_length = 20_000_u64;
8075
assert_eq!(length, expected_length);
81-
82-
let result = std::panic::catch_unwind(|| {
83-
let mut bytes = [0_u8].as_slice();
84-
read_compact_size(&mut bytes);
85-
});
86-
assert!(result.is_err());
8776
}
8877
}

Diff for: code/transaction_decoder_12/src/main.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
1212
transaction_bytes.read(&mut compact_size).unwrap();
1313

1414
match compact_size[0] {
15-
1..=252 => {
16-
compact_size[0] as u64
17-
},
15+
0..=252 => compact_size[0] as u64,
1816
253 => {
1917
let mut buffer = [0; 2];
2018
transaction_bytes.read(&mut buffer).unwrap();
@@ -29,9 +27,6 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
2927
let mut buffer = [0; 8];
3028
transaction_bytes.read(&mut buffer).unwrap();
3129
u64::from_le_bytes(buffer)
32-
},
33-
_ => {
34-
panic!("invalid compact size");
3530
}
3631
}
3732
}
@@ -99,11 +94,5 @@ mod unit_tests {
9994
let length = read_compact_size(&mut bytes);
10095
let expected_length = 20_000_u64;
10196
assert_eq!(length, expected_length);
102-
103-
let result = std::panic::catch_unwind(|| {
104-
let mut bytes = [0_u8].as_slice();
105-
read_compact_size(&mut bytes);
106-
});
107-
assert!(result.is_err());
10897
}
10998
}

Diff for: code/transaction_decoder_13/src/main.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
2020
transaction_bytes.read(&mut compact_size).unwrap();
2121

2222
match compact_size[0] {
23-
1..=252 => {
24-
compact_size[0] as u64
25-
},
23+
0..=252 => compact_size[0] as u64,
2624
253 => {
2725
let mut buffer = [0; 2];
2826
transaction_bytes.read(&mut buffer).unwrap();
@@ -37,9 +35,6 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
3735
let mut buffer = [0; 8];
3836
transaction_bytes.read(&mut buffer).unwrap();
3937
u64::from_le_bytes(buffer)
40-
},
41-
_ => {
42-
panic!("invalid compact size");
4338
}
4439
}
4540
}
@@ -115,11 +110,5 @@ mod unit_tests {
115110
let length = read_compact_size(&mut bytes);
116111
let expected_length = 20_000_u64;
117112
assert_eq!(length, expected_length);
118-
119-
let result = std::panic::catch_unwind(|| {
120-
let mut bytes = [0_u8].as_slice();
121-
read_compact_size(&mut bytes);
122-
});
123-
assert!(result.is_err());
124113
}
125114
}

Diff for: code/transaction_decoder_14/src/main.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
2727
transaction_bytes.read(&mut compact_size).unwrap();
2828

2929
match compact_size[0] {
30-
1..=252 => {
31-
compact_size[0] as u64
32-
},
30+
0..=252 => compact_size[0] as u64,
3331
253 => {
3432
let mut buffer = [0; 2];
3533
transaction_bytes.read(&mut buffer).unwrap();
@@ -44,9 +42,6 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
4442
let mut buffer = [0; 8];
4543
transaction_bytes.read(&mut buffer).unwrap();
4644
u64::from_le_bytes(buffer)
47-
},
48-
_ => {
49-
panic!("invalid compact size");
5045
}
5146
}
5247
}
@@ -126,11 +121,5 @@ mod unit_tests {
126121
let length = read_compact_size(&mut bytes);
127122
let expected_length = 20_000_u64;
128123
assert_eq!(length, expected_length);
129-
130-
let result = std::panic::catch_unwind(|| {
131-
let mut bytes = [0_u8].as_slice();
132-
read_compact_size(&mut bytes);
133-
});
134-
assert!(result.is_err());
135124
}
136125
}

Diff for: code/transaction_decoder_15/src/main.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
4949
transaction_bytes.read(&mut compact_size).unwrap();
5050

5151
match compact_size[0] {
52-
1..=252 => {
53-
compact_size[0] as u64
54-
},
52+
0..=252 => compact_size[0] as u64,
5553
253 => {
5654
let mut buffer = [0; 2];
5755
transaction_bytes.read(&mut buffer).unwrap();
@@ -66,9 +64,6 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
6664
let mut buffer = [0; 8];
6765
transaction_bytes.read(&mut buffer).unwrap();
6866
u64::from_le_bytes(buffer)
69-
},
70-
_ => {
71-
panic!("invalid compact size");
7267
}
7368
}
7469
}
@@ -165,11 +160,5 @@ mod unit_tests {
165160
let length = read_compact_size(&mut bytes);
166161
let expected_length = 20_000_u64;
167162
assert_eq!(length, expected_length);
168-
169-
let result = std::panic::catch_unwind(|| {
170-
let mut bytes = [0_u8].as_slice();
171-
read_compact_size(&mut bytes);
172-
});
173-
assert!(result.is_err());
174163
}
175164
}

Diff for: code/transaction_decoder_16/src/main.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
6060
transaction_bytes.read(&mut compact_size).unwrap();
6161

6262
match compact_size[0] {
63-
1..=252 => {
64-
compact_size[0] as u64
65-
},
63+
0..=252 => compact_size[0] as u64,
6664
253 => {
6765
let mut buffer = [0; 2];
6866
transaction_bytes.read(&mut buffer).unwrap();
@@ -77,9 +75,6 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
7775
let mut buffer = [0; 8];
7876
transaction_bytes.read(&mut buffer).unwrap();
7977
u64::from_le_bytes(buffer)
80-
},
81-
_ => {
82-
panic!("invalid compact size");
8378
}
8479
}
8580
}
@@ -176,11 +171,5 @@ mod unit_tests {
176171
let length = read_compact_size(&mut bytes);
177172
let expected_length = 20_000_u64;
178173
assert_eq!(length, expected_length);
179-
180-
let result = std::panic::catch_unwind(|| {
181-
let mut bytes = [0_u8].as_slice();
182-
read_compact_size(&mut bytes);
183-
});
184-
assert!(result.is_err());
185174
}
186175
}

Diff for: code/transaction_decoder_17/src/main.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
2121
transaction_bytes.read(&mut compact_size).unwrap();
2222

2323
match compact_size[0] {
24-
1..=252 => {
25-
compact_size[0] as u64
26-
},
24+
0..=252 => compact_size[0] as u64,
2725
253 => {
2826
let mut buffer = [0; 2];
2927
transaction_bytes.read(&mut buffer).unwrap();
@@ -38,9 +36,6 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
3836
let mut buffer = [0; 8];
3937
transaction_bytes.read(&mut buffer).unwrap();
4038
u64::from_le_bytes(buffer)
41-
},
42-
_ => {
43-
panic!("invalid compact size");
4439
}
4540
}
4641
}
@@ -137,11 +132,5 @@ mod unit_tests {
137132
let length = read_compact_size(&mut bytes);
138133
let expected_length = 20_000_u64;
139134
assert_eq!(length, expected_length);
140-
141-
let result = std::panic::catch_unwind(|| {
142-
let mut bytes = [0_u8].as_slice();
143-
read_compact_size(&mut bytes);
144-
});
145-
assert!(result.is_err());
146135
}
147136
}

Diff for: code/transaction_decoder_18/src/main.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
2222
transaction_bytes.read(&mut compact_size).unwrap();
2323

2424
match compact_size[0] {
25-
1..=252 => {
26-
compact_size[0] as u64
27-
},
25+
0..=252 => compact_size[0] as u64,
2826
253 => {
2927
let mut buffer = [0; 2];
3028
transaction_bytes.read(&mut buffer).unwrap();
@@ -39,9 +37,6 @@ fn read_compact_size(transaction_bytes: &mut &[u8]) -> u64 {
3937
let mut buffer = [0; 8];
4038
transaction_bytes.read(&mut buffer).unwrap();
4139
u64::from_le_bytes(buffer)
42-
},
43-
_ => {
44-
panic!("invalid compact size");
4540
}
4641
}
4742
}
@@ -164,11 +159,5 @@ mod unit_tests {
164159
let length = read_compact_size(&mut bytes);
165160
let expected_length = 20_000_u64;
166161
assert_eq!(length, expected_length);
167-
168-
let result = std::panic::catch_unwind(|| {
169-
let mut bytes = [0_u8].as_slice();
170-
read_compact_size(&mut bytes);
171-
});
172-
assert!(result.is_err());
173162
}
174163
}

0 commit comments

Comments
 (0)