Skip to content

Commit

Permalink
clean up transaction files
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsuo committed Dec 18, 2014
1 parent 5550d96 commit c14fe6a
Show file tree
Hide file tree
Showing 11 changed files with 304 additions and 37 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ A (self-educational, incomplete, and likely incorrect) library for working with

# To Do
- https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses
# TODO: multiple inputs
# TODO: convenience functions
# TODO: clean up and documentation

## Wallet
First, we're going to implement a thin-client wallet.
Expand Down
2 changes: 2 additions & 0 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
Coverage
HTTPClient
JSON
8 changes: 6 additions & 2 deletions src/Coin.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ export
generate_keys,
get_pub_key,

# wif.jl
# addresses.jl
private2wif,
wif2private,
wif_check_sum,
pub2base58,

# base58.jl
encode58,
decode58,
decode58_to_array,

# messages.jl,
create_header,
Expand Down Expand Up @@ -50,9 +52,11 @@ using Crypto
include("utils.jl")
include("base58.jl")
include("keys.jl")
include("wif.jl")
include("addresses.jl")
include("messages.jl")
include("signatures.jl")
include("op.jl")
include("tx.jl")

Crypto.init()

Expand Down
42 changes: 40 additions & 2 deletions src/wif.jl → src/addresses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ function private2wif(private_key; network_id = "80", compression = "")
private_key = string(network_id, private_key, compression)

hashed = Crypto.digest("SHA256", private_key, is_hex=true)
hashed = Crypto.digest("SHA256", hashed, is_hex=true)
hashed = Crypto.digest("SHA256", hashed)

checksum = Crypto.hex_array_to_string(hashed[1:4])
checksum = Crypto.oct2hex(hashed[1:4])

private_key = string(private_key, checksum)

Expand All @@ -48,3 +48,41 @@ function wif_check_sum(wif)
result = hex(decode58(wif))
return get_checksum(result[1:end-8], is_hex=true) == result[end-8+1:end]
end

function pub2base58(pub_key::String; network_id = "00")
pub_key_length = div(length(pub_key), 2)

# If public key is elliptic curve coordinate, hash with SHA-256
if pub_key_length == 65
pub_key = Crypto.digest("SHA256", pub_key, is_hex = true)
pub_key = Crypto.oct2hex(pub_key)
pub_key_length = div(length(pub_key), 2)
end

# If public key has been SHA-256 hashed, hash with RIPEMD-160
if pub_key_length == 32
pub_key = Crypto.digest("RIPEMD160", pub_key, is_hex = true)
pub_key = Crypto.oct2hex(pub_key)
pub_key_length = div(length(pub_key), 2)
end

# If public key has been RIPEMD-160 hashed, add network id
if pub_key_length == 20
pub_key = string(network_id, pub_key)
pub_key_length = div(length(pub_key), 2)
end

# If public key has network id added, add checksum
if pub_key_length == 21
checksum = get_checksum(pub_key, is_hex = true)
pub_key = string(pub_key, checksum)
pub_key_length = div(length(pub_key), 2)
end

# If public key has checksum added
if pub_key_length == 25
pub_key = encode58(Crypto.hex2oct(pub_key))
end

return pub_key
end
37 changes: 30 additions & 7 deletions src/base58.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,24 @@ const base58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
##############################################################################

# We really only need these for base 58 because Julia's default base 58 math
# uses a different alphabet. Still asking users to supply a base to have nice,
# but descriptive function names.
# uses a different alphabet.
function encode58(n::Array{Uint8})
result = encode58(Crypto.oct2int(n))

# TODO: this isn't correct in the case of general # of 0 bytes
for byte in n
if byte == 0x00
result = string(base58[1], result) # Add the zero element
end
end

return result
end

# - n: integer we want to convert
# - b: base we want to convert to
function encode58(n; b = 58)
function encode58(n::Integer)
# Require base to be 58 for now
@assert b == 58
b = 58

output = ""

Expand All @@ -43,9 +54,9 @@ end

# Decode from base 58 to integer
# - n: base 58 number we want to convert
function decode58(n; b = 58)
function decode58(n::String)
# Require base to be 58 for now
@assert b == 58
b = 58

result = BigInt(0)

Expand All @@ -55,3 +66,15 @@ function decode58(n; b = 58)

return result
end

function decode58_to_array(n::String)
result = Crypto.hex2oct(hex(decode58(n)))

# TODO: this isn't correct in the case of general # of 0s
# Capture 2 leading 0s
if n[1:2] == repeat(base58[1], 2) # Get the zero element
result = [0x00, result]
end

return result
end
13 changes: 9 additions & 4 deletions src/keys.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@
##
##############################################################################

type Keys
priv_key::Array{Uint8}
pub_key::Array{Uint8}
end

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 (Crypto.hex_array_to_string(priv_key), pub_key)
return (Crypto.oct2hex(priv_key), pub_key)
end

function get_pub_key(priv_key::String; network_id = 0x00, version = "1")
get_pub_key(Crypto.hex_string_to_array(priv_key),
get_pub_key(Crypto.hex2oct(priv_key),
network_id = network_id,
version = version)
end
Expand All @@ -40,7 +45,7 @@ function get_pub_key(priv_key::Array{Uint8}; network_id = 0x00, version = "1")

# Add version byte in front of RIPEMD-160 hash (0x00 for Main Network)
# Reference: https://bitcoin.org/en/developer-reference#address-conversion
pub_key = [bytearray(network_id), pub_key]
pub_key = [Crypto.int2oct(network_id), pub_key]

# Get checksum by performing SHA256 hash twice and taking first 4 bytes
checksum = get_checksum(pub_key)
Expand All @@ -53,7 +58,7 @@ function get_pub_key(priv_key::Array{Uint8}; network_id = 0x00, version = "1")
# Base58Check encoding. This is the most commonly used Bitcoin Address format
# Reference: https://en.bitcoin.it/wiki/Base58Check_encoding
# TODO: array to string to BigInt is really round-about
pub_key = parseint(BigInt, Crypto.hex_array_to_string(pub_key), 16)
pub_key = parseint(BigInt, Crypto.oct2hex(pub_key), 16)
pub_key = encode58(pub_key)

# Append address version byte in hex
Expand Down
30 changes: 15 additions & 15 deletions src/messages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ import Base.convert
# merkleblock
# alert

# Define the known magic values
const magic_mainnet = 0xd9b4bef9
const magic_testnet = 0xdab5bffa
const magic_testnet3 = 0x0709110b
const magic_namecoin = 0xfeb4bef9

type Message
magic::Uint32 # Network identifier
command::Array{Uint8} # Message command, right padded with \0 to 12 bytes
Expand All @@ -81,7 +87,7 @@ type Message
checksum = uint32(parseint(get_checksum(payload, is_hex=true)[1:8], 16))

# Turn payload hex string into array of bytes
payload = Crypto.hex_string_to_array(payload)
payload = Crypto.hex2oct(payload)

new(magic, command.data, payload)
end
Expand All @@ -92,7 +98,7 @@ type OutPoint
index::Uint32 # Index of specific output in tx. 1st output is 0

function OutPoint(hash::String, index::Integer)
OutPoint(Crypto.hex_string_to_array(hash), uint32(index))
OutPoint(Crypto.hex2oct(hash), uint32(index))
end

function OutPoint(hash::Array{Uint8}, index::Integer)
Expand All @@ -112,7 +118,7 @@ function convert(::Type{Array{Uint8}}, outpoint::OutPoint)
result = Array(Uint8, 0)

append!(result, reverse(outpoint.hash))
append!(result, reverse(bytearray(outpoint.index)))
append!(result, reverse(Crypto.int2oct(outpoint.index)))

return result
end
Expand All @@ -123,7 +129,7 @@ type Tx_Input
sequence::Uint32 # Tx version as defined by the sender

function Tx_Input(previous_output::OutPoint, scriptSig::String; sequence = 0xffffffff)
scriptSig = Crypto.hex_string_to_array(scriptSig)
scriptSig = Crypto.hex2oct(scriptSig)
Tx_Input(previous_output, scriptSig, sequence = sequence)
end

Expand All @@ -138,7 +144,7 @@ function convert(::Type{Array{Uint8}}, tx_in::Tx_Input)
append!(result, bytearray(tx_in.previous_output))
append!(result, reverse(to_varint(length(tx_in.scriptSig))))
append!(result, tx_in.scriptSig)
append!(result, reverse(bytearray(tx_in.sequence)))
append!(result, reverse(Crypto.int2oct(tx_in.sequence)))
end

type Tx_Output
Expand All @@ -149,7 +155,7 @@ type Tx_Output
# value: transaction value in Satoshi
# scriptPubKey: script as hex string
function Tx_Output(value, scriptPubKey::String)
scriptPubKey = Crypto.hex_string_to_array(scriptPubKey)
scriptPubKey = Crypto.hex2oct(scriptPubKey)
Tx_Output(value, scriptPubKey)
end

Expand All @@ -166,7 +172,7 @@ function convert(::Type{Array{Uint8}}, tx_out::Tx_Output)

# TODO: This is a really terrible way to get little
# endian byte array
append!(result, reverse(bytearray(tx_out.value)))
append!(result, reverse(Crypto.int2oct(tx_out.value)))

append!(result, reverse(to_varint(length(tx_out.scriptPubKey))))

Expand All @@ -190,7 +196,7 @@ function convert(::Type{Array{Uint8}}, tx::Tx)
result = Array(Uint8, 0)

# Add version
append!(result, reverse(bytearray(tx.version)))
append!(result, reverse(Crypto.int2oct(tx.version)))

# Add number of inputs
append!(result, reverse(to_varint(length(tx.inputs))))
Expand All @@ -209,17 +215,11 @@ function convert(::Type{Array{Uint8}}, tx::Tx)
end

# Add lock_time
append!(result, reverse(bytearray(tx.lock_time)))
append!(result, reverse(Crypto.int2oct(tx.lock_time)))

return result
end

# Define the known magic values
const magic_mainnet = 0xd9b4bef9
const magic_testnet = 0xdab5bffa
const magic_testnet3 = 0x0709110b
const magic_namecoin = 0xfeb4bef9

# const commands = ["version", "verack", "addr", "inv", "getdata", "notfound",
# "getblocks", "getheaders", "tx", "block", "headers",
# "getaddr", "mempool", "checkorder", "submitorder", "reply",
Expand Down
4 changes: 4 additions & 0 deletions src/op.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const OP_DUP = 0x76
const OP_HASH160 = 0xa9
const OP_EQUALVERIFY = 0x88
const OP_CHECKSIG = 0xac
Loading

0 comments on commit c14fe6a

Please sign in to comment.