forked from broderickhyman/albiondata-client
-
Notifications
You must be signed in to change notification settings - Fork 40
/
Copy pathuploader_http_pow.go
152 lines (129 loc) · 3.55 KB
/
uploader_http_pow.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package client
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"runtime"
"strconv"
"strings"
"github.com/ao-data/albiondata-client/log"
)
type httpUploaderPow struct {
baseURL string
transport *http.Transport
}
type Pow struct {
Key string `json:"key"`
Wanted string `json:"wanted"`
}
// newHTTPUploaderPow creates a new HTTP uploader
func newHTTPUploaderPow(url string) uploader {
if !ConfigGlobal.NoCPULimit {
// Limit to 25% of available cpu cores
procs := runtime.NumCPU() / 4
if procs < 1 {
procs = 1
}
runtime.GOMAXPROCS(procs)
}
return &httpUploaderPow{
baseURL: strings.Replace(url, "http+pow", "http", -1),
transport: &http.Transport{},
}
}
func (u *httpUploaderPow) getPow(target interface{}) {
log.Debugf("GETTING POW")
fullURL := u.baseURL + "/pow"
client := &http.Client{}
req, _ := http.NewRequest("GET", fullURL, nil)
req.Header.Add("User-Agent", fmt.Sprintf("albiondata-client/%v", version))
resp, err := client.Do(req)
if err != nil {
log.Errorf("Error in Pow Get request: %v", err)
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
log.Errorf("Got bad response code: %v", resp.StatusCode)
return
}
json.NewDecoder(resp.Body).Decode(target)
if err != nil {
log.Errorf("Error in parsing Pow Get request: %v", err)
return
}
}
// Prooves to the server that a pow was solved by submitting
// the pow's key, the solution and a nats msg as a POST request
// the topic becomes part of the URL
func (u *httpUploaderPow) uploadWithPow(pow Pow, solution string, natsmsg []byte, topic string, serverid int, identifier string) {
fullURL := u.baseURL + "/pow/" + topic
client := &http.Client{}
data := url.Values{
"key": {pow.Key},
"solution": {solution},
"serverid": {strconv.Itoa(serverid)},
"natsmsg": {string(natsmsg)},
"identifier": {string(identifier)},
}
req, _ := http.NewRequest("POST", fullURL, strings.NewReader(data.Encode()))
req.Header.Add("User-Agent", fmt.Sprintf("albiondata-client/%v", version))
resp, err := client.Do(req)
if err != nil {
log.Errorf("Error while prooving pow: %v", err)
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
log.Errorf("HTTP Error while prooving pow. returned: %v (%v)", resp.StatusCode, string(body))
return
}
log.Infof("Successfully sent ingest request to %v", u.baseURL)
}
// Generates a random hex string e.g.: faa2743d9181dca5
func randomHex(n int) (string, error) {
bytes := make([]byte, n)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return hex.EncodeToString(bytes), nil
}
// Converts a string to bits e.g.: 0110011...
func toBinaryBytes(s string) string {
var buffer bytes.Buffer
for i := 0; i < len(s); i++ {
fmt.Fprintf(&buffer, "%08b", s[i])
}
return fmt.Sprintf("%s", buffer.Bytes())
}
// Solves a pow looping through possible solutions
// until a correct one is found
// returns the solution
func solvePow(pow Pow) string {
solution := ""
for {
randhex, _ := randomHex(8)
if strings.HasPrefix(toBinaryBytes(fmt.Sprintf("%x", sha256.Sum256([]byte("aod^"+randhex+"^"+pow.Key)))), pow.Wanted) {
log.Debugf("SOLVED!")
solution = randhex
break
}
}
return solution
}
func (u *httpUploaderPow) sendToIngest(body []byte, topic string, state *albionState, identifier string) {
pow := Pow{}
u.getPow(&pow)
solution := solvePow(pow)
u.uploadWithPow(pow, solution, body, topic, state.AODataServerID, identifier)
}