Skip to content

Commit

Permalink
feat(channel): emit newContract event
Browse files Browse the repository at this point in the history
  • Loading branch information
Kalovelo authored and davidyuk committed Jan 25, 2023
1 parent 27314fb commit 8d71bba
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 14 deletions.
3 changes: 2 additions & 1 deletion src/channel/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
ChannelAction,
ChannelStatus,
ChannelFsm,
ChannelMessage,
} from './internal';
import { ChannelError } from '../utils/errors';
import { Encoded } from '../utils/encoder';
Expand Down Expand Up @@ -78,7 +79,7 @@ export default class Channel {

_fsmId?: Encoded.Bytearray;

_messageQueue: object[] = [];
_messageQueue: ChannelMessage[] = [];

_isMessageQueueLocked = false;

Expand Down
8 changes: 3 additions & 5 deletions src/channel/Contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,9 @@ export default class ChannelContract extends ChannelSpend {
? 'initiatorId' : 'responderId';
const owner = this._options[addressKey];
changeState(this, message2.params.data.state);
state2.resolve({
accepted: true,
address: encodeContractAddress(owner, params.round),
signedTx: message2.params.data.state,
});
const address = encodeContractAddress(owner, params.round);
emit(this, 'newContract', address);
state2.resolve({ accepted: true, address, signedTx: message2.params.data.state });
return { handler: channelOpen };
})
),
Expand Down
17 changes: 15 additions & 2 deletions src/channel/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
ChannelIncomingMessageError,
ChannelError,
} from '../utils/errors';
import { encodeContractAddress } from '../utils/crypto';

export interface ChannelAction {
guard: (channel: Channel, state?: ChannelFsm) => boolean;
Expand Down Expand Up @@ -211,9 +212,21 @@ export async function enqueueAction(
return promise;
}

async function handleMessage(channel: Channel, message: object): Promise<void> {
async function handleMessage(channel: Channel, message: ChannelMessage): Promise<void> {
const { handler, state: st } = channel._fsm;
enterState(channel, await Promise.resolve(handler(channel, message, st)));
const nextState = await Promise.resolve(handler(channel, message, st));
enterState(channel, nextState);
// TODO: emit message and handler name (?) to move this code to Contract constructor
if (
message?.params?.data?.updates?.[0]?.op === 'OffChainNewContract'
// if name is channelOpen, the contract was created by other participant
&& nextState?.handler.name === 'channelOpen'
) {
const round = channel.round();
if (round == null) throw new UnexpectedTsError('Round is null');
const owner = message?.params?.data?.updates?.[0]?.owner;
emit(channel, 'newContract', encodeContractAddress(owner, round + 1));
}
}

async function dequeueMessage(channel: Channel): Promise<void> {
Expand Down
43 changes: 37 additions & 6 deletions test/integration/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import {
IllegalArgumentError,
InsufficientBalanceError,
ChannelConnectionError,
encodeContractAddress,
ChannelIncomingMessageError,
UnknownChannelStateError,
AeSdk,
Expand All @@ -47,6 +46,7 @@ import MemoryAccount from '../../src/account/Memory';
import { Encoded, Encoding } from '../../src/utils/encoder';
import { appendSignature } from '../../src/channel/handlers';
import { assertNotNull, ensureEqual } from '../utils';
import { TxUnpacked } from '../../src/tx/builder/schema';

const wsUrl = process.env.TEST_WS_URL ?? 'ws://localhost:3014/channel';

Expand Down Expand Up @@ -815,6 +815,11 @@ describe('Channel', () => {
.should.be.equal(true);
});

// TODO: return unpacked entries in Channel.state
async function getChannelState(channel: Channel): Promise<TxUnpacked & { tag: Tag.StateTrees }> {
return unpackTx((await channel.state()).trees);
}

it('can create a contract and accept', async () => {
initiatorCh.disconnect();
responderCh.disconnect();
Expand All @@ -832,6 +837,10 @@ describe('Channel', () => {
});
await Promise.all([waitForChannel(initiatorCh), waitForChannel(responderCh)]);
contract = await aeSdkInitiatior.initializeContract({ sourceCode: contractSourceCode });
const initiatorNewContract = sinon.spy();
initiatorCh.on('newContract', initiatorNewContract);
const responderNewContract = sinon.spy();
responderCh.on('newContract', responderNewContract);
const roundBefore = initiatorCh.round();
assertNotNull(roundBefore);
const callData = contract._calldata.encode('Identity', 'init', []);
Expand All @@ -853,20 +862,42 @@ describe('Channel', () => {
sinon.match.string,
sinon.match({
updates: sinon.match([{
abi_version: 3,
abi_version: AbiVersion.Fate,
call_data: callData,
code: await contract.$compile(),
deposit: 1000,
op: 'OffChainNewContract',
owner: sinon.match.string,
vm_version: 5,
vm_version: VmVersion.Fate,
}]),
}),
);
const { updates: [{ owner }] } = responderSignTag.lastCall.lastArg;
// TODO: extract this calculation https://github.com/aeternity/aepp-sdk-js/issues/1619
expect(encodeContractAddress(owner, roundBefore + 1)).to.equal(result.address);
async function getContractAddresses(channel: Channel): Promise<Encoded.ContractAddress[]> {
return Object.keys((await getChannelState(channel)).contracts) as Encoded.ContractAddress[];
}
expect(initiatorNewContract.callCount).to.equal(1);
expect(initiatorNewContract.firstCall.args).to.eql([result.address]);
expect(responderNewContract.callCount).to.equal(1);
expect(responderNewContract.firstCall.args).to.eql([result.address]);
expect(await getContractAddresses(initiatorCh)).to.eql([result.address]);
expect(await getContractAddresses(responderCh)).to.eql([result.address]);
contractAddress = result.address;

await responderCh.createContract({
code: await contract.$compile(),
callData: contract._calldata.encode('Identity', 'init', []),
deposit: new BigNumber('10e18'),
vmVersion: VmVersion.Fate,
abiVersion: AbiVersion.Fate,
}, responderSign);
const contracts = await getContractAddresses(initiatorCh);
expect(contracts.length).to.equal(2);
expect(await getContractAddresses(responderCh)).to.eql(contracts);
const secondContract = contracts.filter((c) => c !== result.address);
expect(initiatorNewContract.callCount).to.equal(2);
expect(initiatorNewContract.secondCall.args).to.eql(secondContract);
expect(responderNewContract.callCount).to.equal(2);
expect(responderNewContract.secondCall.args).to.eql(secondContract);
});

it('can create a contract and reject', async () => {
Expand Down

0 comments on commit 8d71bba

Please sign in to comment.