Skip to content

Commit f9f5958

Browse files
committed
Better support for ECDSA signatures in ABI arguments
1 parent 47a9dbc commit f9f5958

File tree

3 files changed

+41
-8
lines changed

3 files changed

+41
-8
lines changed

packages/cashscript/src/Argument.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,20 @@ export function encodeFunctionArgument(argument: FunctionArgument, typeStr: stri
5959
throw Error(`Value for type ${type} should be a Uint8Array or hex string`);
6060
}
6161

62-
// Redefine SIG as a bytes65 so it is included in the size checks below
63-
// Note that ONLY Schnorr signatures are accepted
62+
// Redefine SIG as a bytes65 (Schnorr) or bytes71, bytes72 (ECDSA)
6463
if (type === PrimitiveType.SIG && argument.byteLength !== 0) {
65-
type = new BytesType(65);
64+
if (![65, 71, 72].includes(argument.byteLength)) {
65+
throw new TypeError(`bytes${argument.byteLength}`, type);
66+
}
67+
type = new BytesType(argument.byteLength);
6668
}
6769

68-
// Redefine DATASIG as a bytes64 so it is included in the size checks below
69-
// Note that ONLY Schnorr signatures are accepted
70+
// Redefine DATASIG as a bytes64 (Schnorr) or bytes70 (ECDSA) so it is included in the size checks below
7071
if (type === PrimitiveType.DATASIG && argument.byteLength !== 0) {
71-
type = new BytesType(64);
72+
if (![64, 70].includes(argument.byteLength)) {
73+
throw new TypeError(`bytes${argument.byteLength}`, type);
74+
}
75+
type = new BytesType(argument.byteLength);
7276
}
7377

7478
// Bounded bytes types require a correctly sized argument

packages/cashscript/test/e2e/HodlVault.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
ElectrumNetworkProvider,
66
Network,
77
TransactionBuilder,
8+
SignatureAlgorithm,
9+
HashType,
810
} from '../../src/index.js';
911
import {
1012
alicePriv,
@@ -94,5 +96,28 @@ describe('HodlVault', () => {
9496
const txOutputs = getTxOutputs(tx);
9597
expect(txOutputs).toEqual(expect.arrayContaining([{ to, amount }]));
9698
});
99+
100+
it('should succeed when price is high enough, ECDSA sig and datasig', async () => {
101+
// given
102+
const message = oracle.createMessage(100000n, 30000n);
103+
const oracleSig = oracle.signMessage(message, SignatureAlgorithm.ECDSA);
104+
const to = hodlVault.address;
105+
const amount = 10000n;
106+
const { utxos, changeAmount } = gatherUtxos(await hodlVault.getUtxos(), { amount, fee: 2000n });
107+
108+
const signatureTemplate = new SignatureTemplate(alicePriv, HashType.SIGHASH_ALL, SignatureAlgorithm.ECDSA);
109+
110+
// when
111+
const tx = await new TransactionBuilder({ provider })
112+
.addInputs(utxos, hodlVault.unlock.spend(signatureTemplate, oracleSig, message))
113+
.addOutput({ to: to, amount: amount })
114+
.addOutput({ to: to, amount: changeAmount })
115+
.setLocktime(100_000)
116+
.send();
117+
118+
// then
119+
const txOutputs = getTxOutputs(tx);
120+
expect(txOutputs).toEqual(expect.arrayContaining([{ to, amount }]));
121+
});
97122
});
98123
});

packages/cashscript/test/fixture/PriceOracle.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { padMinimallyEncodedVmNumber, flattenBinArray, secp256k1 } from '@bitauth/libauth';
22
import { encodeInt, sha256 } from '@cashscript/utils';
3+
import { SignatureAlgorithm } from '../../src/index.js';
34

45
export class PriceOracle {
56
constructor(public privateKey: Uint8Array) {}
@@ -12,8 +13,11 @@ export class PriceOracle {
1213
return flattenBinArray([encodedBlockHeight, encodedBchUsdPrice]);
1314
}
1415

15-
signMessage(message: Uint8Array): Uint8Array {
16-
const signature = secp256k1.signMessageHashSchnorr(this.privateKey, sha256(message));
16+
signMessage(message: Uint8Array, signatureAlgorithm: SignatureAlgorithm = SignatureAlgorithm.SCHNORR): Uint8Array {
17+
const signature = signatureAlgorithm === SignatureAlgorithm.SCHNORR ?
18+
secp256k1.signMessageHashSchnorr(this.privateKey, sha256(message)) :
19+
secp256k1.signMessageHashDER(this.privateKey, sha256(message));
20+
1721
if (typeof signature === 'string') throw new Error();
1822
return signature;
1923
}

0 commit comments

Comments
 (0)