Skip to content

Commit

Permalink
Add silent payment psbt fields
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewtoth committed Jan 3, 2025
1 parent 0aac96f commit 5500d1c
Show file tree
Hide file tree
Showing 29 changed files with 1,100 additions and 9 deletions.
26 changes: 26 additions & 0 deletions src/cjs/lib/converter/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,22 @@ const tapLeafScript = __importStar(require('./input/tapLeafScript.cjs'));
const tapMerkleRoot = __importStar(require('./input/tapMerkleRoot.cjs'));
const tapScriptSig = __importStar(require('./input/tapScriptSig.cjs'));
const witnessUtxo = __importStar(require('./input/witnessUtxo.cjs'));
const silentPaymentOutputLabel = __importStar(
require('./output/silentPaymentOutputLabel.cjs'),
);
const silentPaymentV0Info = __importStar(
require('./output/silentPaymentV0Info.cjs'),
);
const tapTree = __importStar(require('./output/tapTree.cjs'));
const bip32Derivation = __importStar(require('./shared/bip32Derivation.cjs'));
const checkPubkey = __importStar(require('./shared/checkPubkey.cjs'));
const redeemScript = __importStar(require('./shared/redeemScript.cjs'));
const silentPaymentDleq = __importStar(
require('./shared/silentPaymentDleq.cjs'),
);
const silentPaymentEcdhShare = __importStar(
require('./shared/silentPaymentEcdhShare.cjs'),
);
const tapBip32Derivation = __importStar(
require('./shared/tapBip32Derivation.cjs'),
);
Expand All @@ -41,6 +53,12 @@ const globals = {
globalXpub,
// pass an Array of key bytes that require pubkey beside the key
checkPubkey: checkPubkey.makeChecker([]),
silentPaymentEcdhShare: silentPaymentEcdhShare.makeConverter(
typeFields_js_1.GlobalTypes.GLOBAL_SP_ECDH_SHARE,
),
silentPaymentDleq: silentPaymentDleq.makeConverter(
typeFields_js_1.GlobalTypes.GLOBAL_SP_DLEQ,
),
};
exports.globals = globals;
const inputs = {
Expand Down Expand Up @@ -74,6 +92,12 @@ const inputs = {
typeFields_js_1.InputTypes.TAP_INTERNAL_KEY,
),
tapMerkleRoot,
silentPaymentEcdhShare: silentPaymentEcdhShare.makeConverter(
typeFields_js_1.InputTypes.SP_ECDH_SHARE,
),
silentPaymentDleq: silentPaymentDleq.makeConverter(
typeFields_js_1.InputTypes.SP_DLEQ,
),
};
exports.inputs = inputs;
const outputs = {
Expand All @@ -96,5 +120,7 @@ const outputs = {
tapInternalKey: tapInternalKey.makeConverter(
typeFields_js_1.OutputTypes.TAP_INTERNAL_KEY,
),
silentPaymentV0Info,
silentPaymentOutputLabel,
};
exports.outputs = outputs;
48 changes: 48 additions & 0 deletions src/cjs/lib/converter/output/silentPaymentOutputLabel.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';
var __importStar =
(this && this.__importStar) ||
function(mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null)
for (var k in mod)
if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result['default'] = mod;
return result;
};
Object.defineProperty(exports, '__esModule', { value: true });
const typeFields_js_1 = require('../../typeFields.cjs');
const tools = __importStar(require('uint8array-tools'));
function decode(keyVal) {
if (keyVal.key[0] !== typeFields_js_1.OutputTypes.SP_V0_LABEL) {
throw new Error(
'Decode Error: could not decode silentPaymentOutputLabel with key 0x' +
tools.toHex(keyVal.key),
);
}
return Number(tools.readUInt32(keyVal.value, 0, 'LE'));
}
exports.decode = decode;
function encode(data) {
const key = Uint8Array.from([typeFields_js_1.OutputTypes.SP_V0_LABEL]);
const value = new Uint8Array(4);
tools.writeUInt32(value, 0, data, 'LE');
return {
key,
value,
};
}
exports.encode = encode;
exports.expected = 'number';
function check(data) {
return typeof data === 'number';
}
exports.check = check;
function canAdd(currentData, newData) {
return (
!!currentData &&
!!newData &&
currentData.silentPaymentOutputLabel === undefined
);
}
exports.canAdd = canAdd;
65 changes: 65 additions & 0 deletions src/cjs/lib/converter/output/silentPaymentV0Info.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict';
var __importStar =
(this && this.__importStar) ||
function(mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null)
for (var k in mod)
if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result['default'] = mod;
return result;
};
Object.defineProperty(exports, '__esModule', { value: true });
const typeFields_js_1 = require('../../typeFields.cjs');
const tools = __importStar(require('uint8array-tools'));
const isValidPubKey = pubkey =>
pubkey.length === 33 && [2, 3].includes(pubkey[0]);
function decode(keyVal) {
if (keyVal.key[0] !== typeFields_js_1.OutputTypes.SP_V0_INFO) {
throw new Error(
'Decode Error: could not decode silentPaymentV0Info with key 0x' +
tools.toHex(keyVal.key),
);
}
if (keyVal.value.length !== 66) {
throw new Error('Decode Error: SP_V0_INFO is not proper length');
}
const scanKey = keyVal.value.slice(0, 33);
if (!isValidPubKey(scanKey)) {
throw new Error('Decode Error: SP_V0_INFO scanKey is not a valid pubkey');
}
const spendKey = keyVal.value.slice(33);
if (!isValidPubKey(spendKey)) {
throw new Error('Decode Error: SP_V0_INFO spendKey is not a valid pubkey');
}
return {
scanKey,
spendKey,
};
}
exports.decode = decode;
function encode(data) {
const key = new Uint8Array([typeFields_js_1.OutputTypes.SP_V0_INFO]);
return {
key,
value: Uint8Array.from([...data.scanKey, ...data.spendKey]),
};
}
exports.encode = encode;
exports.expected = '{ scanKey: Uint8Array; spendKey: Uint8Array; }';
function check(data) {
return (
data.scanKey instanceof Uint8Array &&
data.spendKey instanceof Uint8Array &&
isValidPubKey(data.scanKey) &&
isValidPubKey(data.spendKey)
);
}
exports.check = check;
function canAdd(currentData, newData) {
return (
!!currentData && !!newData && currentData.silentPaymentV0Info === undefined
);
}
exports.canAdd = canAdd;
74 changes: 74 additions & 0 deletions src/cjs/lib/converter/shared/silentPaymentDleq.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
'use strict';
var __importStar =
(this && this.__importStar) ||
function(mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null)
for (var k in mod)
if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result['default'] = mod;
return result;
};
Object.defineProperty(exports, '__esModule', { value: true });
const tools = __importStar(require('uint8array-tools'));
const isValidPubKey = pubkey =>
pubkey.length === 33 && [2, 3].includes(pubkey[0]);
function makeConverter(TYPE_BYTE, isValidPubkey = isValidPubKey) {
function decode(keyVal) {
if (keyVal.key[0] !== TYPE_BYTE) {
throw new Error(
'Decode Error: could not decode silentPaymentDleq with key 0x' +
tools.toHex(keyVal.key),
);
}
const scanKey = keyVal.key.slice(1);
if (!isValidPubkey(scanKey)) {
throw new Error(
'Decode Error: silentPaymentDleq has invalid scanKey in key 0x' +
tools.toHex(keyVal.key),
);
}
if (keyVal.value.length !== 64) {
throw new Error('Decode Error: silentPaymentDleq not a 64-byte proof');
}
return {
scanKey,
proof: keyVal.value,
};
}
function encode(data) {
const head = Uint8Array.from([TYPE_BYTE]);
const key = tools.concat([head, data.scanKey]);
return {
key,
value: data.proof,
};
}
const expected = '{ scanKey: Uint8Array; proof: Uint8Array; }';
function check(data) {
return (
data.scanKey instanceof Uint8Array &&
data.proof instanceof Uint8Array &&
isValidPubkey(data.scanKey) &&
data.proof.length === 64
);
}
function canAddToArray(array, item, dupeSet) {
const dupeString = tools.toHex(item.scanKey);
if (dupeSet.has(dupeString)) return false;
dupeSet.add(dupeString);
return (
array.filter(v => tools.compare(v.scanKey, item.scanKey) === 0).length ===
0
);
}
return {
decode,
encode,
check,
expected,
canAddToArray,
};
}
exports.makeConverter = makeConverter;
77 changes: 77 additions & 0 deletions src/cjs/lib/converter/shared/silentPaymentEcdhShare.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
'use strict';
var __importStar =
(this && this.__importStar) ||
function(mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null)
for (var k in mod)
if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result['default'] = mod;
return result;
};
Object.defineProperty(exports, '__esModule', { value: true });
const tools = __importStar(require('uint8array-tools'));
const isValidDERKey = pubkey =>
(pubkey.length === 33 && [2, 3].includes(pubkey[0])) ||
(pubkey.length === 65 && 4 === pubkey[0]);
function makeConverter(TYPE_BYTE, isValidPubkey = isValidDERKey) {
function decode(keyVal) {
if (keyVal.key[0] !== TYPE_BYTE) {
throw new Error(
'Decode Error: could not decode silentPaymentEcdhShare with key 0x' +
tools.toHex(keyVal.key),
);
}
const scanKey = keyVal.key.slice(1);
if (!isValidPubkey(scanKey)) {
throw new Error(
'Decode Error: silentPaymentEcdhShare has invalid scanKey in key 0x' +
tools.toHex(keyVal.key),
);
}
if (!isValidPubkey(keyVal.value)) {
throw new Error(
'Decode Error: silentPaymentEcdhShare not a 33-byte pubkey',
);
}
return {
scanKey,
share: keyVal.value,
};
}
function encode(data) {
const head = Uint8Array.from([TYPE_BYTE]);
const key = tools.concat([head, data.scanKey]);
return {
key,
value: data.share,
};
}
const expected = '{ scanKey: Uint8Array; share: Uint8Array; }';
function check(data) {
return (
data.scanKey instanceof Uint8Array &&
data.share instanceof Uint8Array &&
isValidPubkey(data.scanKey) &&
isValidPubkey(data.share)
);
}
function canAddToArray(array, item, dupeSet) {
const dupeString = tools.toHex(item.scanKey);
if (dupeSet.has(dupeString)) return false;
dupeSet.add(dupeString);
return (
array.filter(v => tools.compare(v.scanKey, item.scanKey) === 0).length ===
0
);
}
return {
decode,
encode,
check,
expected,
canAddToArray,
};
}
exports.makeConverter = makeConverter;
52 changes: 52 additions & 0 deletions src/cjs/lib/parser/fromBuffer.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,22 @@ function psbtFromKeyVals(
}
globalMap.globalXpub.push(convert.globals.globalXpub.decode(keyVal));
break;
case typeFields_js_1.GlobalTypes.GLOBAL_SP_ECDH_SHARE:
if (globalMap.silentPaymentEcdhShare === undefined) {
globalMap.silentPaymentEcdhShare = [];
}
globalMap.silentPaymentEcdhShare.push(
convert.globals.silentPaymentEcdhShare.decode(keyVal),
);
break;
case typeFields_js_1.GlobalTypes.GLOBAL_SP_DLEQ:
if (globalMap.silentPaymentDleq === undefined) {
globalMap.silentPaymentDleq = [];
}
globalMap.silentPaymentDleq.push(
convert.globals.silentPaymentDleq.decode(keyVal),
);
break;
default:
// This will allow inclusion during serialization.
if (!globalMap.unknownKeyVals) globalMap.unknownKeyVals = [];
Expand Down Expand Up @@ -330,6 +346,22 @@ function psbtFromKeyVals(
);
input.tapMerkleRoot = convert.inputs.tapMerkleRoot.decode(keyVal);
break;
case typeFields_js_1.InputTypes.SP_ECDH_SHARE:
if (input.silentPaymentEcdhShare === undefined) {
input.silentPaymentEcdhShare = [];
}
input.silentPaymentEcdhShare.push(
convert.inputs.silentPaymentEcdhShare.decode(keyVal),
);
break;
case typeFields_js_1.InputTypes.SP_DLEQ:
if (input.silentPaymentDleq === undefined) {
input.silentPaymentDleq = [];
}
input.silentPaymentDleq.push(
convert.inputs.silentPaymentDleq.decode(keyVal),
);
break;
default:
// This will allow inclusion during serialization.
if (!input.unknownKeyVals) input.unknownKeyVals = [];
Expand Down Expand Up @@ -397,6 +429,26 @@ function psbtFromKeyVals(
convert.outputs.tapBip32Derivation.decode(keyVal),
);
break;
case typeFields_js_1.OutputTypes.SP_V0_INFO:
checkKeyBuffer(
'output',
keyVal.key,
typeFields_js_1.OutputTypes.SP_V0_INFO,
);
output.silentPaymentV0Info = convert.outputs.silentPaymentV0Info.decode(
keyVal,
);
break;
case typeFields_js_1.OutputTypes.SP_V0_LABEL:
checkKeyBuffer(
'output',
keyVal.key,
typeFields_js_1.OutputTypes.SP_V0_LABEL,
);
output.silentPaymentOutputLabel = convert.outputs.silentPaymentOutputLabel.decode(
keyVal,
);
break;
default:
if (!output.unknownKeyVals) output.unknownKeyVals = [];
output.unknownKeyVals.push(keyVal);
Expand Down
Loading

0 comments on commit 5500d1c

Please sign in to comment.