add single call scan_silent_payments function
This commit is contained in:
parent
a05a9cb3de
commit
902ae8cf7e
84
README.md
84
README.md
@ -152,6 +152,90 @@ SELECT secp256k1_xonly_key_match(
|
||||
) FROM x_coords; -- Returns: true
|
||||
```
|
||||
|
||||
#### `scan_silent_payments(outputs, keys, label_tweaks)`
|
||||
|
||||
Efficiently scans for Bitcoin Silent Payments (BIP 352) by combining multiple cryptographic operations into a single function. This function implements the core silent payments scanning algorithm, avoiding the serialization/deserialization overhead that would occur when using individual functions.
|
||||
|
||||
**Parameters:**
|
||||
- `outputs` (LIST[BIGINT]): Array of 64-bit integers representing the first 8 bytes (big-endian) of output x-coordinates to scan for matches
|
||||
- `keys` (LIST[BLOB]): Array containing exactly 3 elements: [scan_private_key (32 bytes), spend_public_key (33 bytes), tweak_key (33 bytes)]
|
||||
- `label_tweaks` (LIST[BLOB]): Array of 33-byte compressed public keys representing label tweak keys for labeled outputs (can be empty)
|
||||
|
||||
**Returns:** BOOLEAN (true if any matching output is found, false otherwise)
|
||||
|
||||
**Algorithm:**
|
||||
1. **Tweak Multiplication**: Multiplies the tweak_key by the scan_private_key using `secp256k1_ec_pubkey_tweak_mul(tweak_key, scan_private_key)`
|
||||
2. **Base Shared Secret**: Computes the base shared secret using `secp256k1_tagged_sha256('BIP0352/SharedSecret', tweaked_key || int_to_big_endian(0))`
|
||||
3. **Base Output Key**: Creates base output key by combining spend_public_key with the public key derived from the base shared secret
|
||||
4. **Direct Match Check**: Extracts first 8 bytes of base output key's x-coordinate and checks against the outputs list
|
||||
5. **Label Tweak Processing**: For each label tweak key:
|
||||
- Combines the base output key with the label tweak key using elliptic curve addition
|
||||
- Checks if the combined result matches any output in the list
|
||||
- Also checks the negated version of the combined result (covers both possible y-coordinates)
|
||||
6. **Early Return**: Returns true immediately upon finding any match
|
||||
|
||||
**Example:**
|
||||
```sql
|
||||
-- Basic silent payments scanning without label tweaks
|
||||
WITH
|
||||
scan_priv AS (SELECT from_hex('0000000000000000000000000000000000000000000000000000000000000001') as key),
|
||||
spend_pub AS (SELECT secp256k1_ec_pubkey_create(from_hex('0000000000000000000000000000000000000000000000000000000000000002')) as key),
|
||||
tweak_key AS (SELECT secp256k1_ec_pubkey_create(from_hex('0000000000000000000000000000000000000000000000000000000000000003')) as key),
|
||||
|
||||
-- Outputs to scan (first 8 bytes of x-coordinates as BIGINT)
|
||||
outputs_to_scan AS (SELECT [1234567890123456789, 9876543210987654321] as list),
|
||||
|
||||
keys AS (SELECT [(SELECT key FROM scan_priv), (SELECT key FROM spend_pub), (SELECT key FROM tweak_key)] as keys_array)
|
||||
SELECT scan_silent_payments(
|
||||
(SELECT list FROM outputs_to_scan),
|
||||
(SELECT keys_array FROM keys),
|
||||
CAST([] AS BLOB[]) -- No label tweaks
|
||||
); -- Returns: true/false depending on whether any output matches
|
||||
|
||||
-- Silent payments scanning with label tweaks
|
||||
WITH
|
||||
scan_priv AS (SELECT from_hex('1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef') as key),
|
||||
spend_pub AS (SELECT secp256k1_ec_pubkey_create(from_hex('fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321')) as key),
|
||||
tweak_key AS (SELECT secp256k1_ec_pubkey_create(from_hex('abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890')) as key),
|
||||
|
||||
-- Label tweak keys for labeled outputs
|
||||
label_tweaks AS (SELECT [
|
||||
secp256k1_ec_pubkey_create(from_hex('1111111111111111111111111111111111111111111111111111111111111111')),
|
||||
secp256k1_ec_pubkey_create(from_hex('2222222222222222222222222222222222222222222222222222222222222222'))
|
||||
] as tweaks),
|
||||
|
||||
outputs_to_scan AS (SELECT [hash_prefix_to_int(from_hex('abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890'), 0)] as list),
|
||||
keys AS (SELECT [(SELECT key FROM scan_priv), (SELECT key FROM spend_pub), (SELECT key FROM tweak_key)] as keys_array)
|
||||
SELECT scan_silent_payments(
|
||||
(SELECT list FROM outputs_to_scan),
|
||||
(SELECT keys_array FROM keys),
|
||||
(SELECT tweaks FROM label_tweaks)
|
||||
); -- Returns: true if any output (base or labeled) matches
|
||||
|
||||
-- Equivalent to this complex multi-function SQL (but much more efficient):
|
||||
-- SELECT secp256k1_xonly_key_match(
|
||||
-- outputs,
|
||||
-- secp256k1_ec_pubkey_combine([
|
||||
-- spend_public_key,
|
||||
-- secp256k1_ec_pubkey_create(secp256k1_tagged_sha256('BIP0352/SharedSecret',
|
||||
-- secp256k1_ec_pubkey_tweak_mul(tweak_key, scan_private_key) || int_to_big_endian(0)))
|
||||
-- ]),
|
||||
-- label_tweak_keys
|
||||
-- );
|
||||
```
|
||||
|
||||
**Performance Benefits:**
|
||||
- **Reduced Overhead**: Eliminates serialization/deserialization between individual secp256k1 function calls
|
||||
- **Memory Efficiency**: Operates on internal secp256k1 data structures without intermediate conversions
|
||||
- **Atomic Operation**: Single function call handles entire scanning workflow
|
||||
- **Early Termination**: Returns immediately upon finding first match, avoiding unnecessary computations
|
||||
|
||||
**Use Cases:**
|
||||
- Bitcoin Silent Payments wallet scanning (BIP 352)
|
||||
- Batch checking of multiple output candidates
|
||||
- Efficient labeled silent payments processing
|
||||
- High-performance blockchain analysis requiring multiple key operations
|
||||
|
||||
### Cryptographic Hash Functions
|
||||
|
||||
#### `secp256k1_tagged_sha256(tag, message)`
|
||||
|
||||
@ -547,6 +547,238 @@ inline void Secp256k1XOnlyKeyMatchScalarFun(DataChunk &args, ExpressionState &st
|
||||
});
|
||||
}
|
||||
|
||||
// Function to scan for silent payments
|
||||
inline void ScanSilentPaymentsScalarFun(DataChunk &args, ExpressionState &state, Vector &result) {
|
||||
D_ASSERT(args.ColumnCount() == 3);
|
||||
|
||||
// Get the secp256k1 context
|
||||
secp256k1_context *ctx = GetSecp256k1Context();
|
||||
|
||||
TernaryExecutor::ExecuteWithNulls<list_entry_t, list_entry_t, list_entry_t, bool>(
|
||||
args.data[0], args.data[1], args.data[2], result, args.size(),
|
||||
[&](list_entry_t outputs_list, list_entry_t keys_list, list_entry_t label_tweaks_list, ValidityMask &mask,
|
||||
idx_t idx) {
|
||||
// Get the child vectors
|
||||
auto &outputs_child_vector = ListVector::GetEntry(args.data[0]);
|
||||
auto &keys_child_vector = ListVector::GetEntry(args.data[1]);
|
||||
auto &label_tweaks_child_vector = ListVector::GetEntry(args.data[2]);
|
||||
|
||||
// Validate that keys list has exactly 3 elements
|
||||
if (keys_list.length != 3) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the three keys: scan private key, spend public key, and tweak key
|
||||
idx_t scan_key_idx = keys_list.offset;
|
||||
idx_t spend_key_idx = keys_list.offset + 1;
|
||||
idx_t tweak_key_idx = keys_list.offset + 2;
|
||||
|
||||
if (FlatVector::IsNull(keys_child_vector, scan_key_idx) ||
|
||||
FlatVector::IsNull(keys_child_vector, spend_key_idx) ||
|
||||
FlatVector::IsNull(keys_child_vector, tweak_key_idx)) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto scan_private_key = FlatVector::GetData<string_t>(keys_child_vector)[scan_key_idx];
|
||||
auto spend_public_key = FlatVector::GetData<string_t>(keys_child_vector)[spend_key_idx];
|
||||
auto tweak_key = FlatVector::GetData<string_t>(keys_child_vector)[tweak_key_idx];
|
||||
|
||||
// Validate key sizes
|
||||
if (scan_private_key.GetSize() != 32 || spend_public_key.GetSize() != 33 || tweak_key.GetSize() != 33) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the spend public key once
|
||||
secp256k1_pubkey spend_pubkey;
|
||||
if (secp256k1_ec_pubkey_parse(ctx, &spend_pubkey, (const unsigned char *)spend_public_key.GetData(), 33) !=
|
||||
1) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the tweak key
|
||||
secp256k1_pubkey tweak_pubkey;
|
||||
if (secp256k1_ec_pubkey_parse(ctx, &tweak_pubkey, (const unsigned char *)tweak_key.GetData(), 33) != 1) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implement: secp256k1_ec_pubkey_tweak_mul(tweak_key, SILENT_PAYMENTS_SCAN_PRIVATE_KEY)
|
||||
if (secp256k1_ec_pubkey_tweak_mul(ctx, &tweak_pubkey, (const unsigned char *)scan_private_key.GetData()) !=
|
||||
1) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Serialize the tweaked key
|
||||
unsigned char tweaked_key_serialized[33];
|
||||
size_t tweaked_len = 33;
|
||||
if (secp256k1_ec_pubkey_serialize(ctx, tweaked_key_serialized, &tweaked_len, &tweak_pubkey,
|
||||
SECP256K1_EC_COMPRESSED) != 1) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate the base shared secret: secp256k1_tagged_sha256('BIP0352/SharedSecret', tweaked_key ||
|
||||
// int_to_big_endian(0))
|
||||
std::string tag = "BIP0352/SharedSecret";
|
||||
|
||||
// Concatenate tweaked_key_serialized + int_to_big_endian(0)
|
||||
unsigned char base_data[37]; // 33 + 4 bytes
|
||||
memcpy(base_data, tweaked_key_serialized, 33);
|
||||
// int_to_big_endian(0) = {0, 0, 0, 0}
|
||||
base_data[33] = 0;
|
||||
base_data[34] = 0;
|
||||
base_data[35] = 0;
|
||||
base_data[36] = 0;
|
||||
|
||||
// Compute base shared secret
|
||||
unsigned char base_shared_secret[32];
|
||||
if (secp256k1_tagged_sha256(ctx, base_shared_secret, (const unsigned char *)tag.c_str(), tag.size(),
|
||||
base_data, 37) != 1) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create public key from base shared secret
|
||||
secp256k1_pubkey base_shared_pubkey;
|
||||
if (secp256k1_ec_pubkey_create(ctx, &base_shared_pubkey, base_shared_secret) != 1) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Combine with spend public key to get base output key
|
||||
const secp256k1_pubkey *base_pubkeys[2] = {&spend_pubkey, &base_shared_pubkey};
|
||||
secp256k1_pubkey base_output_key;
|
||||
if (secp256k1_ec_pubkey_combine(ctx, &base_output_key, base_pubkeys, 2) != 1) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Serialize the base output key to compressed format
|
||||
unsigned char base_compressed[33];
|
||||
size_t base_len = 33;
|
||||
if (secp256k1_ec_pubkey_serialize(ctx, base_compressed, &base_len, &base_output_key,
|
||||
SECP256K1_EC_COMPRESSED) != 1) {
|
||||
mask.SetInvalid(idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract first 8 bytes of base x-coordinate as big-endian int64
|
||||
int64_t base_prefix = ExtractBigEndianInt64(base_compressed + 1);
|
||||
|
||||
// Check direct match against outputs list
|
||||
for (idx_t j = 0; j < outputs_list.length; j++) {
|
||||
idx_t output_idx = outputs_list.offset + j;
|
||||
|
||||
if (FlatVector::IsNull(outputs_child_vector, output_idx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto output_int64 = FlatVector::GetData<int64_t>(outputs_child_vector)[output_idx];
|
||||
if (output_int64 == base_prefix) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If no label tweaks provided, we're done (no match found)
|
||||
if (label_tweaks_list.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Process each label tweak by adding it to the base output key
|
||||
for (idx_t k = 0; k < label_tweaks_list.length; k++) {
|
||||
idx_t label_idx = label_tweaks_list.offset + k;
|
||||
|
||||
if (FlatVector::IsNull(label_tweaks_child_vector, label_idx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto label_tweak = FlatVector::GetData<string_t>(label_tweaks_child_vector)[label_idx];
|
||||
|
||||
// Validate that the label tweak is exactly 33 bytes (compressed pubkey)
|
||||
if (label_tweak.GetSize() != 33) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse the label tweak public key
|
||||
secp256k1_pubkey label_tweak_pubkey;
|
||||
if (secp256k1_ec_pubkey_parse(ctx, &label_tweak_pubkey, (const unsigned char *)label_tweak.GetData(),
|
||||
33) != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Combine the base output key with the label tweak key
|
||||
const secp256k1_pubkey *tweak_pubkeys[2] = {&base_output_key, &label_tweak_pubkey};
|
||||
secp256k1_pubkey tweaked_output_key;
|
||||
if (secp256k1_ec_pubkey_combine(ctx, &tweaked_output_key, tweak_pubkeys, 2) != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Serialize the tweaked output key to compressed format
|
||||
unsigned char tweaked_compressed[33];
|
||||
size_t tweaked_len = 33;
|
||||
if (secp256k1_ec_pubkey_serialize(ctx, tweaked_compressed, &tweaked_len, &tweaked_output_key,
|
||||
SECP256K1_EC_COMPRESSED) != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract first 8 bytes of tweaked x-coordinate as big-endian int64
|
||||
int64_t tweaked_prefix = ExtractBigEndianInt64(tweaked_compressed + 1);
|
||||
|
||||
// Check if this tweaked x value matches any in the outputs list
|
||||
for (idx_t j = 0; j < outputs_list.length; j++) {
|
||||
idx_t output_idx = outputs_list.offset + j;
|
||||
|
||||
if (FlatVector::IsNull(outputs_child_vector, output_idx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto output_int64 = FlatVector::GetData<int64_t>(outputs_child_vector)[output_idx];
|
||||
if (output_int64 == tweaked_prefix) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Try with negated result of the addition
|
||||
secp256k1_pubkey negated_tweaked_key = tweaked_output_key;
|
||||
if (secp256k1_ec_pubkey_negate(ctx, &negated_tweaked_key) != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Serialize the negated tweaked key to compressed format
|
||||
unsigned char negated_tweaked_compressed[33];
|
||||
size_t negated_tweaked_len = 33;
|
||||
if (secp256k1_ec_pubkey_serialize(ctx, negated_tweaked_compressed, &negated_tweaked_len,
|
||||
&negated_tweaked_key, SECP256K1_EC_COMPRESSED) != 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract first 8 bytes of negated tweaked x-coordinate as big-endian int64
|
||||
int64_t negated_tweaked_prefix = ExtractBigEndianInt64(negated_tweaked_compressed + 1);
|
||||
|
||||
// Check if this negated tweaked x value matches any in the outputs list
|
||||
for (idx_t j = 0; j < outputs_list.length; j++) {
|
||||
idx_t output_idx = outputs_list.offset + j;
|
||||
|
||||
if (FlatVector::IsNull(outputs_child_vector, output_idx)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto output_int64 = FlatVector::GetData<int64_t>(outputs_child_vector)[output_idx];
|
||||
if (output_int64 == negated_tweaked_prefix) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
static void LoadInternal(DatabaseInstance &instance) {
|
||||
// Register the secp256k1_ec_pubkey_combine function that accepts an array of blobs
|
||||
auto secp256k1_ec_pubkey_combine_function =
|
||||
@ -597,6 +829,14 @@ static void LoadInternal(DatabaseInstance &instance) {
|
||||
{LogicalType::LIST(LogicalType::BIGINT), LogicalType::BLOB, LogicalType::LIST(LogicalType::BLOB)},
|
||||
LogicalType::BOOLEAN, Secp256k1XOnlyKeyMatchScalarFun);
|
||||
ExtensionUtil::RegisterFunction(instance, secp256k1_xonly_key_match_function);
|
||||
|
||||
// Register the scan_silent_payments function
|
||||
auto scan_silent_payments_function =
|
||||
ScalarFunction("scan_silent_payments",
|
||||
{LogicalType::LIST(LogicalType::BIGINT), LogicalType::LIST(LogicalType::BLOB),
|
||||
LogicalType::LIST(LogicalType::BLOB)},
|
||||
LogicalType::BOOLEAN, ScanSilentPaymentsScalarFun);
|
||||
ExtensionUtil::RegisterFunction(instance, scan_silent_payments_function);
|
||||
}
|
||||
|
||||
void Secp256k1Extension::Load(DuckDB &db) {
|
||||
|
||||
@ -715,3 +715,143 @@ SELECT
|
||||
FROM comprehensive_test;
|
||||
----
|
||||
true
|
||||
|
||||
# Test scan_silent_payments function with no label tweaks (empty case)
|
||||
query I
|
||||
WITH
|
||||
scan_priv AS (SELECT from_hex('0000000000000000000000000000000000000000000000000000000000000001') as key),
|
||||
spend_pub AS (SELECT secp256k1_ec_pubkey_create(from_hex('0000000000000000000000000000000000000000000000000000000000000002')) as key),
|
||||
tweak_key AS (SELECT secp256k1_ec_pubkey_create(from_hex('0000000000000000000000000000000000000000000000000000000000000003')) as key),
|
||||
outputs AS (SELECT [8646911284551352320] as list), -- Dummy output that won't match
|
||||
keys AS (SELECT [(SELECT key FROM scan_priv), (SELECT key FROM spend_pub), (SELECT key FROM tweak_key)] as keys_array)
|
||||
SELECT scan_silent_payments(
|
||||
(SELECT list FROM outputs),
|
||||
(SELECT keys_array FROM keys),
|
||||
CAST([] AS BLOB[]) -- Empty label tweaks
|
||||
);
|
||||
----
|
||||
false
|
||||
|
||||
# Test scan_silent_payments function with matching output (functional test)
|
||||
query I
|
||||
WITH
|
||||
scan_priv AS (SELECT from_hex('0000000000000000000000000000000000000000000000000000000000000001') as key),
|
||||
spend_pub AS (SELECT secp256k1_ec_pubkey_create(from_hex('0000000000000000000000000000000000000000000000000000000000000002')) as key),
|
||||
tweak_key AS (SELECT secp256k1_ec_pubkey_create(from_hex('0000000000000000000000000000000000000000000000000000000000000003')) as key),
|
||||
|
||||
-- Calculate what the output should be for this specific input
|
||||
calculated_output AS (
|
||||
SELECT secp256k1_ec_pubkey_combine([
|
||||
(SELECT key FROM spend_pub),
|
||||
secp256k1_ec_pubkey_create(secp256k1_tagged_sha256('BIP0352/SharedSecret',
|
||||
secp256k1_ec_pubkey_tweak_mul((SELECT key FROM tweak_key), (SELECT key FROM scan_priv)) || int_to_big_endian(0)))
|
||||
]) as output_key
|
||||
),
|
||||
|
||||
-- Extract first 8 bytes as BIGINT
|
||||
output_prefix AS (
|
||||
SELECT hash_prefix_to_int(from_hex(substring(to_hex(output_key), 3, 64)), 0) as prefix
|
||||
FROM calculated_output
|
||||
),
|
||||
|
||||
outputs AS (SELECT [(SELECT prefix FROM output_prefix)] as list),
|
||||
keys AS (SELECT [(SELECT key FROM scan_priv), (SELECT key FROM spend_pub), (SELECT key FROM tweak_key)] as keys_array)
|
||||
SELECT scan_silent_payments(
|
||||
(SELECT list FROM outputs),
|
||||
(SELECT keys_array FROM keys),
|
||||
CAST([] AS BLOB[]) -- Empty label tweaks
|
||||
);
|
||||
----
|
||||
true
|
||||
|
||||
# Test scan_silent_payments function with no label tweaks (BIP testcase)
|
||||
query I
|
||||
WITH
|
||||
scan_priv AS (SELECT from_hex('0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c') as key),
|
||||
spend_pub AS (SELECT secp256k1_ec_pubkey_create(from_hex('9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3')) as key),
|
||||
tweak_key AS (SELECT from_hex('024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004') as key),
|
||||
outputs AS (SELECT [4512552348537027144] as list), -- First 8 bytes of expected output x-coordinate
|
||||
keys AS (SELECT [(SELECT key FROM scan_priv), (SELECT key FROM spend_pub), (SELECT key FROM tweak_key)] as keys_array)
|
||||
SELECT scan_silent_payments(
|
||||
(SELECT list FROM outputs),
|
||||
(SELECT keys_array FROM keys),
|
||||
CAST([] AS BLOB[]) -- Empty label tweaks
|
||||
);
|
||||
----
|
||||
true
|
||||
|
||||
# Test scan_silent_payments function with label tweaks
|
||||
query I
|
||||
WITH
|
||||
scan_priv AS (SELECT from_hex('0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c') as key),
|
||||
spend_pub AS (SELECT secp256k1_ec_pubkey_create(from_hex('9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3')) as key),
|
||||
tweak_key AS (SELECT secp256k1_ec_pubkey_create(from_hex('0000000000000000000000000000000000000000000000000000000000000002')) as key),
|
||||
|
||||
-- Test with a label tweak key
|
||||
label_tweak AS (SELECT secp256k1_ec_pubkey_create(from_hex('0000000000000000000000000000000000000000000000000000000000000001')) as key),
|
||||
|
||||
outputs AS (SELECT [1234567890] as list), -- Dummy output that won't match
|
||||
keys AS (SELECT [(SELECT key FROM scan_priv), (SELECT key FROM spend_pub), (SELECT key FROM tweak_key)] as keys_array),
|
||||
tweaks AS (SELECT [(SELECT key FROM label_tweak)] as tweaks_array)
|
||||
SELECT scan_silent_payments(
|
||||
(SELECT list FROM outputs),
|
||||
(SELECT keys_array FROM keys),
|
||||
(SELECT tweaks_array FROM tweaks)
|
||||
);
|
||||
----
|
||||
false
|
||||
|
||||
# Test scan_silent_payments function with change label tweak (BIP testcase)
|
||||
query I
|
||||
WITH
|
||||
scan_priv AS (SELECT from_hex('11b7a82e06ca2648d5fded2366478078ec4fc9dc1d8ff487518226f229d768fd') as key),
|
||||
spend_pub AS (SELECT from_hex('03ecd43b9fdad484ff57278b21878b844276ce390622d03dd0cfb4288b7e02a6f5') as key),
|
||||
tweak_key AS (SELECT from_hex('0314bec14463d6c0181083d607fecfba67bb83f95915f6f247975ec566d5642ee8') as key),
|
||||
label_tweak AS (SELECT from_hex('03ecd43b9fdad484ff57278b21878b844276ce390622d03dd0cfb4288b7e02a6f5') as key),
|
||||
outputs AS (SELECT [-4740445252767345406] as list), -- First 8 bytes of expected output x-coordinate
|
||||
keys AS (SELECT [(SELECT key FROM scan_priv), (SELECT key FROM spend_pub), (SELECT key FROM tweak_key)] as keys_array),
|
||||
tweaks AS (SELECT [(SELECT key FROM label_tweak)] as tweaks_array)
|
||||
SELECT scan_silent_payments(
|
||||
(SELECT list FROM outputs),
|
||||
(SELECT keys_array FROM keys),
|
||||
(SELECT tweaks_array FROM tweaks)
|
||||
);
|
||||
----
|
||||
true
|
||||
|
||||
# Test scan_silent_payments function with NULL inputs
|
||||
query I
|
||||
SELECT scan_silent_payments(NULL, NULL, NULL);
|
||||
----
|
||||
NULL
|
||||
|
||||
# Test scan_silent_payments function with invalid keys array length
|
||||
query I
|
||||
WITH
|
||||
scan_priv AS (SELECT from_hex('0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1f2c') as key),
|
||||
spend_pub AS (SELECT secp256k1_ec_pubkey_create(from_hex('9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3')) as key),
|
||||
outputs AS (SELECT [1234567890] as list),
|
||||
keys AS (SELECT [(SELECT key FROM scan_priv), (SELECT key FROM spend_pub)] as keys_array) -- Only two keys instead of three
|
||||
SELECT scan_silent_payments(
|
||||
(SELECT list FROM outputs),
|
||||
(SELECT keys_array FROM keys),
|
||||
CAST([] AS BLOB[])
|
||||
);
|
||||
----
|
||||
NULL
|
||||
|
||||
# Test scan_silent_payments function with wrong key sizes
|
||||
query I
|
||||
WITH
|
||||
wrong_scan_priv AS (SELECT from_hex('0f694e068028a717f8af6b9411f9a133dd3565258714cc226594b34db90c1') as key), -- 31 bytes instead of 32
|
||||
spend_pub AS (SELECT secp256k1_ec_pubkey_create(from_hex('9d6ad855ce3417ef84e836892e5a56392bfba05fa5d97ccea30e266f540e08b3')) as key),
|
||||
tweak_key AS (SELECT secp256k1_ec_pubkey_create(from_hex('0000000000000000000000000000000000000000000000000000000000000001')) as key),
|
||||
outputs AS (SELECT [1234567890] as list),
|
||||
keys AS (SELECT [(SELECT key FROM wrong_scan_priv), (SELECT key FROM spend_pub), (SELECT key FROM tweak_key)] as keys_array)
|
||||
SELECT scan_silent_payments(
|
||||
(SELECT list FROM outputs),
|
||||
(SELECT keys_array FROM keys),
|
||||
CAST([] AS BLOB[])
|
||||
);
|
||||
----
|
||||
NULL
|
||||
|
||||
Loading…
Reference in New Issue
Block a user