icostan/cryptos-ruby

View on GitHub
lib/cryptos/transaction.rb

Summary

Maintainability
A
45 mins
Test Coverage
module Cryptos
  Transaction = Struct.new :version, :inputs, :outputs, :locktime do
    include Utils::Hexas, Utils::Hashes

    def self.from_ioc(input, output, change, version: 1, locktime: 0)
      new version, [input], [output, change], locktime
    end

    def serialize
      inputs_hex = inputs.map(&:serialize).join
      outputs_hex = outputs.map(&:serialize).join
      int_to_hex(version) + byte_to_hex(inputs.size) + inputs_hex +
        byte_to_hex(outputs.size) + outputs_hex + int_to_hex(locktime)
    end

    def hash
      hex_to_little sha256(sha256(serialize))
    end

    def signature_hash(script_pubkey = nil, sighash_type = 0x01)
      inputs.first.script_sig = script_pubkey if script_pubkey
      hash = sha256(sha256(serialize + int_to_hex(sighash_type)))
      [hash].pack('H*')
    end

    def sign(private_key, public_key, script_pubkey, sighash_type = 0x01)
      bytes_string = signature_hash script_pubkey, sighash_type
      r, s = ecdsa_sign private_key.value, bytes_string
      der = Cryptos::Der.new r: r, s: s
      inputs.first.script_sig = Script.sig_pubkey(der, public_key)
      serialize
    end

    def sign_input(index, address, sighash_type = 0x01)
      # TODO: get script_pubkey from input?
      script_pubkey = Cryptos::Script.p2pkh address
      bytes_string = signature_hash script_pubkey, sighash_type

      r, s = ecdsa_sign address.public_key.private_key.value, bytes_string
      der = Cryptos::Der.new r: r, s: s
      inputs[index].script_sig = Script.sig_pubkey der, address.public_key

      serialize
    end

    def sign_single_input(address)
      sign_input 0, address
    end

    def multi_sign_input(index, address1, address2, sighash_type = 0x01)
      redeem_script = Cryptos::Script.multisig address1, address2
      bytes_string = signature_hash redeem_script, sighash_type

      r, s = ecdsa_sign address1.public_key.private_key.value, bytes_string
      der1 = Cryptos::Der.new r: r, s: s
      r, s = ecdsa_sign address2.public_key.private_key.value, bytes_string
      der2 = Cryptos::Der.new r: r, s: s
      inputs[index].script_sig = Script.sig_multisig der1, der2, redeem_script

      serialize
    end

    def sign_atomic_swap(secret, secret_hash, to_address, locktime, from_address, sighash_type = 0x01)
      redeem_script = Cryptos::Script.swap secret_hash, to_address, locktime, from_address
      bytes_string = signature_hash redeem_script, sighash_type

      r, s = ecdsa_sign to_address.public_key.private_key.value, bytes_string
      der = Cryptos::Der.new r: r, s: s
      sig_swap = Script.sig_swap der, to_address.public_key, secret, redeem_script
      inputs[0].script_sig = sig_swap

      serialize
    end

    def broadcast(cli)
      cli.send_raw_transaction serialize
    end

    def to_s
      inputs.to_s + outputs.to_s
    end
  end
end