Skip to content

Commit 686de1f

Browse files
committed
Make DLCReady Writable
1 parent 1876c2b commit 686de1f

File tree

6 files changed

+118
-12
lines changed

6 files changed

+118
-12
lines changed

coinlib/lib/src/crypto/schnorr_adaptor_signature.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:typed_data';
2+
import 'package:coinlib/src/common/serial.dart';
13
import 'ec_private_key.dart';
24
import 'schnorr_signature.dart';
35
import 'package:coinlib/src/secp256k1/secp256k1.dart';
@@ -6,7 +8,7 @@ import 'package:coinlib/src/secp256k1/secp256k1.dart';
68
/// signature can be adapted (decrypted) using [adapt] with the discrete-log of
79
/// the point (private key to a public key). The signature will be complete and
810
/// valid if the correct adaptor was given.
9-
class SchnorrAdaptorSignature {
11+
class SchnorrAdaptorSignature with Writable {
1012

1113
/// The signature that contains the adapted nonce but requires the adaptor
1214
/// scalar
@@ -15,6 +17,12 @@ class SchnorrAdaptorSignature {
1517
final bool parity;
1618

1719
SchnorrAdaptorSignature(this.preSig, this.parity);
20+
SchnorrAdaptorSignature.fromReader(BytesReader reader)
21+
: preSig = SchnorrSignature(reader.readSlice(SchnorrSignature.length)),
22+
parity = reader.readUInt8() == 1;
23+
24+
SchnorrAdaptorSignature.fromBytes(Uint8List bytes)
25+
: this.fromReader(BytesReader(bytes));
1826

1927
/// Adapts the adaptor signature with the discrete log to the adaptor point
2028
/// given as an [ECPrivateKey]. The resulting signature is not verified.
@@ -52,4 +60,10 @@ class SchnorrAdaptorSignature {
5260
}
5361
}
5462

63+
@override
64+
void write(Writer writer) {
65+
writer.writeSlice(preSig.data);
66+
writer.writeUInt8(parity ? 1 : 0);
67+
}
68+
5569
}

coinlib/lib/src/dlc/builder.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,6 @@ class DLCStatefulBuilder {
314314
);
315315

316316
return DLCReady(
317-
terms: terms,
318317
refundTransaction: signedRefundTx,
319318
cets: {
320319
for (final cet in _cets)

coinlib/lib/src/dlc/ready.dart

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,48 @@
1+
import 'dart:typed_data';
2+
import 'package:coinlib/src/common/serial.dart';
13
import 'package:coinlib/src/crypto/ec_public_key.dart';
24
import 'package:coinlib/src/crypto/schnorr_adaptor_signature.dart';
35
import 'package:coinlib/src/tx/transaction.dart';
4-
import 'terms.dart';
56

6-
class CETReady {
7+
class CETReady with Writable {
78

89
final Transaction tx;
910
final SchnorrAdaptorSignature adaptorSig;
1011

1112
CETReady(this.tx, this.adaptorSig);
13+
CETReady.fromReader(BytesReader reader)
14+
: tx = Transaction.fromReader(reader),
15+
adaptorSig = SchnorrAdaptorSignature.fromReader(reader);
16+
CETReady.fromBytes(Uint8List bytes) : this.fromReader(BytesReader(bytes));
17+
18+
@override
19+
void write(Writer writer) {
20+
tx.write(writer);
21+
adaptorSig.write(writer);
22+
}
1223

1324
}
1425

15-
class DLCReady {
26+
/// Carries data for a DLC with fully signed [cets] and a [refundTransaction].
27+
/// After this has been created, the DLC requires funding before it can be used.
28+
class DLCReady with Writable {
1629

17-
final DLCTerms terms;
1830
final Transaction refundTransaction;
19-
// TODO: Make immutable
2031
final Map<ECPublicKey, CETReady> cets;
2132

2233
DLCReady({
23-
required this.terms,
2434
required this.refundTransaction,
25-
required this.cets,
26-
});
35+
required Map<ECPublicKey, CETReady> cets,
36+
}) : cets = Map.unmodifiable(cets);
37+
DLCReady.fromReader(BytesReader reader)
38+
: refundTransaction = Transaction.fromReader(reader),
39+
cets = reader.readXPubKeyMap(() => CETReady.fromReader(reader));
40+
DLCReady.fromBytes(Uint8List bytes) : this.fromReader(BytesReader(bytes));
41+
42+
@override
43+
void write(Writer writer) {
44+
refundTransaction.write(writer);
45+
writer.writeOrderedXPubkeyMap(cets, (cet) => cet.write(writer));
46+
}
2747

2848
}

coinlib/test/crypto/schnorr_adaptor_signature_test.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ void main() {
3333

3434
});
3535

36+
test("can read/write", () {
37+
final readSig = SchnorrAdaptorSignature.fromBytes(
38+
goodAdaptorSig.toBytes(),
39+
);
40+
expect(bytesToHex(readSig.preSig.data), validSchnorrSig);
41+
expect(readSig.parity, false);
42+
});
43+
3644
});
3745

3846
}

coinlib/test/dlc/builder_test.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ void main() {
239239
Network.mainnet.minFee,
240240
)!;
241241

242-
expect(dlc.terms.toBytes(), buildersFor2CETs.first.terms.toBytes());
243242
expect(dlc.cets, hasLength(2));
244243
expect(
245244
dlc.cets.values.map((cet) => cet.tx),
@@ -264,7 +263,7 @@ void main() {
264263
expect(
265264
dlc.refundTransaction,
266265
txMatcher(
267-
dlc.terms.refundLocktime,
266+
buildersFor2CETs.first.terms.refundLocktime,
268267
[
269268
getFundOutputWithValue(
270269
4, CoinUnit.coin.toSats("2") + sharedDust,

coinlib/test/dlc/ready_test.dart

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import 'package:coinlib/coinlib.dart';
2+
import 'package:test/test.dart';
3+
import '../vectors/keys.dart';
4+
import 'helpers.dart';
5+
6+
void main() {
7+
8+
group("DLCReady", () {
9+
10+
late DLCReady dlc;
11+
12+
setUpAll(() async {
13+
14+
await loadCoinlib();
15+
16+
final builders = List.generate(
17+
2,
18+
(i) => DLCStatefulBuilder(
19+
terms: exampleTerms,
20+
ourPublicKey: getPubKey(i),
21+
),
22+
);
23+
24+
final pkg2s = List.generate(
25+
2,
26+
(i) => builders[i].partOne(
27+
packages: {
28+
for (int j = 0; j < 2; j++)
29+
if (j != i) getPubKey(j): builders[j].publicPackageOne,
30+
},
31+
privKey: getPrivKey(i),
32+
),
33+
);
34+
35+
dlc = builders.first.partTwo({ getPubKey(1): pkg2s.last });
36+
37+
});
38+
39+
test(
40+
"cets is immutable",
41+
() => expect(
42+
() => dlc.cets.remove(exampleTerms.outcomes.keys.first),
43+
throwsA(anything),
44+
),
45+
);
46+
47+
test("can read and write", () {
48+
49+
final readDlc = DLCReady.fromBytes(dlc.toBytes());
50+
expect(readDlc.refundTransaction.toHex(), dlc.refundTransaction.toHex());
51+
52+
for (
53+
final f in <Writable Function(CETReady)>[
54+
(cet) => cet.tx,
55+
(cet) => cet.adaptorSig,
56+
]
57+
) {
58+
Iterable<String> cetsToHex(DLCReady dlc)
59+
=> dlc.cets.values.map((cet) => f(cet).toHex());
60+
expect(cetsToHex(readDlc), unorderedMatches(cetsToHex(dlc)));
61+
}
62+
});
63+
64+
});
65+
66+
}

0 commit comments

Comments
 (0)