# encoding: ascii-8bit require 'ffi' # Wraps bitcoinconsensus.so (https://github.com/bitcoin/bitcoin) # commit: 90c71548c795787b008bc337cb9332f75d1bccdb module Bitcoin module BitcoinConsensus extend FFI::Library SCRIPT_VERIFY_NONE = 0 SCRIPT_VERIFY_P2SH = (1 << 0) SCRIPT_VERIFY_STRICTENC = (1 << 1) SCRIPT_VERIFY_DERSIG = (1 << 2) SCRIPT_VERIFY_LOW_S = (1 << 3) SCRIPT_VERIFY_NULLDUMMY = (1 << 4) SCRIPT_VERIFY_SIGPUSHONLY = (1 << 5) SCRIPT_VERIFY_MINIMALDATA = (1 << 6) SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 7) SCRIPT_VERIFY_CLEANSTACK = (1 << 8) ERR_CODES = { 0 => :ok, 1 => :tx_index, 2 => :tx_size_mismatch, 3 => :tx_deserialize } def self.ffi_load_functions(file) class_eval <<-RUBY ffi_lib [ %[#{file}] ] attach_function :bitcoinconsensus_version, [], :uint # int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, # const unsigned char *txTo , unsigned int txToLen, # unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); attach_function :bitcoinconsensus_verify_script, [:pointer, :uint, :pointer, :uint, :uint, :uint, :pointer], :int RUBY end def self.lib_available? @__lib_path ||= [ ENV['BITCOINCONSENSUS_LIB_PATH'], 'vendor/bitcoin/src/.libs/libbitcoinconsensus.so' ].find{|f| File.exists?(f.to_s) } end def self.init return if @bitcoin_consensus lib_path = lib_available? ffi_load_functions(lib_path) @bitcoin_consensus = true end # api version def self.version init bitcoinconsensus_version end def self.verify_script(input_index, script_pubkey, tx_payload, script_flags) init scriptPubKey = FFI::MemoryPointer.new(:uchar, script_pubkey.bytesize).put_bytes(0, script_pubkey) txTo = FFI::MemoryPointer.new(:uchar, tx_payload.bytesize).put_bytes(0, tx_payload) error_ret = FFI::MemoryPointer.new(:uint) ret = bitcoinconsensus_verify_script(scriptPubKey, scriptPubKey.size, txTo, txTo.size, input_index, script_flags, error_ret) case ret when 0 false when 1 (ERR_CODES[error_ret.read_int] == :ok) ? true : false else raise "error invalid result" end end end end