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

Add check of deposit data in stake #1035

Open
wants to merge 1 commit into
base: mainnet
Choose a base branch
from
Open
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
54 changes: 54 additions & 0 deletions src/contracts/pods/EigenPodManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ contract EigenPodManager is
bytes calldata signature,
bytes32 depositDataRoot
) external payable onlyWhenNotPaused(PAUSED_NEW_EIGENPODS) {
_checkDepositData(pubkey, signature, depositDataRoot);

IEigenPod pod = ownerToPod[msg.sender];
if (address(pod) == address(0)) {
//deploy a pod if the sender doesn't have one already
Expand Down Expand Up @@ -277,6 +279,58 @@ contract EigenPodManager is
}
}

function _checkDepositData(
bytes calldata pubkey,
bytes calldata signature,
bytes32 depositDataRoot
) internal view {
// Compute deposit data root (`DepositData` hash tree root)
bytes32 pubkeyRoot = sha256(abi.encodePacked(pubkey, bytes16(0)));
bytes32 signatureRoot = sha256(
abi.encodePacked(
sha256(abi.encodePacked(signature[:64])),
sha256(abi.encodePacked(signature[64:], bytes32(0)))
)
);
bytes32 withdrawalCredentials = bytes32(
abi.encodePacked(bytes1(uint8(1)), bytes11(0), getPod(msg.sender))
);
bytes32 node = sha256(
abi.encodePacked(
sha256(abi.encodePacked(pubkeyRoot, withdrawalCredentials)),
sha256(
abi.encodePacked(
_toLittleEndian64(uint64(msg.value / 1e9)),
bytes24(0),
signatureRoot
)
)
)
);

// Verify computed and expected deposit data roots match
require(
node == depositDataRoot,
"EigenPodManager.stake: Reconstructed DepositData does not match supplied depositDataRoot"
);
}

function _toLittleEndian64(
uint64 value
) internal pure returns (bytes memory ret) {
ret = new bytes(8);
bytes8 bytesValue = bytes8(value);
// Byteswapping during copying to bytes.
ret[0] = bytesValue[7];
ret[1] = bytesValue[6];
ret[2] = bytesValue[5];
ret[3] = bytesValue[4];
ret[4] = bytesValue[3];
ret[5] = bytesValue[2];
ret[6] = bytesValue[1];
ret[7] = bytesValue[0];
}

// VIEW FUNCTIONS
/// @notice Returns the address of the `podOwner`'s EigenPod (whether it is deployed yet or not).
function getPod(address podOwner) public view returns (IEigenPod) {
Expand Down
84 changes: 77 additions & 7 deletions src/test/unit/EigenPodManagerUnit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,17 @@ contract EigenPodManagerUnitTests_CreationTests is EigenPodManagerUnitTests, IEi
}

contract EigenPodManagerUnitTests_StakeTests is EigenPodManagerUnitTests {

function test_stake_podAlreadyDeployed() deployPodForStaker(defaultStaker) public {
// Declare dummy variables
bytes memory pubkey = bytes("pubkey");
bytes memory sig = bytes("sig");
bytes32 depositDataRoot = bytes32("depositDataRoot");
bytes memory pubkey = _generateDummyVariable("pubkey", 48);
bytes memory sig = _generateDummyVariable("sig", 96);
bytes32 depositDataRoot = this.computeDepositDataRoot(
address(this),
32 ether,
pubkey,
sig
);

// Stake
eigenPodManager.stake{value: 32 ether}(pubkey, sig, depositDataRoot);
Expand All @@ -169,9 +174,14 @@ contract EigenPodManagerUnitTests_StakeTests is EigenPodManagerUnitTests {

function test_stake_newPodDeployed() public {
// Declare dummy variables
bytes memory pubkey = bytes("pubkey");
bytes memory sig = bytes("sig");
bytes32 depositDataRoot = bytes32("depositDataRoot");
bytes memory pubkey = _generateDummyVariable("pubkey", 48);
bytes memory sig = _generateDummyVariable("sig", 96);
bytes32 depositDataRoot = this.computeDepositDataRoot(
address(this),
32 ether,
pubkey,
sig
);

// Stake
eigenPodManager.stake{value: 32 ether}(pubkey, sig, depositDataRoot);
Expand All @@ -182,6 +192,66 @@ contract EigenPodManagerUnitTests_StakeTests is EigenPodManagerUnitTests {
// Expect pod has 32 ether
assertEq(address(defaultPod).balance, 32 ether, "ETH not staked in EigenPod");
}

function computeDepositDataRoot(
address podOwner,
uint256 amount,
bytes calldata pubkey,
bytes calldata signature
) external view returns (bytes32 depositDataRoot) {
// Compute deposit data root (`DepositData` hash tree root)
bytes32 pubkeyRoot = sha256(abi.encodePacked(pubkey, bytes16(0)));
bytes32 signatureRoot = sha256(
abi.encodePacked(
sha256(abi.encodePacked(signature[:64])),
sha256(abi.encodePacked(signature[64:], bytes32(0)))
)
);
bytes32 withdrawalCredentials = bytes32(
abi.encodePacked(
bytes1(uint8(1)),
bytes11(0),
eigenPodManager.getPod(podOwner)
)
);
depositDataRoot = sha256(
abi.encodePacked(
sha256(abi.encodePacked(pubkeyRoot, withdrawalCredentials)),
sha256(
abi.encodePacked(
_toLittleEndian64(uint64(amount / 1e9)),
bytes24(0),
signatureRoot
)
)
)
);
}

function _toLittleEndian64(
uint64 value
) internal pure returns (bytes memory ret) {
ret = new bytes(8);
bytes8 bytesValue = bytes8(value);
// Byteswapping during copying to bytes.
ret[0] = bytesValue[7];
ret[1] = bytesValue[6];
ret[2] = bytesValue[5];
ret[3] = bytesValue[4];
ret[4] = bytesValue[3];
ret[5] = bytesValue[2];
ret[6] = bytesValue[1];
ret[7] = bytesValue[0];
}

function _generateDummyVariable(
string memory key,
uint256 length
) internal pure returns (bytes memory data) {
for (uint256 i = 0; i < length / bytes(key).length + 1; i++) {
data = abi.encodePacked(data, key);
}
}
}

contract EigenPodManagerUnitTests_ShareUpdateTests is EigenPodManagerUnitTests {
Expand Down