cryptocoin 0.0.1b

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +85 -0
  7. data/Rakefile +2 -0
  8. data/cryptocoin.gemspec +24 -0
  9. data/lib/cryptocoin/core_ext/integer.rb +24 -0
  10. data/lib/cryptocoin/core_ext/string.rb +30 -0
  11. data/lib/cryptocoin/digest.rb +36 -0
  12. data/lib/cryptocoin/merkle_tree.rb +78 -0
  13. data/lib/cryptocoin/network/bitcoin.rb +9 -0
  14. data/lib/cryptocoin/network/dogecoin.rb +9 -0
  15. data/lib/cryptocoin/network/litecoin.rb +9 -0
  16. data/lib/cryptocoin/network.rb +53 -0
  17. data/lib/cryptocoin/protocol/block_header.rb +58 -0
  18. data/lib/cryptocoin/protocol/inventory_vector.rb +28 -0
  19. data/lib/cryptocoin/protocol/message/addr.rb +29 -0
  20. data/lib/cryptocoin/protocol/message/alert.rb +0 -0
  21. data/lib/cryptocoin/protocol/message/block.rb +13 -0
  22. data/lib/cryptocoin/protocol/message/getaddr.rb +17 -0
  23. data/lib/cryptocoin/protocol/message/getblocks.rb +44 -0
  24. data/lib/cryptocoin/protocol/message/getdata.rb +31 -0
  25. data/lib/cryptocoin/protocol/message/getheaders.rb +44 -0
  26. data/lib/cryptocoin/protocol/message/headers.rb +30 -0
  27. data/lib/cryptocoin/protocol/message/inv.rb +31 -0
  28. data/lib/cryptocoin/protocol/message/mempool.rb +16 -0
  29. data/lib/cryptocoin/protocol/message/notfound.rb +31 -0
  30. data/lib/cryptocoin/protocol/message/ping.rb +24 -0
  31. data/lib/cryptocoin/protocol/message/pong.rb +24 -0
  32. data/lib/cryptocoin/protocol/message/reject.rb +54 -0
  33. data/lib/cryptocoin/protocol/message/tx.rb +11 -0
  34. data/lib/cryptocoin/protocol/message/verack.rb +16 -0
  35. data/lib/cryptocoin/protocol/message/version.rb +59 -0
  36. data/lib/cryptocoin/protocol/message.rb +27 -0
  37. data/lib/cryptocoin/protocol/net_addr.rb +55 -0
  38. data/lib/cryptocoin/protocol/packet.rb +74 -0
  39. data/lib/cryptocoin/protocol/var_len_int.rb +85 -0
  40. data/lib/cryptocoin/protocol/var_len_str.rb +18 -0
  41. data/lib/cryptocoin/protocol.rb +8 -0
  42. data/lib/cryptocoin/script/op_code/constants.rb +157 -0
  43. data/lib/cryptocoin/script/op_code/functions.rb +515 -0
  44. data/lib/cryptocoin/script/op_code.rb +59 -0
  45. data/lib/cryptocoin/script.rb +234 -0
  46. data/lib/cryptocoin/structure/address.rb +64 -0
  47. data/lib/cryptocoin/structure/block.rb +109 -0
  48. data/lib/cryptocoin/structure/key_pair.rb +57 -0
  49. data/lib/cryptocoin/structure/merkle_branch.rb +37 -0
  50. data/lib/cryptocoin/structure/transaction/input.rb +80 -0
  51. data/lib/cryptocoin/structure/transaction/output.rb +49 -0
  52. data/lib/cryptocoin/structure/transaction.rb +94 -0
  53. data/lib/cryptocoin/version.rb +3 -0
  54. data/lib/cryptocoin.rb +29 -0
  55. data/spec/script_spec.rb +42 -0
  56. data/spec/spec_helper.rb +20 -0
  57. metadata +145 -0
@@ -0,0 +1,59 @@
1
+ require 'cryptocoin/script/op_code/constants'
2
+
3
+ module Cryptocoin
4
+ class Script
5
+ # Taken from the Bitcoin official client
6
+ class OpCode
7
+ include Cryptocoin::Script::OpCode::Constants
8
+ def self.from_name(name)
9
+ c = Cryptocoin::Script::OpCode::Constants.const_get(name.upcase)
10
+ self.new([c].pack('C'))
11
+ rescue NameError
12
+ false
13
+ end
14
+
15
+ def initialize(bin)
16
+ @bin = bin
17
+ @hex = bin.unpack('H*')[0].to_i(16)
18
+
19
+ if const_by_val(@hex) or @hex.between?(OP_PUSHDATA0, OP_PUSHDATA1)
20
+ @valid = true
21
+ else
22
+ @valid = false
23
+ end
24
+ end
25
+
26
+ def name
27
+ # Return special case first
28
+ return nil if !@valid
29
+ return 'OP_PUSHDATA0' if @hex.between?(OP_PUSHDATA0, OP_PUSHDATA1)
30
+ const_by_val(hex).to_s
31
+ end
32
+
33
+ def hex
34
+ @hex
35
+ end
36
+
37
+ def raw
38
+ @bin
39
+ end
40
+
41
+ def valid?
42
+ @valid
43
+ end
44
+
45
+ def disabled?
46
+ return nil if !@valid
47
+ [OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_INVERT, OP_AND, OP_OR, OP_XOR, OP_2MUL, OP_2DIV, OP_MUL, OP_DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT].include?(@hex)
48
+ end
49
+
50
+ private
51
+
52
+ def const_by_val(val)
53
+ Cryptocoin::Script::OpCode::Constants.constants.find{ |name|
54
+ Cryptocoin::Script::OpCode::Constants.const_get(name) == val
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,234 @@
1
+ require 'cryptocoin/script/op_code'
2
+ require 'cryptocoin/script/op_code/functions'
3
+
4
+ module Cryptocoin
5
+ # Script, or as I prefer to stylize it, SCRIPT, is Satoshi's
6
+ # non-Turing complete scripting language for Bitcoin transactions
7
+ # It's similar to Forth and Postscript in that it's a stack based
8
+ # language, while missing certain features such as loops to prevent
9
+ # malicious code from being created within a transaction.
10
+ #
11
+ # This is the parser for a SCRIPT directive
12
+ #
13
+ # My thinking behind the design of this parser is that, while it is
14
+ # tied to transactions usually, it can be used to parse general statements
15
+ # that a user inputs
16
+ class Script
17
+ include Cryptocoin::Script::OpCode::Constants
18
+ include Cryptocoin::Script::OpCode::Functions
19
+
20
+ attr_accessor :stack
21
+
22
+ # Create binary script from string
23
+ # Returns false if unable to create binary string
24
+ def self.from_s(str)
25
+ # split each value from str
26
+ # look up value in constants
27
+ # push data if pushdata instruction, noting that pushdata0 will need to read the binary size of the next string and then add the size plus the string
28
+ instructions = str.split(' ')
29
+ raw = ''
30
+ instructions.each_index do |i|
31
+ code = Cryptocoin::Script::OpCode.from_name(instructions[i].upcase)
32
+ return false if !code
33
+ case code.hex
34
+ when OP_PUSHDATA0
35
+ data = instructions.slice!(i+1)
36
+ data = [data].pack('H*')
37
+ raw += [data.bytesize].pack('C') + data
38
+ when OP_PUSHDATA1..OP_PUSHDATA4
39
+ # Crude implementation because it ignores what data says about the length
40
+ bytes = instructions.slice!(i+1)
41
+ data = instructions.slice!(i+1)
42
+ raw += code.raw + [bytes].pack('H*') + [data].pack('H*')
43
+ else
44
+ raw += code.raw
45
+ end
46
+ end
47
+ self.new(raw)
48
+ end
49
+
50
+ # Validates that the script is okay within the ruleset of the
51
+ # Bitcoin protocol
52
+ def initialize(raw)
53
+ return false if raw.bytesize > 10000
54
+ @script = raw
55
+ @raw = raw
56
+ @transaction_valid = true
57
+ @valid = false
58
+ @subscript = false
59
+ @codeseparator = 0
60
+ @script_string = []
61
+ @stack = []
62
+ end
63
+
64
+ # Sets the script as subscript and parses it to remove
65
+ # OP_CODESEPARATOR and the signature
66
+ def is_subscript!(signature)
67
+ @sub_sig = signature
68
+ @subscript = true
69
+ end
70
+
71
+ def is_subscript?
72
+ @subscript
73
+ end
74
+
75
+ # Sets the transaction so that it can be used if
76
+ # OP_CHECKSIG is called within directive
77
+ # +tx+ is the current transaction and +input+ is the
78
+ # current input index
79
+ def set_transaction(tx, input)
80
+ @transaction = tx
81
+ @input_index = input
82
+ end
83
+
84
+ def raw
85
+ if is_subscript?
86
+ # Remove OP_CODESEPARTOR
87
+ parse(true)
88
+ # Remove signature
89
+ @raw = @raw.split(@sub_sig).join
90
+ end
91
+ @raw
92
+ end
93
+
94
+ def to_s
95
+ @script_string.join(' ')
96
+ end
97
+
98
+ # Parses directive according to current Bitcoin client rules
99
+ # Returns true if a valid script, returns valid is invalid
100
+ # The functions are also aware if it's subscript and don't
101
+ # parse as much (i.e. we don't actually check the validity)
102
+ # if so.
103
+ def parse!(is_subscript=false)
104
+ opcode_count = 0
105
+ @alt_stack, @exec_stack = [], [] # These stacks are not carried over
106
+ seek = 0
107
+ @raw = ""
108
+ while seek < @script.length
109
+ opcode_count+=1
110
+ _i = @script[seek]
111
+ seek += 1
112
+ opcode = Cryptocoin::Script::OpCode.new(_i)
113
+ return false if !opcode.valid?
114
+ return false if opcode_count > 201 and opcode.hex > OP_16
115
+ return false if opcode.disabled?
116
+
117
+ if is_subscript
118
+ if !(opcode.hex == OP_CODESEPARATOR)
119
+ @raw += _i
120
+ @script_string.push(opcode.name)
121
+ end
122
+ else
123
+ @raw += _i
124
+ @script_string.push(opcode.name)
125
+ end
126
+
127
+ if opcode.hex.between?(OP_PUSHDATA0, OP_PUSHDATA4)
128
+ @stack, @script_string, @raw, seek = op_pushdata(@stack, opcode, @script, @script_string, @raw, seek)
129
+ end
130
+
131
+ if !is_subscript?
132
+ case opcode.hex
133
+ when OP_1NEGATE, OP_1, OP_2..OP_16
134
+ @stack = op_numeric(@stack, opcode)
135
+ when OP_NOP, OP_NOP1..OP_NOP10
136
+ op_nop
137
+ when OP_IF, OP_NOTIF
138
+ @stack, @exec_stack = op_if(@stack, @exec_stack, opcode)
139
+ when OP_ELSE
140
+ @exec_stack = op_else(@exec_stack)
141
+ when OP_ENDIF
142
+ exec_stack = op_endif(exec_stack)
143
+ when OP_RETURN
144
+ @transaction_valid = op_return
145
+ when OP_CODESEPARATOR
146
+ @codeseparator = op_codeseparator(seek)
147
+ when OP_VERIFY
148
+ @stack = op_verify(@stack)
149
+ when OP_TOALTSTACK
150
+ @stack, alt_stack = op_toaltstack(@stack, alt_stack)
151
+ when OP_FROMALTSTACK
152
+ @stack, alt_stack = op_fromaltstack(@stack, alt_stack)
153
+ when OP_2DROP
154
+ @stack = op_2drop(@stack)
155
+ when OP_2DUP
156
+ @stack = op_2dup(@stack)
157
+ when OP_3DUP
158
+ @stack = op_3dup(@stack)
159
+ when OP_2OVER
160
+ @stack = op_2over(@stack)
161
+ when OP_2ROT
162
+ @stack = op_2rot(@stack)
163
+ when OP_2SWAP
164
+ @stack = op_2swap(@stack)
165
+ when OP_IFDUP
166
+ @stack = op_ifdup(@stack)
167
+ when OP_DEPTH
168
+ @stack = op_depth(@stack)
169
+ when OP_DROP
170
+ @stack = op_drop(@stack)
171
+ when OP_DUP
172
+ @stack = op_dup(@stack)
173
+ when OP_NIP
174
+ @stack = op_nip(@stack)
175
+ when OP_OVER
176
+ @stack = op_over(@stack)
177
+ when OP_PICK, OP_ROLL
178
+ @stack = op_pick_or_roll(@stack, opcode)
179
+ when OP_ROT
180
+ @stack = op_rot(@stack)
181
+ when OP_SWAP
182
+ @stack = op_swap(@stack)
183
+ when OP_TUCK
184
+ @stack = op_tuck(@stack)
185
+ when OP_SIZE
186
+ @stack = op_size(@stack)
187
+ when OP_RETURN
188
+ @transaction_valid = op_return
189
+ when OP_EQUAL
190
+ @stack = op_equal(@stack)
191
+ when OP_1ADD, OP_1SUB, OP_NEGATE, OP_ABS, OP_NOT, OP_0NOTEQUAL
192
+ @stack = single_stack_arithmetic(@stack)
193
+ when OP_ADD, OP_SUB, OP_BOOLAND, OP_BOOLOR, OP_NUMEQUAL, OP_NUMEQUALVERIFY, OP_NUMNOTEQUAL, OP_LESSTHAN, OP_GREATERTHAN, OP_LESSTHANOREQUAL, OP_GREATERTHANOREQUAL, OP_MIN, OP_MAX
194
+ @stack = double_stack_arithmetic(@stack)
195
+ when OP_WITHIN
196
+ @stack = op_within(@stack)
197
+ when OP_RIPEMD160, OP_SHA256, OP_SHA1, OP_HASH160, OP_HASH256
198
+ @stack = digest(@stack)
199
+ when OP_CHECKSIG
200
+ raise ArgumentError, "Transaction and input are not set" if !@transaction
201
+ op_checksig(@stack, @script, @code_separator, @transaction)
202
+ when OP_CHECKSIGVERIFY
203
+ raise ArgumentError, "Transaction and input are not set" if !@transaction
204
+ @stack = op_checksig(@stack, @script, @code_separator, @transaction)
205
+ @stack = op_verify(@stack)
206
+ when OP_CHECKMULTISIG
207
+ raise ArgumentError, "Transaction and input are not set" if !@transaction
208
+ @stack = check_multisig(@stack, @script, code_separator, @transaction, opcode_count)
209
+ when OP_CHECKMULTISIGVERIFY
210
+ raise ArgumentError, "Transaction and input are not set" if !@transaction
211
+ @stack = check_multisig(@stack, @script, code_separator, @transaction, opcode_count)
212
+ @stack = op_verify(@stack)
213
+ end
214
+ end
215
+ end
216
+
217
+ # if top of stack is truthy, true
218
+ # else, false
219
+ if @stack.last != 0 && !@stack.empty?
220
+ @valid = true
221
+ else
222
+ @valid = false
223
+ end
224
+ end
225
+
226
+ def valid?
227
+ @valid
228
+ end
229
+
230
+ def transaction_valid?
231
+ @transaction_valid
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,64 @@
1
+ require 'cryptocoin/core_ext/string'
2
+
3
+ module Cryptocoin
4
+ module Structure
5
+ # An address is just a specific way to represent a series of bytes within the network
6
+ class Address
7
+ attr_reader :network
8
+ # Creates a new Address structure from a given hash and network
9
+ # Will return false if the address is just 1 as this the representation of 0
10
+ def self.from_s(address_hash, network)
11
+ s = address_hash.from_base58.to_s(16)
12
+ '0'+s = s if s.bytesize.odd?
13
+ return false if s == '00'
14
+
15
+ i = (address_hash.match(/^([1]+)/) ? $1 : '').size
16
+ self.new(['00'*i + s].pack('H*'), network)
17
+ end
18
+
19
+ def initialize(address, network)
20
+ @address_raw = address
21
+ @network = network
22
+ end
23
+
24
+ # Returns identifier for address type
25
+ # Current address types are p2sh and hash160
26
+ def type
27
+ return :p2sh if digest[0..1] == network.p2sh_version
28
+ return :hash160 if digest[0..1] == network.hash160_version
29
+ return :private_key if digest[0..1] == network.private_key_version
30
+ :unknown
31
+ end
32
+
33
+ def to_s
34
+ digest.encode_to_base58
35
+ end
36
+
37
+ def checksum
38
+ digest[-8..-1]
39
+ end
40
+
41
+ def digest
42
+ @address_raw.unpack('H*')[0]
43
+ end
44
+
45
+ def valid?
46
+ valid_checksum? and valid_network_version?
47
+ end
48
+
49
+ private
50
+
51
+ def valid_checksum?
52
+ if type == :private_key
53
+ Cryptocoin::Digest.new([digest[0..-9]].pack('H*'), :binary).double_sha256[0..7] == checksum
54
+ else
55
+ Cryptocoin::Digest.new([digest[0..-9]].pack('H*'), :binary).double_sha256[0..7] == checksum
56
+ end
57
+ end
58
+
59
+ def valid_network_version?
60
+ type != :unknown
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,109 @@
1
+ require 'cryptocoin/structure/transaction'
2
+ require 'cryptocoin/protocol/var_len_int'
3
+ require 'stringio'
4
+
5
+ module Cryptocoin
6
+ module Structure
7
+ class Block
8
+ attr_reader :transactions_count, :transactions
9
+ def self.parse_from_raw(raw)
10
+ io = StringIO.new(raw)
11
+ self.parse_from_io(io)
12
+ end
13
+
14
+ def self.parse_from_io(io, is_aux_pow=false)
15
+ # Save file for future use
16
+ # File.open('block.dat', 'w') do |f|
17
+ # f.puts(io.read)
18
+ # io.seek(0)
19
+ # end
20
+ txs = []
21
+ version_raw = io.read(4)
22
+ prev_block_raw = io.read(32)
23
+ merkle_root_raw = io.read(32)
24
+ timestamp_raw = io.read(4)
25
+ bits_raw = io.read(4)
26
+ nonce_raw = io.read(4)
27
+
28
+ if is_aux_pow
29
+ # TODO: actually save this information
30
+ aux_coinbase_tx = Cryptocoin::Structure::Transaction.parse_from_io(io)
31
+ aux_block_hash = io.read(32)
32
+ aux_coinbase_branch = Cryptocoin::Structure::MerkleBranch.parse_from_io(io)
33
+ aux_blockchain_branch = Cryptocoin::Structure::MerkleBranch.parse_from_io(io)
34
+ aux_parent_block = io.read(80) #This is a block header, parse this later
35
+ end
36
+
37
+ tx_count = Cryptocoin::Protocol::VarLenInt.parse_from_io(io)
38
+ p "Transaction count: #{tx_count}, #{tx_count.to_i}"
39
+ tx_count.times do
40
+ txs.push(Cryptocoin::Structure::Transaction.parse_from_io(io))
41
+ end
42
+ self.new(version_raw, prev_block_raw, merkle_root_raw, timestamp_raw, bits_raw, nonce_raw, tx_count, txs)
43
+ end
44
+
45
+ # txs is an array to keep consistent with the transaction model
46
+ def initialize(version_raw, prev_block_raw, merkle_root_raw, timestamp_raw, bits_raw, nonce_raw, tx_count, txs)
47
+ @version_raw = version_raw
48
+ @prev_block_raw = prev_block_raw
49
+ @merkle_root_raw = merkle_root_raw
50
+ @timestamp_raw = timestamp_raw
51
+ @bits_raw = bits_raw
52
+ @nonce_raw = nonce_raw
53
+ @transactions_count = tx_count
54
+ @transactions = txs
55
+ end
56
+
57
+ def head
58
+ @version_raw + @prev_block_raw + @merkle_root_raw + @timestamp_raw + @bits_raw + @nonce_raw
59
+ end
60
+
61
+ def body
62
+ @transactions_count.raw + @transactions.reduce { |tx, memo| memo += tx }
63
+ end
64
+
65
+ def version
66
+ @version ||= @version_raw.unpack('L')[0]
67
+ end
68
+
69
+ def previous_block_digest
70
+ @previous_block_digest ||= @prev_block_raw.reverse.unpack('H*')[0]
71
+ end
72
+
73
+ def merkle_root_digest
74
+ @merkle_root_digest ||= @merkle_root_raw.reverse.unpack('H*')[0]
75
+ end
76
+
77
+ def timestamp
78
+ @timestamp ||= @timestamp_raw.unpack('L')[0]
79
+ end
80
+
81
+ def bits
82
+ @bits ||= @bits_raw.unpack('L')[0]
83
+ end
84
+
85
+ def target
86
+ require 'openssl'
87
+ @bytes if @bytes
88
+
89
+ puts @bits_raw.unpack('H*')[0]
90
+
91
+ bytes = OpenSSL::BN.new(@bits_raw.unpack('H*')[0].reverse_every(2), 16).to_s(0).unpack('C*')
92
+ size = bytes.size - 4
93
+ nbits = size << 24
94
+ nbits |= (bytes[4] << 16) if size >= 1
95
+ nbits |= (bytes[5] << 8) if size >= 2
96
+ nbits |= (bytes[6] ) if size >= 3
97
+ @bytes = nbits
98
+ end
99
+
100
+ def nonce
101
+ @nonce ||= @nonce_raw.unpack('L')[0]
102
+ end
103
+
104
+ def digest
105
+ @digest ||= Cryptocoin::Digest.new(self.head, :binary).double_sha256.reverse_every(2)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,57 @@
1
+ require 'cryptocoin/core_ext/string'
2
+ require 'cryptocoin/core_ext/integer'
3
+ require 'cryptocoin/structure/address'
4
+
5
+ module Cryptocoin
6
+ module Structure
7
+ class KeyPair
8
+ attr_reader :network, :public_key, :private_key
9
+
10
+ def self.generate(network)
11
+ key = OpenSSL::PKey::EC.new("secp256k1").generate_key
12
+ new(key, network)
13
+ end
14
+
15
+ def initialize(key_pair, network)
16
+ @network = network
17
+
18
+ @public_key = PublicKey.new(key_pair.public_key, network)
19
+ @private_key = PrivateKey.new(key_pair.private_key, network)
20
+ end
21
+
22
+ class PublicKey
23
+ def initialize(public_key, network)
24
+ @network = network
25
+ @public_key = public_key
26
+ end
27
+
28
+ def to_s
29
+ @public_key.to_bn.to_i.to_s(16)
30
+ end
31
+
32
+ def to_address
33
+ kh = @network.hash160_version + Cryptocoin::Digest.new(to_s, :string).hash160
34
+ checksum = Cryptocoin::Digest.new([kh].pack('H*'), :binary).double_sha256[0..7]
35
+ Cryptocoin::Structure::Address.from_s((kh + checksum).encode_to_base58, @network)
36
+ end
37
+ end
38
+
39
+ class PrivateKey
40
+ def initialize(private_key, network)
41
+ @network = network
42
+ @private_key = private_key
43
+ end
44
+
45
+ def to_s
46
+ @private_key.to_bn.to_i.to_s(16)
47
+ end
48
+
49
+ def to_address
50
+ kh = @network.private_key_version + Cryptocoin::Digest.new(to_s, :string).double_sha256
51
+ checksum = Cryptocoin::Digest.new([kh].pack('H*'), :binary).double_sha256[0..7]
52
+ Cryptocoin::Structure::Address.from_s((kh + checksum).to_i(16).to_base58, @network)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,37 @@
1
+ require 'cryptocoin/protocol/var_len_int'
2
+
3
+ module Cryptocoin
4
+ module Structure
5
+ class MerkleBranch
6
+ def self.parse_from_io(io)
7
+ branch_length = Cryptocoin::Protocol::VarLenInt.parse_from_io(io)
8
+
9
+ branch_hashes = branch_length.times.map { |i|
10
+ io.read(32)
11
+ }
12
+
13
+ branch_side_mark = io.read(4)
14
+
15
+ self.new(branch_length, branch_hashes, branch_side_mark)
16
+ end
17
+
18
+ def initialize(branch_length, branch_hashes, branch_side_mark)
19
+ @branch_length_raw = branch_length
20
+ @branch_hashes_raw = branch_hashes
21
+ @branch_side_mark_raw = branch_side_mark
22
+ end
23
+
24
+ def branch_length
25
+ @branch_length ||= @branch_length_raw.to_i
26
+ end
27
+
28
+ def branch_hashes
29
+ @branch_hashes ||= @branch_hashes_raw.map { |branch_hash| branch_hash.reverse.unpack('H*')[0] }
30
+ end
31
+
32
+ def branch_side_mark
33
+ @branch_side_mark ||= @branch_side_mark_raw.unpack('l')[0]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,80 @@
1
+ require 'cryptocoin/protocol/var_len_int'
2
+
3
+ module Cryptocoin
4
+ module Structure
5
+ class Transaction
6
+ class Input
7
+ attr_reader :index, :previous_output_hash, :previous_output_index, :script_sig_length, :script_sig, :sequence
8
+
9
+ # Parses a raw binary transaction input
10
+ # Takes a raw transaction input and outputs a
11
+ # Crytocoin::Structure::Transaction::Input object
12
+ def self.parse_from_raw(i, raw)
13
+ c = 0
14
+ previous_output_hash_raw = raw[c..32]
15
+ previous_output_index_raw = raw[c+32..36]
16
+ sequence = raw[-5..-1]
17
+ c += 36
18
+ script_sig_length = Cryptocoin::Protocol::VarLenInt.parse_from_raw(raw[c..-1])
19
+ script_sig = raw[c+script_sig_length..-5]
20
+ self.new(previous_output_hash_raw, previous_output_index_raw, script_sig_length, script_sig, sequence, i)
21
+ end
22
+
23
+ # Parses the top transaction input from io
24
+ # Returns a Cryptocoin::Structure::Transaction::Input object
25
+ def self.parse_from_io(i, io)
26
+ previous_output_hash_raw = io.read(32)
27
+ previous_output_index_raw = io.read(4)
28
+ script_sig_length = Cryptocoin::Protocol::VarLenInt.parse_from_io(io)
29
+ script_sig = io.read(script_sig_length.to_i)
30
+ sequence_raw = io.read(4)
31
+ self.new(previous_output_hash_raw, previous_output_index_raw, script_sig_length, script_sig, sequence_raw, i)
32
+ end
33
+
34
+ # Creates a new transaction input from supplied arguments, then freezes itself
35
+ # Types: (str) previous_output_hash, (str) previous_output_index
36
+ # (Cryptocoin::Protocol::VarLenInt) sig_script_length, (str) script_sig
37
+ # (str) sequence, (int) index
38
+ def initialize(previous_output_hash, previous_output_index, script_sig_length, script_sig_raw, sequence, index)
39
+ @previous_output_hash_raw = previous_output_hash
40
+ @previous_output_index_raw = previous_output_index
41
+ @script_sig_length = script_sig_length
42
+ @script_sig_raw = script_sig_raw
43
+ @sequence_raw = sequence
44
+ @index = index
45
+ end
46
+
47
+ def previous_output_hash
48
+ @previous_output_hash_raw.reverse.unpack('H*')[0]
49
+ end
50
+
51
+ def previous_output_index
52
+ @previous_output_index_raw.unpack('V')[0]
53
+ end
54
+
55
+ def script_sig
56
+ @script_sig ||= Cryptocoin::Script.new(@script_sig_raw)
57
+ end
58
+
59
+ def raw
60
+ @previous_output_hash_raw + @previous_output_index_raw + @script_sig_length.raw + @script_sig + @sequence
61
+ end
62
+
63
+ def size
64
+ raw.bytesize
65
+ end
66
+
67
+ # Provides a copy of the transaction in which you can set the raw binary values
68
+ def copy
69
+ r = self
70
+ ['previous_output_hash_raw', 'previous_output_index_raw', 'script_sig_length', 'script_sig', 'sequence_raw'].each do |i|
71
+ r.class.send(:define_method, "#{i}=") do |j|
72
+ instance_variable_set("@#{i}", j)
73
+ end
74
+ end
75
+ r
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,49 @@
1
+ require 'cryptocoin/script'
2
+ require 'cryptocoin/protocol/var_len_int'
3
+
4
+ module Cryptocoin
5
+ module Structure
6
+ class Transaction
7
+ class Output
8
+ attr_reader :index, :value, :pk_script_length, :pk_script
9
+
10
+ def self.parse_from_io(i, io)
11
+ value_raw = io.read(8)
12
+ puts "CURRENT IO POS: #{io.pos}"
13
+ pk_script_length = Cryptocoin::Protocol::VarLenInt.parse_from_io(io)
14
+ pk_script_raw = io.read(pk_script_length.to_i)
15
+ self.new(value_raw, pk_script_length, pk_script_raw, i)
16
+ end
17
+
18
+ def initialize(value_raw, pk_scipt_length, pk_script_raw, i)
19
+ @value_raw = value_raw
20
+ @pk_script_length = pk_script_length
21
+ @pk_script_raw = pk_script_raw
22
+ @index = i
23
+ end
24
+
25
+ def value
26
+ @value_raw.unpack('Q')[0]
27
+ end
28
+
29
+ def pk_script
30
+ @pk_script ||= Cryptocoin::Script.new(@pk_script_raw)
31
+ end
32
+
33
+ def raw
34
+ @value_raw + @pk_script_length.raw + @pk_script
35
+ end
36
+
37
+ def copy
38
+ r = self
39
+ ['value_raw', 'pk_script_length', 'pk_script_raw', 'index'].each do |i|
40
+ r.class.send(:define_method, "#{i}=") do |j|
41
+ instance_variable_set("@#{i}", j)
42
+ end
43
+ end
44
+ r
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end