Skip to content

Commit e221a28

Browse files
committed
Throw on an attempt to submit the same transaction twice
1 parent 247b75a commit e221a28

File tree

2 files changed

+16
-6
lines changed

2 files changed

+16
-6
lines changed

packages/cashscript/src/network/MockNetworkProvider.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const bobAddress = 'bchtest:qz6q5gqnxdldkr07xpls5474mmzmlesd6qnux4skuc';
1010
const carolAddress = 'bchtest:qqsr7nqwe6rq5crj63gy5gdqchpnwmguusmr7tfmsj';
1111

1212
export default class MockNetworkProvider implements NetworkProvider {
13-
// we use lockingBytecode as the key for utxoMap to make cashaddresses and tokenaddresses interchangeable
13+
// we use lockingBytecode as the key for utxoMap to make cash addresses and token addresses interchangeable
1414
private utxoMap: Record<string, Utxo[]> = {};
1515
private transactionMap: Record<string, string> = {};
1616
public network: Network = Network.MOCKNET;
@@ -45,6 +45,11 @@ export default class MockNetworkProvider implements NetworkProvider {
4545
const transactionBin = hexToBin(txHex);
4646

4747
const txid = binToHex(sha256(sha256(transactionBin)).reverse());
48+
49+
if (this.transactionMap[txid]) {
50+
throw new Error(`Transaction with txid ${txid} was already submitted: txn-mempool-conflict`);
51+
}
52+
4853
this.transactionMap[txid] = txHex;
4954

5055
const decoded = decodeTransaction(transactionBin);
@@ -57,7 +62,7 @@ export default class MockNetworkProvider implements NetworkProvider {
5762
for (const address of Object.keys(this.utxoMap)) {
5863
const utxos = this.utxoMap[address];
5964
const index = utxos.findIndex(
60-
(utxo) => utxo.txid === binToHex(input.outpointTransactionHash) && utxo.vout === input.outpointIndex
65+
(utxo) => utxo.txid === binToHex(input.outpointTransactionHash) && utxo.vout === input.outpointIndex,
6166
);
6267

6368
if (index !== -1) {
@@ -93,7 +98,8 @@ export default class MockNetworkProvider implements NetworkProvider {
9398
}
9499

95100
addUtxo(addressOrLockingBytecode: string, utxo: Utxo): void {
96-
const lockingBytecode = isHex(addressOrLockingBytecode) ? addressOrLockingBytecode : binToHex(addressToLockScript(addressOrLockingBytecode));
101+
const lockingBytecode = isHex(addressOrLockingBytecode) ?
102+
addressOrLockingBytecode : binToHex(addressToLockScript(addressOrLockingBytecode));
97103
if (!this.utxoMap[lockingBytecode]) {
98104
this.utxoMap[lockingBytecode] = [];
99105
}

packages/cashscript/test/e2e/network/MockNetworkProvider.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,21 @@ describe('Transaction Builder', () => {
4343
const sigTemplate = new SignatureTemplate(alicePriv);
4444

4545
// spend both utxos to bob
46-
new TransactionBuilder({provider})
46+
const builder = new TransactionBuilder({ provider })
4747
.addInput(p2pkhUtxos[0], p2pkhInstance.unlock.spend(alicePub, sigTemplate))
4848
.addInput(aliceUtxos[0], sigTemplate.unlockP2PKH())
49-
.addOutput({ to: bobAddress, amount: 1000n })
50-
.send();
49+
.addOutput({ to: bobAddress, amount: 1000n });
50+
51+
const tx = builder.build();
52+
await builder.send();
5153

5254
// utxos should be removed from the provider
5355
expect(await provider.getUtxos(aliceAddress)).toHaveLength(0);
5456
expect(await provider.getUtxos(p2pkhInstance.address)).toHaveLength(0);
5557

5658
// utxo should be added to bob
5759
expect(await provider.getUtxos(bobAddress)).toHaveLength(1);
60+
61+
await expect(provider.sendRawTransaction(tx)).rejects.toThrow('txn-mempool-conflict');
5862
});
5963
});

0 commit comments

Comments
 (0)