Class: Mastercoin::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/mastercoin-ruby/transaction.rb

Defined Under Namespace

Classes: NoMastercoinTransactionException

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tx_hash) ⇒ Transaction

Returns a new instance of Transaction.

Raises:



12
13
14
15
16
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/mastercoin-ruby/transaction.rb', line 12

def initialize(tx_hash)
  @store = Mastercoin.storage
  self.data_addresses = []
  self.rejected_outputs = [] 
  self.target_address = nil
  self.btc_tx = @store.get_tx(tx_hash)
  self.sending_address = btc_tx.inputs.first.get_prev_out.get_address
  self.source_address = Mastercoin::ExodusPayment.highest_output_for_tx(self.btc_tx)

  exodus_value = nil

  raise TransactionNotFoundException.new("Transaction #{tx_hash} could not be found. Is your blockchain up to date?") if self.btc_tx.nil?

  unless self.has_genesis_as_output?
    raise NoMastercoinTransactionException.new("This transaction does not contain a txout to the genesis address, invalid.")
  end

  unless self.has_three_outputs?
    raise NoMastercoinTransactionException.new("This transaction does not contain three outputs, invalid.")
  end
 
  if self.btc_tx.outputs.collect{|x| x.script.is_multisig?}.include?(true)
    self.multisig = true
  else
    self.multisig = false
  end

  # This should probably be next up for refactor, smelling code creep!

  self.btc_tx.outputs.each do |output|
    if output.get_address == Mastercoin::EXODUS_ADDRESS
      exodus_value = output.value
    end
  end

  if multisig
    self.btc_tx.outputs.each do |output|
      if output.script.is_multisig?
        keys = output.script.get_multisig_pubkeys.collect{|x| x.unpack("H*")[0]}
        self.data = Mastercoin::Message.probe_and_read(keys, self.sending_address)
      elsif output.get_address == Mastercoin::EXODUS_ADDRESS
        # Do nothing for now
      else
        self.target_address = output.get_address if output.value == exodus_value
      end
    end
  else
    exodus_size_outputs = self.btc_tx.outputs.find_all{|x| x.value == exodus_value}

    self.btc_tx.outputs.each do |output|
      if output.get_address == Mastercoin::EXODUS_ADDRESS
        # Do nothing yet; this is simply the exodus address
      elsif Mastercoin::SimpleSend.decode_from_address(output.get_address).looks_like_mastercoin? # This looks like a data packet
        Mastercoin.log.debug "Found data for address #{output.get_address}"
        raise NoMastercoinTransactionException.new("More then one data-addresses found, invalidating.") if self.data.present?
        self.data = Mastercoin::SimpleSend.decode_from_address(output.get_address)
      end
    end

    Mastercoin.log.debug "Looking for data sequence #{self.data.sequence} +1 == #{self.data.sequence.to_i + 1}"

    exodus_size_outputs.each do |output|
      address = output.get_address
      sequence = Mastercoin::Util.get_sequence(address)
      Mastercoin.log.debug "Sequence: #{sequence} for #{address}"

      if (self.data.sequence.to_i + 1).to_s  == sequence.to_s
        self.target_address = address
        Mastercoin.log.debug "Target address found #{self.target_address}"
      end
    end

    unless self.target_address
      Mastercoin.log.debug "Target address not found attempting 'peek & decode' Level 1, checking exodus-sized outputs."
      # Find data outputs and brute force receiver
      found = exodus_size_outputs.reject do |output|
        output.get_address == Mastercoin::EXODUS_ADDRESS || Mastercoin::SimpleSend.decode_from_address(output.get_address).looks_like_mastercoin? # This looks like a data packet
      end

      if found.length == 1
        Mastercoin.log.debug "Only one possible target left; found target address."
        self.target_address = found.first.get_address
      else
        Mastercoin.log.debug "Target address not found attempting 'peek & decode' Level 2, checking all-non exodus-sized outputs."

        self.btc_tx.outputs.each do |output|
          address = output.get_address
          sequence = Mastercoin::Util.get_sequence(address)
          Mastercoin.log.debug "Sequence: #{sequence} for #{address}"

          if (self.data.sequence.to_i + 1).to_s  == sequence.to_s
            self.target_address = address
            Mastercoin.log.debug "Target address found #{self.target_address}"
          end
        end
      end
    end
  end
  raise NoMastercoinTransactionException.new("Could not find valid looking target address, invalid") if self.target_address.nil? && !self.data.class == SellingOffer
  raise NoMastercoinTransactionException.new("Could not find a valid looking data-address, invalid.") unless self.data
end

Instance Attribute Details

#amountObject

Returns the value of attribute amount.



6
7
8
# File 'lib/mastercoin-ruby/transaction.rb', line 6

def amount
  @amount
end

#btc_txObject

Returns the value of attribute btc_tx.



5
6
7
# File 'lib/mastercoin-ruby/transaction.rb', line 5

def btc_tx
  @btc_tx
end

#currency_idObject

Returns the value of attribute currency_id.



6
7
8
# File 'lib/mastercoin-ruby/transaction.rb', line 6

def currency_id
  @currency_id
end

#dataObject

Returns the value of attribute data.



9
10
11
# File 'lib/mastercoin-ruby/transaction.rb', line 9

def data
  @data
end

#data_addressesObject

Returns the value of attribute data_addresses.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def data_addresses
  @data_addresses
end

#multisigObject

Returns the value of attribute multisig.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def multisig
  @multisig
end

#rejected_outputsObject

Returns the value of attribute rejected_outputs.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def rejected_outputs
  @rejected_outputs
end

#sending_addressObject

Returns the value of attribute sending_address.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def sending_address
  @sending_address
end

#source_addressObject

Returns the value of attribute source_address.



10
11
12
# File 'lib/mastercoin-ruby/transaction.rb', line 10

def source_address
  @source_address
end

#target_addressObject

Returns the value of attribute target_address.



7
8
9
# File 'lib/mastercoin-ruby/transaction.rb', line 7

def target_address
  @target_address
end

#transaction_typeObject

Returns the value of attribute transaction_type.



6
7
8
# File 'lib/mastercoin-ruby/transaction.rb', line 6

def transaction_type
  @transaction_type
end

Instance Method Details

#has_genesis_as_output?Boolean

Returns:



118
119
120
# File 'lib/mastercoin-ruby/transaction.rb', line 118

def has_genesis_as_output?
  self.btc_tx.outputs.collect{|x| x.get_address == Mastercoin::EXODUS_ADDRESS}.any?
end

#has_three_outputs?Boolean

Returns:



114
115
116
# File 'lib/mastercoin-ruby/transaction.rb', line 114

def has_three_outputs?
  self.btc_tx.outputs.size >= 3
end

#to_sObject



122
123
124
# File 'lib/mastercoin-ruby/transaction.rb', line 122

def to_s
  self.data.explain(self.sending_address, self.target_address)
end