<commit_before>package sia

import (
	"errors"
	"net"
	"net/rpc/jsonrpc"
	"strconv"
	"time"
)

const timeout = time.Second * 5

// A NetAddress contains the information needed to contact a peer over the
// Internet.
type NetAddress struct {
	Host string
	Port uint16
}

// TBD
var BootstrapPeers = []NetAddress{}

// An RPCServer handles RPCs. This implementation currently uses JSON over TCP.
type RPCServer struct {
	listener    net.Listener
	addressbook map[NetAddress]struct{}
}

func NewRPCServer(port uint16) (rpcs *RPCServer, err error) {
	tcpServ, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
	if err != nil {
		return
	}
	rpcs = &RPCServer{
		listener:    tcpServ,
		addressbook: make(map[NetAddress]struct{}),
	}
	go rpcs.handleRPC()
	return
}

// Close closes the TCP connection used by the server. This will cause the
// handleRPC goroutine to terminate.
func (rpcs *RPCServer) Close() {
	rpcs.listener.Close()
}

// handleRPC runs in the background, accepting incoming RPCs and serving them.
// handleRPC will return after RPCServer.Close() is called, because the
// Accept() call will fail.
func (rpcs *RPCServer) handleRPC() {
	for {
		conn, err := rpcs.listener.Accept()
		if err != nil {
			return
		}
		go jsonrpc.ServeConn(conn)
	}
}

// RPC makes a remote procedure call on a NetAddress.
func (rpcs *RPCServer) RPC(addr NetAddress, proc string, args interface{}, resp interface{}) error {
	conn, err := jsonrpc.Dial("tcp", net.JoinHostPort(addr.Host, strconv.Itoa(int(addr.Port))))
	if err != nil {
		return err
	}
	defer conn.Close()

	select {
	case call := <-conn.Go(proc, args, resp, nil).Done:
		return call.Error
	case <-time.After(timeout):
		return errors.New("request timed out")
	}
}

// PeerList is an RPC that fills 'addr' with at most 'num' peers known to the RPCServer.
// TODO: add a random set of peers (map iteration may already handle this...)
func (rpcs *RPCServer) PeerList(num int, addr *map[NetAddress]struct{}) error {
	if num > 10 {
		num = 10
	}
	for i := range rpcs.addressbook {
		if num <= 0 {
			break
		}
		(*addr)[i] = struct{}{}
		num--
	}
	return nil
}

// Bootstrap calls Request on a predefined set of peers in order to build up an
// initial peer list. It returns the number of peers added.
func (rpcs *RPCServer) Bootstrap() int {
	n := len(rpcs.addressbook)
	for _, host := range BootstrapPeers {
		rpcs.RPC(host, "RPCServer.PeerList", 10, &rpcs.addressbook)
	}
	return len(rpcs.addressbook) - n<commit_msg>first stab at TCP<commit_after>package sia

import (
	//"errors"
	"net"
	"strconv"
	"time"
)

const (
	timeout   = time.Second * 5
	maxMsgLen = 1 << 16
)

// A NetAddress contains the information needed to contact a peer over the
// Internet.
type NetAddress struct {
	Host string
	Port uint16
}

// TBD
var BootstrapPeers = []NetAddress{}

// A TCPServer sends and receives messages. It also maintains an address book
// of peers to broadcast to and make requests of.
type TCPServer struct {
	net.Listener
	addressbook map[NetAddress]struct{}
}

func NewTCPServer(port uint16) (tcps *TCPServer, err error) {
	tcpServ, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
	if err != nil {
		return
	}
	tcps = &TCPServer{
		Listener:    tcpServ,
		addressbook: make(map[NetAddress]struct{}),
	}
	go tcps.listen()
	return
}

// listen runs in the background, accepting incoming connections and serving
// them. listen will return after TCPServer.Close() is called, because the
// Accept() call will fail.
func (tcps *TCPServer) listen() {
	for {
		conn, err := tcps.Accept()
		if err != nil {
			return
		}
		// it is the handler's responsibility to close the connection
		go tcps.handleConn(conn)
	}
}

// handleConn reads header data from a connection, unmarshals the data
// structures it contains, and routes the data to other functions for
// processing.
func (tcps *TCPServer) handleConn(conn net.Conn) {
	defer conn.Close()
	var (
		msgType   = make([]byte, 1)
		msgLenBuf = make([]byte, 4)
		msgData   []byte // length determined by msgLen
	)
	// TODO: make this DRYer?
	if n, err := conn.Read(msgType); err != nil || n != 1 {
		// TODO: log error
		return
	}
	if n, err := conn.Read(msgLenBuf); err != nil || n != 4 {
		// TODO: log error
		return
	}
	msgLen := DecUint64(msgLenBuf)
	if msgLen > maxMsgLen {
		// TODO: log error
		return
	}
	msgData = make([]byte, msgLen)
	if n, err := conn.Read(msgData); err != nil || uint64(n) != msgLen {
		// TODO: log error
		return
	}

	switch msgType[0] {
	// Block
	case 'B':
		var b Block
		if err := Unmarshal(msgData, &b); err != nil {
			// TODO: log error
			return
		}
		//state.ProcessBlock(b)?

	// Transaction
	case 'T':
		var t Transaction
		if err := Unmarshal(msgData, &t); err != nil {
			// TODO: log error
			return
		}
		//state.ProcessTransaction(t)?

	// Unknown
	default:
		// TODO: log error
	}
	return
}

// send initiates a TCP connection and writes a message to it.
// TODO: add timeout
func (tcps *TCPServer) send(msg []byte, addr NetAddress) (err error) {
	conn, err := net.Dial("tcp", net.JoinHostPort(addr.Host, strconv.Itoa(int(addr.Port))))
	if err != nil {
		return
	}
	_, err = conn.Write(msg)
	return
}

// PeerList is an RPC that fills 'addr' with at most 'num' peers known to the TCPServer.
// TODO: add a random set of peers (map iteration may already handle this...)
func (tcps *TCPServer) PeerList(num int, addr *map[NetAddress]struct{}) error {
	//
	return nil
}

// Bootstrap calls Request on a predefined set of peers in order to build up an
// initial peer list. It returns the number of peers added.
func (tcps *TCPServer) Bootstrap() int {
	n := len(tcps.addressbook)
	// for _, host := range BootstrapPeers {

	// }
	return len(tcps.addressbook) - n
{'fraction_non_alphanumeric': 0.12679258481986708, 'fraction_numerical': 0.005071703392794683, 'mean_word_length': 3.4436674436674437, 'pattern_counts': {'":': 2, '<': 8, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3985347', 'n_tokens_mistral': 2025, 'n_tokens_neox': 1924, 'n_words': 671}