Class: RawTx

Inherits:
Object
  • Object
show all
Defined in:
lib/counterparty/raw_tx.rb

Overview

This class is mostly used to decode json transactions from raw transactions much of this implementation was inspired from: gist.github.com/shesek/5835695

Constant Summary collapse

BASE64_CHARS =

This is a map of offset to characters used for decoding base64 strings:

"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
MAX_32BIT_INTEGER =
2**32-1

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(as_hexstring) ⇒ RawTx

Creates a raw transaction from a hexstring

Raises:

  • (ArgumentError)


11
12
13
14
# 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

Class Method Details

.bytes_to_base64_s(input_bytes) ⇒ Object

Convert an array of bytes to a base64 string.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# 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

.bytes_to_ui(bytes) ⇒ Object

Convert an array of 8 bit numbers into an unsigned int, Remember that for each input byte, the most significant nibble comes last.



67
68
69
70
# 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

.nibbles_to_ui(nibbles) ⇒ Object

Convert an array of 4 bit numbers into an unsigned int, works for numbers up to 32-bit only

Raises:

  • (ArgumentError)


58
59
60
61
62
63
# 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

Instance Method Details

#to_hash(options = {}) ⇒ Object

Returns this transaction in a standard json format

Raises:

  • (ArgumentError)


17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 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