Skip to content

Instantly share code, notes, and snippets.

@redsquirrel
Last active September 12, 2019 13:35
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save redsquirrel/bce4ffbf0c677ac78fa7 to your computer and use it in GitHub Desktop.
Save redsquirrel/bce4ffbf0c677ac78fa7 to your computer and use it in GitHub Desktop.
Streaming global Bitcoin transactions in less than 80 lines of Ruby code. Described at https://github.com/redsquirrel/jargon/blob/master/articles/btc-tx-streaming.md
require 'rubygems'
require 'bitcoin'
require 'eventmachine'
require 'resolv'
require 'set'
module BitcoinTransactionReader
def initialize(ip_address, database)
@ip_address = ip_address
@database = database
@parser = Bitcoin::Protocol::Parser.new(self)
end
# begin EventMachine methods
def post_init
version = Bitcoin::Protocol::Version.new(
from: "127.0.0.1:8333",
to: @ip_address,
user_agent: MY_USER_AGENT_STRING,
)
send_data(version.to_pkt)
end
def receive_data(data)
@parser.parse(data)
end
# end EventMachine methods
# begin Parser methods
def on_version(version)
puts "Handshake completed with: #{version.inspect}"
end
def on_ping(nonce)
send_data(Bitcoin::Protocol.pong_pkt(nonce))
end
def on_inv_transaction(hash)
send_data(Bitcoin::Protocol.getdata_pkt(:tx, [hash]))
end
def on_inv_block_v2(hash, idx, count)
# I'm just here so I don't get fined.
end
def on_tx(tx)
@database.inbound_transaction(tx)
end
# end Parser methods
end
class BitcoinTransactionDatabase
def initialize
@trasaction_hashes = Set.new
end
def inbound_transaction(tx)
return if @trasaction_hashes.include?(tx.hash)
@trasaction_hashes.add(tx.hash)
total_out = tx.out.inject(0) { |total, out| total + out.value }
puts tx.hash + " amount: " + total_out.to_s
end
end
MY_USER_AGENT_STRING = put_something_here
seed = Bitcoin.network[:dns_seeds].sample
puts "Grabbing addresses based on seed: " + seed
addresses = Resolv::DNS.new.getresources(seed, Resolv::DNS::Resource::IN::A).map {|r|r.address.to_s}
database = BitcoinTransactionDatabase.new
EventMachine.run do
addresses.each do |address|
puts "Attempting to connect to " + address
EventMachine.connect(address, 8333, BitcoinTransactionReader, address, database)
end
end
@mFarghaly
Copy link

@redsquirrel
Copy link
Author

@mFarghaly Thanks! 🙌

@aeden
Copy link

aeden commented Mar 4, 2016

I don't actually understand DNS well enough to explain what's going on here: Resolv::DNS.new.getresources(seed, Resolv::DNS::Resource::IN::A). If anyone wants to explain it via the gist comments, feel free!

It's just doing a normal DNS A record lookup for the host name that is in seed. According to https://en.bitcoin.it/wiki/Satoshi_Client_Node_Discovery#DNS_Addresses there are several seeded host names, so it could be any of them. The DNS result is a set of records, which are IP addresses.

@redsquirrel
Copy link
Author

@aeden Thanks man. Is this a typical use of A records?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment