Skip to content

Commit

Permalink
support terminate state channel with current state (#428)
Browse files Browse the repository at this point in the history
* support terminate state channel with current state

* update
  • Loading branch information
ianhe8x authored Nov 3, 2024
1 parent 876f04f commit 689f15b
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 14 deletions.
58 changes: 44 additions & 14 deletions contracts/StateChannel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ contract StateChannel is Initializable, OwnableUpgradeable, SQParameter {
emit ChannelCheckpoint(query.channelId, query.spent, query.isFinal);

// update channel state
_settlement(query, false);
_settlement(query.channelId, query.spent, query.isFinal);
}

/**
Expand Down Expand Up @@ -414,7 +414,36 @@ contract StateChannel is Initializable, OwnableUpgradeable, SQParameter {
emit ChannelTerminate(query.channelId, query.spent, expiration, isIndexer);

// update channel state.
_settlement(query, false);
_settlement(query.channelId, query.spent, query.isFinal);
}

function terminateWithCurrentState(uint256 channelId) external {
ChannelState storage state = channels[channelId];

// check sender
bool isIndexer = msg.sender == state.indexer;
bool isConsumer = msg.sender == state.consumer;
if (!isIndexer && !isConsumer) {
address controller = IIndexerRegistry(
settings.getContractAddress(SQContracts.IndexerRegistry)
).getController(state.indexer);
isIndexer = msg.sender == controller;
}
if (_isContract(state.consumer)) {
isConsumer = IConsumer(state.consumer).checkSender(channelId, msg.sender);
}
require(isIndexer || isConsumer, 'G008');

// set state to terminate
state.status = ChannelStatus.Terminating;
uint256 expiration = block.timestamp + terminateExpiration;
state.terminatedAt = expiration;
state.terminateByIndexer = isIndexer;

emit ChannelTerminate(channelId, state.spent, expiration, isIndexer);

// update channel state.
_settlement(channelId, state.spent, false);
}

/**
Expand Down Expand Up @@ -444,7 +473,7 @@ contract StateChannel is Initializable, OwnableUpgradeable, SQParameter {
_checkStateSign(query.channelId, payload, query.indexerSign, query.consumerSign);

// update channel state
_settlement(query, true);
_settlement(query.channelId, query.spent, true);
}

/**
Expand Down Expand Up @@ -512,21 +541,22 @@ contract StateChannel is Initializable, OwnableUpgradeable, SQParameter {
}

/// @notice Settlement the new state
function _settlement(QueryState calldata query, bool finalize) private {
function _settlement(uint256 channelId, uint256 spent, bool isFinal) private {
// update channel state
uint256 amount = query.spent - channels[query.channelId].spent;
ChannelState storage state = channels[channelId];
uint256 amount = spent - state.spent;

if (channels[query.channelId].total > query.spent) {
channels[query.channelId].spent = query.spent;
if (state.total > spent) {
state.spent = spent;
} else {
amount = channels[query.channelId].total - channels[query.channelId].spent;
channels[query.channelId].spent = channels[query.channelId].total;
amount = state.total - state.spent;
state.spent = state.total;
}

// reward pool
if (amount > 0) {
address indexer = channels[query.channelId].indexer;
bytes32 deploymentId = channels[query.channelId].deploymentId;
address indexer = state.indexer;
bytes32 deploymentId = state.deploymentId;
// rewards pool is deprecated
// address rewardPoolAddress = settings.getContractAddress(SQContracts.RewardsPool);
// IERC20(settings.getContractAddress(SQContracts.SQToken)).approve(
Expand All @@ -552,12 +582,12 @@ contract StateChannel is Initializable, OwnableUpgradeable, SQParameter {
amount,
eraManager.safeUpdateAndGetEra()
);
emit ChannelLabor2(query.channelId, deploymentId, indexer, amount);
emit ChannelLabor2(channelId, deploymentId, indexer, amount);
}

// finalise channel if meet the requirements
if (finalize || query.isFinal) {
_finalize(query.channelId);
if (isFinal) {
_finalize(channelId);
}
}

Expand Down
13 changes: 13 additions & 0 deletions publish/ABI/StateChannel.json
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,19 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "channelId",
"type": "uint256"
}
],
"name": "terminateWithCurrentState",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down
41 changes: 41 additions & 0 deletions test/StateChannel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,47 @@ describe('StateChannel Contract', () => {
expect(state.status).to.equal(0);
});

it('terminate State Channel with current onchain state', async () => {
await stateChannel.setTerminateExpiration(5); // 5s

const channelId = ethers.utils.randomBytes(32);
await openChannel(
stateChannel,
channelId,
deploymentId,
runner,
consumer,
etherParse('1'),
etherParse('0.1'),
60
);

const query1 = await buildQueryState(channelId, runner, consumer, etherParse('0.1'), false);
await stateChannel.connect(runner).checkpoint(query1);
let state1 = await stateChannel.channel(channelId);
expect(state1.spent).to.equal(etherParse('0.1'));

await expect(stateChannel.connect(runner).terminateWithCurrentState(channelId)).to.emit(
stateChannel,
'ChannelTerminate'
);
state1 = await stateChannel.channel(channelId);
expect(state1.status).to.equal(2); // Terminate

await expect(stateChannel.claim(channelId)).to.be.revertedWith('SC008');

await delay(6);
await stateChannel.claim(channelId);

const balance2 = await token.balanceOf(consumer.address);
expect(balance2).to.equal(etherParse('4.9'));

await startNewEra(eraManager);
await rewardsHelper.connect(runner).indexerCatchup(runner.address);
const indexerReward = await rewardsDistributor.userRewards(runner.address, runner.address);

expect(indexerReward).to.eq(etherParse('0.1'));
});
/**
* when only one indexer in the pool and that indexer unregistered,
* channel can still be terminated, consumer can claim the channel token
Expand Down

0 comments on commit 689f15b

Please sign in to comment.