Skip to content
This repository has been archived by the owner on Nov 7, 2023. It is now read-only.

feature/PP-204 #27

Merged
merged 5 commits into from
Aug 12, 2022
Merged
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
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,27 @@
},
"devDependencies": {
"@openeth/truffle-typings": "0.0.6",
"@types/chai": "^4.3.1",
"@types/eth-sig-util": "2.1.0",
"@types/mocha": "^9.1.1",
"@types/sinon": "^10.0.13",
"@types/sinon-chai": "^3.2.8",
"@types/chai": "^4.3.1",
"@types/chai-as-promised": "^7.1.5",
"@types/node": "~13.13.4",
"@types/semver": "~7.3.4",
"@typescript-eslint/eslint-plugin": "~4.28.3",
"@typescript-eslint/parser": "~4.28.3",
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
"eslint": "~7.30.0",
"husky": "~6.0.0",
"mocha": "^10.0.0",
"prettier": "2.3.0",
"sinon": "^14.0.0",
"sinon-chai": "^3.7.0",
"ts-loader": "~9.2.3",
"ts-node": "8.6.2",
"ts-sinon": "^2.0.2",
"typescript": "4.3.5",
"webpack": "~5.43.0",
"webpack-cli": "~4.7.2",
Expand Down
11 changes: 11 additions & 0 deletions src/ContractInteractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,17 @@ export default class ContractInteractor {
`No receipt found for this transaction ${transactionHash}`
);
}

async verifyForwarder(
suffixData: string,
request: RelayRequest,
signature: string
): Promise<void> {
const forwarder = await this._createForwarder(
request.relayData.callForwarder
);
await forwarder.verify(suffixData, request.request, signature);
}
}

/**
Expand Down
199 changes: 199 additions & 0 deletions test/ContractInteractor.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import sinon, { stubInterface } from 'ts-sinon';
import { expect, use, assert } from 'chai';
import chaiAsPromised from 'chai-as-promised';
import sinonChai from 'sinon-chai';
import { IForwarderInstance } from '@rsksmart/rif-relay-contracts/types/truffle-contracts';
import {
constants,
ContractInteractor,
EnvelopingConfig,
Web3Provider
} from '../src';
import {
ForwardRequest,
RelayData,
RelayRequest
} from '@rsksmart/rif-relay-contracts';

use(sinonChai);
use(chaiAsPromised);

const GAS_PRICE_PERCENT = 0; //
const MAX_RELAY_NONCE_GAP = 3;
const DEFAULT_RELAY_TIMEOUT_GRACE_SEC = 1800;
const DEFAULT_LOOKUP_WINDOW_BLOCKS = 60000;
const DEFAULT_CHAIN_ID = 33;

describe('ContractInteractor', () => {
const defaultConfig: EnvelopingConfig = {
preferredRelays: [],
onlyPreferredRelays: false,
relayLookupWindowParts: 1,
relayLookupWindowBlocks: DEFAULT_LOOKUP_WINDOW_BLOCKS,
gasPriceFactorPercent: GAS_PRICE_PERCENT,
minGasPrice: 60000000, // 0.06 GWei
maxRelayNonceGap: MAX_RELAY_NONCE_GAP,
sliceSize: 3,
relayTimeoutGrace: DEFAULT_RELAY_TIMEOUT_GRACE_SEC,
methodSuffix: '',
jsonStringifyRequest: false,
chainId: DEFAULT_CHAIN_ID,
relayHubAddress: constants.ZERO_ADDRESS,
deployVerifierAddress: constants.ZERO_ADDRESS,
relayVerifierAddress: constants.ZERO_ADDRESS,
forwarderAddress: constants.ZERO_ADDRESS,
smartWalletFactoryAddress: constants.ZERO_ADDRESS,
logLevel: 0,
clientId: '1'
};
let mockWeb3Provider: Web3Provider;
let contractInteractor: ContractInteractor;

before(() => {
mockWeb3Provider = stubInterface<Web3Provider>();
contractInteractor = new ContractInteractor(
mockWeb3Provider,
defaultConfig
);
});

describe('verifyForwarder', () => {
let _createForwarderStub: sinon.SinonStub;
let fakeIForwarderInstance: sinon.SinonStubbedInstance<IForwarderInstance> &
IForwarderInstance;
const fakeSuffixData = 'fakeSuffix';
const fakeRelayRequest: RelayRequest = {
request: {
to: 'fake_address',
data: 'fake_data',
gas: '1'
} as ForwardRequest,
relayData: {
gasPrice: '0',
callForwarder: 'fake_address'
} as RelayData
};
const fakeSignature = 'fake_signature';

before(() => {
fakeIForwarderInstance = stubInterface<IForwarderInstance>();
_createForwarderStub = sinon
.stub(contractInteractor, '_createForwarder')
.callsFake(() => Promise.resolve(fakeIForwarderInstance));
});

it('should verify EOA and call once _createForwarder', async () => {
await expect(
contractInteractor.verifyForwarder(
fakeSuffixData,
fakeRelayRequest,
fakeSignature
)
).to.eventually.be.undefined;
expect(contractInteractor._createForwarder).to.have.been.calledOnce;
});

it('should fail if EOA is not the owner', async () => {
const error = new TypeError(
'VM Exception while processing transaction: revert Not the owner of the SmartWallet'
);
fakeIForwarderInstance.verify.throwsException(error);
await assert.isRejected(
contractInteractor.verifyForwarder(
fakeSuffixData,
fakeRelayRequest,
fakeSignature
),
error.message
);
});

it('should fail if nonce mismatch', async () => {
const error = new TypeError(
'VM Exception while processing transaction: revert nonce mismatch'
);
fakeIForwarderInstance.verify.throwsException(error);
await assert.isRejected(
contractInteractor.verifyForwarder(
fakeSuffixData,
fakeRelayRequest,
fakeSignature
),
error.message
);
});

it('should fail if signature mismatch', async () => {
const error = new TypeError(
'VM Exception while processing transaction: revert Signature mismatch'
);
fakeIForwarderInstance.verify.throwsException(error);
await assert.isRejected(
contractInteractor.verifyForwarder(
fakeSuffixData,
fakeRelayRequest,
fakeSignature
),
error.message
);
});

it('should fail if suffixData is null', async () => {
const error = new TypeError(
"Cannot read properties of null (reading 'substring')"
);
fakeIForwarderInstance.verify.throwsException(error);
await assert.isRejected(
contractInteractor.verifyForwarder(
null,
fakeRelayRequest,
fakeSignature
),
error.message
);
});

it('should fail if RelayRequest is null', async () => {
const error = new TypeError(
"Cannot read properties of null (reading 'relayData')"
);
fakeIForwarderInstance.verify.throwsException(error);
await assert.isRejected(
contractInteractor.verifyForwarder(
fakeSuffixData,
null,
fakeSignature
),
error.message
);
});

it('should fail if Signature is null', async () => {
const error = new TypeError(
"Cannot read properties of null (reading 'length')"
);
fakeIForwarderInstance.verify.throwsException(error);
await assert.isRejected(
contractInteractor.verifyForwarder(
fakeSuffixData,
fakeRelayRequest,
null
),
error.message
);
});

it('should fail if callForwarder is null', async () => {
_createForwarderStub.restore();
fakeRelayRequest.relayData.callForwarder = null;
await assert.isRejected(
contractInteractor.verifyForwarder(
fakeSuffixData,
fakeRelayRequest,
fakeSignature
),
'Invalid address passed to IForwarder.at(): null'
);
});
});
});
franciscotobar marked this conversation as resolved.
Show resolved Hide resolved