diff --git a/lib/bitcoin/block.rb b/lib/bitcoin/block.rb index 5297bf5..a9de39a 100644 --- a/lib/bitcoin/block.rb +++ b/lib/bitcoin/block.rb @@ -1,6 +1,6 @@ require_relative '../bitcoin_data_io' -require_relative '../encoding_helper' -require_relative '../hash_helper' +require_relative '../helpers/encoding_helper' +require_relative '../helpers/hash_helper' module Bitcoin class Block diff --git a/lib/bitcoin/bloom_filter.rb b/lib/bitcoin/bloom_filter.rb new file mode 100644 index 0000000..1e4ff42 --- /dev/null +++ b/lib/bitcoin/bloom_filter.rb @@ -0,0 +1,43 @@ +require_relative '../helpers/hash_helper' +require_relative '../helpers/encoding_helper' +require_relative 'network/messages/generic' + +module Bitcoin + class BloomFilter + include EncodingHelper + BIP37_CONSTANT = 0xfba4c795 + + def initialize(size, function_count, tweak) + @size = size + @bit_field = [0] * (size * 8) + @function_count = function_count + @tweak = tweak + end + + attr_reader :bit_field + + def add(item) + @function_count.times do |i| + seed = i * BIP37_CONSTANT + @tweak + + hash_result = HashHelper.murmur3(item, seed: seed) + bit = hash_result % (@size * 8) + @bit_field[bit] = 1 + end + end + + def filter_bytes + bit_field_to_bytes(@bit_field) + end + + def filterload(flag: 1) + result = encode_varint(@size) + result += filter_bytes + result += int_to_little_endian(@function_count, 4) + result += int_to_little_endian(@tweak, 4) + result += int_to_little_endian(flag, 1) + + Bitcoin::Network::Messages::Generic.new('filterload', result) + end + end +end diff --git a/lib/bitcoin/fetcher/uri_fetcher.rb b/lib/bitcoin/fetcher/uri_fetcher.rb index 94c3491..5dcc6a0 100644 --- a/lib/bitcoin/fetcher/uri_fetcher.rb +++ b/lib/bitcoin/fetcher/uri_fetcher.rb @@ -1,6 +1,6 @@ require_relative './fetcher' require_relative '../tx' -require_relative '../../encoding_helper' +require_relative '../../helpers/encoding_helper' require 'net/http' require 'stringio' diff --git a/lib/bitcoin/network/envelope.rb b/lib/bitcoin/network/envelope.rb index 3569be1..2c98145 100644 --- a/lib/bitcoin/network/envelope.rb +++ b/lib/bitcoin/network/envelope.rb @@ -1,8 +1,8 @@ # encoding: ascii-8bit require_relative '../../bitcoin_data_io' -require_relative '../../encoding_helper' -require_relative '../../hash_helper' +require_relative '../../helpers/encoding_helper' +require_relative '../../helpers/hash_helper' module Bitcoin module Network diff --git a/lib/bitcoin/network/messages/base_message.rb b/lib/bitcoin/network/messages/base_message.rb index bed5a4c..6f6316d 100644 --- a/lib/bitcoin/network/messages/base_message.rb +++ b/lib/bitcoin/network/messages/base_message.rb @@ -1,5 +1,5 @@ # encoding: ascii-8bit -require_relative '../../../encoding_helper' +require_relative '../../../helpers/encoding_helper' module Bitcoin module Network diff --git a/lib/bitcoin/network/messages/generic.rb b/lib/bitcoin/network/messages/generic.rb new file mode 100644 index 0000000..8e9d46f --- /dev/null +++ b/lib/bitcoin/network/messages/generic.rb @@ -0,0 +1,22 @@ +require_relative './base_message' + +module Bitcoin + module Network + module Messages + class Generic < BaseMessage + def initialize(command, payload) + @command = command + @payload = payload + end + + def command + @command + end + + def serialize + @payload + end + end + end + end +end diff --git a/lib/bitcoin/network/messages/get_data.rb b/lib/bitcoin/network/messages/get_data.rb new file mode 100644 index 0000000..c4f6908 --- /dev/null +++ b/lib/bitcoin/network/messages/get_data.rb @@ -0,0 +1,32 @@ +require_relative './base_message' + +module Bitcoin + module Network + module Messages + class GetData < BaseMessage + TX_DATA_TYPE = 1 + BLOCK_DATA_TYPE = 2 + FILTERED_BLOCK_DATA_TYPE = 3 + COMPACT_BLOCK_DATA_TYPE = 4 + COMMAND = "getdata" + + def initialize + @data = [] + end + + def add_data(data_type:, identifier:) + @data << [data_type, identifier] + end + + def serialize + result = encode_varint(@data.length) + @data.each do |data_type, identifier| + result += int_to_little_endian(data_type, 4) + result += identifier.reverse + end + result + end + end + end + end +end diff --git a/lib/bitcoin/op.rb b/lib/bitcoin/op.rb index 0cc6a0b..71e3034 100644 --- a/lib/bitcoin/op.rb +++ b/lib/bitcoin/op.rb @@ -1,6 +1,6 @@ -require_relative '../hash_helper' -require_relative '../encoding_helper' -require_relative '../script_helper' +require_relative '../helpers/hash_helper' +require_relative '../helpers/encoding_helper' +require_relative '../helpers/script_helper' require_relative '../ecc/signature' require_relative '../ecc/s256_point' diff --git a/lib/bitcoin/script.rb b/lib/bitcoin/script.rb index 08aafa5..ddb5ad0 100644 --- a/lib/bitcoin/script.rb +++ b/lib/bitcoin/script.rb @@ -1,5 +1,5 @@ require_relative '../bitcoin_data_io' -require_relative '../encoding_helper' +require_relative '../helpers/encoding_helper' require_relative './op' module Bitcoin diff --git a/lib/bitcoin/tx.rb b/lib/bitcoin/tx.rb index 83ba2a9..dc13d16 100644 --- a/lib/bitcoin/tx.rb +++ b/lib/bitcoin/tx.rb @@ -1,6 +1,6 @@ require_relative '../bitcoin_data_io' -require_relative '../encoding_helper' -require_relative '../hash_helper' +require_relative '../helpers/encoding_helper' +require_relative '../helpers/hash_helper' require_relative './fetcher/uri_fetcher' require_relative 'script' require 'net/http' diff --git a/lib/ecc/private_key.rb b/lib/ecc/private_key.rb index aa3d5f4..18f1e7f 100644 --- a/lib/ecc/private_key.rb +++ b/lib/ecc/private_key.rb @@ -1,7 +1,7 @@ require_relative 's256_point' require_relative 'signature' require_relative 'secp256k1_constants' -require_relative '../encoding_helper' +require_relative '../helpers/encoding_helper' require 'openssl' module ECC diff --git a/lib/ecc/s256_point.rb b/lib/ecc/s256_point.rb index a501bcd..3f73e0c 100644 --- a/lib/ecc/s256_point.rb +++ b/lib/ecc/s256_point.rb @@ -1,9 +1,9 @@ require_relative 's256_field' require_relative 'point' require_relative 'secp256k1_constants' -require_relative '../encoding_helper' -require_relative '../hash_helper' -require_relative '../address_helper' +require_relative '../helpers/encoding_helper' +require_relative '../helpers/hash_helper' +require_relative '../helpers/address_helper' module ECC class S256Point < Point include EncodingHelper diff --git a/lib/ecc/signature.rb b/lib/ecc/signature.rb index 2bb761d..a9c59a8 100644 --- a/lib/ecc/signature.rb +++ b/lib/ecc/signature.rb @@ -1,4 +1,4 @@ -require_relative '../encoding_helper' +require_relative '../helpers/encoding_helper' module ECC class Signature include EncodingHelper diff --git a/lib/hash_helper.rb b/lib/hash_helper.rb deleted file mode 100644 index 311396b..0000000 --- a/lib/hash_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'digest' - -module HashHelper - def self.hash160(string) - first_round = Digest::SHA256.digest(string) - Digest::RMD160.digest(first_round) - end - - def self.hash256(string) - first_round = Digest::SHA256.digest(string) - Digest::SHA256.digest(first_round) - end -end diff --git a/lib/address_helper.rb b/lib/helpers/address_helper.rb similarity index 91% rename from lib/address_helper.rb rename to lib/helpers/address_helper.rb index 3bd1dd0..bf9289e 100644 --- a/lib/address_helper.rb +++ b/lib/helpers/address_helper.rb @@ -1,6 +1,6 @@ # encoding: ascii-8bit -require 'encoding_helper' +require_relative 'encoding_helper' module AddressHelper include EncodingHelper diff --git a/lib/encoding_helper.rb b/lib/helpers/encoding_helper.rb similarity index 81% rename from lib/encoding_helper.rb rename to lib/helpers/encoding_helper.rb index 1ca63ee..a653254 100644 --- a/lib/encoding_helper.rb +++ b/lib/helpers/encoding_helper.rb @@ -83,6 +83,29 @@ def encode_varint(integer) end end + def bit_field_to_bytes(bit_field) + raise EncodingError.new("bit_field's length is not divisible by 8") if bit_field.length % 8 != 0 + + result = [0] * (bit_field.length / 8) + bit_field.each_with_index do |bit, index| + byte_index, bit_index = index.divmod(8) + result[byte_index] |= 1 << bit_index unless bit.zero? + end + result.pack('c*') + end + + def bytes_to_bit_field(bytes) + bytes = bytes.unpack('C*') + bit_field = [] + bytes.each do |byte| + 8.times do + bit_field << (byte & 1) + byte >>= 1 + end + end + bit_field + end + private def base58_to_num(base58_string) diff --git a/lib/helpers/hash_helper.rb b/lib/helpers/hash_helper.rb new file mode 100644 index 0000000..ec4ebb5 --- /dev/null +++ b/lib/helpers/hash_helper.rb @@ -0,0 +1,68 @@ +require 'digest' + +module HashHelper + MASK32 = 0xFFFFFFFF + + def self.hash160(string) + first_round = Digest::SHA256.digest(string) + Digest::RMD160.digest(first_round) + end + + def self.hash256(string) + first_round = Digest::SHA256.digest(string) + Digest::SHA256.digest(first_round) + end + + # rubocop:disable Metrics/MethodLength + def self.murmur3(string, seed: 0) # rubocop:disable Metrics/AbcSize + key_bytes = string.bytes + result_hash = seed + + rounded_end = (key_bytes.length & 0xfffffffc) + (0...rounded_end).step(4) do |i| + aux = block32(key_bytes, i) + result_hash ^= scramble32(aux) + result_hash = rotl32(result_hash, 13) + result_hash = result_hash * 5 + 0xe6546b64 + end + + val = key_bytes.length & 3 + + aux = 0 + (0...3).reverse_each do |i| + aux |= (key_bytes[rounded_end + i] & 0xff) << (8 * i) if val >= (i + 1) + end + + result_hash ^= scramble32(aux) + finalization_mix(result_hash ^ key_bytes.length) + end + # rubocop:enable Metrics/MethodLength + + private + + def self.finalization_mix(result_hash) + result_hash ^= ((result_hash & MASK32) >> 16) + result_hash *= 0x85ebca6b + result_hash ^= ((result_hash & MASK32) >> 13) + result_hash *= 0xc2b2ae35 + (result_hash ^ ((result_hash & MASK32) >> 16)) & MASK32 + end + + def self.block32(key_bytes, index) + (1..3).map do |i| + key_bytes[index + i] << (8 * i) + end.reduce(key_bytes[index], :|) + end + + def self.rotl32(item, bits_to_rotate) + ((item << bits_to_rotate) | ((item & MASK32) >> (32 - bits_to_rotate))) & MASK32 + end + + def self.scramble32(aux) + aux = (aux * 0xcc9e2d51) & MASK32 + aux = rotl32(aux, 15) + (aux * 0x1b873593) & MASK32 + end + + private_class_method :finalization_mix, :block32, :rotl32, :scramble32 +end diff --git a/lib/script_helper.rb b/lib/helpers/script_helper.rb similarity index 77% rename from lib/script_helper.rb rename to lib/helpers/script_helper.rb index 1d7140b..3dca396 100644 --- a/lib/script_helper.rb +++ b/lib/helpers/script_helper.rb @@ -1,4 +1,8 @@ +require_relative 'encoding_helper' + module ScriptHelper + include EncodingHelper + def encode_num(num) return '' if num.zero? diff --git a/spec/bitcoin/block_spec.rb b/spec/bitcoin/block_spec.rb index 3bd9458..d225432 100644 --- a/spec/bitcoin/block_spec.rb +++ b/spec/bitcoin/block_spec.rb @@ -1,5 +1,5 @@ require 'bitcoin/block' -require 'encoding_helper' +require 'helpers/encoding_helper' RSpec.describe Bitcoin::Block do include EncodingHelper diff --git a/spec/bitcoin/bloom_filter.rb b/spec/bitcoin/bloom_filter.rb new file mode 100644 index 0000000..cf2c1be --- /dev/null +++ b/spec/bitcoin/bloom_filter.rb @@ -0,0 +1,43 @@ +require_relative '../../lib/bitcoin/bloom_filter' +require_relative '../../lib/helpers/encoding_helper' + +RSpec.describe Bitcoin::BloomFilter do + include EncodingHelper + + describe '.new' do + it 'initializes the bit_field appropiately' do + expect(described_class.new(1, 2, 3).bit_field).to eq [0] * 8 + end + end + + describe '#add' do + it 'mutates the bit_field correctly' do + expected_bit_field = bytes_to_bit_field(from_hex_to_bytes('0000000a080000000140')) + bloom_filter = described_class.new(10, 5, 99) + expect { bloom_filter.add('Hello World') } + .to((change { bloom_filter.bit_field }.to expected_bit_field)) + end + end + + describe '#filter_bytes' do + it 'returns the proper filter bytes' do + bloom_filter = described_class.new(10, 5, 99) + expect(bloom_filter.filter_bytes).to eq bit_field_to_bytes([0] * 10 * 8) + bloom_filter.add('Hello World') + expect(bloom_filter.filter_bytes).to eq from_hex_to_bytes('0000000a080000000140') + bloom_filter.add('Goodbye!') + expect(bloom_filter.filter_bytes).to eq from_hex_to_bytes('4000600a080000010940') + end + end + + describe '#filterload' do + it 'returns the proper generic filterload message' do + bloom_filter = described_class.new(10, 5, 99) + bloom_filter.add('Hello World') + bloom_filter.add('Goodbye!') + expect(bloom_filter.filterload.command).to eq 'filterload' + expect(bloom_filter.filterload.serialize) + .to eq from_hex_to_bytes('0a4000600a080000010940050000006300000001') + end + end +end diff --git a/spec/bitcoin/network/envelope_spec.rb b/spec/bitcoin/network/envelope_spec.rb index c95dfa5..1b9231f 100644 --- a/spec/bitcoin/network/envelope_spec.rb +++ b/spec/bitcoin/network/envelope_spec.rb @@ -1,7 +1,7 @@ # encoding: ascii-8bit require 'bitcoin/network/envelope' -require 'encoding_helper' +require 'helpers/encoding_helper' RSpec.describe Bitcoin::Network::Envelope do include EncodingHelper diff --git a/spec/bitcoin/network/messages/get_data_spec.rb b/spec/bitcoin/network/messages/get_data_spec.rb new file mode 100644 index 0000000..ac4a77c --- /dev/null +++ b/spec/bitcoin/network/messages/get_data_spec.rb @@ -0,0 +1,23 @@ +require 'bitcoin/network/messages/get_data' +require 'helpers/encoding_helper' + +RSpec.describe Bitcoin::Network::Messages::GetData do + include EncodingHelper + + def serialized_message_hex + get_data = described_class.new + block1 = from_hex_to_bytes('00000000000000cac712b726e4326e596170574c01a16001692510c44025eb30') + get_data.add_data(data_type: described_class::FILTERED_BLOCK_DATA_TYPE, identifier: block1) + block2 = from_hex_to_bytes('00000000000000beb88910c46f6b442312361c6693a7fb52065b583979844910') + get_data.add_data(data_type: described_class::FILTERED_BLOCK_DATA_TYPE, identifier: block2) + bytes_to_hex(get_data.serialize) + end + + describe "#serialize" do + it "serializes version" do + expected_hex = "020300000030eb2540c41025690160a1014c577061596e32e426b712c7ca000000000000000\ +30000001049847939585b0652fba793661c361223446b6fc41089b8be00000000000000" + expect(serialized_message_hex).to eq expected_hex + end + end +end diff --git a/spec/bitcoin/network/messages/get_headers_spec.rb b/spec/bitcoin/network/messages/get_headers_spec.rb index 3f0e22a..d9a0eb3 100644 --- a/spec/bitcoin/network/messages/get_headers_spec.rb +++ b/spec/bitcoin/network/messages/get_headers_spec.rb @@ -1,6 +1,6 @@ require 'bitcoin/network/messages/get_headers' require 'bitcoin/block' -require 'encoding_helper' +require 'helpers/encoding_helper' RSpec.describe Bitcoin::Network::Messages::GetHeaders do include EncodingHelper diff --git a/spec/bitcoin/network/messages/headers_spec.rb b/spec/bitcoin/network/messages/headers_spec.rb index 35501d1..30ad6fd 100644 --- a/spec/bitcoin/network/messages/headers_spec.rb +++ b/spec/bitcoin/network/messages/headers_spec.rb @@ -1,6 +1,6 @@ require 'bitcoin/network/messages/headers' require 'bitcoin/block' -require 'encoding_helper' +require 'helpers/encoding_helper' require 'stringio' RSpec.describe Bitcoin::Network::Messages::Headers do diff --git a/spec/bitcoin/network/messages/version_spec.rb b/spec/bitcoin/network/messages/version_spec.rb index e3c6528..3e34e5d 100644 --- a/spec/bitcoin/network/messages/version_spec.rb +++ b/spec/bitcoin/network/messages/version_spec.rb @@ -1,5 +1,5 @@ require 'bitcoin/network/messages/version' -require 'encoding_helper' +require 'helpers/encoding_helper' require 'timecop' RSpec.describe Bitcoin::Network::Messages::Version do diff --git a/spec/bitcoin/network/simple_node_spec.rb b/spec/bitcoin/network/simple_node_spec.rb index 5ffd190..1c454df 100644 --- a/spec/bitcoin/network/simple_node_spec.rb +++ b/spec/bitcoin/network/simple_node_spec.rb @@ -1,7 +1,7 @@ # encoding: ascii-8bit require 'bitcoin/network/simple_node' -require 'encoding_helper' +require 'helpers/encoding_helper' RSpec.describe Bitcoin::Network::SimpleNode do include EncodingHelper diff --git a/spec/bitcoin/op_spec.rb b/spec/bitcoin/op_spec.rb index 964fe71..b9cd733 100644 --- a/spec/bitcoin/op_spec.rb +++ b/spec/bitcoin/op_spec.rb @@ -2,8 +2,8 @@ require 'bitcoin/op' require 'ecc/s256_point' require 'ecc/signature' -require 'hash_helper' -require 'encoding_helper' +require 'helpers/hash_helper' +require 'helpers/encoding_helper' RSpec.describe Bitcoin::Op do include EncodingHelper diff --git a/spec/bitcoin/script_spec.rb b/spec/bitcoin/script_spec.rb index b6065ef..9ac714f 100644 --- a/spec/bitcoin/script_spec.rb +++ b/spec/bitcoin/script_spec.rb @@ -1,6 +1,6 @@ require 'bitcoin/script' require 'bitcoin/op' -require 'encoding_helper' +require 'helpers/encoding_helper' RSpec.describe Bitcoin::Script do def _raw_script(hex_script) diff --git a/spec/bitcoin_data_io_spec.rb b/spec/bitcoin_data_io_spec.rb index 448d307..cdfb2a2 100644 --- a/spec/bitcoin_data_io_spec.rb +++ b/spec/bitcoin_data_io_spec.rb @@ -1,4 +1,5 @@ -require 'encoding_helper' +require 'helpers/encoding_helper' +require 'bitcoin_data_io' RSpec.describe BitcoinDataIO do def io(_hex_data) diff --git a/spec/ecc/ecc_spec.rb b/spec/ecc/ecc_spec.rb index f719ae1..e7df776 100644 --- a/spec/ecc/ecc_spec.rb +++ b/spec/ecc/ecc_spec.rb @@ -43,11 +43,11 @@ let(:solution) { ECC::Point.new(x, y, a, b) } it 'returns the point scalar product (scalar on the right side)' do - expect(point1 * scalar ).to eq solution + expect(point1 * scalar).to eq solution end it 'returns the point scalar product (scalar on the left side)' do - expect(scalar * point1 ).to eq solution + expect(scalar * point1).to eq solution end end end diff --git a/spec/ecc/s256_point_spec.rb b/spec/ecc/s256_point_spec.rb index b94d85c..643083c 100644 --- a/spec/ecc/s256_point_spec.rb +++ b/spec/ecc/s256_point_spec.rb @@ -4,8 +4,8 @@ require 'ecc/signature' require 'ecc/secp256k1_constants' require 'ecc/private_key' -require 'encoding_helper' -require 'hash_helper' +require 'helpers/encoding_helper' +require 'helpers/hash_helper' RSpec.describe ECC::S256Point do describe 'init' do diff --git a/spec/hash_helper_spec.rb b/spec/hash_helper_spec.rb deleted file mode 100644 index 5b8c819..0000000 --- a/spec/hash_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -RSpec.describe HashHelper do - describe '#hash160' do - it 'computes the hash by sha256 followed by ripemd160' do - hash = '77bc43ce98ed7de19a42b6f2b8978df300890a2d' - expect(described_class.hash160('Bitcoin Guild rocks!').unpack1('H*')).to eq(hash) - end - end - - describe '#hash256' do - it 'computes the hash by two passes of sha256' do - hash = 'a63898c9855b802d6db18886928affbc22b928c3ccd683e21d62da8f0af00a42' - expect(described_class.hash256('Bitcoin Guild rocks!').unpack1('H*')).to eq(hash) - end - end -end diff --git a/spec/address_helper_spec.rb b/spec/helpers/address_helper_spec.rb similarity index 97% rename from spec/address_helper_spec.rb rename to spec/helpers/address_helper_spec.rb index 2851ce6..193c590 100644 --- a/spec/address_helper_spec.rb +++ b/spec/helpers/address_helper_spec.rb @@ -1,4 +1,4 @@ -require 'address_helper' +require 'helpers/address_helper' RSpec.describe AddressHelper do let(:described_module) { Object.new.extend described_class } diff --git a/spec/encoding_helper_spec.rb b/spec/helpers/encoding_helper_spec.rb similarity index 81% rename from spec/encoding_helper_spec.rb rename to spec/helpers/encoding_helper_spec.rb index 809059b..27ddf8e 100644 --- a/spec/encoding_helper_spec.rb +++ b/spec/helpers/encoding_helper_spec.rb @@ -1,5 +1,5 @@ # encoding: ascii-8bit -require 'encoding_helper' +require 'helpers/encoding_helper' RSpec.describe EncodingHelper do let(:described_module) { Object.new.extend described_class } @@ -133,4 +133,26 @@ expect(described_module.bytes_to_hex("\xA0/")).to eq "a02f" end end + + describe '#bit_field_to_bytes' do + it 'produces the proper bytes' do + bit_field = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0] + bytes = "@\x00`\n\b\x00\x00\x01\t@" + expect(described_module.bit_field_to_bytes(bit_field)) + .to eq bytes + end + end + + describe '#bytes_to_bit_field' do + it 'produces the proper bit_field' do + bit_field = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0] + bytes = "@\x00`\n\b\x00\x00\x01\t@" + + expect(described_module.bytes_to_bit_field(bytes)).to eq bit_field + end + end end diff --git a/spec/helpers/hash_helper_spec.rb b/spec/helpers/hash_helper_spec.rb new file mode 100644 index 0000000..bfc1901 --- /dev/null +++ b/spec/helpers/hash_helper_spec.rb @@ -0,0 +1,29 @@ +require 'helpers/hash_helper' + +RSpec.describe HashHelper do + describe '.hash160' do + it 'computes the hash by sha256 followed by ripemd160' do + hash = '77bc43ce98ed7de19a42b6f2b8978df300890a2d' + expect(described_class.hash160('Bitcoin Guild rocks!').unpack1('H*')).to eq(hash) + end + end + + describe '.hash256' do + it 'computes the hash by two passes of sha256' do + hash = 'a63898c9855b802d6db18886928affbc22b928c3ccd683e21d62da8f0af00a42' + expect(described_class.hash256('Bitcoin Guild rocks!').unpack1('H*')).to eq(hash) + end + end + + describe '.murmur3' do + it 'computes the correct murmur3 hash for different message and seeds' do + [1203516251, 669393163, 819509628, 3765971536].each_with_index do |expected_hash, seed| + expect(described_class.murmur3('Bitcoin Guild rocks!', seed: seed)).to eq(expected_hash) + end + expect(described_class.murmur3('Goodbye!', seed: 8443760525)).to eq(468028502) + [1411415842, 2371772749, 4164319582, 2164673664].each_with_index do |expected_hash, seed| + expect(described_class.murmur3("Bitcoin Guild rocks! #{seed}")).to eq(expected_hash) + end + end + end +end diff --git a/spec/script_helper.rb b/spec/helpers/script_helper_spec.rb similarity index 96% rename from spec/script_helper.rb rename to spec/helpers/script_helper_spec.rb index 6bdfa4c..9b0ce1d 100644 --- a/spec/script_helper.rb +++ b/spec/helpers/script_helper_spec.rb @@ -1,3 +1,5 @@ +require 'helpers/script_helper' + RSpec.describe ScriptHelper do let(:described_module) { Object.new.extend described_class }