Skip to content

Commit

Permalink
Support uintN; 0 < N <=256, N%8 == 0 for typed data
Browse files Browse the repository at this point in the history
  • Loading branch information
bbondy committed Apr 9, 2022
1 parent 775e329 commit 1158c7d
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 68 deletions.
44 changes: 44 additions & 0 deletions components/brave_wallet/common/brave_wallet_types.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,48 @@ bool SolanaAccountInfo::operator!=(const SolanaAccountInfo& info) const {
return !operator==(info);
}

bool ValidSolidityBits(size_t bits) {
return bits != 0 && bits % 8 == 0 && bits <= 256;
}

absl::optional<uint256_t> MaxSolidityUint(size_t bits) {
if (!ValidSolidityBits(bits))
return absl::nullopt;
// Max hex for intN value is 0x[ff]... for num bytes
uint256_t value = 0;
const size_t num_bytes = bits / 8;
for (size_t i = 0; i < num_bytes; i++) {
value <<= 8;
value += 0xff;
}
return value;
}

absl::optional<int256_t> MaxSolidityInt(size_t bits) {
if (!ValidSolidityBits(bits))
return absl::nullopt;
// Max hex for intN value is 0x7f[ff]... for num bytes - 1
int256_t value = 0x7f;
const size_t num_bytes = bits / 8;
for (size_t i = 0; i < num_bytes - 1; i++) {
value <<= 8;
value += 0xff;
}
return value;
}

absl::optional<int256_t> MinSolidityInt(size_t bits) {
if (!ValidSolidityBits(bits))
return absl::nullopt;
// Min hex for intN value is 0x80[00]... for num bytes - 1
// A simple bit shift doesn't work quite right because of
// using boost's int256_t type.
int256_t value = 0x80 * -1;
const size_t num_bytes = bits / 8;
for (size_t i = 0; i < num_bytes - 1; i++) {
value *= 256;
}
return value;
}

} // namespace brave_wallet
11 changes: 11 additions & 0 deletions components/brave_wallet/common/brave_wallet_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ constexpr int128_t kMin128BitInt = std::numeric_limits<int128_t>::min() >> 1;

constexpr uint64_t kMaxSafeIntegerUint64 = 9007199254740991; // 2^53-1

// Determines the min/max value for Solidity types such as uint56
// uintN where 0 < N <= 256; N % 8 == 0
// Note that you shouldn't use uint256_t and int256_t in general
// for passing around values that need to be capped in those ranges.
// This is being used for sign typed data where values are not passed
// around.
bool ValidSolidityBits(size_t bits);
absl::optional<uint256_t> MaxSolidityUint(size_t bits);
absl::optional<int256_t> MaxSolidityInt(size_t bits);
absl::optional<int256_t> MinSolidityInt(size_t bits);

struct TransactionReceipt {
TransactionReceipt();
~TransactionReceipt();
Expand Down
39 changes: 39 additions & 0 deletions components/brave_wallet/common/brave_wallet_types_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,43 @@ TEST(BraveWalletTypesTest, SolanaSignatureStatusToValue) {
EXPECT_EQ(*status_from_value, status);
}

TEST(BraveWalletTypesTest, ValidSolidityBits) {
for (size_t i = 8; i <= 256; i += 8) {
EXPECT_TRUE(ValidSolidityBits(i));
}
std::vector<size_t> invalid_num_bits = {0, 7, 257};
for (size_t i : invalid_num_bits) {
EXPECT_FALSE(ValidSolidityBits(i));
EXPECT_EQ(MaxSolidityUint(i), absl::nullopt);
EXPECT_EQ(MaxSolidityInt(i), absl::nullopt);
EXPECT_EQ(MinSolidityInt(i), absl::nullopt);
}
}

TEST(BraveWalletTypesTest, MaxSolidityUint) {
EXPECT_EQ(MaxSolidityUint(8), uint256_t(255));
EXPECT_EQ(MaxSolidityUint(16), uint256_t(65535));
EXPECT_EQ(MaxSolidityUint(24), uint256_t(16777215));
EXPECT_EQ(MaxSolidityUint(128),
uint256_t(std::numeric_limits<uint128_t>::max()));
EXPECT_EQ(MaxSolidityUint(256),
uint256_t(std::numeric_limits<uint256_t>::max()));
}

TEST(BraveWalletTypesTest, MaxSolidityInt) {
EXPECT_EQ(MaxSolidityInt(8), int256_t(127));
EXPECT_EQ(MaxSolidityInt(16), int256_t(32767));
EXPECT_EQ(MaxSolidityInt(24), int256_t(8388607));
EXPECT_EQ(MaxSolidityInt(128), int256_t(kMax128BitInt));
EXPECT_EQ(MaxSolidityInt(256), int256_t(kMax256BitInt));
}

TEST(BraveWalletTypesTest, MinSolidityInt) {
EXPECT_EQ(MinSolidityInt(8), int256_t(-128));
EXPECT_EQ(MinSolidityInt(16), int256_t(-32768));
EXPECT_EQ(MinSolidityInt(24), int256_t(-8388608));
EXPECT_EQ(MinSolidityInt(128), int256_t(kMin128BitInt));
EXPECT_EQ(MinSolidityInt(256), int256_t(kMin256BitInt));
}

} // namespace brave_wallet
86 changes: 18 additions & 68 deletions components/brave_wallet/common/eth_sign_typed_data_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ absl::optional<std::vector<uint8_t>> EthSignTypedDataHelper::EncodeField(
result.push_back(0);
result.insert(result.end(), address.begin(), address.end());
} else if (base::StartsWith(type, "bytes", base::CompareCase::SENSITIVE)) {
unsigned type_check;
if (!base::StringToUint(type.data() + 5, &type_check) || type_check > 32)
unsigned num_bits;
if (!base::StringToUint(type.data() + 5, &num_bits) || num_bits > 32)
return absl::nullopt;
const std::string* value_str = value.GetIfString();
if (!value_str || !IsValidHexString(*value_str))
Expand All @@ -241,9 +241,9 @@ absl::optional<std::vector<uint8_t>> EthSignTypedDataHelper::EncodeField(
}
} else if (base::StartsWith(type, "uint", base::CompareCase::SENSITIVE)) {
// uint8 to uint256 in steps of 8
unsigned type_check;
if (!base::StringToUint(type.data() + 4, &type_check) || type_check % 8 ||
type_check > 256)
unsigned num_bits;
if (!base::StringToUint(type.data() + 4, &num_bits) || num_bits % 8 ||
num_bits > 256)
return absl::nullopt;

absl::optional<double> value_double = value.GetIfDouble();
Expand All @@ -262,44 +262,20 @@ absl::optional<std::vector<uint8_t>> EthSignTypedDataHelper::EncodeField(
return absl::nullopt;
}

// check if value excceeds type bound
switch (type_check) {
case 8:
if (encoded_value > std::numeric_limits<uint8_t>::max())
return absl::nullopt;
break;
case 16:
if (encoded_value > std::numeric_limits<uint16_t>::max())
return absl::nullopt;
break;
case 32:
if (encoded_value > std::numeric_limits<uint32_t>::max())
return absl::nullopt;
break;
case 64:
if (encoded_value > std::numeric_limits<uint64_t>::max())
return absl::nullopt;
break;
case 128:
if (encoded_value > std::numeric_limits<uint128_t>::max())
return absl::nullopt;
break;
case 256:
if (encoded_value > std::numeric_limits<uint256_t>::max())
return absl::nullopt;
break;
default:
return absl::nullopt;
absl::optional<uint256_t> max_value = MaxSolidityUint(num_bits);
if (max_value == absl::nullopt || encoded_value > *max_value) {
return absl::nullopt;
}

// Append the encoded value to byte array result in big endian order
for (int i = 256 - 8; i >= 0; i -= 8) {
result.push_back(static_cast<uint8_t>((encoded_value >> i) & 0xFF));
}
} else if (base::StartsWith(type, "int", base::CompareCase::SENSITIVE)) {
// int8 to int256 in steps of 8
unsigned type_check;
if (!base::StringToUint(type.data() + 3, &type_check) || type_check % 8 ||
type_check > 256)
unsigned num_bits;
if (!base::StringToUint(type.data() + 3, &num_bits) || num_bits % 8 ||
num_bits > 256)
return absl::nullopt;
absl::optional<double> value_double = value.GetIfDouble();
const std::string* value_str = value.GetIfString();
Expand All @@ -317,39 +293,13 @@ absl::optional<std::vector<uint8_t>> EthSignTypedDataHelper::EncodeField(
return absl::nullopt;
}

// check if value excceeds type bound
switch (type_check) {
case 8:
if (encoded_value > std::numeric_limits<int8_t>::max() ||
encoded_value < std::numeric_limits<int8_t>::min())
return absl::nullopt;
break;
case 16:
if (encoded_value > std::numeric_limits<int16_t>::max() ||
encoded_value < std::numeric_limits<int16_t>::min())
return absl::nullopt;
break;
case 32:
if (encoded_value > std::numeric_limits<int32_t>::max() ||
encoded_value < std::numeric_limits<int32_t>::min())
return absl::nullopt;
break;
case 64:
if (encoded_value > std::numeric_limits<int64_t>::max() ||
encoded_value < std::numeric_limits<int64_t>::min())
return absl::nullopt;
break;
case 128:
if (encoded_value > kMax128BitInt || encoded_value < kMin128BitInt)
return absl::nullopt;
break;
case 256:
if (encoded_value > kMax256BitInt || encoded_value < kMin256BitInt)
return absl::nullopt;
break;
default:
return absl::nullopt;
absl::optional<int256_t> min_value = MinSolidityInt(num_bits);
absl::optional<int256_t> max_value = MaxSolidityInt(num_bits);
if (min_value == absl::nullopt || max_value == absl::nullopt ||
encoded_value > *max_value || encoded_value < *min_value) {
return absl::nullopt;
}

// Append the encoded value to byte array result in big endian order
for (int i = 256 - 8; i >= 0; i -= 8) {
result.push_back(static_cast<uint8_t>((encoded_value >> i) & 0xFF));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) {
EXPECT_FALSE(helper->EncodeField("uint1", base::Value(1)));
EXPECT_FALSE(helper->EncodeField("uint9", base::Value(1)));
EXPECT_FALSE(helper->EncodeField("uint264", base::Value(1)));
EXPECT_FALSE(helper->EncodeField("uint55", base::Value(1)));
// exceeds 8 bits maximum
EXPECT_FALSE(helper->EncodeField("uint8", base::Value(256)));
// exceeds Number.MAX_SAFE_INTEGER
Expand All @@ -421,6 +422,13 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) {
EXPECT_EQ(
base::ToLowerASCII(base::HexEncode(*encoded_field)),
"0000000000000000000000000000000000000000000000000000000000001000");

encoded_field = helper->EncodeField("uint56", base::Value(4096));
ASSERT_TRUE(encoded_field);
EXPECT_EQ(
base::ToLowerASCII(base::HexEncode(*encoded_field)),
"0000000000000000000000000000000000000000000000000000000000001000");

encoded_field = helper->EncodeField("uint256", base::Value(65536));
ASSERT_TRUE(encoded_field);
EXPECT_EQ(
Expand All @@ -437,6 +445,11 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) {
"00000000000000000000000000000000000000000000000000000000000000ff");
encoded_field = helper->EncodeField("uint32", base::Value("4096"));
ASSERT_TRUE(encoded_field);
EXPECT_EQ(
base::ToLowerASCII(base::HexEncode(*encoded_field)),
"0000000000000000000000000000000000000000000000000000000000001000");
encoded_field = helper->EncodeField("uint56", base::Value("4096"));
ASSERT_TRUE(encoded_field);
EXPECT_EQ(
base::ToLowerASCII(base::HexEncode(*encoded_field)),
"0000000000000000000000000000000000000000000000000000000000001000");
Expand Down Expand Up @@ -468,6 +481,8 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) {
EXPECT_FALSE(helper->EncodeField("uint16", base::Value("0x10000")));
EXPECT_FALSE(helper->EncodeField("uint32", base::Value("4294967296")));
EXPECT_FALSE(helper->EncodeField("uint32", base::Value("0x100000000")));
EXPECT_FALSE(
helper->EncodeField("uint56", base::Value("0x100000000000000")));
EXPECT_FALSE(
helper->EncodeField("uint64", base::Value("18446744073709551616")));
EXPECT_FALSE(
Expand Down Expand Up @@ -511,6 +526,7 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) {
EXPECT_FALSE(helper->EncodeField("int1", base::Value(1)));
EXPECT_FALSE(helper->EncodeField("int9", base::Value(1)));
EXPECT_FALSE(helper->EncodeField("int264", base::Value(1)));
EXPECT_FALSE(helper->EncodeField("int55", base::Value(1)));
// exceeds 8 bits maximum
EXPECT_FALSE(helper->EncodeField("int8", base::Value(128)));
// exceeds Number.MAX_SAFE_INTEGER
Expand Down Expand Up @@ -576,6 +592,10 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) {
EXPECT_FALSE(helper->EncodeField("int32", base::Value("2147483648")));
EXPECT_FALSE(helper->EncodeField("int32", base::Value("-2147483649")));
EXPECT_FALSE(helper->EncodeField("int32", base::Value("0x100000000")));
EXPECT_FALSE(
helper->EncodeField("int56", base::Value("72057594037927935")));
EXPECT_FALSE(
helper->EncodeField("int56", base::Value("-72057594037927936")));
EXPECT_FALSE(
helper->EncodeField("int64", base::Value("9223372036854775808")));
EXPECT_FALSE(
Expand Down Expand Up @@ -630,6 +650,11 @@ TEST(EthSignedTypedDataHelperUnitTest, EncodeField) {
"000000000000000000000000000000000000000000000000000000000000007f");
encoded_field = helper->EncodeField("int32", base::Value("4096"));
ASSERT_TRUE(encoded_field);
EXPECT_EQ(
base::ToLowerASCII(base::HexEncode(*encoded_field)),
"0000000000000000000000000000000000000000000000000000000000001000");
encoded_field = helper->EncodeField("int56", base::Value("4096"));
ASSERT_TRUE(encoded_field);
EXPECT_EQ(
base::ToLowerASCII(base::HexEncode(*encoded_field)),
"0000000000000000000000000000000000000000000000000000000000001000");
Expand Down

0 comments on commit 1158c7d

Please sign in to comment.