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

feat: generate inversions of abi.encodePacked() for StakingMessages.unpack*() #482

Draft
wants to merge 30 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8777f88
perf: `SetSubnetValidatorWeightMessage` (un)packing gas reductions
ARR4N Aug 6, 2024
8b0557a
chore: `forge fmt`
ARR4N Aug 6, 2024
d5f52da
chore: `solhint-disable` assembly checks + some commentary
ARR4N Aug 6, 2024
1c94c27
feat: generated code for `abi.encodePacked()` inversion
ARR4N Aug 9, 2024
c89bd4a
fix: `solc` pragma `^0.8.0` unless using `mcopy` then `^0.8.25`
ARR4N Aug 9, 2024
dac07fc
fix: remove unused variable when dynamically sized at end
ARR4N Aug 9, 2024
c3ac02a
chore: add copyright notices
ARR4N Aug 9, 2024
e264572
chore: placate the linters... ssshhhhh
ARR4N Aug 9, 2024
7f0bd2e
perf: `SetSubnetValidatorWeightMessage` (un)packing gas reductions
ARR4N Aug 6, 2024
9890cca
chore: `forge fmt`
ARR4N Aug 6, 2024
721b63e
chore: `solhint-disable` assembly checks + some commentary
ARR4N Aug 6, 2024
ef87566
feat: generated code for `abi.encodePacked()` inversion
ARR4N Aug 9, 2024
c9e28b6
fix: `solc` pragma `^0.8.0` unless using `mcopy` then `^0.8.25`
ARR4N Aug 9, 2024
1c2ad1e
fix: remove unused variable when dynamically sized at end
ARR4N Aug 9, 2024
7f98e2a
chore: add copyright notices
ARR4N Aug 9, 2024
ee4bbf6
chore: placate the linters... ssshhhhh
ARR4N Aug 9, 2024
e6cf4e5
Merge branch 'arr4n/staking-messages' of github.com:ava-labs/teleport…
ARR4N Aug 9, 2024
3bb7bb6
chore: remove obsolete test of equivalence
ARR4N Aug 9, 2024
34f629f
refactor: `abi.encodePacked()` and `Unpack.unpack_...()` all the things
ARR4N Aug 9, 2024
f32bbcf
chore: make Slither keep quiet
ARR4N Aug 9, 2024
0272bde
chore: disable `line-length-limiter` on flag usage
ARR4N Aug 9, 2024
aed84c7
feat: use literal "dyn" string in `unpackgen` flag
ARR4N Aug 9, 2024
a279844
Merge branch 'staking-contract' into arr4n/staking-messages
ARR4N Aug 12, 2024
de4da9f
Merge branch 'staking-contract' into arr4n/staking-messages
ARR4N Sep 6, 2024
aab0874
feat: `pragma solidity` version override
ARR4N Sep 6, 2024
f4a773c
fix: apply explicit `solc` version to generated tests
ARR4N Sep 6, 2024
1bfcf4b
doc: clarify allowable interchange of `bytes<N>` and `uint<8N>` types
ARR4N Sep 6, 2024
78a1f47
refactor: only change `pack*()` methods to `abi.encodePacked()`
ARR4N Sep 6, 2024
e13cabc
refactor: change `unpack*()` methods to use generated code
ARR4N Sep 6, 2024
a19b841
chore: regenerate ABI bindings
ARR4N Sep 6, 2024
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
278 changes: 53 additions & 225 deletions contracts/staking/StakingMessages.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.25;

import {Unpack} from "./Unpack.sol";

library StakingMessages {
// The information that uniquely identifies a subnet validation period.
// The SHA-256 hash of the concatenation of these field is the validationID.
Expand Down Expand Up @@ -48,28 +50,19 @@ library StakingMessages {
* | 148 bytes |
* +-----------+
*
* @param valiationInfo The information to pack into the message.
* @param validationInfo The information to pack into the message.
* @return The validationID and the packed message.
*/
function packRegisterSubnetValidatorMessage(ValidationInfo memory valiationInfo)
function packRegisterSubnetValidatorMessage(ValidationInfo memory validationInfo)
internal
pure
returns (bytes32, bytes memory)
{
(bytes32 validationID, bytes memory serializedValidationInfo) =
packValidationInfo(valiationInfo);

bytes memory res = new bytes(148);
// Pack the message type
for (uint256 i; i < 4; ++i) {
res[i] = bytes1(uint8(SUBNET_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID >> (8 * (3 - i))));
}
// Pack the validation info
for (uint256 i; i < 144; ++i) {
res[i + 4] = serializedValidationInfo[i];
}

return (validationID, res);
(bytes32 validationID, bytes memory serialized) = packAndHashValidationInfo(validationInfo);
return (
validationID,
abi.encodePacked(SUBNET_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID, serialized)
);
}

/**
Expand All @@ -84,53 +77,26 @@ library StakingMessages {
pure
returns (ValidationInfo memory)
{
require(input.length == 148, "StakingMessages: Invalid message length");
(
bytes4 typeID,
bytes32 subnetID,
bytes32 nodeID,
bytes8 weight,
bytes8 expiry,
bytes memory signature
) = Unpack.unpack_4_32_32_8_8_Dyn_Destructive(input);

// Unpack the type ID
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i])) << uint32((8 * (3 - i)));
}
require(
typeID == SUBNET_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID,
uint32(typeID) == SUBNET_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID,
"StakingMessages: Invalid message type"
);

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

// Unpack the node ID
bytes32 nodeID;
for (uint256 i; i < 32; ++i) {
nodeID |= bytes32(uint256(uint8(input[i + 36])) << (8 * (31 - i)));
}

// Unpack the weight
uint64 weight;
for (uint256 i; i < 8; ++i) {
weight |= uint64(uint8(input[i + 68])) << uint64((8 * (7 - i)));
}

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

// Unpack the signature
bytes memory signature = new bytes(64);
for (uint256 i; i < 64; ++i) {
signature[i] = input[i + 84];
}
require(signature.length == 64, "StakingMessages: Invalid unpacked signature length");

return ValidationInfo({
subnetID: subnetID,
nodeID: nodeID,
weight: weight,
registrationExpiry: expiry,
weight: uint64(weight),
registrationExpiry: uint64(expiry),
signature: signature
});
}
Expand All @@ -157,18 +123,7 @@ library StakingMessages {
bytes32 validationID,
bool valid
) internal pure returns (bytes memory) {
bytes memory res = new bytes(37);
// Pack the type ID.
for (uint256 i; i < 4; ++i) {
res[i] = bytes1(uint8(SUBNET_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID >> (8 * (3 - i))));
}
// Pack the validation ID.
for (uint256 i; i < 32; ++i) {
res[i + 4] = bytes1(uint8(uint256(validationID >> (8 * (31 - i)))));
}
// Pack the validity.
res[36] = bytes1(valid ? 1 : 0);
return res;
return abi.encodePacked(SUBNET_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID, validationID, valid);
}

/**
Expand All @@ -184,28 +139,12 @@ library StakingMessages {
pure
returns (bytes32, bool)
{
require(input.length == 37, "StakingMessages: Invalid message length");

// Unpack the type ID
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i])) << uint32((8 * (3 - i)));
}
(bytes4 typeID, bytes32 validationID, bytes1 valid) = Unpack.unpack_4_32_1(input);
require(
typeID == SUBNET_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID,
uint32(typeID) == SUBNET_VALIDATOR_REGISTRATION_MESSAGE_TYPE_ID,
"StakingMessages: Invalid message type"
);

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

// Unpack the validity
bool valid = input[36] != 0;

return (validationID, valid);
return (validationID, uint8(valid) != 0);
}

/**
Expand Down Expand Up @@ -233,24 +172,9 @@ library StakingMessages {
uint64 nonce,
uint64 weight
) internal pure returns (bytes memory) {
bytes memory res = new bytes(52);
// Pack the type ID.
for (uint256 i; i < 4; ++i) {
res[i] = bytes1(uint8(SET_SUBNET_VALIDATOR_WEIGHT_MESSAGE_TYPE_ID >> (8 * (3 - i))));
}
// Pack the validation ID.
for (uint256 i; i < 32; ++i) {
res[i + 4] = bytes1(uint8(uint256(validationID >> (8 * (31 - i)))));
}
// Pack the nonce.
for (uint256 i; i < 8; ++i) {
res[i + 36] = bytes1(uint8(nonce >> (8 * (7 - i))));
}
// Pack the weight.
for (uint256 i; i < 8; ++i) {
res[i + 44] = bytes1(uint8(weight >> (8 * (7 - i))));
}
return res;
return abi.encodePacked(
SET_SUBNET_VALIDATOR_WEIGHT_MESSAGE_TYPE_ID, validationID, nonce, weight
);
}

/**
Expand All @@ -265,37 +189,13 @@ library StakingMessages {
pure
returns (bytes32, uint64, uint64)
{
require(input.length == 52, "StakingMessages: Invalid message length");

// Unpack the type ID.
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i])) << uint32((8 * (3 - i)));
}
(bytes4 typeID, bytes32 validationID, bytes8 nonce, bytes8 weight) =
Unpack.unpack_4_32_8_8(input);
require(
typeID == SET_SUBNET_VALIDATOR_WEIGHT_MESSAGE_TYPE_ID,
uint32(typeID) == SET_SUBNET_VALIDATOR_WEIGHT_MESSAGE_TYPE_ID,
"StakingMessages: Invalid message type"
);

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

// Unpack the nonce.
uint64 nonce;
for (uint256 i; i < 8; ++i) {
nonce |= uint64(uint8(input[i + 36])) << uint64((8 * (7 - i)));
}

// Unpack the weight.
uint64 weight;
for (uint256 i; i < 8; ++i) {
weight |= uint64(uint8(input[i + 44])) << uint64((8 * (7 - i)));
}

return (validationID, nonce, weight);
return (validationID, uint64(nonce), uint64(weight));
}

/**
Expand All @@ -319,20 +219,7 @@ library StakingMessages {
bytes32 validationID,
uint64 uptime
) internal pure returns (bytes memory) {
bytes memory res = new bytes(44);
// Pack the type ID.
for (uint256 i; i < 4; ++i) {
res[i] = bytes1(uint8(VALIDATION_UPTIME_MESSAGE_TYPE_ID >> (8 * (3 - i))));
}
// Pack the validation ID.
for (uint256 i; i < 32; ++i) {
res[i + 4] = bytes1(uint8(uint256(validationID >> (8 * (31 - i)))));
}
// Pack the uptime.
for (uint256 i; i < 8; ++i) {
res[i + 36] = bytes1(uint8(uptime >> (8 * (7 - i))));
}
return res;
return abi.encodePacked(VALIDATION_UPTIME_MESSAGE_TYPE_ID, validationID, uptime);
}

/**
Expand All @@ -347,30 +234,12 @@ library StakingMessages {
pure
returns (bytes32, uint64)
{
require(input.length == 44, "StakingMessages: Invalid message length");

// Unpack the type ID.
uint32 typeID;
for (uint256 i; i < 4; ++i) {
typeID |= uint32(uint8(input[i])) << uint32((8 * (3 - i)));
}
(bytes4 typeID, bytes32 validationID, bytes8 uptime) = Unpack.unpack_4_32_8(input);
require(
typeID == VALIDATION_UPTIME_MESSAGE_TYPE_ID, "StakingMessages: Invalid message type"
uint32(typeID) == VALIDATION_UPTIME_MESSAGE_TYPE_ID,
"StakingMessages: Invalid message type"
);

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

// Unpack the uptime.
uint64 uptime;
for (uint256 i; i < 8; ++i) {
uptime |= uint64(uint8(input[i + 36])) << uint64((8 * (7 - i)));
}

return (validationID, uptime);
return (validationID, uint64(uptime));
}

/**
Expand All @@ -391,36 +260,23 @@ library StakingMessages {
* | 144 bytes |
* +-----------+
*
* @param validationInfo The information to pack.
* @param info The information to pack.
* @return The validationID and the packed data.
*/
function packValidationInfo(ValidationInfo memory validationInfo)
function packValidationInfo(ValidationInfo memory info) internal pure returns (bytes memory) {
require(info.signature.length == 64, "StakingMessages: Invalid signature length");
return abi.encodePacked(
info.subnetID, info.nodeID, info.weight, info.registrationExpiry, info.signature
);
}

/// @dev Equivalent to returning `packValidationInfo(info)` and the SHA256 hash thereof.
function packAndHashValidationInfo(ValidationInfo memory info)
internal
pure
returns (bytes32, bytes memory)
{
require(validationInfo.signature.length == 64, "StakingMessages: Invalid signature length");
bytes memory res = new bytes(144);
// Pack the subnetID
for (uint256 i; i < 32; ++i) {
res[i] = validationInfo.subnetID[i];
}
// Pack the nodeID
for (uint256 i; i < 32; ++i) {
res[i + 32] = validationInfo.nodeID[i];
}
// Pack the weight
for (uint256 i; i < 8; ++i) {
res[i + 64] = bytes1(uint8(validationInfo.weight >> uint8((8 * (7 - i)))));
}
// Pack the registration expiry
for (uint256 i; i < 8; ++i) {
res[i + 72] = bytes1(uint8(validationInfo.registrationExpiry >> uint64((8 * (7 - i)))));
}
// Pack the signature
for (uint256 i; i < 64; ++i) {
res[i + 80] = validationInfo.signature[i];
}
bytes memory res = packValidationInfo(info);
return (sha256(res), res);
}

Expand All @@ -436,43 +292,15 @@ library StakingMessages {
pure
returns (ValidationInfo memory)
{
require(input.length == 144, "StakingMessages: Invalid message length");

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

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

// Unpack the weight
uint64 weight;
for (uint256 i; i < 8; ++i) {
weight |= uint64(uint8(input[i + 64])) << uint64((8 * (7 - i)));
}

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

// Unpack the signature
bytes memory signature = new bytes(64);
for (uint256 i; i < 64; ++i) {
signature[i] = input[i + 80];
}
(bytes32 subnetID, bytes32 nodeID, bytes8 weight, bytes8 expiry, bytes memory signature) =
Unpack.unpack_32_32_8_8_Dyn_Destructive(input);
require(signature.length == 64, "StakingMessages: Invalid unpacked signature length");

return ValidationInfo({
subnetID: subnetID,
nodeID: nodeID,
weight: weight,
registrationExpiry: expiry,
weight: uint64(weight),
registrationExpiry: uint64(expiry),
signature: signature
});
}
Expand Down
Loading
Loading