diff --git a/src/crypto.cpp b/src/crypto.cpp index a0d0aa7..67cf9df 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -109,9 +109,9 @@ bool check_keys(const hash& pub, const hash& sec) return pub == pub_check; } -static FORCEINLINE void hash_to_scalar(const uint8_t* data, size_t length, uint8_t(&res)[HASH_SIZE]) +static FORCEINLINE void hash_to_scalar(const uint8_t* data, int length, uint8_t (&res)[HASH_SIZE]) { - keccak(data, static_cast(length), res, HASH_SIZE); + keccak(data, length, res, HASH_SIZE); sc_reduce32(res); } @@ -122,18 +122,13 @@ static FORCEINLINE void derivation_to_scalar(const hash& derivation, size_t outp uint8_t output_index[(sizeof(size_t) * 8 + 6) / 7]; } buf; - uint8_t* begin = buf.derivation; - uint8_t* end = buf.output_index; memcpy(buf.derivation, derivation.h, sizeof(buf.derivation)); - size_t k = output_index; - while (k >= 0x80) { - *(end++) = (static_cast(k) & 0x7F) | 0x80; - k >>= 7; - } - *(end++) = static_cast(k); + uint8_t* p = buf.output_index; + writeVarint(output_index, [&p](uint8_t b) { *(p++) = b; }); - hash_to_scalar(begin, end - begin, res); + const uint8_t* data = buf.derivation; + hash_to_scalar(data, static_cast(p - data), res); } class Cache diff --git a/src/pool_block_parser.inl b/src/pool_block_parser.inl index ed0a44b..eb2e9ab 100644 --- a/src/pool_block_parser.inl +++ b/src/pool_block_parser.inl @@ -46,29 +46,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai #define READ_BYTE(x) do { if (!read_byte(x)) return __LINE__; } while (0) #define EXPECT_BYTE(value) do { uint8_t tmp; READ_BYTE(tmp); if (tmp != (value)) return __LINE__; } while (0) - auto read_varint = [&data, data_end](auto& b) -> bool - { - uint64_t result = 0; - int k = 0; - - while (data < data_end) { - if (k >= static_cast(sizeof(b)) * 8) { - return false; - } - - const uint64_t cur_byte = *(data++); - result |= (cur_byte & 0x7F) << k; - k += 7; - - if ((cur_byte & 0x80) == 0) { - b = result; - return true; - } - } - return false; - }; - -#define READ_VARINT(x) do { if (!read_varint(x)) return __LINE__; } while(0) +#define READ_VARINT(x) do { data = readVarint(data, data_end, x); if (!data) return __LINE__; } while(0) auto read_buf = [&data, data_end](void* buf, size_t size) -> bool { diff --git a/src/util.h b/src/util.h index 60bf1c1..073f334 100644 --- a/src/util.h +++ b/src/util.h @@ -109,6 +109,30 @@ FORCEINLINE void writeVarint(T value, std::vector& out) writeVarint(value, [&out](uint8_t b) { out.emplace_back(b); }); } +template +const uint8_t* readVarint(const uint8_t* data, const uint8_t* data_end, T& b) +{ + uint64_t result = 0; + int k = 0; + + while (data < data_end) { + if (k >= static_cast(sizeof(T)) * 8) { + return nullptr; + } + + const uint64_t cur_byte = *(data++); + result |= (cur_byte & 0x7F) << k; + k += 7; + + if ((cur_byte & 0x80) == 0) { + b = result; + return data; + } + } + + return nullptr; +} + template FORCEINLINE constexpr size_t array_size(T(&)[N]) { return N; } template FORCEINLINE constexpr size_t array_size(T(U::*)[N]) { return N; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1defa07..9f3fc54 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -74,6 +74,7 @@ set(SOURCES src/keccak_tests.cpp src/main.cpp src/pool_block_tests.cpp + src/varint_tests.cpp src/wallet_tests.cpp ../external/src/cryptonote/crypto-ops-data.c ../external/src/cryptonote/crypto-ops.c diff --git a/tests/src/varint_tests.cpp b/tests/src/varint_tests.cpp new file mode 100644 index 0000000..8ed5815 --- /dev/null +++ b/tests/src/varint_tests.cpp @@ -0,0 +1,85 @@ +/* + * This file is part of the Monero P2Pool + * Copyright (c) 2021-2022 SChernykh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "common.h" +#include "util.h" +#include "gtest/gtest.h" + +namespace p2pool { + +TEST(varint, read_write) +{ + std::vector v; + v.reserve(16); + + uint64_t check; + + // 0...2^7 - 1 + for (uint64_t value = 0; value < 0x80; ++value) { + v.clear(); + writeVarint(value, v); + ASSERT_EQ(v.size(), 1); + ASSERT_EQ(v[0], value); + ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size()); + ASSERT_EQ(check, value); + } + + // 2^7...2^14 - 1 + for (uint64_t value = 0x80; value < 0x4000; ++value) { + v.clear(); + writeVarint(value, v); + ASSERT_EQ(v.size(), 2); + ASSERT_EQ(v[0], (value & 0x7F) | 0x80); + ASSERT_EQ(v[1], value >> 7); + ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size()); + ASSERT_EQ(check, value); + } + + // 2^14...2^21 - 1 + for (uint64_t value = 0x4000; value < 0x200000; ++value) { + v.clear(); + writeVarint(value, v); + ASSERT_EQ(v.size(), 3); + ASSERT_EQ(v[0], (value & 0x7F) | 0x80); + ASSERT_EQ(v[1], ((value >> 7) & 0x7F) | 0x80); + ASSERT_EQ(v[2], value >> 14); + ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size()); + ASSERT_EQ(check, value); + } + + // 2^64 - 1 + v.clear(); + writeVarint(std::numeric_limits::max(), v); + ASSERT_EQ(v.size(), 10); + for (int i = 0; i < 9; ++i) { + ASSERT_EQ(v[i], 0xFF); + } + ASSERT_EQ(v[9], 1); + ASSERT_EQ(readVarint(v.data(), v.data() + v.size(), check), v.data() + v.size()); + ASSERT_EQ(check, std::numeric_limits::max()); + + // Invalid value 1 + uint8_t buf[16]; + memset(buf, -1, sizeof(buf)); + ASSERT_EQ(readVarint(buf, buf + sizeof(buf), check), nullptr); + + // Invalid value 2 + uint8_t buf2[1] = { 0x80 }; + ASSERT_EQ(readVarint(buf2, buf2 + 1, check), nullptr); +} + +}