Skip to content

Commit 6940994

Browse files
committed
refactor keypaths/bip44 and child derivation
1 parent 0a2fdfc commit 6940994

File tree

7 files changed

+304
-343
lines changed

7 files changed

+304
-343
lines changed

src/bip32.zig

+65-39
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const crypto = @import("crypto");
55
const utils = @import("utils.zig");
66
const Network = @import("const.zig").Network;
77
const script = @import("script.zig");
8+
const KeyPath = @import("keypath.zig").KeyPath;
89

910
pub const SerializedPrivateKeyVersion = enum(u32) {
1011
mainnet = 0x0488aDe4,
@@ -333,6 +334,33 @@ pub fn deriveHardenedChild(extended_privkey: ExtendedPrivateKey, index: u32) !Ex
333334
};
334335
}
335336

337+
pub fn deriveChildFromKeyPath(comptime T: type, extended_key: T, comptime keypath_depth: u8, keypath: KeyPath(keypath_depth)) !T {
338+
return switch (T) {
339+
ExtendedPrivateKey => {
340+
var r = ExtendedPrivateKey{ .privatekey = extended_key.privatekey, .chaincode = extended_key.chaincode };
341+
for (keypath.path) |p| {
342+
if (p.is_hardened == true) {
343+
r = try deriveHardenedChild(r, p.value + 2147483648);
344+
} else {
345+
r = try deriveChildFromExtendedPrivateKey(r, p.value);
346+
}
347+
}
348+
349+
return r;
350+
},
351+
ExtendedPublicKey => {
352+
var r = ExtendedPublicKey{ .key = extended_key.key, .chaincode = extended_key.chaincode };
353+
for (keypath.path) |p| {
354+
assert(p.is_hardened == false);
355+
r = try deriveChildFromExtendedPublicKey(r, p.value);
356+
}
357+
358+
return r;
359+
},
360+
else => @compileError("Expected type ExtendedPrivateKey or ExtendedPublicKey"),
361+
};
362+
}
363+
336364
test "extendedMasterPrivateKeyFromSeed" {
337365
const seed = [64]u8{ 0b10111000, 0b01110011, 0b00100001, 0b00101111, 0b10001000, 0b01011100, 0b11001111, 0b11111011, 0b11110100, 0b01101001, 0b00101010, 0b11111100, 0b10111000, 0b01001011, 0b11000010, 0b11100101, 0b01011000, 0b10000110, 0b11011110, 0b00101101, 0b11111010, 0b00000111, 0b11011001, 0b00001111, 0b01011100, 0b00111100, 0b00100011, 0b10011010, 0b10111100, 0b00110001, 0b11000000, 0b10100110, 0b11001110, 0b00000100, 0b01111110, 0b00110000, 0b11111101, 0b10001011, 0b11110110, 0b10100010, 0b10000001, 0b11100111, 0b00010011, 0b10001001, 0b10101010, 0b10000010, 0b11010111, 0b00111101, 0b11110111, 0b01001100, 0b01111011, 0b10111111, 0b10110011, 0b10110000, 0b01101011, 0b01000110, 0b00111001, 0b10100101, 0b11001110, 0b11100111, 0b01110101, 0b11001100, 0b11001101, 0b00111100 };
338366

@@ -540,6 +568,10 @@ test "extendedPrivateKeyFromAddress" {
540568

541569
try std.testing.expectEqualStrings(&expected, &strprivatekey);
542570
try std.testing.expectEqualStrings(&expectedchaincode, &strchaincode);
571+
572+
const epk2 = try ExtendedPrivateKey.fromAddress("tprv8ZgxMBicQKsPfCxvMSGLjZegGFnZn9VZfVdsnEbuzTGdS9aZjvaYpyh7NsxsrAc8LsRQZ2EYaCfkvwNpas8cKUBbptDzadY7c3hUi8i33XJ".*);
573+
try std.testing.expectEqualStrings("3cce48c84f22343cbdac8e7f252ed8ca11fce329deae7ed635b73822dfed9c77", &try utils.bytesToHex(64, &epk2.privatekey));
574+
try std.testing.expectEqualStrings("ea6f63babb3dc5c58ea4cd11cb3fc9d7baa51c0e14be8230ffb8b1696796a63f", &try utils.bytesToHex(64, &epk2.chaincode));
543575
}
544576

545577
test "serializePublicKey" {
@@ -643,44 +675,38 @@ test "extendedPrivateKeyAddress" {
643675
try std.testing.expectEqualStrings(&expected, &addr);
644676
}
645677

646-
test "t" {
678+
test "deriveChildFromKeyPathExtendedPrivate" {
647679
const epk = try ExtendedPrivateKey.fromAddress("tprv8ZgxMBicQKsPfCxvMSGLjZegGFnZn9VZfVdsnEbuzTGdS9aZjvaYpyh7NsxsrAc8LsRQZ2EYaCfkvwNpas8cKUBbptDzadY7c3hUi8i33XJ".*);
648-
var strkey: [64]u8 = undefined;
649-
_ = try std.fmt.bufPrint(&strkey, "{x}", .{std.fmt.fmtSliceHexLower(&epk.privatekey)});
650-
var strchaincode: [64]u8 = undefined;
651-
_ = try std.fmt.bufPrint(&strchaincode, "{x}", .{std.fmt.fmtSliceHexLower(&epk.chaincode)});
652-
try std.testing.expectEqualStrings("3cce48c84f22343cbdac8e7f252ed8ca11fce329deae7ed635b73822dfed9c77", &strkey);
653-
try std.testing.expectEqualStrings("ea6f63babb3dc5c58ea4cd11cb3fc9d7baa51c0e14be8230ffb8b1696796a63f", &strchaincode);
654-
655-
const pubkey = PublicKey.fromPrivateKey(epk.privatekey);
656-
try std.testing.expectEqualStrings("03b337fd5dbfcf1a5c86cc8e2956a331048264be996a005c5a444280877e303359", &try pubkey.toStrCompressed());
657-
658-
const c1 = try deriveChildFromExtendedPrivateKey(epk, 1);
659-
var strkeyc1: [64]u8 = undefined;
660-
_ = try std.fmt.bufPrint(&strkeyc1, "{x}", .{std.fmt.fmtSliceHexLower(&c1.privatekey)});
661-
var strchaincodec1: [64]u8 = undefined;
662-
_ = try std.fmt.bufPrint(&strchaincodec1, "{x}", .{std.fmt.fmtSliceHexLower(&c1.chaincode)});
663-
try std.testing.expectEqualStrings("9e8d70914e98c5c9982b19877a6d495ab815c02a7896a593f457861e956c61b4", &strkeyc1);
664-
try std.testing.expectEqualStrings("a0596502c28d66ef847e2539bea2f88f3bd4dc367976ff50780fa5ceda45a9bf", &strchaincodec1);
665-
666-
const c2 = try deriveHardenedChild(epk, 2147483649);
667-
var strkeyc2: [64]u8 = undefined;
668-
_ = try std.fmt.bufPrint(&strkeyc2, "{x}", .{std.fmt.fmtSliceHexLower(&c2.privatekey)});
669-
var strchaincodec2: [64]u8 = undefined;
670-
_ = try std.fmt.bufPrint(&strchaincodec2, "{x}", .{std.fmt.fmtSliceHexLower(&c2.chaincode)});
671-
try std.testing.expectEqualStrings("ab22a43e4bbe9990af4ec355e476c0cd5734531bbc3de0992b819878422bd268", &strkeyc2);
672-
try std.testing.expectEqualStrings("ee6538cac715c3207cb3b41ec4c42a2e0a2fc7ab0e0c4e1322b28ee6417c0e64", &strchaincodec2);
673-
674-
// 84'/1'/0'/0
675-
const d1 = try deriveHardenedChild(epk, 2147483648 + 84);
676-
const d2 = try deriveHardenedChild(d1, 2147483648 + 1);
677-
const d3 = try deriveHardenedChild(d2, 2147483648);
678-
const d4 = try deriveChildFromExtendedPrivateKey(d3, 0);
679-
680-
var strkeyd4: [64]u8 = undefined;
681-
_ = try std.fmt.bufPrint(&strkeyd4, "{x}", .{std.fmt.fmtSliceHexLower(&d4.privatekey)});
682-
var strchaincoded4: [64]u8 = undefined;
683-
_ = try std.fmt.bufPrint(&strchaincoded4, "{x}", .{std.fmt.fmtSliceHexLower(&d4.chaincode)});
684-
try std.testing.expectEqualStrings("ef3b8a82492a138ca4e39f6d508ec858e60dbfac64a143b021687aa42897682c", &strkeyd4);
685-
try std.testing.expectEqualStrings("da50dba459aa9adcb6410bba999e5a6827d583d100efea52ede417a767729c70", &strchaincoded4);
680+
681+
const keypath_partial = try KeyPath(3).fromStr("84'/1'/0'");
682+
const epk_partial = try deriveChildFromKeyPath(ExtendedPrivateKey, epk, 3, keypath_partial);
683+
684+
const keypath = try KeyPath(4).fromStr("84'/1'/0'/0");
685+
const r = try deriveChildFromKeyPath(ExtendedPrivateKey, epk, 4, keypath);
686+
687+
try std.testing.expectEqualStrings("ef3b8a82492a138ca4e39f6d508ec858e60dbfac64a143b021687aa42897682c", &try utils.bytesToHex(64, &r.privatekey));
688+
try std.testing.expectEqualStrings("da50dba459aa9adcb6410bba999e5a6827d583d100efea52ede417a767729c70", &try utils.bytesToHex(64, &r.chaincode));
689+
690+
// 84'/1'/0'/0/1
691+
const n = try deriveChildFromExtendedPrivateKey(r, 1);
692+
693+
const keypath_public = try KeyPath(2).fromStr("0/1");
694+
const extended_public = ExtendedPublicKey{ .key = PublicKey.fromPrivateKey(epk_partial.privatekey), .chaincode = epk_partial.chaincode };
695+
696+
const r2 = try deriveChildFromKeyPath(ExtendedPublicKey, extended_public, 2, keypath_public);
697+
const expected_public = PublicKey.fromPrivateKey(n.privatekey);
698+
699+
try std.testing.expectEqual(expected_public.point.x, r2.key.point.x);
700+
try std.testing.expectEqual(expected_public.point.y, r2.key.point.y);
701+
try std.testing.expectEqualSlices(u8, &n.chaincode, &r2.chaincode);
686702
}
703+
704+
//test "deriveChildFromKeyPathExtendedPublic" {
705+
// const epk = try ExtendedPrivateKey.fromAddress("tprv8ZgxMBicQKsPfCxvMSGLjZegGFnZn9VZfVdsnEbuzTGdS9aZjvaYpyh7NsxsrAc8LsRQZ2EYaCfkvwNpas8cKUBbptDzadY7c3hUi8i33XJ".*);
706+
//
707+
// const keypath = try KeyPath(4).fromStr("84'/1'/0'/0");
708+
// const r = try deriveChildFromKeyPath(ExtendedPrivateKey, epk, 4, keypath);
709+
//
710+
// try std.testing.expectEqualStrings("ef3b8a82492a138ca4e39f6d508ec858e60dbfac64a143b021687aa42897682c", &try utils.bytesToHex(64, &r.privatekey));
711+
// try std.testing.expectEqualStrings("da50dba459aa9adcb6410bba999e5a6827d583d100efea52ede417a767729c70", &try utils.bytesToHex(64, &r.chaincode));
712+
//}

src/bip44.zig

-259
This file was deleted.

0 commit comments

Comments
 (0)