Last active
August 26, 2017 00:40
-
-
Save ohac/d1f8e693b66d686c1b40d9c2fe039c26 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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