package main
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"net"
"strings"
"time"
)
const (
Magic uint32 = 0xD9B4BEF9
ProtocolVersion uint32 = 70017
ServerAddress = "127.0.0.1:8333"
MaxHeadersToSend = 1999
)
func main() {
fmt.Println("\n ┓ • • bigpiang by Kevin McSheehan (pad)")
fmt.Println(" ┣┓┓╋┏┓┓┏┓┏┓┏┓ Disclaimer: Benchmark machines that you own.")
fmt.Println(" ┗┛┗┗┣┛┗┗┻┛┗┗┫ x.com/123456\n")
var numGoroutines int
fmt.Print("Enter the number of goroutines: ")
fmt.Scanln(&numGoroutines)
var benchmarkType string
fmt.Print("Enter the benchmark type (headers/ping): ")
fmt.Scanln(&benchmarkType)
for i := 0; i < numGoroutines; i++ {
if benchmarkType == "headers" {
go processBlocks(i)
} else if benchmarkType == "ping" {
go sendPings(i)
} else {
fmt.Println("Invalid benchmark type. Exiting.")
return
}
}
select {}
}
func sendPings(threadNum int) {
conn := connectAndHandshake()
fmt.Printf("Now benchmarking %s...\n", ServerAddress)
for {
if err := sendMessage(conn, "ping", nil); err != nil {
conn = connectAndHandshake()
}
}
}
func processBlocks(threadNum int) {
startHash := "0000000000000000000000000000000000000000000000000000000000000000"
stopHash := "0000000000000000000000000000000000000000000000000000000000000000"
conn := connectAndHandshake()
fmt.Printf("Now benchmarking %s...\n", ServerAddress)
for {
if err := requestBlockHeaders(conn, startHash, stopHash); err != nil {
conn = connectAndHandshake()
}
}
}
func requestBlockHeaders(conn net.Conn, startHash, stopHash string) error {
payload := createGetHeadersPayload(startHash, stopHash)
if err := sendMessage(conn, "getheaders", payload); err != nil {
return err
}
for {
command, _, err := receiveMessage(conn)
if err != nil {
return err
}
if command == "headers" {
break
}
}
return nil
}
func connectAndHandshake() net.Conn {
var conn net.Conn
var err error
for {
conn, err = net.Dial("tcp", ServerAddress)
if err == nil {
if err = handshake(conn); err == nil {
break
}
}
}
return conn
}
func handshake(conn net.Conn) error {
if err := sendMessage(conn, "version", createVersionPayload()); err != nil {
return err
}
handshakeCompleted := false
for !handshakeCompleted {
command, _, err := receiveMessage(conn)
if err != nil {
return err
}
switch command {
case "version":
if err := sendMessage(conn, "verack", nil); err != nil {
return err
}
case "verack":
handshakeCompleted = true
}
}
return nil
}
func sendMessage(conn net.Conn, command string, payload []byte) error {
_, err := conn.Write(packCommand(command, payload))
return err
}
func receiveMessage(conn net.Conn) (string, []byte, error) {
header := make([]byte, 24)
if _, err := io.ReadFull(conn, header); err != nil {
return "", nil, err
}
length := binary.LittleEndian.Uint32(header[16:20])
command := strings.TrimRight(string(header[4:16]), "\x00")
payload := make([]byte, length)
if length > 0 {
if _, err := io.ReadFull(conn, payload); err != nil {
return "", nil, err
}
firstSHA := sha256.Sum256(payload)
checksum := sha256.Sum256(firstSHA[:])
if !bytes.Equal(checksum[:4], header[20:24]) {
return "", nil, fmt.Errorf("invalid checksum")
}
}
return command, payload, nil
}
func packCommand(command string, payload []byte) []byte {
var cmd [12]byte
copy(cmd[:], command)
firstSHA := sha256.Sum256(payload)
checksum := sha256.Sum256(firstSHA[:])
packet := make([]byte, 24+len(payload))
binary.LittleEndian.PutUint32(packet[0:4], Magic)
copy(packet[4:16], cmd[:])
binary.LittleEndian.PutUint32(packet[16:20], uint32(len(payload)))
copy(packet[20:24], checksum[:4])
copy(packet[24:], payload)
return packet
}
func createVersionPayload() []byte {
var addr [26]byte
binary.LittleEndian.PutUint64(addr[:8], 1)
payload := make([]byte, 85)
binary.LittleEndian.PutUint32(payload, ProtocolVersion)
binary.LittleEndian.PutUint64(payload[4:12], uint64(time.Now().Unix()))
copy(payload[12:38], addr[:])
copy(payload[38:64], addr[:])
binary.LittleEndian.PutUint64(payload[64:72], 0)
copy(payload[72:80], []byte("/golang-client:0.0.1/"))
binary.LittleEndian.PutUint32(payload[80:84], 0)
payload[84] = 0
return payload
}
func createGetHeadersPayload(startHash, stopHash string) []byte {
startHashBytes, _ := hex.DecodeString(startHash)
stopHashBytes, _ := hex.DecodeString(stopHash)
reverseBytes(startHashBytes)
reverseBytes(stopHashBytes)
payload := make([]byte, 2+4+32+32)
binary.LittleEndian.PutUint16(payload[0:2], MaxHeadersToSend)
binary.LittleEndian.PutUint32(payload[2:6], ProtocolVersion)
copy(payload[6:38], startHashBytes)
copy(payload[38:70], stopHashBytes)
return payload
}
func reverseBytes(b []byte) {
for i := len(b)/2 - 1; i >= 0; i-- {
opp := len(b) - 1 - i
b[i], b[opp] = b[opp], b[i]
}
}