class RawTx
This class is mostly used to decode json transactions from raw transactions much of this implementation was inspired from: gist.github.com/shesek/5835695
Constants
- BASE64_CHARS
This is a map of offset to characters used for decoding base64 strings:
- MAX_32BIT_INTEGER
Public Class Methods
Convert an array of bytes to a base64 string.
# File lib/counterparty/raw_tx.rb, line 73 def self.bytes_to_base64_s(input_bytes) input_bytes.each_slice(3).collect.with_index{ |bytes,i| # This is the base offset of this 3-byte slice in the sequence: i_triplet = i*3 # Here we compose a triplet by or'ing the shifted bytes: triplet = bytes.collect.with_index{|b,j| b << 8*(2-j) }.reduce(:|) # And here we convert to chars, unless with equals as nil-padding: 0.upto(3).collect do |j| (i_triplet * 8 + j * 6 <= input_bytes.length * 8) ? BASE64_CHARS[((triplet >> 6 * (3 - j)) & 0x3F)] : '=' end }.join end
Convert an array of 8 bit numbers into an unsigned int, Remember that for each input byte, the most significant nibble comes last.
# File lib/counterparty/raw_tx.rb, line 67 def self.bytes_to_ui(bytes) nibbles = bytes.collect{|b| [b & 0x0f, (b & 0xf0) >> 4]}.flatten nibbles.each_with_index.inject(0){|sum,(b,i)| sum += b * 16**i} end
Creates a raw transaction from a hexstring
# File lib/counterparty/raw_tx.rb, line 11 def initialize(as_hexstring) @hexstring = as_hexstring.downcase raise ArgumentError, "Unsupported hex" unless /\A[0-9a-f]+\Z/.match @hexstring end
Convert an array of 4 bit numbers into an unsigned int, works for numbers up to 32-bit only
# File lib/counterparty/raw_tx.rb, line 58 def self.nibbles_to_ui(nibbles) raise ArgumentError if nibbles.length > 4 nibbles.each_with_index.inject(0){ |sum, (b,i)| sum += (b.to_i & 0xff) << (4 * i) } end
Public Instance Methods
Returns this transaction in a standard json format
# File lib/counterparty/raw_tx.rb, line 17 def to_hash(options = {}) bytes = @hexstring.scan(/../).collect(&:hex) size = bytes.length # Now we start shift elements off the byte stack: version = shift_u32(bytes) raise ArgumentError, "Unsupported Version/Tx" unless version == 0x01 # Parse the inputs: ins = (0...shift_varint(bytes)).collect do hash = bytes.slice!(0,32).reverse.collect{|n| '%02x' % n}.join index,script,seq = shift_u32(bytes),shift_varchar(bytes),shift_u32(bytes) # NOTE: We may want to base64 encode the hash, or support this via an # option : self.class.bytes_to_base64_s(hash).reverse), # NOTE: The :seq field isnt actually used right now, so some rawtx decoders # return the varint (like decoder), and some return UINT_MAX (4294967295) { 'txid' => hash, 'vout' => index, 'sequence' => MAX_32BIT_INTEGER, 'scriptSig' => {'hex' => hex_stringify(script), 'asm' => disassemble_script(script) } } end # Parse outputs: outs = (0...shift_varint(bytes)).collect do |i| value, script = shift_u64(bytes), shift_varchar(bytes) { 'value' => self.class.bytes_to_ui(value).to_f/1e8, 'n' => i, 'scriptPubKey' => {'hex' => hex_stringify(script), 'asm' => disassemble_script(script) } } end lock_time = shift_u32 bytes {'vin' => ins, 'vout' => outs, 'lock_time' => lock_time, 'ver' => version, 'vin_sz' => ins.length, 'vout_sz' => outs.length, 'size' => size} end
Private Instance Methods
# File lib/counterparty/raw_tx.rb, line 91 def disassemble_script(bytes) # We don't actually need to reference a hash argument to achieve disassembly: btc_script = Bitcoin::Script.new String.new chunks = btc_script.parse bytes.pack('C*') btc_script.to_string chunks end
# File lib/counterparty/raw_tx.rb, line 98 def hex_stringify(nibbles) nibbles.collect{|c| '%02x' % c}.join end
64 bit requests are an exception, and kept as 8-byte arrays:
# File lib/counterparty/raw_tx.rb, line 127 def shift_u64(bytes) bytes.slice!(0,8) end
en.bitcoin.it/wiki/Protocol_specification#Variable_length_string
# File lib/counterparty/raw_tx.rb, line 103 def shift_varchar(bytes) bytes.slice! 0, shift_varint(bytes) end
en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer
# File lib/counterparty/raw_tx.rb, line 108 def shift_varint(bytes) n = shift_u8 bytes case n when 0xfd then shift_u16 bytes when 0xfe then shift_u32 bytes when 0xff then shift_u64 bytes else n end end