Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External packing lib #604

Draft
wants to merge 2 commits into
base: prorated-rewards
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions contracts/validator-manager/ValidatorMessages.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
* @return The packed message.
*/
function packSubnetConversionMessage(bytes32 subnetConversionID)
internal
external
pure
returns (bytes memory)
{
Expand All @@ -81,7 +81,7 @@
* @param input The byte array to unpack.
* @return the unpacked subnetConversionID.
*/
function unpackSubnetConversionMessage(bytes memory input) internal pure returns (bytes32) {
function unpackSubnetConversionMessage(bytes memory input) external pure returns (bytes32) {
if (input.length != 38) {
revert InvalidMessageLength(uint32(input.length), 38);
}
Expand Down Expand Up @@ -148,35 +148,35 @@
* @param subnetConversionData The struct representing data to pack into the message.
* @return The packed message.
*/
function packSubnetConversionData(SubnetConversionData calldata subnetConversionData)
internal
external
pure
returns (bytes memory)
{
// Hardcoded 20 is for length of the managerAddress on EVM chains
// solhint-disable-next-line func-named-parameters
bytes memory res = abi.encodePacked(
CODEC_ID,
subnetConversionData.subnetID,
subnetConversionData.validatorManagerBlockchainID,
uint32(20),
subnetConversionData.validatorManagerAddress,
uint32(subnetConversionData.initialValidators.length)
);
// The approach below of encoding initialValidators using `abi.encodePacked` in a loop
// was tested against pre-allocating the array and doing manual byte by byte packing and
// it was found to be more gas efficient.
for (uint256 i = 0; i < subnetConversionData.initialValidators.length; i++) {
res = abi.encodePacked(
res,
uint32(subnetConversionData.initialValidators[i].nodeID.length),
subnetConversionData.initialValidators[i].nodeID,
subnetConversionData.initialValidators[i].blsPublicKey,
subnetConversionData.initialValidators[i].weight
);
}
return res;
}

/**
* @notice Packs a RegisterSubnetValidatorMessage message into a byte array.
Expand Down Expand Up @@ -217,42 +217,42 @@
* @param validationPeriod The information to pack into the message.
* @return The validationID and the packed message.
*/
function packRegisterSubnetValidatorMessage(ValidationPeriod memory validationPeriod)
internal
external
pure
returns (bytes32, bytes memory)
{
if (validationPeriod.blsPublicKey.length != 48) {
revert InvalidBLSPublicKey();
}

// solhint-disable-next-line func-named-parameters
bytes memory res = abi.encodePacked(
CODEC_ID,
REGISTER_SUBNET_VALIDATOR_MESSAGE_TYPE_ID,
validationPeriod.subnetID,
uint32(validationPeriod.nodeID.length),
validationPeriod.nodeID,
validationPeriod.blsPublicKey,
validationPeriod.registrationExpiry,
validationPeriod.remainingBalanceOwner.threshold,
uint32(validationPeriod.remainingBalanceOwner.addresses.length)
);
for (uint256 i = 0; i < validationPeriod.remainingBalanceOwner.addresses.length; i++) {
res = abi.encodePacked(res, validationPeriod.remainingBalanceOwner.addresses[i]);
}
res = abi.encodePacked(
res,
validationPeriod.disableOwner.threshold,
uint32(validationPeriod.disableOwner.addresses.length)
);
for (uint256 i = 0; i < validationPeriod.disableOwner.addresses.length; i++) {
res = abi.encodePacked(res, validationPeriod.disableOwner.addresses[i]);
}
res = abi.encodePacked(res, validationPeriod.weight);

return (sha256(res), res);
}

/**
* @notice Unpacks a byte array as a RegisterSubnetValidatorMessage message.
Expand All @@ -261,176 +261,176 @@
* @param input The byte array to unpack.
* @return the unpacked ValidationPeriod.
*/
function unpackRegisterSubnetValidatorMessage(bytes memory input)
internal
external
pure
returns (ValidationPeriod memory)
{
uint32 index = 0;
ValidationPeriod memory validation;

// Unpack the codec ID
// Individual fields are unpacked in their own scopes to avoid stack too deep errors.
{
uint16 codecID;
for (uint256 i; i < 2; ++i) {
codecID |= uint16(uint8(input[i + index])) << uint16((8 * (1 - i)));
}
if (codecID != CODEC_ID) {
revert InvalidCodecID(codecID);
}
index += 2;
}

// Unpack the type ID
{
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
if (typeID != REGISTER_SUBNET_VALIDATOR_MESSAGE_TYPE_ID) {
revert InvalidMessageType();
}
index += 4;
}

// Unpack the subnetID
{
bytes32 subnetID;
for (uint256 i; i < 32; ++i) {
subnetID |= bytes32(uint256(uint8(input[i + index])) << (8 * (31 - i)));
}
validation.subnetID = subnetID;
index += 32;
}

// Unpack the nodeID length
uint32 nodeIDLength;
{
for (uint256 i; i < 4; ++i) {
nodeIDLength |= uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;

// Unpack the nodeID
bytes memory nodeID = new bytes(nodeIDLength);
for (uint256 i; i < nodeIDLength; ++i) {
nodeID[i] = input[i + index];
}
validation.nodeID = nodeID;
index += nodeIDLength;
}

// Unpack the blsPublicKey
{
bytes memory blsPublicKey = new bytes(48);
for (uint256 i; i < 48; ++i) {
blsPublicKey[i] = input[i + index];
}
validation.blsPublicKey = blsPublicKey;
index += 48;
}

// Unpack the registration expiry
{
uint64 expiry;
for (uint256 i; i < 8; ++i) {
expiry |= uint64(uint8(input[i + index])) << uint64((8 * (7 - i)));
}
validation.registrationExpiry = expiry;
index += 8;
}

// Unpack the remainingBalanceOwner threshold
uint32 remainingBalanceOwnerAddressesLength;
{
uint32 remainingBalanceOwnerThreshold;
for (uint256 i; i < 4; ++i) {
remainingBalanceOwnerThreshold |=
uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;

// Unpack the remainingBalanceOwner addresses length
for (uint256 i; i < 4; ++i) {
remainingBalanceOwnerAddressesLength |=
uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;

// Unpack the remainingBalanceOwner addresses
address[] memory remainingBalanceOwnerAddresses =
new address[](remainingBalanceOwnerAddressesLength);
for (uint256 i; i < remainingBalanceOwnerAddressesLength; ++i) {
bytes memory addrBytes = new bytes(20);
for (uint256 j; j < 20; ++j) {
addrBytes[j] = input[j + index];
}
address addr;
// solhint-disable-next-line no-inline-assembly
assembly {
addr := mload(add(addrBytes, 20))
}
remainingBalanceOwnerAddresses[i] = addr;
index += 20;
}
validation.remainingBalanceOwner = PChainOwner({
threshold: remainingBalanceOwnerThreshold,
addresses: remainingBalanceOwnerAddresses
});
}

// Unpack the disableOwner threshold
uint32 disableOwnerAddressesLength;
{
uint32 disableOwnerThreshold;
for (uint256 i; i < 4; ++i) {
disableOwnerThreshold |= uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;

// Unpack the disableOwner addresses length
for (uint256 i; i < 4; ++i) {
disableOwnerAddressesLength |=
uint32(uint8(input[i + index])) << uint32((8 * (3 - i)));
}
index += 4;

// Unpack the disableOwner addresses
address[] memory disableOwnerAddresses = new address[](disableOwnerAddressesLength);
for (uint256 i; i < disableOwnerAddressesLength; ++i) {
bytes memory addrBytes = new bytes(20);
for (uint256 j; j < 20; ++j) {
addrBytes[j] = input[j + index];
}
address addr;
// solhint-disable-next-line no-inline-assembly
assembly {
addr := mload(add(addrBytes, 20))
}
disableOwnerAddresses[i] = addr;
index += 20;
}
validation.disableOwner =
PChainOwner({threshold: disableOwnerThreshold, addresses: disableOwnerAddresses});
}
// Now that we have all the variable lengths, validate the input length
uint32 expectedLength = 122 + nodeIDLength
+ (remainingBalanceOwnerAddressesLength + disableOwnerAddressesLength) * 20;
if (input.length != expectedLength) {
revert InvalidMessageLength(uint32(input.length), expectedLength);
}
// Unpack the weight
{
uint64 weight;
for (uint256 i; i < 8; ++i) {
weight |= uint64(uint8(input[i + index])) << uint64((8 * (7 - i)));
}
validation.weight = weight;
}

return validation;
}

Check warning

Code scanning / Slither

Assembly usage Warning

Check warning

Code scanning / Slither

Cyclomatic complexity Warning

ValidatorMessages.unpackRegisterSubnetValidatorMessage(bytes) has a high cyclomatic complexity (20).

/**
* @notice Packs a SubnetValidatorRegistrationMessage into a byte array.
Expand All @@ -455,7 +455,7 @@
function packSubnetValidatorRegistrationMessage(
bytes32 validationID,
bool valid
) internal pure returns (bytes memory) {
) external pure returns (bytes memory) {
return abi.encodePacked(
CODEC_ID, SUBNET_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID, validationID, valid
);
Expand All @@ -470,7 +470,7 @@
* or is not a validator and never will be a validator to do the expiry time passing.
*/
function unpackSubnetValidatorRegistrationMessage(bytes memory input)
internal
external
pure
returns (bytes32, bool)
{
Expand Down Expand Up @@ -533,7 +533,7 @@
bytes32 validationID,
uint64 nonce,
uint64 weight
) internal pure returns (bytes memory) {
) external pure returns (bytes memory) {
return abi.encodePacked(
CODEC_ID, SUBNET_VALIDATOR_WEIGHT_MESSAGE_TYPE_ID, validationID, nonce, weight
);
Expand All @@ -547,7 +547,7 @@
* @return The validationID, nonce, and weight.
*/
function unpackSubnetValidatorWeightMessage(bytes memory input)
internal
external
pure
returns (bytes32, uint64, uint64)
{
Expand Down Expand Up @@ -616,7 +616,7 @@
function packValidationUptimeMessage(
bytes32 validationID,
uint64 uptime
) internal pure returns (bytes memory) {
) external pure returns (bytes memory) {
return abi.encodePacked(CODEC_ID, VALIDATION_UPTIME_MESSAGE_TYPE_ID, validationID, uptime);
}

Expand All @@ -628,7 +628,7 @@
* @return The validationID and uptime.
*/
function unpackValidationUptimeMessage(bytes memory input)
internal
external
pure
returns (bytes32, uint64)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
WarpMessage,
IWarpMessenger
} from "@avalabs/[email protected]/contracts/interfaces/IWarpMessenger.sol";
import {ValidatorMessages} from "../ValidatorMessages.sol";
import {ValidatorMessages} from "./utils/ValidatorMessagesInternal.sol";
import {ValidatorRegistrationInput, ValidatorStatus} from "../interfaces/IValidatorManager.sol";

abstract contract PoSValidatorManagerTest is ValidatorManagerTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pragma solidity 0.8.25;

import {Test} from "@forge-std/Test.sol";
import {ValidatorManager, SubnetConversionData, InitialValidator} from "../ValidatorManager.sol";
import {ValidatorMessages} from "../ValidatorMessages.sol";
import {ValidatorMessages} from "./utils/ValidatorMessagesInternal.sol";
import {
ValidatorStatus,
ValidatorRegistrationInput,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
pragma solidity 0.8.25;

import {Test} from "@forge-std/Test.sol";
import {ValidatorMessages} from "../ValidatorMessages.sol";
import {ValidatorMessages} from "./utils/ValidatorMessagesInternal.sol";
import {PChainOwner} from "../interfaces/IValidatorManager.sol";

contract ValidatorMessagesTest is Test {
Expand Down
Loading
Loading