Skip to content

Commit ba12e8b

Browse files
authored
Merge pull request #21 from eltNEG/feat/wif
wip: Implement wif
2 parents d544840 + 1165a85 commit ba12e8b

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

src/root.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub const bips = @import("bips/lib.zig");
44
pub const hashes = @import("hashes/lib.zig");
55
pub const secp256k1 = @import("secp256k1");
66
pub const types = @import("types/lib.zig");
7+
pub const wif = @import("wif/wif.zig");
78

89
test {
910
const std = @import("std");

src/wif/lib.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub const Wif = @import("wif.zig").WIF;

src/wif/wif.zig

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
const PrivateKey = @import("../bips/bip32/key.zig").PrivateKey;
2+
const Network = @import("../bips/bip32/bip32.zig").Network;
3+
const std = @import("std");
4+
const Base58Encoder = @import("../base58/base58.zig").Encoder;
5+
const secp256k1 = @import("secp256k1");
6+
7+
/// WIF as defined in https://en.bitcoin.it/wiki/Wallet_import_format
8+
pub const WIF_PREFIX_MAINNET: u8 = 0x80;
9+
pub const WIF_PREFIX_TESTNET: u8 = 0xef;
10+
pub const WIF_COMPRESSED_FLAG: u8 = 0x01;
11+
12+
pub const WIFDecodeError = error{};
13+
14+
pub const WIF = struct {
15+
const Self = @This();
16+
inner: []u8,
17+
18+
pub fn fromPrivateKey(private_key: PrivateKey) !Self {
19+
const max_size = 1 + 32 + 1 + 4; // prefix + key + compressed flag + checksum
20+
var actual_size: u8 = max_size - 1;
21+
if (private_key.compressed) {
22+
actual_size += 1;
23+
}
24+
var buf = [_]u8{0} ** max_size;
25+
26+
if (private_key.network == Network.MAINNET) {
27+
buf[0] = WIF_PREFIX_MAINNET;
28+
} else {
29+
buf[0] = WIF_PREFIX_TESTNET;
30+
}
31+
32+
@memcpy(buf[1..33], private_key.inner.data[0..32]);
33+
34+
if (private_key.compressed) {
35+
buf[33] = WIF_COMPRESSED_FLAG;
36+
}
37+
38+
var sha256 = std.crypto.hash.sha2.Sha256.init(.{});
39+
var out256: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;
40+
41+
sha256.update(buf[0 .. actual_size - 4]);
42+
sha256.final(&out256);
43+
44+
sha256 = std.crypto.hash.sha2.Sha256.init(.{});
45+
46+
sha256.update(out256[0..std.crypto.hash.sha2.Sha256.digest_length]);
47+
sha256.final(&out256);
48+
49+
@memcpy(buf[actual_size - 4 .. actual_size], out256[0..4]);
50+
51+
// base58 encode
52+
var encoder = Base58Encoder{};
53+
var encode_buf = [_]u8{0} ** 52; // max wif len is 52
54+
const encode_size = encoder.encode(buf[0..actual_size], &encode_buf);
55+
const wif = WIF{
56+
.inner = encode_buf[0..encode_size],
57+
};
58+
return wif;
59+
}
60+
61+
pub fn toString(self: WIF) []u8 {
62+
return self.inner;
63+
}
64+
65+
pub fn fromString(wif: []const u8) !WIF {
66+
_ = wif;
67+
return WIF{ .inner = undefined };
68+
}
69+
70+
pub fn toPrivateKey(self: WIF) !PrivateKey {
71+
_ = self;
72+
return PrivateKey{ .network = Network.MAINNET, .compressed = false, .inner = secp256k1.SecretKey{ .data = [_]u8{0} ** 32 } };
73+
}
74+
};
75+
76+
test "WIF with compressed private key" {
77+
const privateKey = PrivateKey{ .network = Network.MAINNET, .compressed = true, .inner = try secp256k1.SecretKey.fromString("7bea4d472aa93e49321bbde5db88b126b9435482e1f39d84664530a5f40408cd") };
78+
const wif = try WIF.fromPrivateKey(privateKey);
79+
const expected = "L1NawHPsZVHsnW4DUBC7K36LzXfcsLck85fMSoEGyT4LMZv9xSjD";
80+
const actual = wif.toString();
81+
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);
82+
}
83+
84+
test "WIF with uncompressed private key 2" {
85+
const privateKey = PrivateKey{ .network = Network.MAINNET, .compressed = false, .inner = try secp256k1.SecretKey.fromString("0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D") };
86+
const wif = try WIF.fromPrivateKey(privateKey);
87+
const expected = "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ";
88+
const actual = wif.toString();
89+
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);
90+
}
91+
92+
test "WIF with uncompressed private key" {
93+
const privateKey = PrivateKey{ .network = Network.MAINNET, .compressed = false, .inner = try secp256k1.SecretKey.fromString("46605abb568e1566834e7ee57e271964534d8fc3b23ca5f546b081ad7e233671") };
94+
const wif = try WIF.fromPrivateKey(privateKey);
95+
const expected = "5JMHFZHuMcVnqVBARmg3jW3LMxdB6qbJtesC5xhXRji6wabvbWu";
96+
const actual = wif.toString();
97+
// std.debug.print("actual========: {s}\n", .{actual});
98+
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);
99+
}
100+
101+
test "WIF with compressed testnet private key" {
102+
const privateKey = PrivateKey{ .network = Network.TESTNET, .compressed = true, .inner = try secp256k1.SecretKey.fromString("46605abb568e1566834e7ee57e271964534d8fc3b23ca5f546b081ad7e233671") };
103+
const wif = try WIF.fromPrivateKey(privateKey);
104+
const expected = "cPwWCAXTX3NLUSq7zjzURugN5jp5FDa832H13KJNoJARUPsaTJ9G";
105+
const actual = wif.toString();
106+
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);
107+
}
108+
109+
test "WIF with uncompressed testnet private key" {
110+
const privateKey = PrivateKey{ .network = Network.TESTNET, .compressed = false, .inner = try secp256k1.SecretKey.fromString("46605abb568e1566834e7ee57e271964534d8fc3b23ca5f546b081ad7e233671") };
111+
const wif = try WIF.fromPrivateKey(privateKey);
112+
const expected = "927uqJ7SwqZvoYgT47Zxc6bJ1cytG18WEbj9Ab42mUT9icaLVhF";
113+
const actual = wif.toString();
114+
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);
115+
}

0 commit comments

Comments
 (0)