Skip to content

Instantly share code, notes, and snippets.

@chen7897499
Last active December 3, 2021 08:22
Show Gist options
  • Save chen7897499/0dec82424af6c786eafacf029fc1d515 to your computer and use it in GitHub Desktop.
Save chen7897499/0dec82424af6c786eafacf029fc1d515 to your computer and use it in GitHub Desktop.
communicate with btcd node
[Application Options]
datadir=./btcd/data
listen=127.0.0.1:9333
simnet=1
nobanning=1
debuglevel=debug
require 'securerandom'
require 'digest'
require 'socket'
require 'pry'
require 'stringio'
class String
def to_bytes(length, byte_order='big')
result = [self.rjust(length*2, '0')].pack('H*')
result.reverse! if byte_order == 'little'
return result
end
end
def encode_varint(i)
if i < 0xfd
i.to_s(16).to_bytes(1, 'little')
elsif i < 0x10000
'\xfd'.b + i.to_s.to_bytes(2, 'little')
elsif i < 0x100000000
'\xfe'.b + i.to_s.to_bytes(4, 'little')
elsif i < 0x10000000000000000
'\xff'.b + i.to_s.to_bytes(8, 'little')
else
raise "integer too large #{i}"
end
end
def version
result = 70015.to_s(16).to_bytes(4, 'little')
result += 1.to_s(16).to_bytes(8,'little')
result += Time.now.to_i.to_s(16).to_bytes(8, 'little')
result += 1.to_s(16).to_bytes(8, 'little')
result += "\x00".b * 10 + "\xff\xff".b + [127,0,0,1].map {|n| [n.to_s(16)].pack("h*") }.join
result += 9333.to_s(16).to_bytes(2, 'big')
result += 1.to_s(16).to_bytes(8, 'little')
result += "\x00".b * 10 + "\xff\xff".b + [127,0,0,1].map {|n| [n.to_s(16)].pack("h*") }.join
result += 9334.to_s(16).to_bytes(2, 'big')
result += SecureRandom.random_bytes(8)
result += encode_varint("/Satoshi:5.64/tinybit:0.0.1/".length)
result += "/Satoshi:5.64/tinybit:0.0.1/"
result += "\xff\xff\xff\xff".b
result += "\x01".b
end
# Checksums use hash256 (where data is hashed twice through sha256)
def hash256(binary)
hash1 = Digest::SHA256.hexdigest(binary)
hash2 = Digest::SHA256.hexdigest([hash1].pack("H*"))
return hash2
end
# Checksums are used when creating addresses
def checksum(hex)
hash = hash256(hex) # Hash the data through SHA256 twice
return [hash].pack("H*")[0..3]
end
def message
@payload = version
result = "\x16\x1c\x14\x12".b
result += "version" + "\x00".b * (12 - "version".length)
result += @payload.length.to_s(16).to_bytes(4, 'little')
result += checksum(@payload)
result += @payload
end
def verack_message
result = "\x16\x1c\x14\x12".b
result += "verack" + "\x00".b * (12 - "verack".length)
result += 0.to_s(16).to_bytes(4, 'little')
result += checksum("")
result += ""
end
def pong_message(nonce)
@payload = [nonce].pack('Q')
result = "\x16\x1c\x14\x12".b
result += "pong" + "\x00".b * (12 - "pong".length)
result += @payload.length.to_s(16).to_bytes(4, 'little')
result += checksum(@payload)
result += @payload
end
def extract_nonce(message)
buf = StringIO.new(message)
magic = buf.read(4)
command = buf.read(12).delete("\x00")
length = buf.read(4).unpack1('V')
checksum = buf.read(4)
payload = buf.read(length)
raise ArgumentError, 'Checksum do not match.' unless checksum == checksum(payload)
payload.unpack1('Q')
end
def gets(s)
magic = s.read(4)
command = s.read(12)
length = s.read(4)
checksum = s.read(4)
payload = s.read(length.unpack1('V'))
magic + command + length + checksum + payload
end
s = TCPSocket.new('localhost', 9333)
s.write(message)
# version message
res = gets(s)
puts 'received'
p res
# verack message
res = gets(s)
puts 'received'
p res
s.write(verack_message)
msg_header_length = 4 + 12 + 4 + 4 # magiclength + commandlength + checksumlength + payload length value
# ping pong handle
loop do
s.flush
# res = s.recv(32, Socket::MSG_WAITALL)
res = s.read(32) # ping message length
puts 'received'
p res
nonce = extract_nonce(res)
message = pong_message(nonce)
s.write(message)
end
@chen7897499
Copy link
Author

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