Skip to content

Instantly share code, notes, and snippets.

@ohac
Last active August 26, 2017 00:40
Show Gist options
  • Save ohac/d1f8e693b66d686c1b40d9c2fe039c26 to your computer and use it in GitHub Desktop.
Save ohac/d1f8e693b66d686c1b40d9c2fe039c26 to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"io"
"math/rand"
"net"
"strconv"
"strings"
"time"
)
type Header struct {
magic uint32
command [12]byte
length uint32
}
func (p *Header) Command() string {
return strings.Trim(string(p.command[:]), "\x00")
}
func (p *Header) Init(magic uint32, command string) {
p.magic = magic
copy(p.command[:], command)
}
func (p *Header) Write(writer io.Writer, sum4 []byte) (err error) {
err = binary.Write(writer, binary.LittleEndian, p)
if err != nil {
return
}
err = binary.Write(writer, binary.LittleEndian, sum4)
return
}
func (p *Header) Read(reader io.Reader) (err error) {
err = binary.Read(reader, binary.LittleEndian, &p.magic)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.command)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.length)
if err != nil {
return
}
var sum4 [4]byte
err = binary.Read(reader, binary.LittleEndian, sum4[:])
return
}
type Version struct {
version int32
services uint64
timestamp int64
addr_recv NetAddress
addr_from NetAddress
nonce uint64
user_agent [3]byte
start_height int32
//relay bool
}
func dhash(data []byte) [32]byte {
if data == nil {
data = make([]byte, 0)
}
hash1 := sha256.Sum256(data)
return sha256.Sum256(hash1[:])
}
func (p *Version) Init(minver int32, conn net.Conn, ua string, start_height int32) (uint32, []byte) {
p.version = minver
p.services = 0
p.timestamp = time.Now().Unix()
p.addr_recv.Init(conn.RemoteAddr())
p.addr_from.Init(conn.LocalAddr())
p.nonce = uint64(rand.Int63())
p.user_agent[0] = byte(len(ua))
copy(p.user_agent[1:], ua)
p.start_height = start_height
psize := binary.Size(p)
var payloadraw bytes.Buffer
binary.Write(&payloadraw, binary.LittleEndian, p)
sum := dhash(payloadraw.Bytes())
sum4 := sum[:4]
return uint32(psize), sum4
}
func (p *Version) Write(writer io.Writer) (err error) {
err = binary.Write(writer, binary.LittleEndian, p)
return
}
func (p *Version) Read(reader io.Reader, length uint32) (uastr string, err error) {
vsiz := uint32(4 + 8 + 8 + 26 + 26 + 8)
if length < vsiz {
err = fmt.Errorf("too short")
return
}
err = binary.Read(reader, binary.LittleEndian, &p.version)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.services)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.timestamp)
if err != nil {
return
}
err = p.addr_recv.Read(reader, length, false)
if err != nil {
return
}
err = p.addr_from.Read(reader, length, false)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.nonce)
if err != nil {
return
}
length -= vsiz
var s uint32
uastr, s, err = ReadVarString(reader, length)
if err != nil {
return
}
length -= s
err = binary.Read(reader, binary.LittleEndian, &p.start_height)
if err != nil {
return
}
length -= 4
if length > 0 {
buf := make([]byte, length)
err = binary.Read(reader, binary.LittleEndian, buf)
fmt.Println("more data", length, buf) // relay
}
return
}
type NetAddress struct {
//time uint32 // not present in version message
services uint64
ipv6v4 [16]byte // big endian
port [2]byte // big endian
}
func (p *NetAddress) Port() int {
return (int(p.port[0]) << 8) | int(p.port[1])
}
func (p *NetAddress) String() string {
return net.IP(p.ipv6v4[:]).String() + ", " + strconv.Itoa(p.Port())
}
func (p *NetAddress) Init(addr net.Addr) {
host, port, _ := net.SplitHostPort(addr.String())
ip := net.ParseIP(host)
//p.time = uint32(time.Now().Unix())
p.services = 0
copy(p.ipv6v4[:], ip.To16())
iport, _ := strconv.Atoi(port)
p.port[0] = byte(iport >> 8)
p.port[1] = byte(iport)
}
func (p *NetAddress) Read(reader io.Reader, length uint32, ts bool) (err error) {
if ts {
if length < 30 {
err = fmt.Errorf("too short")
return
}
var time uint32
err = binary.Read(reader, binary.LittleEndian, &time)
if err != nil {
return
}
length -= 4
}
if length < 26 {
err = fmt.Errorf("too short")
return
}
err = binary.Read(reader, binary.LittleEndian, &p.services)
if err != nil {
return
}
err = binary.Read(reader, binary.BigEndian, &p.ipv6v4)
if err != nil {
return
}
err = binary.Read(reader, binary.BigEndian, &p.port)
return
}
type InvVect struct {
invtype uint32
hash [32]byte
}
type Inv struct {
vects []InvVect
}
func (p *Inv) Read(reader io.Reader, length uint32) (err error) {
if length < (1 + 36) {
err = fmt.Errorf("too short")
return
}
var ninv int
var s uint32
ninv, s, err = ReadVarInt(reader, length)
if err != nil {
return
}
length -= s
fmt.Println(ninv)
p.vects = make([]InvVect, ninv)
invhash := make([]byte, 32)
for i := 0; i < ninv; i++ {
if length < 36 {
err = fmt.Errorf("too short")
return
}
length -= 36
vect := &p.vects[i]
err = binary.Read(reader, binary.LittleEndian, &vect.invtype)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &vect.hash)
if err != nil {
return
}
fmt.Println(vect.invtype)
switch vect.invtype {
case 0: // err
case 1: // tx
case 2: // blk
for i := 0; i < 32; i++ {
invhash[i] = vect.hash[31-i]
}
fmt.Println(hex.EncodeToString(invhash))
case 3: // fblk
}
}
return
}
func (p *Inv) SendGetData(writer io.Writer, magic uint32) (err error) {
header := Header{}
header.Init(magic, "getdata")
var payloadraw bytes.Buffer
err = WriteVarInt(&payloadraw, uint64(len(p.vects)))
if err != nil {
return
}
for i := 0; i < len(p.vects); i++ {
vect := &p.vects[i]
binary.Write(&payloadraw, binary.LittleEndian, &vect.invtype)
binary.Write(&payloadraw, binary.LittleEndian, &vect.hash)
}
img := payloadraw.Bytes()
header.length = uint32(len(img))
sum := dhash(img)
err = header.Write(writer, sum[:4])
if err != nil {
return
}
_, err = writer.Write(img)
return
}
type Addr struct {
}
func ReadVarInt(reader io.Reader, length uint32) (v int, s uint32, err error) {
var clen [1]byte
var n int
n, err = reader.Read(clen[:])
if err != nil || n != 1 {
return
}
s += 1
v = int(clen[0])
if v < 0xfd {
return
}
if v == 0xfd {
var v2 uint16
err = binary.Read(reader, binary.LittleEndian, &v2)
s += 2
v = int(v2)
return
}
if v == 0xfe {
var v2 uint32
err = binary.Read(reader, binary.LittleEndian, &v2)
s += 4
v = int(v2)
return
}
var v2 uint64
err = binary.Read(reader, binary.LittleEndian, &v2)
s += 8
v = int(v2)
return
}
func WriteVarInt(writer io.Writer, v uint64) (err error) {
if v < 0xfd {
x := byte(v)
err = binary.Write(writer, binary.LittleEndian, &x)
return
}
if v <= 0xffff {
x := byte(0xfd)
err = binary.Write(writer, binary.LittleEndian, &x)
if err != nil {
return
}
y := uint16(v)
err = binary.Write(writer, binary.LittleEndian, &y)
return
}
if v <= 0xffffffff {
x := byte(0xfe)
err = binary.Write(writer, binary.LittleEndian, &x)
if err != nil {
return
}
y := uint32(v)
err = binary.Write(writer, binary.LittleEndian, &y)
return
}
x := byte(0xff)
err = binary.Write(writer, binary.LittleEndian, &x)
if err != nil {
return
}
err = binary.Write(writer, binary.LittleEndian, &v)
return
}
func ReadVarString(reader io.Reader, length uint32) (v string, s uint32, err error) {
var n int
n, s, err = ReadVarInt(reader, length)
if err != nil {
return
}
length -= s
if length < uint32(n) {
err = fmt.Errorf("too short")
return
}
s += uint32(n)
buf := make([]byte, n)
err = binary.Read(reader, binary.LittleEndian, buf)
if err != nil {
return
}
v = string(buf)
return
}
func (p *Addr) Read(reader io.Reader, length uint32) (err error) {
if length < (1 + 30) {
err = fmt.Errorf("too short")
return
}
var n int
var s uint32
n, s, err = ReadVarInt(reader, length)
if err != nil {
return
}
length -= s
fmt.Println(n)
var netaddr NetAddress
for i := 0; i < n; i++ {
err = netaddr.Read(reader, length, true)
if err != nil {
return
}
length -= 30
fmt.Println(netaddr.String())
}
return
}
func SendVersion(writer io.Writer, magic uint32, minver int32, conn net.Conn) (err error) {
header := Header{}
header.Init(magic, "version")
payload := Version{}
var sum4 []byte
header.length, sum4 = payload.Init(minver, conn, ".0", 0)
err = header.Write(writer, sum4)
if err != nil {
return
}
err = payload.Write(writer)
return
}
func SendGetAddr(writer io.Writer, magic uint32) (err error) {
header := Header{}
header.Init(magic, "getaddr")
sum := dhash(nil)
err = header.Write(writer, sum[:4])
return
}
func SendPong(writer io.Writer, magic uint32, nonce uint64) (err error) {
header := Header{}
header.Init(magic, "pong")
var payloadraw bytes.Buffer
binary.Write(&payloadraw, binary.LittleEndian, &nonce)
img := payloadraw.Bytes()
header.length = uint32(len(img))
sum := dhash(img)
err = header.Write(writer, sum[:4])
if err != nil {
return
}
_, err = writer.Write(img)
return
}
func Reverse(p []byte) {
l := len(p)
for i := 0; i < l/2; i++ {
p[i], p[l-1-i] = p[l-1-i], p[i]
}
}
func ReverseString(p []byte) string {
p2 := make([]byte, len(p))
copy(p2, p)
Reverse(p2)
return hex.EncodeToString(p2)
}
func SendGetHeaders(writer io.Writer, magic, minver uint32, from []byte, to []byte) (err error) {
header := Header{}
header.Init(magic, "getheaders")
var payloadraw bytes.Buffer
binary.Write(&payloadraw, binary.LittleEndian, &minver)
err = WriteVarInt(&payloadraw, 1)
if err != nil {
return
}
binary.Write(&payloadraw, binary.LittleEndian, &from)
binary.Write(&payloadraw, binary.LittleEndian, &to)
img := payloadraw.Bytes()
header.length = uint32(len(img))
sum := dhash(img)
err = header.Write(writer, sum[:4])
if err != nil {
return
}
_, err = writer.Write(img)
return
}
type BlockHeader struct { // 81bytes
version uint32
prev_block [32]byte
merkle_root [32]byte
timestamp uint32
bits uint32
nonce uint32
txn_count byte // always 0
}
func (p *BlockHeader) Read(reader io.Reader, length uint32) (err error) {
if length < 81 {
err = fmt.Errorf("too short")
return
}
err = binary.Read(reader, binary.LittleEndian, &p.version)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.prev_block)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.merkle_root)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.timestamp)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.bits)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.nonce)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &p.txn_count)
return
}
func (p *BlockHeader) Hash() []byte {
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, &p.version)
binary.Write(&buf, binary.LittleEndian, &p.prev_block)
binary.Write(&buf, binary.LittleEndian, &p.merkle_root)
binary.Write(&buf, binary.LittleEndian, &p.timestamp)
binary.Write(&buf, binary.LittleEndian, &p.bits)
binary.Write(&buf, binary.LittleEndian, &p.nonce)
hash := dhash(buf.Bytes())
return hash[:]
}
type BlockHeaders struct {
headers []BlockHeader
}
func (p *BlockHeaders) Read(reader io.Reader, length uint32) (err error) {
if length < 1 {
err = fmt.Errorf("too short")
return
}
var nheaders int
var s uint32
nheaders, s, err = ReadVarInt(reader, length)
if err != nil {
return
}
length -= s
fmt.Println(nheaders)
if nheaders == 0 {
fmt.Println("zero headers")
return
}
p.headers = make([]BlockHeader, nheaders)
for i := 0; i < nheaders; i++ {
header := &p.headers[i]
err = header.Read(reader, length)
if err != nil {
return
}
length -= 81
}
return
}
func sub(host string, port int, magic uint32, tsf bool, minver int32, dlheaders, dlblocks bool) (err error) {
conn, err2 := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
if err2 != nil {
return err2
}
writer := bufio.NewWriter(conn)
err = SendVersion(writer, magic, minver, conn)
if err != nil {
return
}
err = writer.Flush()
if err != nil {
return
}
// genesis block (block #0 monacoin)
//str := "ff9f1c0116d19de7c9963845e129f9ed1bfc0b376eb54fd7afa42e0d418c8bb6"
//height := 0
// block #690000 monacoin
str := "cea1f24259a327c1373c375bb733d43c5cf6b9e2a1a7a722bb1e1ed1a21f5805"
height := 690000
blkfrom, _ := hex.DecodeString(str)
Reverse(blkfrom)
// block #32000 monacoin
//str = "c0703986c1c6a9052478db5e52432e5a1e55d6b6362b85f0ffdbb61ce3311b77"
str = "0000000000000000000000000000000000000000000000000000000000000000"
blkto, _ := hex.DecodeString(str)
Reverse(blkto)
header := Header{}
reader := bufio.NewReader(conn)
for {
err = header.Read(reader)
if err != nil {
return
}
command := header.Command()
fmt.Println(command, header.length)
buf := make([]byte, header.length)
stopped := false
switch command {
case "version":
uastr := ""
payload := Version{}
uastr, err = payload.Read(reader, header.length)
if err != nil {
return
}
fmt.Println(uastr, payload.version, payload.start_height)
err = SendGetAddr(writer, magic)
if err != nil {
return
}
err = writer.Flush()
if err != nil {
return
}
case "verack":
case "alert":
err = binary.Read(reader, binary.LittleEndian, buf)
if err != nil {
return
}
case "ping":
var nonce uint64
err = binary.Read(reader, binary.LittleEndian, &nonce)
if err != nil {
return
}
err = SendPong(writer, magic, nonce)
if err != nil {
return
}
err = writer.Flush()
if err != nil {
return
}
case "addr":
addr := Addr{}
err = addr.Read(reader, header.length)
if err != nil {
return
}
if dlheaders {
err = SendGetHeaders(writer, magic, uint32(minver), blkfrom, blkto)
if err != nil {
return
}
err = writer.Flush()
if err != nil {
return
}
}
case "inv":
inv := Inv{}
err = inv.Read(reader, header.length)
if err != nil {
return
}
for _, vect := range inv.vects {
if vect.invtype == 2 {
blkto = vect.hash[:]
if dlheaders && stopped {
stopped = false
err = SendGetHeaders(writer, magic, uint32(minver), blkfrom, blkto)
if err != nil {
return
}
err = writer.Flush()
if err != nil {
return
}
}
break
}
}
if dlblocks {
err = inv.SendGetData(writer, magic)
if err != nil {
return
}
err = writer.Flush()
if err != nil {
return
}
}
case "block":
err = binary.Read(reader, binary.LittleEndian, buf)
if err != nil {
return
}
fmt.Println(hex.Dump(buf))
case "tx":
err = binary.Read(reader, binary.LittleEndian, buf)
if err != nil {
return
}
fmt.Println(hex.Dump(buf))
case "headers":
var blkheaders BlockHeaders
err = blkheaders.Read(reader, header.length)
if err != nil {
return
}
fmt.Println("from", ReverseString(blkheaders.headers[0].Hash()))
lasti := len(blkheaders.headers) - 1
blkfrom = blkheaders.headers[lasti].Hash()
strfrom := ReverseString(blkfrom)
height += len(blkheaders.headers)
fmt.Println("to ", strfrom, height)
strto := ReverseString(blkto)
if strfrom == strto {
fmt.Println("stop download headers")
stopped = true
} else {
err = SendGetHeaders(writer, magic, uint32(minver), blkfrom, blkto)
if err != nil {
return
}
err = writer.Flush()
if err != nil {
return
}
}
case "reject":
err = binary.Read(reader, binary.LittleEndian, buf)
if err != nil {
return
}
fmt.Println(hex.Dump(buf))
default:
err = fmt.Errorf("not support: " + command)
return
}
}
return
}
func main() {
dlheaders := flag.Bool("H", false, "download headers")
dlblocks := flag.Bool("b", false, "download blocks")
flag.Parse()
hosts := []string{ // monacoin
"111.104.32.204",
"111.89.180.104",
"121.108.182.249",
"153.120.63.28",
"153.121.54.43",
"191.233.32.153",
"202.181.101.205",
"218.110.146.61",
"219.94.248.221",
"220.216.10.94",
"36.55.226.160",
"49.135.11.184",
"59.157.5.163",
"61.197.187.158",
/*
"[2001:0:9d38:6ab8:1ca7:2c19:7c8e:b2bf]",
"[240f:41:215b:1:ef:68de:be1c:64c1]",
"[2001:0:5ef5:79fd:14f1:d95:3f57:fd97]",
"[2408:210:2848:e600:4594:1819:4f3a:4b06]",
"[2001:0:5ef5:79fb:2021:4b4:c3be:8e9d]",
"[2001:0:9d38:6ab8:3855:289d:24b4:7b0a]",
*/
}
host := hosts[rand.Int()%len(hosts)]
fmt.Println(host)
port := 9401 // monacoin
magic := uint32(0xdbb6c0fb) // monacoin
tsf := false
minver := int32(70002)
err := sub(host, port, magic, tsf, minver, *dlheaders, *dlblocks)
if err != nil {
fmt.Println(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment