Skip to content

Commit 1f4a0c1

Browse files
committed
fix(channel): reestablish flow
1 parent d048a89 commit 1f4a0c1

5 files changed

Lines changed: 66 additions & 30 deletions

File tree

src/channel/Base.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
ChannelMessage,
2121
ChannelEvents,
2222
} from './internal';
23-
import { ChannelError } from '../utils/errors';
23+
import { ChannelError, IllegalArgumentError } from '../utils/errors';
2424
import { Encoded } from '../utils/encoder';
2525
import { TxUnpacked } from '../tx/builder/schema.generated';
2626
import { EntryTag } from '../tx/builder/entry/constants';
@@ -108,9 +108,16 @@ export default class Channel {
108108
}
109109

110110
static async _initialize<T extends Channel>(channel: T, options: ChannelOptions): Promise<T> {
111+
const reconnect = (options.existingFsmId ?? options.existingChannelId) != null;
112+
if (reconnect && (options.existingFsmId == null || options.existingChannelId == null)) {
113+
throw new IllegalArgumentError('`existingChannelId`, `existingFsmId` should be both provided or missed');
114+
}
115+
const reconnectHandler = handlers[
116+
options.reestablish === true ? 'awaitingReestablish' : 'awaitingReconnection'
117+
];
111118
await initialize(
112119
channel,
113-
options.existingFsmId != null ? handlers.awaitingReconnection : handlers.awaitingConnection,
120+
reconnect ? reconnectHandler : handlers.awaitingConnection,
114121
handlers.channelOpen,
115122
options,
116123
);

src/channel/handlers.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,40 @@ export function awaitingConnection(
143143
}
144144
}
145145

146+
export async function awaitingReestablish(
147+
channel: Channel,
148+
message: ChannelMessage,
149+
state: ChannelState,
150+
): Promise<ChannelFsm> {
151+
if (message.method === 'channels.info' && message.params.data.event === 'fsm_up') {
152+
channel._fsmId = message.params.data.fsm_id;
153+
return {
154+
handler: function awaitingChannelReestablished(
155+
_: Channel,
156+
message2: ChannelMessage,
157+
state2: ChannelState,
158+
): ChannelFsm | undefined {
159+
if (
160+
message2.method === 'channels.info'
161+
&& message2.params.data.event === 'channel_reestablished'
162+
) return { handler: awaitingOpenConfirmation };
163+
return handleUnexpectedMessage(channel, message2, state2);
164+
},
165+
};
166+
}
167+
return handleUnexpectedMessage(channel, message, state);
168+
}
169+
146170
export async function awaitingReconnection(
147171
channel: Channel,
148172
message: ChannelMessage,
149173
state: ChannelState,
150174
): Promise<ChannelFsm> {
151-
if (message.method === 'channels.info') {
152-
if (message.params.data.event === 'fsm_up') {
153-
channel._fsmId = message.params.data.fsm_id;
154-
const { signedTx } = await channel.state();
155-
changeState(channel, signedTx == null ? '' : buildTx(signedTx));
156-
return { handler: channelOpen };
157-
}
175+
if (message.method === 'channels.info' && message.params.data.event === 'fsm_up') {
176+
channel._fsmId = message.params.data.fsm_id;
177+
const { signedTx } = await channel.state();
178+
changeState(channel, signedTx == null ? '' : buildTx(signedTx));
179+
return { handler: channelOpen };
158180
}
159181
return handleUnexpectedMessage(channel, message, state);
160182
}
@@ -220,18 +242,25 @@ export function awaitingOnChainTx(
220242
function awaitingOpenConfirmation(
221243
channel: Channel,
222244
message: ChannelMessage,
245+
state: ChannelState,
223246
): ChannelFsm | undefined {
224247
if (message.method === 'channels.info' && message.params.data.event === 'open') {
225248
channel._channelId = message.params.channel_id;
226249
return {
227-
handler(_: Channel, message2: ChannelMessage): ChannelFsm | undefined {
250+
handler: function awaitingChannelsUpdate(
251+
_: Channel,
252+
message2: ChannelMessage,
253+
state2: ChannelState,
254+
): ChannelFsm | undefined {
228255
if (message2.method === 'channels.update') {
229256
changeState(channel, message2.params.data.state);
230257
return { handler: channelOpen };
231258
}
259+
return handleUnexpectedMessage(channel, message2, state2);
232260
},
233261
};
234262
}
263+
return handleUnexpectedMessage(channel, message, state);
235264
}
236265

237266
export async function channelOpen(

src/channel/internal.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ interface CommonChannelOptions {
119119
* Existing FSM id (required if reestablishing a channel)
120120
*/
121121
existingFsmId?: Encoded.Bytearray;
122+
/**
123+
* Needs to be provided if reconnecting with calling `leave` before
124+
*/
125+
// TODO: remove after solving https://github.com/aeternity/aeternity/issues/4399
126+
reestablish?: boolean;
122127
/**
123128
* The time waiting for a new event to be initiated (default: 600000)
124129
*/

test/integration/channel-other.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -164,16 +164,10 @@ describe('Channel other', () => {
164164
.should.be.equal(true);
165165
}).timeout(timeoutBlock);
166166

167-
// https://github.com/aeternity/protocol/blob/d634e7a3f3110657900759b183d0734e61e5803a/node/api/channels_api_usage.md#reestablish
168-
it('can reconnect', async () => {
167+
it('can reconnect a channel without leave', async () => {
169168
expect(initiatorCh.round()).to.be.equal(1);
170-
const result = await initiatorCh.update(
171-
initiator.address,
172-
responder.address,
173-
100,
174-
initiatorSign,
175-
);
176-
expect(result.accepted).to.equal(true);
169+
await initiatorCh.update(initiator.address, responder.address, 100, initiatorSign);
170+
expect(initiatorCh.round()).to.be.equal(2);
177171
const channelId = initiatorCh.id();
178172
const fsmId = initiatorCh.fsmId();
179173
initiatorCh.disconnect();
@@ -188,9 +182,11 @@ describe('Channel other', () => {
188182
expect(ch.fsmId()).to.be.equal(fsmId);
189183
expect(ch.round()).to.be.equal(2);
190184
const state = await ch.state();
191-
ch.disconnect();
192185
assertNotNull(state.signedTx);
193186
expect(state.signedTx.encodedTx.tag).to.be.equal(Tag.ChannelOffChainTx);
187+
await ch.update(initiator.address, responder.address, 100, initiatorSign);
188+
expect(ch.round()).to.be.equal(3);
189+
ch.disconnect();
194190
});
195191

196192
it('can post backchannel update', async () => {

test/integration/channel.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -572,33 +572,32 @@ describe('Channel', () => {
572572
// TODO: check `initiatorAmountFinal` and `responderAmountFinal`
573573
});
574574

575-
let existingChannelId: Encoded.Channel;
576-
let offchainTx: Encoded.Transaction;
577575
it('can leave a channel', async () => {
578576
initiatorCh.disconnect();
579577
responderCh.disconnect();
580578
[initiatorCh, responderCh] = await initializeChannels(initiatorParams, responderParams);
581-
initiatorCh.round(); // existingChannelRound
579+
await initiatorCh.update(initiator.address, responder.address, 100, initiatorSign);
582580
const result = await initiatorCh.leave();
583581
expect(result.channelId).to.satisfy((t: string) => t.startsWith('ch_'));
584582
expect(result.signedTx).to.satisfy((t: string) => t.startsWith('tx_'));
585-
existingChannelId = result.channelId;
586-
offchainTx = result.signedTx;
587583
});
588584

585+
// https://github.com/aeternity/protocol/blob/d634e7a3f3110657900759b183d0734e61e5803a/node/api/channels_api_usage.md#reestablish
589586
it('can reestablish a channel', async () => {
587+
expect(initiatorCh.round()).to.be.equal(2);
590588
initiatorCh = await Channel.initialize({
591589
...sharedParams,
592590
...initiatorParams,
593-
// @ts-expect-error TODO: use existingChannelId instead existingFsmId
594-
existingFsmId: existingChannelId,
595-
offchainTx,
591+
reestablish: true,
592+
existingChannelId: initiatorCh.id(),
593+
existingFsmId: initiatorCh.fsmId(),
596594
});
597595
await waitForChannel(initiatorCh, ['open']);
598-
// TODO: why node doesn't return signed_tx when channel is reestablished?
599-
// initiatorCh.round().should.equal(existingChannelRound)
596+
expect(initiatorCh.round()).to.be.equal(2);
600597
sinon.assert.notCalled(initiatorSignTag);
601598
sinon.assert.notCalled(responderSignTag);
599+
await initiatorCh.update(initiator.address, responder.address, 100, initiatorSign);
600+
expect(initiatorCh.round()).to.be.equal(3);
602601
});
603602

604603
describe('throws errors', () => {

0 commit comments

Comments
 (0)