Skip to content

Commit

Permalink
feat: tests first draft (#16)
Browse files Browse the repository at this point in the history
* feat: get price unsafe

* feat: pragma test utils

* feat: hyperlane test utils draft

* fix: fmt

* fix: typos
  • Loading branch information
EvolveArt authored Aug 28, 2024
1 parent f505be5 commit 6a251ab
Show file tree
Hide file tree
Showing 16 changed files with 456 additions and 506 deletions.
58 changes: 20 additions & 38 deletions solidity/src/Hyperlane.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ contract Hyperlane is IHyperlane {

address[] public _validators;

function parseAndVerifyHyMsg(
bytes calldata encodedHyMsg
)
constructor(address[] memory validators) {
_validators = validators;
}

function parseAndVerifyHyMsg(bytes calldata encodedHyMsg)
public
view
returns (HyMsg memory hyMsg, bool valid, string memory reason)
Expand All @@ -22,9 +24,7 @@ contract Hyperlane is IHyperlane {
(valid, reason) = verifyHyMsg(hyMsg);
}

function verifyHyMsg(
HyMsg memory hyMsg
) public view returns (bool valid, string memory reason) {
function verifyHyMsg(HyMsg memory hyMsg) public view returns (bool valid, string memory reason) {
// TODO: fetch validators from calldata/storage
address[] memory validators = _validators;

Expand All @@ -34,56 +34,41 @@ contract Hyperlane is IHyperlane {

// We're using a fixed point number transformation with 1 decimal to deal with rounding.
// we check that we have will be able to reach a quorum with the current signatures
if (
(((validators.length * 10) / 3) * 2) / 10 + 1 >
hyMsg.signatures.length
) {
if ((((validators.length * 10) / 3) * 2) / 10 + 1 > hyMsg.signatures.length) {
return (false, "no quorum");
}

// Verify signatures
(bool signaturesValid, string memory invalidReason) = verifySignatures(
hyMsg.hash,
hyMsg.signatures,
validators
);
(bool signaturesValid, string memory invalidReason) = verifySignatures(hyMsg.hash, hyMsg.signatures, validators);
if (!signaturesValid) {
return (false, invalidReason);
}

return (true, "");
}

function verifySignatures(
bytes32 hash,
Signature[] memory signatures,
address[] memory validators
) public pure returns (bool valid, string memory reason) {
function verifySignatures(bytes32 hash, Signature[] memory signatures, address[] memory validators)
public
pure
returns (bool valid, string memory reason)
{
uint8 lastIndex = 0;
// TODO: break on quorum
for (uint i = 0; i < signatures.length; i++) {
for (uint256 i = 0; i < signatures.length; i++) {
Signature memory sig = signatures[i];

require(
i == 0 || sig.validatorIndex > lastIndex,
"signature indices must be ascending"
);
require(i == 0 || sig.validatorIndex > lastIndex, "signature indices must be ascending");
lastIndex = sig.validatorIndex;

if (
ecrecover(hash, sig.v, sig.r, sig.s) !=
validators[sig.validatorIndex]
) {
if (ecrecover(hash, sig.v, sig.r, sig.s) != validators[sig.validatorIndex]) {
return (false, "HyMsg signature invalid");
}
}
return (true, "");
}

function parseHyMsg(
bytes calldata encodedHyMsg
) public pure returns (HyMsg memory hyMsg) {
uint index = 0;
function parseHyMsg(bytes calldata encodedHyMsg) public pure returns (HyMsg memory hyMsg) {
uint256 index = 0;

hyMsg.version = encodedHyMsg.toUint8(index);
index += 1;
Expand All @@ -94,7 +79,7 @@ contract Hyperlane is IHyperlane {
index += 1;
hyMsg.signatures = new Signature[](signersLen);

for (uint i = 0; i < signersLen; i++) {
for (uint256 i = 0; i < signersLen; i++) {
hyMsg.signatures[i].validatorIndex = encodedHyMsg.toUint8(index);
index += 1;

Expand All @@ -107,10 +92,7 @@ contract Hyperlane is IHyperlane {
}

// Hash the body
bytes memory body = encodedHyMsg.slice(
index,
encodedHyMsg.length - index
);
bytes memory body = encodedHyMsg.slice(index, encodedHyMsg.length - index);
hyMsg.hash = keccak256(abi.encodePacked(keccak256(body)));

// Parse the rest of the message
Expand Down
61 changes: 34 additions & 27 deletions solidity/src/Pragma.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,74 +13,81 @@ import "./libraries/ErrorsLib.sol";
/// @notice The Pragma contract.
contract Pragma is IPragma, PragmaDecoder {
/* STORAGE */
uint public validTimePeriodSeconds;
uint public singleUpdateFeeInWei;
uint256 public validTimePeriodSeconds;
uint256 public singleUpdateFeeInWei;
mapping(bytes32 => uint64) public latestDataInfoPublishTime;

constructor(
address _hyperlane,
uint16[] memory _dataSourceEmitterChainIds,
bytes32[] memory _dataSourceEmitterAddresses,
uint _validTimePeriodSeconds,
uint _singleUpdateFeeInWei
)
PragmaDecoder(
_hyperlane,
_dataSourceEmitterChainIds,
_dataSourceEmitterAddresses
)
{
uint256 _validTimePeriodSeconds,
uint256 _singleUpdateFeeInWei
) PragmaDecoder(_hyperlane, _dataSourceEmitterChainIds, _dataSourceEmitterAddresses) {
validTimePeriodSeconds = _validTimePeriodSeconds;
singleUpdateFeeInWei = _singleUpdateFeeInWei;
}

/// @inheritdoc IPragma
function updateDataFeeds(bytes[] calldata updateData) external payable {
uint totalNumUpdates = 0;
uint len = updateData.length;
for (uint i = 0; i < len; ) {
uint256 totalNumUpdates = 0;
uint256 len = updateData.length;
for (uint256 i = 0; i < len;) {
totalNumUpdates += updateDataInfoFromUpdate(updateData[i]);

unchecked {
i++;
}
}
uint requiredFee = getTotalFee(totalNumUpdates);
uint256 requiredFee = getTotalFee(totalNumUpdates);
if (msg.value < requiredFee) {
revert ErrorsLib.InsufficientFee();
}
}

/// @inheritdoc IPragma
function getUpdateFee(
bytes[] calldata updateData
) external view returns (uint feeAmount) {
function getUpdateFee(bytes[] calldata updateData) external view returns (uint256 feeAmount) {
// Get the update fee.
}

function getTotalFee(
uint totalNumUpdates
) private view returns (uint requiredFee) {
function getTotalFee(uint256 totalNumUpdates) private view returns (uint256 requiredFee) {
return totalNumUpdates * singleUpdateFeeInWei;
}

function getPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (DataFeed memory data) {
// Get the price no older than.
function getPriceUnsafe(bytes32 id) private view returns (DataFeed memory) {
DataFeed memory data = _latestPriceInfo[id];
if (data.publishTime == 0) {
revert ErrorsLib.DataNotFound();
}
return data;
}

function getPriceNoOlderThan(bytes32 id, uint256 age) external view returns (DataFeed memory data) {
data = getPriceUnsafe(id);

if (diff(block.timestamp, data.publishTime) > age) {
revert ErrorsLib.DataStale();
}
}

/// @inheritdoc IPragma
function dataFeedExists(bytes32 id) external view returns (bool) {
return (latestDataInfoPublishTime[id] != 0);
}

function getValidTimePeriod() public view returns (uint) {
function getValidTimePeriod() public view returns (uint256) {
return validTimePeriodSeconds;
}

function version() public pure returns (string memory) {
return "1.0.0";
}

function diff(uint256 x, uint256 y) internal pure returns (uint256) {
if (x > y) {
return x - y;
} else {
return y - x;
}
}
}
Loading

0 comments on commit 6a251ab

Please sign in to comment.