Skip to content

Commit 5ad8f1a

Browse files
committed
Add some name collision tests
- Add tests for function / parameter name collisions - Add check to disallow contract name collisions and add test for that
1 parent 34f8647 commit 5ad8f1a

File tree

3 files changed

+85
-3
lines changed

3 files changed

+85
-3
lines changed

packages/cashscript/src/debugging.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ export type DebugResults = Record<string, DebugResult>;
99

1010
// debugs the template, optionally logging the execution data
1111
export const debugTemplate = (template: WalletTemplate, artifacts: Artifact[]): DebugResults => {
12+
// If a contract has the same name, but a different bytecode, then it is considered a name collision
13+
const hasArtifactNameCollision = artifacts.some(
14+
(artifact) => (
15+
artifacts.some((other) => other.contractName === artifact.contractName && other.bytecode !== artifact.bytecode)
16+
),
17+
);
18+
19+
if (hasArtifactNameCollision) {
20+
throw new Error('There are multiple artifacts with the same contractName. Please make sure that all artifacts have unique names.');
21+
}
22+
1223
const results: DebugResults = {};
1324
const unlockingScriptIds = Object.keys(template.scripts).filter((key) => 'unlocks' in template.scripts[key]);
1425

packages/cashscript/test/fixture/debugging/multicontract_debugging_contracts.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,34 @@ contract SameNameDifferentPath(int a) {
1212
}
1313
`;
1414

15+
const NAME_COLLISION = `
16+
contract NameCollision(int a) {
17+
function name_collision(int b) {
18+
require(a == 0, "a should be 0");
19+
require(b == 0, "b should be 0");
20+
}
21+
}
22+
`;
23+
24+
const CONTRACT_NAME_COLLISION = `
25+
contract NameCollision(int a) {
26+
function name_collision(int b) {
27+
require(b == 1, "b should be 1");
28+
require(a == 1, "a should be 1");
29+
}
30+
}
31+
`;
32+
33+
const FUNCTION_NAME_COLLISION = `
34+
contract FunctionNameCollision(int a) {
35+
function name_collision(int b) {
36+
require(b == 1, "b should be 1");
37+
require(a == 1, "a should be 1");
38+
}
39+
}
40+
`;
41+
1542
export const ARTIFACT_SAME_NAME_DIFFERENT_PATH = compileString(SAME_NAME_DIFFERENT_PATH);
43+
export const ARTIFACT_NAME_COLLISION = compileString(NAME_COLLISION);
44+
export const ARTIFACT_CONTRACT_NAME_COLLISION = compileString(CONTRACT_NAME_COLLISION);
45+
export const ARTIFACT_FUNCTION_NAME_COLLISION = compileString(FUNCTION_NAME_COLLISION);

packages/cashscript/test/multi-contract-debugging.test.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
import p2pkhArtifact from './fixture/p2pkh.artifact.js';
1616
import bigintArtifact from './fixture/bigint.artifact.js';
1717
import '../src/test/JestExtensions.js';
18-
import { ARTIFACT_SAME_NAME_DIFFERENT_PATH } from './fixture/debugging/multicontract_debugging_contracts.js';
18+
import { ARTIFACT_FUNCTION_NAME_COLLISION, ARTIFACT_NAME_COLLISION, ARTIFACT_CONTRACT_NAME_COLLISION, ARTIFACT_SAME_NAME_DIFFERENT_PATH } from './fixture/debugging/multicontract_debugging_contracts.js';
1919

2020
const bobSignatureTemplate = new SignatureTemplate(bobPriv);
2121

@@ -171,10 +171,33 @@ describe('Multi-Contract-Debugging tests', () => {
171171

172172
it.todo('should fail with correct error message when introspected output bytecode of a different contract does not match');
173173
it.todo('should fail with correct error message when introspected input bytecode of a different contract does not match');
174-
it.todo('should still work if contracts have the same name');
175-
it.todo('should still work if contract or function parameters have the same name across contracts');
176174
it.todo('should still work with duplicate custom require messages across contracts');
177175

176+
it('should still work if contract or function parameters have the same name across contracts', () => {
177+
const nameCollision = new Contract(ARTIFACT_NAME_COLLISION, [0n], { provider });
178+
const functionNameCollision = new Contract(ARTIFACT_FUNCTION_NAME_COLLISION, [1n], { provider });
179+
180+
const nameCollisionUtxo = randomUtxo();
181+
const functionNameCollisionUtxo = randomUtxo();
182+
183+
provider.addUtxo(nameCollision.address, nameCollisionUtxo);
184+
provider.addUtxo(functionNameCollision.address, functionNameCollisionUtxo);
185+
186+
const transaction1 = new TransactionBuilder({ provider })
187+
.addInput(nameCollisionUtxo, nameCollision.unlock.name_collision(0n))
188+
.addInput(functionNameCollisionUtxo, functionNameCollision.unlock.name_collision(0n))
189+
.addOutput({ to: nameCollision.address, amount: 10000n });
190+
191+
expect(transaction1).toFailRequireWith('FunctionNameCollision.cash:4 Require statement failed at input 1 in contract FunctionNameCollision.cash at line 4 with the following message: b should be 1.');
192+
193+
const transaction2 = new TransactionBuilder({ provider })
194+
.addInput(nameCollisionUtxo, nameCollision.unlock.name_collision(1n))
195+
.addInput(functionNameCollisionUtxo, functionNameCollision.unlock.name_collision(1n))
196+
.addOutput({ to: nameCollision.address, amount: 10000n });
197+
198+
expect(transaction2).toFailRequireWith('NameCollision.cash:5 Require statement failed at input 0 in contract NameCollision.cash at line 5 with the following message: b should be 0.');
199+
});
200+
178201
it('should still work with different instances of the same contract, with different paths due to different constructor parameter values', () => {
179202
const p2pkhContract1 = new Contract(ARTIFACT_SAME_NAME_DIFFERENT_PATH, [0n], { provider });
180203
const p2pkhContract2 = new Contract(ARTIFACT_SAME_NAME_DIFFERENT_PATH, [1n], { provider });
@@ -202,6 +225,24 @@ describe('Multi-Contract-Debugging tests', () => {
202225
});
203226

204227
describe('Non-require error messages', () => {
228+
it('should fail with the correct error message when there are name collisions on the contractName', () => {
229+
const nameCollision = new Contract(ARTIFACT_NAME_COLLISION, [0n], { provider });
230+
const contractNameCollision = new Contract(ARTIFACT_CONTRACT_NAME_COLLISION, [1n], { provider });
231+
232+
const nameCollisionUtxo = randomUtxo();
233+
const contractNameCollisionUtxo = randomUtxo();
234+
235+
provider.addUtxo(nameCollision.address, nameCollisionUtxo);
236+
provider.addUtxo(contractNameCollision.address, contractNameCollisionUtxo);
237+
238+
const transaction = new TransactionBuilder({ provider })
239+
.addInput(nameCollisionUtxo, nameCollision.unlock.name_collision(0n))
240+
.addInput(contractNameCollisionUtxo, contractNameCollision.unlock.name_collision(0n))
241+
.addOutput({ to: nameCollision.address, amount: 10000n });
242+
243+
expect(() => transaction.debug()).toThrow('There are multiple artifacts with the same contractName. Please make sure that all artifacts have unique names.');
244+
});
245+
205246
it.todo('should fail with correct error message and statement when a multiline non-require statement fails');
206247
});
207248
});

0 commit comments

Comments
 (0)