Skip to content

Commit

Permalink
Implement wif to private key
Browse files Browse the repository at this point in the history
  • Loading branch information
eltNEG committed Sep 10, 2024
1 parent 1165a85 commit 61405bf
Showing 1 changed file with 64 additions and 8 deletions.
72 changes: 64 additions & 8 deletions src/wif/wif.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ const PrivateKey = @import("../bips/bip32/key.zig").PrivateKey;
const Network = @import("../bips/bip32/bip32.zig").Network;
const std = @import("std");
const Base58Encoder = @import("../base58/base58.zig").Encoder;
const Base58Decoder = @import("../base58/base58.zig").Decoder;
const secp256k1 = @import("secp256k1");

/// WIF as defined in https://en.bitcoin.it/wiki/Wallet_import_format
pub const WIF_PREFIX_MAINNET: u8 = 0x80;
pub const WIF_PREFIX_TESTNET: u8 = 0xef;
pub const WIF_COMPRESSED_FLAG: u8 = 0x01;

pub const WIFDecodeError = error{};
pub const WIFDecodeError = error{ InvalidNetwork, InvalidChecksum };

pub const WIF = struct {
const Self = @This();
Expand Down Expand Up @@ -49,7 +50,7 @@ pub const WIF = struct {
@memcpy(buf[actual_size - 4 .. actual_size], out256[0..4]);

// base58 encode
var encoder = Base58Encoder{};
const encoder = Base58Encoder{};
var encode_buf = [_]u8{0} ** 52; // max wif len is 52
const encode_size = encoder.encode(buf[0..actual_size], &encode_buf);
const wif = WIF{
Expand All @@ -63,13 +64,49 @@ pub const WIF = struct {
}

pub fn fromString(wif: []const u8) !WIF {
_ = wif;
return WIF{ .inner = undefined };
// decode base58
const decoder = Base58Decoder{};

var decoded = [_]u8{0} ** 38; // max len is 38
const decode_size = try decoder.decode(wif, &decoded);

const new_wif = WIF{
.inner = decoded[0..decode_size],
};

// check checksum
var out256: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;

var sha256 = std.crypto.hash.sha2.Sha256.init(.{});
sha256.update(decoded[0 .. decode_size - 4]);
sha256.final(&out256);

sha256 = std.crypto.hash.sha2.Sha256.init(.{});

sha256.update(out256[0..std.crypto.hash.sha2.Sha256.digest_length]);
sha256.final(&out256);

if (decoded[decode_size - 4] != out256[0] or
decoded[decode_size - 3] != out256[1] or
decoded[decode_size - 2] != out256[2] or
decoded[decode_size - 1] != out256[3])
{
return WIFDecodeError.InvalidChecksum;
}

return new_wif;
}

pub fn toPrivateKey(self: WIF) !PrivateKey {
_ = self;
return PrivateKey{ .network = Network.MAINNET, .compressed = false, .inner = secp256k1.SecretKey{ .data = [_]u8{0} ** 32 } };
const network = switch (self.inner[0]) {
WIF_PREFIX_MAINNET => Network.MAINNET,
WIF_PREFIX_TESTNET => Network.TESTNET,
else => return WIFDecodeError.InvalidNetwork,
};
const compressed = self.inner[33] == WIF_COMPRESSED_FLAG;

const data: [32]u8 = self.inner[1..33].*; // secp256k1.SecretKey.fromSlice has some weird effect on the array
return PrivateKey{ .network = network, .compressed = compressed, .inner = try secp256k1.SecretKey.fromSlice(&data) };
}
};

Expand All @@ -79,6 +116,10 @@ test "WIF with compressed private key" {
const expected = "L1NawHPsZVHsnW4DUBC7K36LzXfcsLck85fMSoEGyT4LMZv9xSjD";
const actual = wif.toString();
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);

const got_wif = try WIF.fromString(expected);
const got_privateKey = try got_wif.toPrivateKey();
try std.testing.expectEqualDeep(privateKey, got_privateKey);
}

test "WIF with uncompressed private key 2" {
Expand All @@ -87,15 +128,22 @@ test "WIF with uncompressed private key 2" {
const expected = "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ";
const actual = wif.toString();
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);

const got_wif = try WIF.fromString(expected);
const got_privateKey = try got_wif.toPrivateKey();
try std.testing.expectEqualDeep(privateKey, got_privateKey);
}

test "WIF with uncompressed private key" {
const privateKey = PrivateKey{ .network = Network.MAINNET, .compressed = false, .inner = try secp256k1.SecretKey.fromString("46605abb568e1566834e7ee57e271964534d8fc3b23ca5f546b081ad7e233671") };
const wif = try WIF.fromPrivateKey(privateKey);
const expected = "5JMHFZHuMcVnqVBARmg3jW3LMxdB6qbJtesC5xhXRji6wabvbWu";
const actual = wif.toString();
// std.debug.print("actual========: {s}\n", .{actual});
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);

const got_wif = try WIF.fromString(expected);
const got_privateKey = try got_wif.toPrivateKey();
try std.testing.expectEqualDeep(privateKey, got_privateKey);
}

test "WIF with compressed testnet private key" {
Expand All @@ -104,12 +152,20 @@ test "WIF with compressed testnet private key" {
const expected = "cPwWCAXTX3NLUSq7zjzURugN5jp5FDa832H13KJNoJARUPsaTJ9G";
const actual = wif.toString();
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);

const got_wif = try WIF.fromString(expected);
const got_privateKey = try got_wif.toPrivateKey();
try std.testing.expectEqualDeep(privateKey, got_privateKey);
}

test "WIF with uncompressed testnet private key" {
test "WIF with uncompressed testnet private key\n" {
const privateKey = PrivateKey{ .network = Network.TESTNET, .compressed = false, .inner = try secp256k1.SecretKey.fromString("46605abb568e1566834e7ee57e271964534d8fc3b23ca5f546b081ad7e233671") };
const wif = try WIF.fromPrivateKey(privateKey);
const expected = "927uqJ7SwqZvoYgT47Zxc6bJ1cytG18WEbj9Ab42mUT9icaLVhF";
const actual = wif.toString();
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);

const got_wif = try WIF.fromString(expected);
const got_privateKey = try got_wif.toPrivateKey();
try std.testing.expectEqualDeep(privateKey, got_privateKey);
}

0 comments on commit 61405bf

Please sign in to comment.