248 lines
5.6 KiB
Go
248 lines
5.6 KiB
Go
package pool
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"dappco.re/go/core/proxy"
|
|
)
|
|
|
|
type disconnectCountingListener struct {
|
|
mu sync.Mutex
|
|
count int
|
|
}
|
|
|
|
func (l *disconnectCountingListener) OnJob(job proxy.Job) {}
|
|
|
|
func (l *disconnectCountingListener) OnResultAccepted(sequence int64, accepted bool, errorMessage string) {
|
|
}
|
|
|
|
func (l *disconnectCountingListener) OnDisconnect() {
|
|
l.mu.Lock()
|
|
l.count++
|
|
l.mu.Unlock()
|
|
}
|
|
|
|
func (l *disconnectCountingListener) Count() int {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
return l.count
|
|
}
|
|
|
|
func TestStratumClient_ReadLoop_Ugly(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
listener := &disconnectCountingListener{}
|
|
client := &StratumClient{
|
|
listener: listener,
|
|
conn: serverConn,
|
|
}
|
|
|
|
go client.readLoop()
|
|
|
|
payload := make([]byte, 16385)
|
|
for index := range payload {
|
|
payload[index] = 'a'
|
|
}
|
|
payload = append(payload, '\n')
|
|
writeErr := make(chan error, 1)
|
|
go func() {
|
|
_, err := clientConn.Write(payload)
|
|
writeErr <- err
|
|
}()
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
if got := listener.Count(); got != 1 {
|
|
t.Fatalf("expected oversized line to close the connection, got %d disconnect callbacks", got)
|
|
}
|
|
|
|
select {
|
|
case err := <-writeErr:
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
default:
|
|
}
|
|
}
|
|
|
|
func TestStratumClient_Disconnect_Good(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
listener := &disconnectCountingListener{}
|
|
client := &StratumClient{
|
|
listener: listener,
|
|
conn: serverConn,
|
|
}
|
|
|
|
go client.readLoop()
|
|
time.Sleep(10 * time.Millisecond)
|
|
client.Disconnect()
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
if got := listener.Count(); got != 1 {
|
|
t.Fatalf("expected one disconnect callback, got %d", got)
|
|
}
|
|
}
|
|
|
|
type resultCapturingListener struct {
|
|
mu sync.Mutex
|
|
sequence int64
|
|
accepted bool
|
|
errorMessage string
|
|
results int
|
|
disconnects int
|
|
}
|
|
|
|
func (l *resultCapturingListener) OnJob(job proxy.Job) {}
|
|
|
|
func (l *resultCapturingListener) OnResultAccepted(sequence int64, accepted bool, errorMessage string) {
|
|
l.mu.Lock()
|
|
l.sequence = sequence
|
|
l.accepted = accepted
|
|
l.errorMessage = errorMessage
|
|
l.results++
|
|
l.mu.Unlock()
|
|
}
|
|
|
|
func (l *resultCapturingListener) OnDisconnect() {
|
|
l.mu.Lock()
|
|
l.disconnects++
|
|
l.mu.Unlock()
|
|
}
|
|
|
|
func (l *resultCapturingListener) Snapshot() (int64, bool, string, int, int) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
return l.sequence, l.accepted, l.errorMessage, l.results, l.disconnects
|
|
}
|
|
|
|
func TestStratumClient_HandleMessage_Bad(t *testing.T) {
|
|
listener := &resultCapturingListener{}
|
|
client := &StratumClient{
|
|
listener: listener,
|
|
sessionID: "session-1",
|
|
}
|
|
|
|
client.handleMessage(jsonRPCResponse{
|
|
ID: 7,
|
|
Error: &jsonRPCErrorBody{
|
|
Code: -1,
|
|
Message: "Low difficulty share",
|
|
},
|
|
})
|
|
|
|
sequence, accepted, errorMessage, results, disconnects := listener.Snapshot()
|
|
if sequence != 7 || accepted || errorMessage != "Low difficulty share" || results != 1 {
|
|
t.Fatalf("expected rejected submit callback, got sequence=%d accepted=%v error=%q results=%d", sequence, accepted, errorMessage, results)
|
|
}
|
|
if disconnects != 0 {
|
|
t.Fatalf("expected no disconnect on submit rejection, got %d", disconnects)
|
|
}
|
|
}
|
|
|
|
func TestStratumClient_HandleMessage_Good(t *testing.T) {
|
|
listener := &resultCapturingListener{}
|
|
client := &StratumClient{
|
|
listener: listener,
|
|
sessionID: "session-1",
|
|
}
|
|
|
|
client.handleMessage(jsonRPCResponse{
|
|
ID: 7,
|
|
})
|
|
|
|
sequence, accepted, errorMessage, results, disconnects := listener.Snapshot()
|
|
if sequence != 7 || !accepted || errorMessage != "" || results != 1 {
|
|
t.Fatalf("expected accepted submit callback, got sequence=%d accepted=%v error=%q results=%d", sequence, accepted, errorMessage, results)
|
|
}
|
|
if disconnects != 0 {
|
|
t.Fatalf("expected no disconnect on submit accept, got %d", disconnects)
|
|
}
|
|
}
|
|
|
|
func TestStratumClient_HandleMessage_Ugly(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
listener := &resultCapturingListener{}
|
|
client := &StratumClient{
|
|
listener: listener,
|
|
conn: serverConn,
|
|
}
|
|
defer client.Disconnect()
|
|
|
|
client.handleMessage(jsonRPCResponse{
|
|
ID: 1,
|
|
Error: &jsonRPCErrorBody{
|
|
Code: -1,
|
|
Message: "Unauthenticated",
|
|
},
|
|
})
|
|
|
|
_, _, _, results, disconnects := listener.Snapshot()
|
|
if results != 0 {
|
|
t.Fatalf("expected login rejection not to be reported as a share result, got %d results", results)
|
|
}
|
|
if disconnects != 1 {
|
|
t.Fatalf("expected login rejection to disconnect once, got %d", disconnects)
|
|
}
|
|
}
|
|
|
|
func TestStratumClient_Login_Good(t *testing.T) {
|
|
serverConn, clientConn := net.Pipe()
|
|
defer clientConn.Close()
|
|
|
|
client := &StratumClient{
|
|
config: proxy.PoolConfig{
|
|
User: "WALLET",
|
|
Pass: "legacy",
|
|
Password: "preferred",
|
|
RigID: "rig-alpha",
|
|
Algo: "cn/r",
|
|
},
|
|
conn: serverConn,
|
|
}
|
|
|
|
writeDone := make(chan struct{})
|
|
go func() {
|
|
client.Login()
|
|
close(writeDone)
|
|
}()
|
|
|
|
buffer := make([]byte, 2048)
|
|
n, err := clientConn.Read(buffer)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var request jsonRPCRequest
|
|
if err := json.Unmarshal(buffer[:n], &request); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
params, ok := request.Params.(map[string]interface{})
|
|
if !ok {
|
|
t.Fatalf("expected login params map, got %T", request.Params)
|
|
}
|
|
if got := params["pass"]; got != "preferred" {
|
|
t.Fatalf("expected preferred password, got %v", got)
|
|
}
|
|
if got := params["rigid"]; got != "rig-alpha" {
|
|
t.Fatalf("expected rigid field to be forwarded, got %v", got)
|
|
}
|
|
if got := params["algo"]; got == nil {
|
|
t.Fatal("expected algo extension to be forwarded")
|
|
}
|
|
|
|
client.Disconnect()
|
|
select {
|
|
case <-writeDone:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("expected login write to complete")
|
|
}
|
|
}
|