Skip to content
Open
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
62 changes: 41 additions & 21 deletions lib/src/transaction.dart
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,37 @@ class Transaction {
: 0)) as int;
}

int _byteLengthParticl(bool _ALLOW_WITNESS) {
// Particl wire format header: version (2 bytes) + locktime (4 bytes) = 6.
// No SegWit marker/flag bytes.
// Each output has a varint type prefix.
// Witness is always present for Particl-versioned txns with inputs.
var hasWitness = _ALLOW_WITNESS && ins.isNotEmpty;

return (6 +
varuint.encodingLength(ins.length) +
varuint.encodingLength(outs.length) +
ins.fold(0, (sum, input) => sum + 40 + varSliceSize(input.script!)) +
outs.fold(
0,
(sum, output) =>
sum +
varuint.encodingLength(1) +
8 +
varSliceSize(output.script!)) +
(payload != null ? varSliceSize(payload!) : 0) +
(hasWitness
? ins.fold(0, (sum, input) {
if (input.witness == null) {
input.witness = [];
return sum + varuint.encodingLength(0);
} else {
return sum + vectorSize(input.witness!);
}
})
: 0)) as int;
}

int vectorSize(List<Uint8List> someVector) {
var length = someVector.length;
return varuint.encodingLength(length) +
Expand Down Expand Up @@ -432,7 +463,7 @@ class Transaction {
[Uint8List? buffer, initialOffset, bool _ALLOW_WITNESS = false]) {

// _ALLOW_WITNESS is used to separate witness part when calculating tx id
buffer ??= Uint8List(_byteLength(_ALLOW_WITNESS));
buffer ??= Uint8List(_byteLengthParticl(_ALLOW_WITNESS));

// Any changes made to the ByteData will also change the buffer, and vice versa.
// https://api.dart.dev/stable/2.7.1/dart-typed_data/ByteBuffer/asByteData.html
Expand All @@ -454,21 +485,6 @@ class Transaction {
offset += 4;
}

void writeUInt16(i) {
bytes.setUint16(offset, i, Endian.little);
offset += 2;
}

void writeInt32(i) {
bytes.setInt32(offset, i, Endian.little);
offset += 4;
}

void writeInt16(i) {
bytes.setInt16(offset, i, Endian.little);
offset += 2;
}

void writeUInt64(i) {
bytes.setUint64(offset, i, Endian.little);
offset += 8;
Expand All @@ -491,8 +507,11 @@ class Transaction {
});
}

writeInt32(version);
writeUInt16(locktime);
// Particl header: 2-byte version + 4-byte locktime.
// Matches Particl Core: s << GetVersion(); s << GetType(); s << nLockTime;
writeUInt8(version & 0xFF);
writeUInt8((version >> 8) & 0xFF);
writeUInt32(locktime);

writeVarInt(ins.length);
ins.forEach((txIn) {
Expand All @@ -514,9 +533,10 @@ class Transaction {
writeVarSlice(txOut.script);
});

if (_ALLOW_WITNESS && hasWitnesses()) {
ins.forEach((txInt) {
writeVector(txInt.witness);
// Particl always includes witness for Particl-versioned txns with inputs.
if (_ALLOW_WITNESS && ins.isNotEmpty) {
ins.forEach((txIn) {
writeVector(txIn.witness ?? []);
});
}

Expand Down
23 changes: 16 additions & 7 deletions lib/src/transaction_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class TransactionBuilder {
int? witnessValue,
Uint8List? witnessScript,
int? hashType,
bool isParticl = false,
String overridePrefix = ''}) {
// TODO checkSignArgs

Expand Down Expand Up @@ -237,24 +238,32 @@ class TransactionBuilder {
.data
.output;
} else if (type == SCRIPT_TYPES['P2PKH']) {
var prevOutScript = pubkeyToOutputScript(ourPubKey, network);
input.prevOutType = SCRIPT_TYPES['P2PKH'];
var p2pkhScript = pubkeyToOutputScript(ourPubKey, network);
// Particl requires all inputs to use witness format (empty scriptSig,
// sig+pubkey in witness), even for P2PKH prevouts. BIP143 is used
// for all Particl signing with the P2PKH script as the scriptCode.
input.prevOutType =
isParticl ? SCRIPT_TYPES['P2WPKH'] : SCRIPT_TYPES['P2PKH'];
if (isParticl) input.hasWitness = true;
input.signatures = [null];
input.pubkeys = [ourPubKey];
input.signScript = prevOutScript;
input.signScript = p2pkhScript;
} else {
// TODO other type
}
} else {
var prevOutScript = pubkeyToOutputScript(ourPubKey, network);
input.prevOutType = SCRIPT_TYPES['P2PKH'];
var p2pkhScript = pubkeyToOutputScript(ourPubKey, network);
input.prevOutType =
isParticl ? SCRIPT_TYPES['P2WPKH'] : SCRIPT_TYPES['P2PKH'];
if (isParticl) input.hasWitness = true;
input.signatures = [null];
input.pubkeys = [ourPubKey];
input.signScript = prevOutScript;
input.signScript = p2pkhScript;
}
}
var signatureHash;
if (input.hasWitness != null && input.hasWitness!) {
// Particl always uses BIP143 sighash for all input types.
if (isParticl || (input.hasWitness != null && input.hasWitness!)) {
signatureHash =
_tx.hashForWitnessV0(vin, input.signScript!, input.value!, hashType);
} else {
Expand Down