diff --git a/README.md b/README.md index 17e5f3d..eb2d94d 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ First, we're going to implement a thin-client wallet. - Clean up tests to make more consistent - Different nets (e.g., https://github.com/haskoin/haskoin/blob/master/prodnet/Network/Haskoin/Constants.hs) +- DER: https://bitcointa.lk/threads/quick-question-on-der-encoding-of-signature-pair-r-s.329293/#post-7058009 +- ECDSA signing: https://bitcointa.lk/threads/quick-question-on-der-encoding-of-signature-pair-r-s.329293/#post-7023595 +- Why der: http://bitcoin.stackexchange.com/questions/14415/why-use-der-encoding-for-signatures + ### Public key distribution - See [ref](https://github.com/danielsuo/Crypto.jl) diff --git a/src/Coin.jl b/src/Coin.jl index a46ab8d..591aa22 100644 --- a/src/Coin.jl +++ b/src/Coin.jl @@ -3,7 +3,7 @@ module Coin export # keys.jl generate_keys, - get_public_key, + get_pub_key, # wif.jl private2wif, @@ -25,7 +25,6 @@ export # utils.jl reverse_endian, get_checksum, - hex_string_to_array, to_varint, bytearray diff --git a/src/keys.jl b/src/keys.jl index 7bf166e..64dd7d3 100644 --- a/src/keys.jl +++ b/src/keys.jl @@ -12,46 +12,53 @@ ## ############################################################################## -function generate_keys(network_id = "00", version = "1") - secret_key = join([hex(x) for x in Crypto.random(256)]) - public_key = get_public_key(secret_key, network_id = network_id, version = version) +function generate_keys(network_id = 0x00, version = "1") + priv_key = Crypto.random(256) + pub_key = get_pub_key(priv_key, network_id = network_id, version = version) - return (secret_key, public_key) + return (Crypto.hex_array_to_string(priv_key), pub_key) end -function get_public_key(secret_key; network_id = "00", version = "1") +function get_pub_key(priv_key::String; network_id = 0x00, version = "1") + get_pub_key(Crypto.hex_string_to_array(priv_key), + network_id = network_id, + version = version) +end + +function get_pub_key(priv_key::Array{Uint8}; network_id = 0x00, version = "1") # Generate corresponding public key generated with against ECDSA secp256k1 # (65 bytes, 1 byte 0x04, 32 bytes corresponding to X coordinate, 32 bytes # corresponding to Y coordinate) - public_key = Crypto.ec_public_key_create(secret_key) + pub_key = Crypto.ec_pub_key(priv_key) # Perform SHA-256 hashing on the public key - public_key = Crypto.digest("SHA256", public_key, is_hex=true) + pub_key = Crypto.digest("SHA256", pub_key) # Perform RIPEMD-160 hashing on the result of SHA-256 - public_key = Crypto.digest("RIPEMD160", public_key, is_hex=true) + pub_key = Crypto.digest("RIPEMD160", pub_key) # Add version byte in front of RIPEMD-160 hash (0x00 for Main Network) # Reference: https://bitcoin.org/en/developer-reference#address-conversion - public_key = string(network_id, public_key) + pub_key = [bytearray(network_id), pub_key] # Get checksum by performing SHA256 hash twice and taking first 4 bytes - checksum = get_checksum(public_key, is_hex=true) + checksum = get_checksum(pub_key) # Add the 4 checksum bytes from stage 7 at the end of extended RIPEMD-160 # hash from stage 4. This is the 25-byte binary Bitcoin Address. - public_key = string(public_key, checksum) + append!(pub_key, checksum) # Convert the result from a byte string into a base58 string using # Base58Check encoding. This is the most commonly used Bitcoin Address format # Reference: https://en.bitcoin.it/wiki/Base58Check_encoding - public_key = parseint(BigInt, public_key, 16) - public_key = encode58(public_key) + # TODO: array to string to BigInt is really round-about + pub_key = parseint(BigInt, Crypto.hex_array_to_string(pub_key), 16) + pub_key = encode58(pub_key) # Append address version byte in hex # Reference: https://en.bitcoin.it/wiki/List_of_address_prefixes - public_key = string(version, public_key) + pub_key = string(version, pub_key) - return public_key + return pub_key end diff --git a/src/messages.jl b/src/messages.jl index b1c46d0..c057de8 100644 --- a/src/messages.jl +++ b/src/messages.jl @@ -1,5 +1,4 @@ import Base.convert -import Base.print ############################################################################## ## @@ -82,7 +81,7 @@ type Message checksum = uint32(parseint(get_checksum(payload, is_hex=true)[1:8], 16)) # Turn payload hex string into array of bytes - payload = hex_string_to_array(payload) + payload = Crypto.hex_string_to_array(payload) new(magic, command.data, payload) end @@ -93,7 +92,7 @@ type OutPoint index::Uint32 # Index of specific output in tx. 1st output is 0 function OutPoint(hash::String, index::Integer) - OutPoint(hex_string_to_array(hash), uint32(index)) + OutPoint(Crypto.hex_string_to_array(hash), uint32(index)) end function OutPoint(hash::Array{Uint8}, index::Integer) @@ -124,7 +123,7 @@ type Tx_Input sequence::Uint32 # Tx version as defined by the sender function Tx_Input(previous_output::OutPoint, scriptSig::String; sequence = 0xffffffff) - scriptSig = hex_string_to_array(scriptSig) + scriptSig = Crypto.hex_string_to_array(scriptSig) Tx_Input(previous_output, scriptSig, sequence = sequence) end @@ -150,7 +149,7 @@ type Tx_Output # value: transaction value in Satoshi # scriptPubKey: script as hex string function Tx_Output(value, scriptPubKey::String) - scriptPubKey = hex_string_to_array(scriptPubKey) + scriptPubKey = Crypto.hex_string_to_array(scriptPubKey) Tx_Output(value, scriptPubKey) end diff --git a/src/utils.jl b/src/utils.jl index b2075b1..c2861f8 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -52,33 +52,13 @@ function reverse_endian(hex_data::Integer) return result end -# TODO: String manipulation is really not the best way -function convert(::Type{Array{Uint8}}, x::Integer) - padding = 0 - if typeof(x) != BigInt - padding = sizeof(x) * 2 - end - hex_string = hex(x, padding) - return hex_string_to_array(hex_string) -end - function get_checksum(message::String; is_hex = false) create_digest(x) = Crypto.digest("SHA256", x, is_hex = is_hex) - return create_digest(create_digest(message))[1:8] + return Crypto.hex_array_to_string(create_digest(create_digest(message))[1:4]) end -function hex_string_to_array(hex_string::String) - hex_length = length(hex_string) - - # Left pad with 0 to make hex_string even length - if hex_length % 2 != 0 - hex_string = string("0", hex_string) - hex_length += 1 - end - - hex_length = div(hex_length, 2) - - return [uint8(parseint(hex_string[2i-1:2i], 16)) for i in 1:hex_length] +function get_checksum(message::Array{Uint8}) + return Crypto.digest("SHA256", Crypto.digest("SHA256", message))[1:4] end # Convenience function for converting to Array{Uint8} @@ -92,11 +72,11 @@ function string(array::Array{Uint8}) end print(x::Array{Uint8}) = print(string(x)) -println(x::Array{Uint8}) = print(x); print() +println(x::Array{Uint8}) = print(x); print("\n") function string(x::Unsigned) return string("0x", hex(x, div(sizeof(x), 2))) end print(x::Unsigned) = print(string(x)) -println(x::Unsigned) = print(x); print() \ No newline at end of file +println(x::Unsigned) = print(x); print("\n") \ No newline at end of file diff --git a/src/wif.jl b/src/wif.jl index e73f07a..55f4c6b 100644 --- a/src/wif.jl +++ b/src/wif.jl @@ -14,6 +14,7 @@ # Convert private key to WIF. # TODO: turn keys into objects to hold metadata +# TODO: Update to use byte arrays # - network_id: which network to use; 0x80 for mainnet, 0xef for testnet # - compression: 01 if private key corresponds to compressed public key function private2wif(private_key; network_id = "80", compression = "") @@ -22,7 +23,7 @@ function private2wif(private_key; network_id = "80", compression = "") hashed = Crypto.digest("SHA256", private_key, is_hex=true) hashed = Crypto.digest("SHA256", hashed, is_hex=true) - checksum = hashed[1:8] + checksum = Crypto.hex_array_to_string(hashed[1:4]) private_key = string(private_key, checksum) diff --git a/test/runtests.jl b/test/runtests.jl index d4acd85..010db5e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,7 +33,7 @@ base58data = parseint(BigInt, "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471 ############################################################################## secret_key = "18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725" -public_key = get_public_key(secret_key) +public_key = get_pub_key(secret_key) @test public_key == "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM" ############################################################################## @@ -104,12 +104,6 @@ tx = Tx([tx_in], [tx_out]) @test reverse_endian(23) == 1657324662872342528 @test reverse_endian(-23) == -1585267068834414593 -# hex_string_to_array: empty string -@test hex_string_to_array("") == [] -# hex_string_to_array: odd- and even-length strings -@test hex_string_to_array("adfcef981") == [0x0a, 0xdf, 0xce, 0xf9, 0x81] -@test hex_string_to_array("aadfcef981") == [0xaa, 0xdf, 0xce, 0xf9, 0x81] - # Test VarInt conversion @test to_varint(1) == [0x01] @test to_varint(252) == [0xfc]