Sfoglia il codice sorgente

Ensure that >> on a int128/int256 works

Add some tests that were originally written for solana.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 5 anni fa
parent
commit
3aadc43fcc
5 ha cambiato i file con 813 aggiunte e 1 eliminazioni
  1. BIN
      stdlib/stdlib.bc
  2. 39 0
      stdlib/stdlib.c
  3. 3 1
      tests/ewasm.rs
  4. 1 0
      tests/ewasm_tests/mod.rs
  5. 770 0
      tests/ewasm_tests/primitives.rs

BIN
stdlib/stdlib.bc


+ 39 - 0
stdlib/stdlib.c

@@ -315,6 +315,17 @@ typedef union
 	};
 } two64;
 
+// 128 bit shift left.
+typedef union
+{
+	__int128_t all;
+	struct
+	{
+		uint64_t low;
+		int64_t high;
+	};
+} two64s;
+
 // This assumes r >= 0 && r <= 127
 __uint128_t __ashlti3(__uint128_t val, int r)
 {
@@ -373,6 +384,34 @@ __uint128_t __lshrti3(__uint128_t val, int r)
 	return result.all;
 }
 
+__uint128_t __ashrti3(__uint128_t val, int r)
+{
+	two64s in;
+	two64s result;
+
+	in.all = val;
+
+	if (r == 0)
+	{
+		// nothing to do
+		result.all = in.all;
+	}
+	else if (r & 64)
+	{
+		// Shift more than or equal 64
+		result.high = in.high >> 63;
+		result.low = in.high >> (r & 63);
+	}
+	else
+	{
+		// Shift less than 64
+		result.low = (in.low >> r) | (in.high << (64 - r));
+		result.high = in.high >> r;
+	}
+
+	return result.all;
+}
+
 // sabre wants the storage keys as a hex string. Convert the uint256 pointed
 // to be by v into a hex string
 char *__u256ptohex(uint8_t *v, char *str)

+ 3 - 1
tests/ewasm.rs

@@ -11,6 +11,8 @@ use solang::file_cache::FileCache;
 use solang::sema::diagnostics;
 use solang::{compile, Target};
 
+mod ewasm_tests;
+
 type Address = [u8; 20];
 
 fn address_new() -> Address {
@@ -742,7 +744,7 @@ impl TestRuntime {
     }
 }
 
-fn build_solidity(src: &'static str) -> TestRuntime {
+fn build_solidity(src: &str) -> TestRuntime {
     let mut cache = FileCache::new();
 
     cache.set_file_contents("test.sol", src.to_string());

+ 1 - 0
tests/ewasm_tests/mod.rs

@@ -0,0 +1 @@
+mod primitives;

+ 770 - 0
tests/ewasm_tests/primitives.rs

@@ -0,0 +1,770 @@
+use crate::build_solidity;
+use ethereum_types::Address;
+use num_bigint::{BigInt, BigUint};
+use rand::Rng;
+use std::ops::Add;
+use std::ops::BitAnd;
+use std::ops::Div;
+use std::ops::Mul;
+use std::ops::Rem;
+use std::ops::Shl;
+use std::ops::Shr;
+use std::ops::Sub;
+use std::str::FromStr;
+
+#[test]
+#[should_panic]
+fn assert_false() {
+    // without a working assert, this is not going to work
+    let mut vm = build_solidity(
+        r#"
+        contract foo {
+            function assert_fails() public {
+                require(false, "humpty-dumpty");
+            }
+        }"#,
+    );
+
+    vm.constructor(&[]);
+
+    vm.function("assert_fails", &[]);
+}
+
+#[test]
+fn assert_true() {
+    // without a working assert, this is not going to work
+    let mut vm = build_solidity(
+        r#"
+        contract foo {
+            function assert_fails() public {
+                require(true, "humpty-dumpty");
+            }
+        }"#,
+    );
+
+    vm.constructor(&[]);
+
+    vm.function("assert_fails", &[]);
+}
+
+#[test]
+fn boolean() {
+    // we need to test: literals
+    // passing address around
+    // abi encoding/decoding address
+    // comparing address to another
+    let mut vm = build_solidity(
+        r#"
+        contract foo {
+            function return_true() public returns (bool) {
+                return true;
+            }
+
+            function return_false() public returns (bool) {
+                return false;
+            }
+
+            function true_arg(bool b) public {
+                assert(b);
+            }
+
+            function false_arg(bool b) public {
+                assert(!b);
+            }
+        }"#,
+    );
+
+    vm.constructor(&[]);
+
+    let returns = vm.function("return_true", &[]);
+
+    assert_eq!(returns, vec![ethabi::Token::Bool(true),]);
+
+    let returns = vm.function("return_false", &[]);
+
+    assert_eq!(returns, vec![ethabi::Token::Bool(false),]);
+
+    vm.function("true_arg", &[ethabi::Token::Bool(true)]);
+    vm.function("false_arg", &[ethabi::Token::Bool(false)]);
+}
+
+#[test]
+fn address() {
+    // we need to test: literals
+    // passing address around
+    // abi encoding/decoding address
+    // comparing address to another
+
+    let mut vm = build_solidity(
+        r#"
+        contract foo {
+            function return_address() public returns (address) {
+                return 0xE0f5206BBD039e7b0592d8918820024e2a7437b9;
+            }
+
+            function address_arg(address a) public {
+                assert(a == 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c);
+            }
+        }"#,
+    );
+
+    vm.constructor(&[]);
+
+    let returns = vm.function("return_address", &[]);
+
+    assert_eq!(
+        returns,
+        vec![ethabi::Token::Address(
+            Address::from_str("E0f5206BBD039e7b0592d8918820024e2a7437b9").unwrap()
+        )]
+    );
+
+    vm.function(
+        "address_arg",
+        &[ethabi::Token::Address(
+            Address::from_str("CA35b7d915458EF540aDe6068dFe2F44E8fa733c").unwrap(),
+        )],
+    );
+}
+
+#[test]
+fn test_enum() {
+    // we need to test enum literals
+    // abi encoding/decode literals
+    // comparing enums
+
+    let mut vm = build_solidity(
+        r#"
+        contract foo {
+            enum bar { bar0, bar1, bar2, bar3, bar4, bar5, bar6, bar7, bar8, bar9, bar10 }
+
+            function return_enum() public returns (bar) {
+                return bar.bar9;
+            }
+
+            function enum_arg(bar a) public {
+                assert(a == bar.bar6);
+            }
+        }"#,
+    );
+
+    vm.constructor(&[]);
+
+    let returns = vm.function("return_enum", &[]);
+
+    assert_eq!(
+        returns,
+        vec![ethabi::Token::Uint(ethereum_types::U256::from(9))]
+    );
+
+    vm.function(
+        "enum_arg",
+        &[ethabi::Token::Uint(ethereum_types::U256::from(6))],
+    );
+}
+
+#[test]
+fn bytes() {
+    let mut rng = rand::thread_rng();
+
+    for width in 1..32 {
+        let src = r#"
+        contract test {
+            function return_literal() public returns (bytes7) {
+                return hex"01020304050607";
+            }
+
+            function return_arg(bytes7 x) public returns (bytes7) {
+                return x;
+            }
+
+            function or(bytesN a, bytesN b) public returns (bytesN) {
+                return a | b;
+            }
+
+            function and(bytesN a, bytesN b) public returns (bytesN) {
+                return a & b;
+            }
+
+            function xor(bytesN a, bytesN b) public returns (bytesN) {
+                return a ^ b;
+            }
+
+            function shift_left(bytesN a, uint32 r) public returns (bytesN) {
+                return a << r;
+            }
+
+            function shift_right(bytesN a, uint32 r) public returns (bytesN) {
+                return a >> r;
+            }
+        }"#
+        .replace("bytesN", &format!("bytes{}", width));
+
+        let mut vm = build_solidity(&src);
+
+        vm.constructor(&[]);
+
+        let returns = vm.function("return_literal", &[]);
+
+        assert_eq!(
+            returns,
+            vec![ethabi::Token::FixedBytes(vec![1, 2, 3, 4, 5, 6, 7]),]
+        );
+
+        let returns = vm.function(
+            "return_arg",
+            &[ethabi::Token::FixedBytes(vec![1, 2, 3, 4, 5, 6, 7])],
+        );
+
+        assert_eq!(
+            returns,
+            vec![ethabi::Token::FixedBytes(vec![1, 2, 3, 4, 5, 6, 7])]
+        );
+
+        for _ in 0..10 {
+            let mut a = Vec::new();
+            let mut b = Vec::new();
+
+            a.resize(width, 0);
+            b.resize(width, 0);
+
+            rng.fill(&mut a[..]);
+            rng.fill(&mut b[..]);
+
+            let or = vm.function(
+                "or",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::FixedBytes(b.to_vec()),
+                ],
+            );
+
+            let res: Vec<u8> = a.iter().zip(b.iter()).map(|(a, b)| a | b).collect();
+
+            println!(
+                "{} | {} = {}",
+                hex::encode(&a),
+                hex::encode(&b),
+                hex::encode(&res)
+            );
+
+            assert_eq!(or, vec![ethabi::Token::FixedBytes(res)]);
+
+            let and = vm.function(
+                "and",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::FixedBytes(b.to_vec()),
+                ],
+            );
+
+            let res: Vec<u8> = a.iter().zip(b.iter()).map(|(a, b)| a & b).collect();
+
+            assert_eq!(and, vec![ethabi::Token::FixedBytes(res)]);
+
+            let xor = vm.function(
+                "xor",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::FixedBytes(b.to_vec()),
+                ],
+            );
+
+            let res: Vec<u8> = a.iter().zip(b.iter()).map(|(a, b)| a ^ b).collect();
+
+            assert_eq!(xor, vec![ethabi::Token::FixedBytes(res)]);
+
+            let r = rng.gen::<u32>() % (width as u32 * 8);
+
+            println!("w = {} r = {}", width, r);
+
+            let shl = vm.function(
+                "shift_left",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = (BigUint::from_bytes_be(&a) << r).to_bytes_be();
+
+            while res.len() > width {
+                res.remove(0);
+            }
+
+            while res.len() < width {
+                res.insert(0, 0);
+            }
+
+            assert_eq!(shl, vec![ethabi::Token::FixedBytes(res)]);
+
+            let shr = vm.function(
+                "shift_right",
+                &[
+                    ethabi::Token::FixedBytes(a.to_vec()),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = (BigUint::from_bytes_be(&a) >> r).to_bytes_be();
+
+            while res.len() < width {
+                res.insert(0, 0);
+            }
+
+            assert_eq!(shr, vec![ethabi::Token::FixedBytes(res)]);
+        }
+    }
+}
+
+#[test]
+fn uint() {
+    let mut rng = rand::thread_rng();
+
+    for width in &[8, 16, 32, 64, 128, 256] {
+        let width: usize = *width;
+        let src = r#"
+        contract test {
+            function add(uintN a, uintN b) public returns (uintN) {
+                return a + b;
+            }
+
+            function sub(uintN a, uintN b) public returns (uintN) {
+                return a - b;
+            }
+
+            function mul(uintN a, uintN b) public returns (uintN) {
+                return a * b;
+            }
+
+            function div(uintN a, uintN b) public returns (uintN) {
+                return a / b;
+            }
+
+            function mod(uintN a, uintN b) public returns (uintN) {
+                return a % b;
+            }
+
+            function pow(uintN a, uintN b) public returns (uintN) {
+                return a ** b;
+            }
+
+            function or(uintN a, uintN b) public returns (uintN) {
+                return a | b;
+            }
+
+            function and(uintN a, uintN b) public returns (uintN) {
+                return a & b;
+            }
+
+            function xor(uintN a, uintN b) public returns (uintN) {
+                return a ^ b;
+            }
+
+            function shift_left(uintN a, uint32 r) public returns (uintN) {
+                return a << r;
+            }
+
+            function shift_right(uintN a, uint32 r) public returns (uintN) {
+                return a >> r;
+            }
+        }"#
+        .replace("uintN", &format!("uint{}", width));
+
+        let mut vm = build_solidity(&src);
+
+        vm.constructor(&[]);
+
+        for _ in 0..10 {
+            let mut a = Vec::new();
+            let mut b = Vec::new();
+
+            a.resize(width / 8, 0);
+            b.resize(width / 8, 0);
+
+            rng.fill(&mut a[..]);
+            rng.fill(&mut b[..]);
+
+            let mut a = ethereum_types::U256::from_big_endian(&a);
+            let mut b = ethereum_types::U256::from_big_endian(&b);
+
+            rng.fill(&mut a.0[..]);
+            rng.fill(&mut b.0[..]);
+
+            truncate_uint(&mut a, width);
+            truncate_uint(&mut b, width);
+
+            let add = vm.function("add", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let (mut res, _) = a.overflowing_add(b);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(add, vec![ethabi::Token::Uint(res)]);
+
+            let sub = vm.function("sub", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let (mut res, _) = a.overflowing_sub(b);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(sub, vec![ethabi::Token::Uint(res)]);
+
+            let mul = vm.function("mul", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let (mut res, _) = a.overflowing_mul(b);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(mul, vec![ethabi::Token::Uint(res)]);
+
+            let pow = vm.function("pow", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let (mut res, _) = a.overflowing_pow(b);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(pow, vec![ethabi::Token::Uint(res)]);
+
+            if b != ethereum_types::U256::zero() {
+                let div = vm.function("div", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+                let mut res = a.div(b);
+
+                truncate_uint(&mut res, width);
+
+                assert_eq!(div, vec![ethabi::Token::Uint(res)]);
+
+                let add = vm.function("mod", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+                let mut res = a.rem(b);
+
+                truncate_uint(&mut res, width);
+
+                assert_eq!(add, vec![ethabi::Token::Uint(res)]);
+            }
+
+            let or = vm.function("or", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] | b.0[0],
+                a.0[1] | b.0[1],
+                a.0[2] | b.0[2],
+                a.0[3] | b.0[3],
+            ]);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(or, vec![ethabi::Token::Uint(res)]);
+
+            let and = vm.function("and", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] & b.0[0],
+                a.0[1] & b.0[1],
+                a.0[2] & b.0[2],
+                a.0[3] & b.0[3],
+            ]);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(and, vec![ethabi::Token::Uint(res)]);
+
+            let xor = vm.function("xor", &[ethabi::Token::Uint(a), ethabi::Token::Uint(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] ^ b.0[0],
+                a.0[1] ^ b.0[1],
+                a.0[2] ^ b.0[2],
+                a.0[3] ^ b.0[3],
+            ]);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(xor, vec![ethabi::Token::Uint(res)]);
+
+            let r = rng.gen::<u32>() % (width as u32);
+
+            let shl = vm.function(
+                "shift_left",
+                &[
+                    ethabi::Token::Uint(a),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = a.shl(r);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(shl, vec![ethabi::Token::Uint(res)]);
+
+            let shr = vm.function(
+                "shift_right",
+                &[
+                    ethabi::Token::Uint(a),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = a.shr(r);
+
+            truncate_uint(&mut res, width);
+
+            assert_eq!(shr, vec![ethabi::Token::Uint(res)]);
+        }
+    }
+}
+
+fn truncate_uint(n: &mut ethereum_types::U256, width: usize) {
+    let mut bits = 256 - width;
+
+    let mut offset = 3;
+
+    while bits > 64 {
+        n.0[offset] = 0;
+
+        offset -= 1;
+        bits -= 64;
+    }
+
+    if bits > 0 {
+        n.0[offset] &= (1 << (64 - bits)) - 1;
+    }
+}
+
+#[test]
+fn int() {
+    let mut rng = rand::thread_rng();
+
+    for width in &[8, 16, 32, 64, 128, 256] {
+        let width: usize = *width;
+        let src = r#"
+        contract test {
+            function add(intN a, intN b) public returns (intN) {
+                return a + b;
+            }
+
+            function sub(intN a, intN b) public returns (intN) {
+                return a - b;
+            }
+
+            function mul(intN a, intN b) public returns (intN) {
+                return a * b;
+            }
+
+            function div(intN a, intN b) public returns (intN) {
+                 return a / b;
+            }
+
+            function mod(intN a, intN b) public returns (intN) {
+                return a % b;
+            }
+
+            function or(intN a, intN b) public returns (intN) {
+                return a | b;
+            }
+
+            function and(intN a, intN b) public returns (intN) {
+                return a & b;
+            }
+
+            function xor(intN a, intN b) public returns (intN) {
+                return a ^ b;
+            }
+
+            function shift_left(intN a, uint32 r) public returns (intN) {
+                return a << r;
+            }
+
+            function shift_right(intN a, uint32 r) public returns (intN) {
+                return a >> r;
+            }
+        }"#
+        .replace("intN", &format!("int{}", width));
+
+        let mut vm = build_solidity(&src);
+
+        vm.constructor(&[]);
+
+        for _ in 0..10 {
+            let mut a_bs = Vec::new();
+            let mut b_bs = Vec::new();
+
+            a_bs.resize(width / 8, 0);
+            b_bs.resize(width / 8, 0);
+
+            rng.fill(&mut a_bs[..]);
+            rng.fill(&mut b_bs[..]);
+
+            let mut a = ethereum_types::U256::from_big_endian(&a_bs);
+            let mut b = ethereum_types::U256::from_big_endian(&b_bs);
+
+            truncate_int(&mut a, width);
+            truncate_int(&mut b, width);
+
+            let big_a = eth_to_bigint(&a, width);
+            let big_b = eth_to_bigint(&b, width);
+
+            let add = vm.function("add", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let res = big_a.clone().add(&big_b);
+
+            let res = bigint_to_eth(&res, width);
+
+            assert_eq!(add, vec![ethabi::Token::Int(res)]);
+
+            let sub = vm.function("sub", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let res = bigint_to_eth(&big_a.clone().sub(&big_b), width);
+
+            assert_eq!(sub, vec![ethabi::Token::Int(res)]);
+
+            let mul = vm.function("mul", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let res = bigint_to_eth(&big_a.clone().mul(&big_b), width);
+
+            assert_eq!(mul, vec![ethabi::Token::Int(res)]);
+
+            if b != ethereum_types::U256::zero() {
+                let div = vm.function("div", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+                let res = bigint_to_eth(&big_a.clone().div(&big_b), width);
+
+                assert_eq!(div, vec![ethabi::Token::Int(res)]);
+
+                let add = vm.function("mod", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+                let res = big_a.clone().rem(&big_b);
+
+                let res = bigint_to_eth(&res, width);
+
+                assert_eq!(add, vec![ethabi::Token::Int(res)]);
+            }
+
+            let or = vm.function("or", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] | b.0[0],
+                a.0[1] | b.0[1],
+                a.0[2] | b.0[2],
+                a.0[3] | b.0[3],
+            ]);
+
+            truncate_int(&mut res, width);
+
+            assert_eq!(or, vec![ethabi::Token::Int(res)]);
+
+            let and = vm.function("and", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] & b.0[0],
+                a.0[1] & b.0[1],
+                a.0[2] & b.0[2],
+                a.0[3] & b.0[3],
+            ]);
+
+            truncate_int(&mut res, width);
+
+            assert_eq!(and, vec![ethabi::Token::Int(res)]);
+
+            let xor = vm.function("xor", &[ethabi::Token::Int(a), ethabi::Token::Int(b)]);
+
+            let mut res = ethereum_types::U256([
+                a.0[0] ^ b.0[0],
+                a.0[1] ^ b.0[1],
+                a.0[2] ^ b.0[2],
+                a.0[3] ^ b.0[3],
+            ]);
+
+            truncate_int(&mut res, width);
+
+            assert_eq!(xor, vec![ethabi::Token::Int(res)]);
+
+            let r = rng.gen::<u32>() % (width as u32);
+
+            let shl = vm.function(
+                "shift_left",
+                &[
+                    ethabi::Token::Int(a),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let mut res = a.shl(r);
+
+            truncate_int(&mut res, width);
+
+            assert_eq!(shl, vec![ethabi::Token::Int(res)]);
+
+            let shr = vm.function(
+                "shift_right",
+                &[
+                    ethabi::Token::Int(a),
+                    ethabi::Token::Uint(ethereum_types::U256::from(r)),
+                ],
+            );
+
+            let res = bigint_to_eth(&big_a.clone().shr(r), width);
+
+            assert_eq!(shr, vec![ethabi::Token::Int(res)]);
+        }
+    }
+}
+
+fn truncate_int(n: &mut ethereum_types::U256, width: usize) {
+    let sign =
+        n.bitand(ethereum_types::U256::from(1) << (width - 1)) != ethereum_types::U256::zero();
+
+    let mut bits = 256 - width;
+
+    let mut offset = 3;
+
+    while bits > 64 {
+        n.0[offset] = if sign { u64::MAX } else { 0 };
+
+        offset -= 1;
+        bits -= 64;
+    }
+
+    if bits > 0 {
+        if sign {
+            n.0[offset] |= !((1 << (64 - bits)) - 1);
+        } else {
+            n.0[offset] &= (1 << (64 - bits)) - 1;
+        }
+    }
+}
+
+fn bigint_to_eth(v: &BigInt, width: usize) -> ethereum_types::U256 {
+    let mut buf = v.to_signed_bytes_be();
+    let width = width / 8;
+
+    while buf.len() > width {
+        buf.remove(0);
+    }
+
+    let sign = if (buf[0] & 128) != 0 { 0xff } else { 0 };
+
+    while buf.len() < 32 {
+        buf.insert(0, sign);
+    }
+
+    ethereum_types::U256::from_big_endian(&buf)
+}
+
+fn eth_to_bigint(v: &ethereum_types::U256, width: usize) -> BigInt {
+    let mut buf = Vec::new();
+
+    buf.resize(32, 0);
+
+    v.to_big_endian(&mut buf);
+
+    let width = width / 8;
+
+    while buf.len() > width {
+        buf.remove(0);
+    }
+
+    BigInt::from_signed_bytes_be(&buf)
+}