Skip to content

Commit

Permalink
Merge pull request #21 from eltNEG/feat/wif
Browse files Browse the repository at this point in the history
wip: Implement wif
  • Loading branch information
AbdelStark authored Sep 10, 2024
2 parents d544840 + 1165a85 commit ba12e8b
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub const bips = @import("bips/lib.zig");
pub const hashes = @import("hashes/lib.zig");
pub const secp256k1 = @import("secp256k1");
pub const types = @import("types/lib.zig");
pub const wif = @import("wif/wif.zig");

test {
const std = @import("std");
Expand Down
1 change: 1 addition & 0 deletions src/wif/lib.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const Wif = @import("wif.zig").WIF;
115 changes: 115 additions & 0 deletions src/wif/wif.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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 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 WIF = struct {
const Self = @This();
inner: []u8,

pub fn fromPrivateKey(private_key: PrivateKey) !Self {
const max_size = 1 + 32 + 1 + 4; // prefix + key + compressed flag + checksum
var actual_size: u8 = max_size - 1;
if (private_key.compressed) {
actual_size += 1;
}
var buf = [_]u8{0} ** max_size;

if (private_key.network == Network.MAINNET) {
buf[0] = WIF_PREFIX_MAINNET;
} else {
buf[0] = WIF_PREFIX_TESTNET;
}

@memcpy(buf[1..33], private_key.inner.data[0..32]);

if (private_key.compressed) {
buf[33] = WIF_COMPRESSED_FLAG;
}

var sha256 = std.crypto.hash.sha2.Sha256.init(.{});
var out256: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;

sha256.update(buf[0 .. actual_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);

@memcpy(buf[actual_size - 4 .. actual_size], out256[0..4]);

// base58 encode
var 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{
.inner = encode_buf[0..encode_size],
};
return wif;
}

pub fn toString(self: WIF) []u8 {
return self.inner;
}

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

pub fn toPrivateKey(self: WIF) !PrivateKey {
_ = self;
return PrivateKey{ .network = Network.MAINNET, .compressed = false, .inner = secp256k1.SecretKey{ .data = [_]u8{0} ** 32 } };
}
};

test "WIF with compressed private key" {
const privateKey = PrivateKey{ .network = Network.MAINNET, .compressed = true, .inner = try secp256k1.SecretKey.fromString("7bea4d472aa93e49321bbde5db88b126b9435482e1f39d84664530a5f40408cd") };
const wif = try WIF.fromPrivateKey(privateKey);
const expected = "L1NawHPsZVHsnW4DUBC7K36LzXfcsLck85fMSoEGyT4LMZv9xSjD";
const actual = wif.toString();
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);
}

test "WIF with uncompressed private key 2" {
const privateKey = PrivateKey{ .network = Network.MAINNET, .compressed = false, .inner = try secp256k1.SecretKey.fromString("0C28FCA386C7A227600B2FE50B7CAE11EC86D3BF1FBE471BE89827E19D72AA1D") };
const wif = try WIF.fromPrivateKey(privateKey);
const expected = "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ";
const actual = wif.toString();
try std.testing.expectEqualSlices(u8, expected[0..], actual[0..]);
}

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..]);
}

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

test "WIF with uncompressed testnet private key" {
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..]);
}

0 comments on commit ba12e8b

Please sign in to comment.