Files
smom-dbis-138/test/channels/PaymentChannelManager.t.sol
2026-03-02 12:14:09 -08:00

227 lines
8.7 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Test} from "forge-std/Test.sol";
import {PaymentChannelManager} from "../../contracts/channels/PaymentChannelManager.sol";
import {IPaymentChannelManager} from "../../contracts/channels/IPaymentChannelManager.sol";
contract PaymentChannelManagerTest is Test {
PaymentChannelManager public manager;
address public admin;
address public alice;
address public bob;
uint256 public alicePk;
uint256 public bobPk;
uint256 constant CHALLENGE_WINDOW = 1 hours;
function setUp() public {
admin = address(0x1);
(alice, alicePk) = makeAddrAndKey("alice");
(bob, bobPk) = makeAddrAndKey("bob");
manager = new PaymentChannelManager(admin, CHALLENGE_WINDOW);
vm.deal(alice, 100 ether);
vm.deal(bob, 100 ether);
}
function _signState(
uint256 channelId,
uint256 nonce,
uint256 balanceA,
uint256 balanceB,
uint256 pk
) internal view returns (uint8 v, bytes32 r, bytes32 s) {
bytes32 stateHash = keccak256(abi.encodePacked(channelId, nonce, balanceA, balanceB));
bytes32 ethSigned = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", stateHash));
(v, r, s) = vm.sign(pk, ethSigned);
}
function test_openChannel() public {
vm.prank(alice);
uint256 channelId = manager.openChannel{value: 10 ether}(bob);
assertEq(channelId, 0);
IPaymentChannelManager.Channel memory ch = manager.getChannel(channelId);
assertEq(ch.participantA, alice);
assertEq(ch.participantB, bob);
assertEq(ch.depositA, 10 ether);
assertEq(ch.depositB, 0);
assertEq(uint256(ch.status), uint256(IPaymentChannelManager.ChannelStatus.Open));
assertEq(address(manager).balance, 10 ether);
}
function test_openChannel_revertsZeroParticipant() public {
vm.prank(alice);
vm.expectRevert("zero participant");
manager.openChannel(address(0));
}
function test_openChannel_revertsZeroDeposit() public {
vm.prank(alice);
vm.expectRevert("zero deposit");
manager.openChannel(bob);
}
function test_fundChannel() public {
vm.prank(alice);
uint256 channelId = manager.openChannel{value: 10 ether}(bob);
vm.prank(bob);
manager.fundChannel{value: 10 ether}(channelId);
IPaymentChannelManager.Channel memory ch = manager.getChannel(channelId);
assertEq(ch.depositA, 10 ether);
assertEq(ch.depositB, 10 ether);
assertEq(address(manager).balance, 20 ether);
}
function test_closeChannelCooperative() public {
vm.prank(alice);
uint256 channelId = manager.openChannel{value: 10 ether}(bob);
vm.prank(bob);
manager.fundChannel{value: 10 ether}(channelId);
uint256 balanceA = 15 ether;
uint256 balanceB = 5 ether;
uint256 nonce = 1;
(uint8 vA, bytes32 rA, bytes32 sA) = _signState(channelId, nonce, balanceA, balanceB, alicePk);
(uint8 vB, bytes32 rB, bytes32 sB) = _signState(channelId, nonce, balanceA, balanceB, bobPk);
uint256 aliceBefore = alice.balance;
uint256 bobBefore = bob.balance;
vm.prank(alice);
manager.closeChannelCooperative(channelId, nonce, balanceA, balanceB, vA, rA, sA, vB, rB, sB);
assertEq(alice.balance, aliceBefore + balanceA);
assertEq(bob.balance, bobBefore + balanceB);
assertEq(uint256(manager.getChannel(channelId).status), uint256(IPaymentChannelManager.ChannelStatus.Closed));
}
function test_submitCloseAndFinalize() public {
vm.prank(alice);
uint256 channelId = manager.openChannel{value: 10 ether}(bob);
vm.prank(bob);
manager.fundChannel{value: 10 ether}(channelId);
uint256 balanceA = 12 ether;
uint256 balanceB = 8 ether;
uint256 nonce = 1;
(uint8 vA, bytes32 rA, bytes32 sA) = _signState(channelId, nonce, balanceA, balanceB, alicePk);
(uint8 vB, bytes32 rB, bytes32 sB) = _signState(channelId, nonce, balanceA, balanceB, bobPk);
vm.prank(alice);
manager.submitClose(channelId, nonce, balanceA, balanceB, vA, rA, sA, vB, rB, sB);
IPaymentChannelManager.Channel memory ch = manager.getChannel(channelId);
assertEq(uint256(ch.status), uint256(IPaymentChannelManager.ChannelStatus.Dispute));
assertEq(ch.disputeNonce, nonce);
assertEq(ch.disputeBalanceA, balanceA);
assertEq(ch.disputeBalanceB, balanceB);
vm.warp(block.timestamp + CHALLENGE_WINDOW + 1);
uint256 aliceBefore = alice.balance;
uint256 bobBefore = bob.balance;
vm.prank(bob);
manager.finalizeClose(channelId);
assertEq(alice.balance, aliceBefore + balanceA);
assertEq(bob.balance, bobBefore + balanceB);
assertEq(uint256(manager.getChannel(channelId).status), uint256(IPaymentChannelManager.ChannelStatus.Closed));
}
function test_challengeClose_newestStateWins() public {
vm.prank(alice);
uint256 channelId = manager.openChannel{value: 10 ether}(bob);
vm.prank(bob);
manager.fundChannel{value: 10 ether}(channelId);
uint256 balanceA1 = 12 ether;
uint256 balanceB1 = 8 ether;
(uint8 vA1, bytes32 rA1, bytes32 sA1) = _signState(channelId, 1, balanceA1, balanceB1, alicePk);
(uint8 vB1, bytes32 rB1, bytes32 sB1) = _signState(channelId, 1, balanceA1, balanceB1, bobPk);
vm.prank(alice);
manager.submitClose(channelId, 1, balanceA1, balanceB1, vA1, rA1, sA1, vB1, rB1, sB1);
uint256 balanceA2 = 5 ether;
uint256 balanceB2 = 15 ether;
(uint8 vA2, bytes32 rA2, bytes32 sA2) = _signState(channelId, 2, balanceA2, balanceB2, alicePk);
(uint8 vB2, bytes32 rB2, bytes32 sB2) = _signState(channelId, 2, balanceA2, balanceB2, bobPk);
vm.prank(bob);
manager.challengeClose(channelId, 2, balanceA2, balanceB2, vA2, rA2, sA2, vB2, rB2, sB2);
IPaymentChannelManager.Channel memory ch = manager.getChannel(channelId);
assertEq(ch.disputeNonce, 2);
assertEq(ch.disputeBalanceA, balanceA2);
assertEq(ch.disputeBalanceB, balanceB2);
vm.warp(block.timestamp + CHALLENGE_WINDOW + 1);
uint256 aliceBefore = alice.balance;
uint256 bobBefore = bob.balance;
vm.prank(alice);
manager.finalizeClose(channelId);
assertEq(alice.balance, aliceBefore + balanceA2);
assertEq(bob.balance, bobBefore + balanceB2);
}
function test_challengeClose_revertsOlderState() public {
vm.prank(alice);
uint256 channelId = manager.openChannel{value: 10 ether}(bob);
vm.prank(bob);
manager.fundChannel{value: 10 ether}(channelId);
uint256 balanceA1 = 12 ether;
uint256 balanceB1 = 8 ether;
(uint8 vA1, bytes32 rA1, bytes32 sA1) = _signState(channelId, 1, balanceA1, balanceB1, alicePk);
(uint8 vB1, bytes32 rB1, bytes32 sB1) = _signState(channelId, 1, balanceA1, balanceB1, bobPk);
vm.prank(alice);
manager.submitClose(channelId, 1, balanceA1, balanceB1, vA1, rA1, sA1, vB1, rB1, sB1);
uint256 balanceA0 = 20 ether;
uint256 balanceB0 = 0;
(uint8 vA0, bytes32 rA0, bytes32 sA0) = _signState(channelId, 0, balanceA0, balanceB0, alicePk);
(uint8 vB0, bytes32 rB0, bytes32 sB0) = _signState(channelId, 0, balanceA0, balanceB0, bobPk);
vm.prank(bob);
vm.expectRevert("not newer");
manager.challengeClose(channelId, 0, balanceA0, balanceB0, vA0, rA0, sA0, vB0, rB0, sB0);
}
function test_finalizeClose_revertsBeforeDeadline() public {
vm.prank(alice);
uint256 channelId = manager.openChannel{value: 10 ether}(bob);
vm.prank(bob);
manager.fundChannel{value: 10 ether}(channelId);
uint256 balanceA = 10 ether;
uint256 balanceB = 10 ether;
(uint8 vA, bytes32 rA, bytes32 sA) = _signState(channelId, 1, balanceA, balanceB, alicePk);
(uint8 vB, bytes32 rB, bytes32 sB) = _signState(channelId, 1, balanceA, balanceB, bobPk);
vm.prank(alice);
manager.submitClose(channelId, 1, balanceA, balanceB, vA, rA, sA, vB, rB, sB);
vm.expectRevert("window open");
manager.finalizeClose(channelId);
}
function test_pauseBlocksOpen() public {
vm.prank(admin);
manager.pause();
vm.prank(alice);
vm.expectRevert("paused");
manager.openChannel(bob);
}
function test_getChannelCount() public {
assertEq(manager.getChannelCount(), 0);
vm.prank(alice);
manager.openChannel{value: 1 ether}(bob);
assertEq(manager.getChannelCount(), 1);
}
}