Browse Source

Merge pull request #101 from notgiven688/cnv4

Cnv4
pull/123/head
notgiven688 2 years ago
committed by GitHub
parent
commit
8a3e42cf46
24 changed files with 1949 additions and 1552 deletions
  1. +41
    -11
      README.md
  2. +1
    -0
      SDK/miner_compressed/mine.html
  3. +30
    -41
      SDK/miner_compressed/webmr.js
  4. +4
    -0
      SDK/miner_raw/mine.html
  5. +1
    -1
      SDK/miner_raw/miner/cn.js
  6. +3
    -3
      SDK/miner_raw/miner/miner.js
  7. +9
    -3
      SDK/miner_raw/miner/worker.js
  8. +1
    -21
      hash_cn/correct_hashes.txt
  9. +475
    -494
      hash_cn/libhash/cryptonight.c
  10. +1
    -1
      hash_cn/libhash/cryptonight.h
  11. +3
    -3
      hash_cn/libhash/main.c
  12. +1
    -8
      hash_cn/webassembly/Makefile
  13. +482
    -494
      hash_cn/webassembly/cryptonight.c
  14. +1
    -1
      hash_cn/webassembly/cryptonight.h
  15. +3
    -7
      hash_cn/webassembly/main.c
  16. +35
    -61
      hash_cn/webassembly/simple_profile.html
  17. +444
    -0
      hash_cn/webassembly/variant4_random_math.h
  18. +33
    -12
      server/Server/AlgorithmHelper.cs
  19. +236
    -217
      server/Server/Fleck/WebSocketConnection.cs
  20. +17
    -36
      server/Server/PoolConnection.cs
  21. +6
    -8
      server/Server/PoolList.cs
  22. +74
    -84
      server/Server/Program.cs
  23. +2
    -1
      server/Server/Server.csproj
  24. +46
    -45
      server/pools.json

+ 41
- 11
README.md View File

@ -1,6 +1,6 @@
# webminerpool
**Complete sources** for a Monero (cryptonight/cryptonight-lite) webminer. **Hard fork ready**.
**Complete sources** for a Monero (cryptonight and variants) webminer. **Hard fork ready**.
###
@ -14,28 +14,55 @@ Thanks to [nierdz](https://github.com/notgiven688/webminerpool/pull/62) there is
# Will the hardfork (<span style="color:red">March 2019</span>) be supported?
Yes. Work is in progress. Updated code will be available at least one week before fork day.
Yes. Update to the current Master branch and you should be fine. Much work was put into optimizing the miner
once again.
# What is new?
Unfortunately the newest version of cryptonight, cn/r (cnv4), does perform poorly on the browser. To partly compensate for this I added cn-pico/trtl and cn-half. If you mine to a pool
which allows autoswitching algorithms (at the moment [moneroocean.stream](https://moneroocean.stream)) webminerpool will automatically
switch to an algorithm which is most profitable at the moment.
## Update notes: It is beneficial to first update your clients (step_A) to the newest mining script (Version 7, the version number can be found in the "handshake-data" within the source code). Wait a few days till your user base followed (because of browser caching) and then update to the newest server version (step_B). This is recommended because of the possibility that the new server negotiates a mining algorithm with the pool, which is not supported by an old client (and therefore is not forwarded by the server).
## step_A and step_B have to be performed before March 9th!
# Currently supported algorithms
| # | xmrig short notation | webminerpool internal | description |
| -- | --------------| --------------------------------- | ------------------------------------------------ |
| 1 | cn | algo="cn", variant=-1 | autodetect cryptonight variant (block.major - 6) |
| 2 | cn/0 | algo="cn", variant=0 | original cryptonight |
| 3 | cn/1 | algo="cn", variant=1 | also known as monero7 and cryptonight v7 |
| 4 | cn/2 | algo="cn", variant=2 or 3 | cryptonight variant 2 |
| 5 | cn/r | algo="cn", variant=4 | cryptonight variant 4 also known as cryptonightR |
| 6 | cn-lite | algo="cn-lite", variant=-1 | same as #1 with memory/2, iterations/2 |
| 7 | cn-lite/0 | algo="cn-lite", variant=0 | same as #2 with memory/2, iterations/2 |
| 8 | cn-lite/1 | algo="cn-lite", variant=1 | same as #3 with memory/2, iterations/2 |
| 9 | cn-pico/trtl | algo="cn-pico", variant=2 or 3 | same as #4 with memory/8, iterations/8 |
| 10 | cn-half | algo="cn-half", variant=2 or 3 | same as #4 with memory/1, iterations/2 |
# What is new?
- **March 1, 2019**
- Added cryptonight v4. Hard fork ready! Added support for cn/half and cn-pico/trtl. Added support for auto-algo switching. (**client-side** / **server-side**)
- **September 27, 2018**
- Added cryptonight v2. Hard fork ready! (**client-side** / **server-side**).
- Added cryptonight v2. Hard fork ready! (**client-side** / **server-side**)
- **June 15, 2018**
- Support for blocks with more than 2^8 transactions. (**client-side** / **server-side**).
- Support for blocks with more than 2^8 transactions. (**client-side** / **server-side**)
- **May 21, 2018**
- Support for multiple open tabs. Only one tab is constantly mining if several tabs/browser windows are open. (**client-side**).
- **May 6, 2018**
- Check if webasm is available. Please update the script. (**client-side**).
- Check if webasm is available. Please update the script. (**client-side**)
- **May 5, 2018**
- Support for multiple websocket servers in the client script (load-distribution).
- **April 26, 2018**
- A further improvement to fully support the [extended stratum protocol](https://github.com/xmrig/xmrig-proxy/blob/dev/doc/STRATUM_EXT.md#mining-algorithm-negotiation) (**server-side**).
- A simple json config-file holding all available pools (**server-side**).
- A further improvement to fully support the [extended stratum protocol](https://github.com/xmrig/xmrig-proxy/blob/dev/doc/STRATUM_EXT.md#mining-algorithm-negotiation) (**server-side**)
- A simple json config-file holding all available pools (**server-side**)
- **April 22, 2018**
- All cryptonight and cryptonight-light based coins are supported in a single miner. [Stratum extension](https://github.com/xmrig/xmrig-proxy/blob/dev/doc/STRATUM_EXT.md#mining-algorithm-negotiation) were implemented: The server now takes pool suggestions (algorithm and variant) into account. Defaults can be specified for each pool - that makes it possible to mine coins like Stellite, Turtlecoin,.. (**client/server-side**)
@ -198,7 +225,10 @@ To use this snippet, you need to define `$WEBMINER_DONATION_LEVEL`, `$WEBMINER_D
By default a server-side 3% dev-donation is configured. Leaving this fee at the current level is highly appreciated. If you want
to turn it off or just find the content of this repository helpful consider a one time donation, the addresses are as follows:
- BTC - 175jHD6ErDhZHoW4u54q5mr98L9KSgm56D
- XMR - 49kkH7rdoKyFsb1kYPKjCYiR2xy1XdnJNAY1e7XerwQFb57XQaRP7Npfk5xm1MezGn2yRBz6FWtGCFVKnzNTwSGJ3ZrLtHU
- AEON - WmtUFkPrboCKzL5iZhia4iNHKw9UmUXzGgbm5Uo3HPYwWcsY1JTyJ2n335gYiejNysLEs1G2JZxEm3uXUX93ArrV1yrXDyfPH
```
BTC - 175jHD6ErDhZHoW4u54q5mr98L9KSgm56D
XMR - 49kkH7rdoKyFsb1kYPKjCYiR2xy1XdnJNAY1e7XerwQFb57XQaRP7Npfk5xm1MezGn2yRBz6FWtGCFVKnzNTwSGJ3ZrLtHU
AEON - WmtUFkPrboCKzL5iZhia4iNHKw9UmUXzGgbm5Uo3HPYwWcsY1JTyJ2n335gYiejNysLEs1G2JZxEm3uXUX93ArrV1yrXDyfPH
```

+ 1
- 0
SDK/miner_compressed/mine.html View File

@ -27,6 +27,7 @@
startMining("moneroocean.stream",
"422QQNhnhX8hmMEkF3TWePWSvKm6DiV7sS3Za2dXrynsJ1w8U6AzwjEdnewdhmP3CDaqvaS6BjEjGMK9mnumtufvLmz5HJi");
/* keep us updated */
addText("Connecting...");

+ 30
- 41
SDK/miner_compressed/webmr.js
File diff suppressed because it is too large
View File


+ 4
- 0
SDK/miner_raw/mine.html View File

@ -1,6 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<!--A TEXT FIELD-->

+ 1
- 1
SDK/miner_raw/miner/cn.js
File diff suppressed because it is too large
View File


+ 3
- 3
SDK/miner_raw/miner/miner.js View File

@ -62,7 +62,7 @@ var openWebSocket = function () {
}
var splitted = server.split(";")
var chosen = splitted[Math.floor(Math.random() * Math.floor(splitted.length))];
var chosen = splitted[Math.floor(Math.random() * splitted.length)];
ws = new WebSocket(chosen);
@ -165,7 +165,7 @@ function startMiningWithId(loginid, numThreads = -1, userid = "") {
identifier: "handshake",
loginid: loginid,
userid: userid,
version: 6
version: 7
};
startBroadcast(() => { addWorkers(numThreads); reconnector(); });
@ -185,7 +185,7 @@ function startMining(pool, login, password = "", numThreads = -1, userid = "") {
login: login,
password: password,
userid: userid,
version: 6
version: 7
};
startBroadcast(() => { addWorkers(numThreads); reconnector(); });

+ 9
- 3
SDK/miner_raw/miner/worker.js View File

@ -4,7 +4,7 @@
importScripts('cn.js'); // imports the cn.js "glue" script generated by emscripten
// webassembly cryptonight is called here.
var cn = Module.cwrap('hash_cn', 'string', ['string', 'string', 'number', 'number']);
var cn = Module.cwrap('hash_cn', 'string', ['string', 'number', 'number', 'number']);
// A few helper (string) functions to help us working with the hex string
// which is used
@ -44,12 +44,18 @@ onmessage = function (e) {
var target = hex2int(job.target);
var inonce = getRandomInt(0, 0xFFFFFFFF);
hexnonce = int2hex(inonce);
var blob = job.blob.substring(0,78) + hexnonce + job.blob.substring(86,job.blob.length);
try {
if(job.algo === "cn")
hash = cn(job.blob, hexnonce, 0, job.variant);
hash = cn(blob, 0, job.variant, job.height);
else if(job.algo === "cn-lite")
hash = cn(job.blob, hexnonce, 1, job.variant);
hash = cn(blob, 1, job.variant, job.height);
else if(job.algo === "cn-pico")
hash = cn(blob, 2, job.variant, job.height);
else if(job.algo === "cn-half")
hash = cn(blob, 3, job.variant, job.height);
else throw "algorithm not supported!";
var hashval = hex2int(hash.substring(56, 64));

+ 1
- 21
hash_cn/correct_hashes.txt View File

@ -1,21 +1 @@
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Input 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Cryptonight 1ddd6d4c6b9c41a89daa323b1e140e62d5ebf40a5962028cd1b4acd68deed830
Cryptonight Light 4c3428f39e1f9ecda3b0726fd4f4fca62843597c480f033ae38d113282b273bf
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Input 01111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113
Cryptonight 9708d6f60fd9505c43b7a62d9ba271703e6128518088bcc5d837bc720aa91d15
Cryptonight Light 278fe67e8d9e7e8b0c2039fe7b9c6bdcb071cd43595ce282648e9e8ae290080e
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Input 11000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Cryptonight v7 a67b43084e5368debd3da0e448668fb362e70e8f6b3a5e082d0477fecc52302b
Cryptonight Light v7 2c7a31aae3adf05cb845fb081c4504f71562b30dfd6a1792ef9f3d6db93e0481
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Input 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113
Cryptonight v7 843ae6fc006a6fa74c247ce64368f78c6f4af804446c9003442b67e4bb1fdaf7
Cryptonight Light v7 c2e3bd88bffd1bd7855af2dae2a52fef6efd36f00db514a6594718c5b67fab21
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
see simple_profile.html in hash_cn/webassembly

+ 475
- 494
hash_cn/libhash/cryptonight.c
File diff suppressed because it is too large
View File


+ 1
- 1
hash_cn/libhash/cryptonight.h View File

@ -5,7 +5,7 @@
extern "C" {
#endif
void cryptonight(void *output, const void *input, size_t len, int lite, int variant);
void cryptonight(void *output, const void *input, size_t len, int algo, int variant, int height);
struct cryptonight_ctx;
#ifdef __cplusplus

+ 3
- 3
hash_cn/libhash/main.c View File

@ -7,7 +7,7 @@
#include "cryptonight.h"
char *hash_cn(char *hex, int lite, int variant)
char *hash_cn(char *hex, int algo, int variant, int height)
{
char *output = (char *)malloc((64 + 1) * sizeof(char));
@ -27,7 +27,7 @@ char *hash_cn(char *hex, int lite, int variant)
unsigned char hash[32];
cryptonight(&hash, &val, len, lite, variant);
cryptonight(&hash, &val, len, algo, variant, height);
char *ptr = &output[0];
@ -42,4 +42,4 @@ char *hash_cn(char *hex, int lite, int variant)
void hash_free(void *ptr)
{
free(ptr);
}
}

+ 1
- 8
hash_cn/webassembly/Makefile View File

@ -1,16 +1,9 @@
TARGET = prog
LIBS = -lm
# Looks like there is a regression in the emcc compiler. In my tests
# the compiled code made with current versions are slower than the
# 1.37.40 reference.
CC = ~/.emcc_wrapper/1.37.40/emscripten/emcc -O3 -s SINGLE_FILE=1 -s NO_FILESYSTEM=1 -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ccall", "cwrap"]' --llvm-lto 1 -s TOTAL_MEMORY=67108864 -s WASM=1 -s "BINARYEN_TRAP_MODE='allow'" -s EXPORTED_FUNCTIONS="['_hash_cn']" --shell-file html_template/shell_minimal.html
CC = emcc -O3 -s NO_FILESYSTEM=1 -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ccall", "cwrap"]' --llvm-lto 1 -s TOTAL_MEMORY=67108864 -s WASM=1 -s "BINARYEN_TRAP_MODE='allow'" -s EXPORTED_FUNCTIONS="['_hash_cn']" --shell-file html_template/shell_minimal.html
CFLAGS = -Wall
# SINGLE_FILE=1
# -s ASSERTIONS=1
# -s SINGLE_FILE=1
.PHONY: default all clean

+ 482
- 494
hash_cn/webassembly/cryptonight.c
File diff suppressed because it is too large
View File


+ 1
- 1
hash_cn/webassembly/cryptonight.h View File

@ -5,7 +5,7 @@
extern "C" {
#endif
void cryptonight(void *output, const void *input, size_t len, int lite, int variant);
void cryptonight(void *output, const void *input, size_t len, int algo, int variant, int height);
struct cryptonight_ctx;
#ifdef __cplusplus

+ 3
- 7
hash_cn/webassembly/main.c View File

@ -9,7 +9,7 @@
char output[(32 * 2) + 1];
char* hash_cn(char* hex, char* nonce,int lite, int variant)
char* hash_cn(char* hex,int lite, int variant, int height)
{
int len = strlen(hex) / 2;
@ -18,20 +18,16 @@ char* hash_cn(char* hex, char* nonce,int lite, int variant)
char *pos = hex;
for( size_t i = 0; i < len; i++) { sscanf(pos, "%2hhx", &inp[i]); pos += 2; }
pos = nonce;
for(size_t i = 39; i < 43; i++) { sscanf(pos, "%2hhx", &inp[i]); pos += 2; }
unsigned char hash[32];
if(variant == -1)
variant = ((const uint8_t *)inp)[0] >= 7 ? ((const uint8_t *)inp)[0] - 6 : 0;
cryptonight(hash, inp, len, lite, variant);
cryptonight(hash, inp, len, lite, variant, height);
char *ptr = &output[0];
for (size_t i = 0; i < 32; i++) { ptr += sprintf (ptr, "%02x",hash[i]); }
return &output[0];
}
}

+ 35
- 61
hash_cn/webassembly/simple_profile.html View File

@ -1,91 +1,65 @@
<!DOCTYPE html>
<html>
<body>
<button onclick="myFunction()">Click me</button>
<p id="demo"></p>
<script src="cn.js"></script>
<script>
var cn = Module.cwrap('hash_cn', 'string', ['string','string','number','number']);
<head>
<meta charset="utf-8"/>
</head>
// https://github.com/SChernykh/monero/blob/5fd83c13fbf8dc304909345e60a853c15b0de1e5/tests/hash/tests-slow-2.txt
//
// 4cf1ff9ca46eb433b36cd9f70e02b14cc06bfd18ca77fa9ccaafd1fd96c674b0 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374
// 7d292e43f4751714ec07dbcb0e4bbffe2a7afb6066420960684ff57d7474c871 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67
// 335563425256edebf1d92dc342369c2f4770ebb4112ba975659bd8a0f210abd0 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265
// 47758e86d2f57210366cec36fff26f9464d89efd116fe6ef28b718b5da120801 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c
// 48787b48d5c68f0c1dd825c32580af741cc0ee314f08133135c1e86d87a24a95 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369
// 93bdf47495854f7cfaaca1af8c0f39ef4a3024c10eb0dea23726b0e06ef29e84 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465
// a375a71d0541057ccc96719150dfe10b6e6f486b19cf4a0835e19605413a8417 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974
// 163478a76f8f1432533fbdd1284d65c89f37479e54f20841c6ce4eba56c73854 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e
// 356b0470c6eea75cad7a108179e232905b23bdaf03c2824c6e619d503ee93677 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c
// a47e2b007dc25bb279e197a1b91f67ecebe2ddd8791cd32dd2cb76dd21ed943f 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e
<!DOCTYPE html>
<html>
<body>
<button onclick="myFunction()">Click me</button>
<p id="demo"></p>
<script src="cn.js"></script>
<button onclick="checkvariants()">Click me</button>
<script>
var cn = Module.cwrap('hash_cn', 'string', ['string','string','number','number']);
var cn = Module.cwrap('hash_cn', 'string', ['string','number','number','number']);
function myFunction2() {
function profile() {
toTest = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
var t0 = performance.now();
for(i=0;i<100;i++) cn(toTest,toTest.substring(78,86),0,2);
var hash = "0"; // algo, variant, height
for(i=0;i<10;i++) hash=cn(toTest,3,2,1806260);
var t1 = performance.now();
alert("10 cryptonight hashes took " + (t1 - t0) + " milliseconds.")
alert(hash);
}
function myFunction() {
function checkvariants() {
blob = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
alert(cn(blob,1,0,0)); // 4c3428f39e1f9ecda3b0726fd4f4fca62843597c480f033ae38d113282b273bf
blob = "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113";
alert(cn(blob,1,1,0)); // c2e3bd88bffd1bd7855af2dae2a52fef6efd36f00db514a6594718c5b67fab21
blob = "01111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113";
alert(cn(blob,blob.substring(78,86),0,0));
blob = "6465206f6d6e69627573206475626974616e64756d";
alert(cn(blob,0,0,0)); // 2f8e3df40bd11f9ac90c743ca8e32bb391da4fb98612aa3b6cdc639ee00b31f5
blob = "01111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113";
alert(cn(blob,blob.substring(78,86),1,0));
blob = "38274c97c45a172cfc97679870422e3a1ab0784960c60514d816271415c306ee3a3ed1a77e31f6a885c3cb";
alert(cn(blob,0,1,0)); // ed082e49dbd5bbe34a3726a0d1dad981146062b39d36d62c71eb1ed8ab49459b
blob = "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113";
alert(cn(blob,blob.substring(78,86),0,1));
blob = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
alert(cn(blob,0,2,0)); // 353fdc068fd47b03c04b9431e005e00b68c2168a3cc7335c8b9b308156591a4f
blob = "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113";
alert(cn(blob,blob.substring(78,86),1,1));
blob = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
alert(cn(blob,0,3,0)); // 353fdc068fd47b03c04b9431e005e00b68c2168a3cc7335c8b9b308156591a4f
blob = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
alert(cn(blob,blob.substring(78,86),0,2));
alert(cn(blob,0,4,1806260)); // f759588ad57e758467295443a9bd71490abff8e9dad1b95b6bf2f5d0d78387bc
blob = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
alert(cn(blob,blob.substring(78,86),1,2));
}
function myFunction3() {
test("5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374");
test("4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67");
test("656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265");
test("657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c");
test("71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369");
test("757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465");
test("697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974");
test("657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e");
test("4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c");
test("73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e");
alert(cn(blob,0,5,1806260)); // f759588ad57e758467295443a9bd71490abff8e9dad1b95b6bf2f5d0d78387bc
blob = "4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67";
alert(cn(blob,0,5,1806261)); // 5bb833deca2bdd7252a9ccd7b4ce0b6a4854515794b56c207262f7a5b9bdb566
blob = "757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465";
alert(cn(blob,0,5,1806265)); // 1d290443a4b542af04a82f6b2494a6ee7f20f2754c58e0849032483a56e8e2ef
blob = "73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e";
alert(cn(blob,0,5,1806269)); // 75c6f2ae49a20521de97285b431e717125847fb8935ed84a61e7f8d36a2c3d8e
}
function test(blob) {
hash = cn(blob,blob.substring(78,86),0,2);
alert(hash);
}
</script>

+ 444
- 0
hash_cn/webassembly/variant4_random_math.h View File

@ -0,0 +1,444 @@
#ifndef VARIANT4_RANDOM_MATH_H
#define VARIANT4_RANDOM_MATH_H
// Register size can be configured to either 32 bit (uint32_t) or 64 bit (uint64_t)
typedef uint32_t v4_reg;
enum V4_Settings
{
// Generate code with minimal theoretical latency = 45 cycles, which is equivalent to 15 multiplications
TOTAL_LATENCY = 15 * 3,
// Always generate at least 60 instructions
NUM_INSTRUCTIONS_MIN = 60,
// Never generate more than 70 instructions (final RET instruction doesn't count here)
NUM_INSTRUCTIONS_MAX = 70,
// Available ALUs for MUL
// Modern CPUs typically have only 1 ALU which can do multiplications
ALU_COUNT_MUL = 1,
// Total available ALUs
// Modern CPUs have 4 ALUs, but we use only 3 because random math executes together with other main loop code
ALU_COUNT = 3,
};
enum V4_InstructionList
{
MUL, // a*b
ADD, // a+b + C, C is an unsigned 32-bit constant
SUB, // a-b
ROR, // rotate right "a" by "b & 31" bits
ROL, // rotate left "a" by "b & 31" bits
XOR, // a^b
RET, // finish execution
V4_INSTRUCTION_COUNT = RET,
};
// V4_InstructionDefinition is used to generate code from random data
// Every random sequence of bytes is a valid code
//
// There are 9 registers in total:
// - 4 variable registers
// - 5 constant registers initialized from loop variables
// This is why dst_index is 2 bits
enum V4_InstructionDefinition
{
V4_OPCODE_BITS = 3,
V4_DST_INDEX_BITS = 2,
V4_SRC_INDEX_BITS = 3,
};
// uint32 seems to be faster
struct V4_Instruction
{
uint8_t dst_index;
uint8_t src_index;
uint8_t opcode;
uint32_t C;
};
#ifndef FORCEINLINE
#if defined(__GNUC__)
#define FORCEINLINE __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
#define FORCEINLINE __forceinline
#else
#define FORCEINLINE inline
#endif
#endif
#ifndef UNREACHABLE_CODE
#if defined(__GNUC__)
#define UNREACHABLE_CODE __builtin_unreachable()
#elif defined(_MSC_VER)
#define UNREACHABLE_CODE __assume(false)
#else
#define UNREACHABLE_CODE
#endif
#endif
// Random math interpreter's loop is fully unrolled and inlined to achieve 100% branch prediction on CPU:
// every switch-case will point to the same destination on every iteration of Cryptonight main loop
//
// This is about as fast as it can get without using low-level machine code generation
static FORCEINLINE void v4_random_math(const struct V4_Instruction* code, v4_reg* r)
{
enum
{
REG_BITS = sizeof(v4_reg) * 8,
};
#define V4_EXEC(i) \
{ \
const struct V4_Instruction* op = code + i; \
const v4_reg src = r[op->src_index]; \
v4_reg* dst = r + op->dst_index; \
switch (op->opcode) \
{ \
case MUL: \
*dst *= src; \
break; \
case ADD: \
*dst += src + op->C; \
break; \
case SUB: \
*dst -= src; \
break; \
case ROR: \
{ \
const uint32_t shift = src % REG_BITS; \
*dst = (*dst >> shift) | (*dst << ((REG_BITS - shift) % REG_BITS)); \
} \
break; \
case ROL: \
{ \
const uint32_t shift = src % REG_BITS; \
*dst = (*dst << shift) | (*dst >> ((REG_BITS - shift) % REG_BITS)); \
} \
break; \
case XOR: \
*dst ^= src; \
break; \
case RET: \
return; \
default: \
UNREACHABLE_CODE; \
break; \
} \
}
#define V4_EXEC_10(j) \
V4_EXEC(j + 0) \
V4_EXEC(j + 1) \
V4_EXEC(j + 2) \
V4_EXEC(j + 3) \
V4_EXEC(j + 4) \
V4_EXEC(j + 5) \
V4_EXEC(j + 6) \
V4_EXEC(j + 7) \
V4_EXEC(j + 8) \
V4_EXEC(j + 9)
// Generated program can have 60 + a few more (usually 2-3) instructions to achieve required latency
// I've checked all block heights < 10,000,000 and here is the distribution of program sizes:
//
// 60 27960
// 61 105054
// 62 2452759
// 63 5115997
// 64 1022269
// 65 1109635
// 66 153145
// 67 8550
// 68 4529
// 69 102
// Unroll 70 instructions here
V4_EXEC_10(0); // instructions 0-9
V4_EXEC_10(10); // instructions 10-19
V4_EXEC_10(20); // instructions 20-29
V4_EXEC_10(30); // instructions 30-39
V4_EXEC_10(40); // instructions 40-49
V4_EXEC_10(50); // instructions 50-59
V4_EXEC_10(60); // instructions 60-69
#undef V4_EXEC_10
#undef V4_EXEC
}
// If we don't have enough data available, generate more
static FORCEINLINE void check_data(size_t* data_index, const size_t bytes_needed, int8_t* data, const size_t data_size)
{
if (*data_index + bytes_needed > data_size)
{
//hash_extra_blake(data, data_size, (char*) data);
blake((uint8_t *) data,data_size,(uint8_t *) data);
*data_index = 0;
}
}
// Generates as many random math operations as possible with given latency and ALU restrictions
// "code" array must have space for NUM_INSTRUCTIONS_MAX+1 instructions
static inline int v4_random_math_init(struct V4_Instruction* code, const uint64_t height)
{
// MUL is 3 cycles, 3-way addition and rotations are 2 cycles, SUB/XOR are 1 cycle
// These latencies match real-life instruction latencies for Intel CPUs starting from Sandy Bridge and up to Skylake/Coffee lake
//
// AMD Ryzen has the same latencies except 1-cycle ROR/ROL, so it'll be a bit faster than Intel Sandy Bridge and newer processors
// Surprisingly, Intel Nehalem also has 1-cycle ROR/ROL, so it'll also be faster than Intel Sandy Bridge and newer processors
// AMD Bulldozer has 4 cycles latency for MUL (slower than Intel) and 1 cycle for ROR/ROL (faster than Intel), so average performance will be the same
// Source: https://www.agner.org/optimize/instruction_tables.pdf
const int op_latency[V4_INSTRUCTION_COUNT] = { 3, 2, 1, 2, 2, 1 };
// Instruction latencies for theoretical ASIC implementation
const int asic_op_latency[V4_INSTRUCTION_COUNT] = { 3, 1, 1, 1, 1, 1 };
// Available ALUs for each instruction
const int op_ALUs[V4_INSTRUCTION_COUNT] = { ALU_COUNT_MUL, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT };
int8_t data[32];
memset(data, 0, sizeof(data));
uint64_t tmp = (height);
memcpy(data, &tmp, sizeof(uint64_t));
data[20] = 0xda; // change seed
// Set data_index past the last byte in data
// to trigger full data update with blake hash
// before we start using it
size_t data_index = sizeof(data);
int code_size;
// There is a small chance (1.8%) that register R8 won't be used in the generated program
// So we keep track of it and try again if it's not used
bool r8_used;
do {
int latency[9];
int asic_latency[9];
// Tracks previous instruction and value of the source operand for registers R0-R3 throughout code execution
// byte 0: current value of the destination register
// byte 1: instruction opcode
// byte 2: current value of the source register
//
// Registers R4-R8 are constant and are treated as having the same value because when we do
// the same operation twice with two constant source registers, it can be optimized into a single operation
uint32_t inst_data[9] = { 0, 1, 2, 3, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF };
bool alu_busy[TOTAL_LATENCY + 1][ALU_COUNT];
bool is_rotation[V4_INSTRUCTION_COUNT];
bool rotated[4];
int rotate_count = 0;
memset(latency, 0, sizeof(latency));
memset(asic_latency, 0, sizeof(asic_latency));
memset(alu_busy, 0, sizeof(alu_busy));
memset(is_rotation, 0, sizeof(is_rotation));
memset(rotated, 0, sizeof(rotated));
is_rotation[ROR] = true;
is_rotation[ROL] = true;
int num_retries = 0;
code_size = 0;
int total_iterations = 0;
r8_used = false;
// Generate random code to achieve minimal required latency for our abstract CPU
// Try to get this latency for all 4 registers
while (((latency[0] < TOTAL_LATENCY) || (latency[1] < TOTAL_LATENCY) || (latency[2] < TOTAL_LATENCY) || (latency[3] < TOTAL_LATENCY)) && (num_retries < 64))
{
// Fail-safe to guarantee loop termination
++total_iterations;
if (total_iterations > 256)
break;
check_data(&data_index, 1, data, sizeof(data));
const uint8_t c = ((uint8_t*)data)[data_index++];
// MUL = opcodes 0-2
// ADD = opcode 3
// SUB = opcode 4
// ROR/ROL = opcode 5, shift direction is selected randomly
// XOR = opcodes 6-7
uint8_t opcode = c & ((1 << V4_OPCODE_BITS) - 1);
if (opcode == 5)
{
check_data(&data_index, 1, data, sizeof(data));
opcode = (data[data_index++] >= 0) ? ROR : ROL;
}
else if (opcode >= 6)
{
opcode = XOR;
}
else
{
opcode = (opcode <= 2) ? MUL : (opcode - 2);
}
uint8_t dst_index = (c >> V4_OPCODE_BITS) & ((1 << V4_DST_INDEX_BITS) - 1);
uint8_t src_index = (c >> (V4_OPCODE_BITS + V4_DST_INDEX_BITS)) & ((1 << V4_SRC_INDEX_BITS) - 1);
const int a = dst_index;
int b = src_index;
// Don't do ADD/SUB/XOR with the same register
if (((opcode == ADD) || (opcode == SUB) || (opcode == XOR)) && (a == b))
{
// Use register R8 as source instead
b = 8;
src_index = 8;
}
// Don't do rotation with the same destination twice because it's equal to a single rotation
if (is_rotation[opcode] && rotated[a])
{
continue;
}
// Don't do the same instruction (except MUL) with the same source value twice because all other cases can be optimized:
// 2xADD(a, b, C) = ADD(a, b*2, C1+C2), same for SUB and rotations
// 2xXOR(a, b) = NOP
if ((opcode != MUL) && ((inst_data[a] & 0xFFFF00) == (opcode << 8) + ((inst_data[b] & 255) << 16)))
{
continue;
}
// Find which ALU is available (and when) for this instruction
int next_latency = (latency[a] > latency[b]) ? latency[a] : latency[b];
int alu_index = -1;
while (next_latency < TOTAL_LATENCY)
{
for (int i = op_ALUs[opcode] - 1; i >= 0; --i)
{
if (!alu_busy[next_latency][i])
{
// ADD is implemented as two 1-cycle instructions on a real CPU, so do an additional availability check
if ((opcode == ADD) && alu_busy[next_latency + 1][i])
{
continue;
}
// Rotation can only start when previous rotation is finished, so do an additional availability check
if (is_rotation[opcode] && (next_latency < rotate_count * op_latency[opcode]))
{
continue;
}
alu_index = i;
break;
}
}
if (alu_index >= 0)
{
break;
}
++next_latency;
}
// Don't generate instructions that leave some register unchanged for more than 7 cycles
if (next_latency > latency[a] + 7)
{
continue;
}
next_latency += op_latency[opcode];
if (next_latency <= TOTAL_LATENCY)
{
if (is_rotation[opcode])
{
++rotate_count;
}
// Mark ALU as busy only for the first cycle when it starts executing the instruction because ALUs are fully pipelined
alu_busy[next_latency - op_latency[opcode]][alu_index] = true;
latency[a] = next_latency;
// ASIC is supposed to have enough ALUs to run as many independent instructions per cycle as possible, so latency calculation for ASIC is simple
asic_latency[a] = ((asic_latency[a] > asic_latency[b]) ? asic_latency[a] : asic_latency[b]) + asic_op_latency[opcode];
rotated[a] = is_rotation[opcode];
inst_data[a] = code_size + (opcode << 8) + ((inst_data[b] & 255) << 16);
code[code_size].opcode = opcode;
code[code_size].dst_index = dst_index;
code[code_size].src_index = src_index;
code[code_size].C = 0;
if (src_index == 8)
{
r8_used = true;
}
if (opcode == ADD)
{
// ADD instruction is implemented as two 1-cycle instructions on a real CPU, so mark ALU as busy for the next cycle too
alu_busy[next_latency - op_latency[opcode] + 1][alu_index] = true;
// ADD instruction requires 4 more random bytes for 32-bit constant "C" in "a = a + b + C"
check_data(&data_index, sizeof(uint32_t), data, sizeof(data));
uint32_t t;
memcpy(&t, data + data_index, sizeof(uint32_t));
code[code_size].C = (t);
data_index += sizeof(uint32_t);
}
++code_size;
if (code_size >= NUM_INSTRUCTIONS_MIN)
{
break;
}
}
else
{
++num_retries;
}
}
// ASIC has more execution resources and can extract as much parallelism from the code as possible
// We need to add a few more MUL and ROR instructions to achieve minimal required latency for ASIC
// Get this latency for at least 1 of the 4 registers
const int prev_code_size = code_size;
while ((code_size < NUM_INSTRUCTIONS_MAX) && (asic_latency[0] < TOTAL_LATENCY) && (asic_latency[1] < TOTAL_LATENCY) && (asic_latency[2] < TOTAL_LATENCY) && (asic_latency[3] < TOTAL_LATENCY))
{
int min_idx = 0;
int max_idx = 0;
for (int i = 1; i < 4; ++i)
{
if (asic_latency[i] < asic_latency[min_idx]) min_idx = i;
if (asic_latency[i] > asic_latency[max_idx]) max_idx = i;
}
const uint8_t pattern[3] = { ROR, MUL, MUL };
const uint8_t opcode = pattern[(code_size - prev_code_size) % 3];
latency[min_idx] = latency[max_idx] + op_latency[opcode];
asic_latency[min_idx] = asic_latency[max_idx] + asic_op_latency[opcode];
code[code_size].opcode = opcode;
code[code_size].dst_index = min_idx;
code[code_size].src_index = max_idx;
code[code_size].C = 0;
++code_size;
}
// There is ~98.15% chance that loop condition is false, so this loop will execute only 1 iteration most of the time
// It never does more than 4 iterations for all block heights < 10,000,000
} while (!r8_used || (code_size < NUM_INSTRUCTIONS_MIN) || (code_size > NUM_INSTRUCTIONS_MAX));
// It's guaranteed that NUM_INSTRUCTIONS_MIN <= code_size <= NUM_INSTRUCTIONS_MAX here
// Add final instruction to stop the interpreter
code[code_size].opcode = RET;
code[code_size].dst_index = 0;
code[code_size].src_index = 0;
code[code_size].C = 0;
return code_size;
}
#endif

+ 33
- 12
server/Server/AlgorithmHelper.cs View File

@ -1,6 +1,6 @@
// The MIT License (MIT)
// Copyright (c) 2018 - the webminerpool developer
// Copyright (c) 2018-2019 - the webminerpool developer
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
@ -27,38 +27,59 @@ using JsonData = System.Collections.Generic.Dictionary;
namespace Server
{
public class AlgorithmHelper
public static class AlgorithmHelper
{
// quite a mess
// https://github.com/xmrig/xmrig-proxy/blob/dev/doc/STRATUM_EXT.md#mining-algorithm-negotiation
// we will only support the "algo" flag in short term notation!
private static Dictionary<string, Tuple<string, int>> lookup = new Dictionary<string, Tuple<string, int>>
{
{ "cryptonight/0", new Tuple<string, int>("cn", 0) },
{ "cryptonight/1", new Tuple<string, int>("cn", 1) },
{ "cryptonight/2", new Tuple<string, int>("cn", 2) },
{ "cryptonight-lite/0", new Tuple<string, int>("cn-lite", 0) },
{ "cryptonight-lite/1", new Tuple<string, int>("cn-lite", 1) },
{ "cryptonight-lite/2", new Tuple<string, int>("cn-lite", 2) },
{ "cn", new Tuple<string, int>("cn", -1) },
{ "cn/0", new Tuple<string, int>("cn", 0) },
{ "cn/1", new Tuple<string, int>("cn", 1) },
{ "cn/2", new Tuple<string, int>("cn", 2) },
{ "cn/r", new Tuple<string, int>("cn", 4) },
{ "cn-lite/0", new Tuple<string, int>("cn-lite", 0) },
{ "cn-lite/1", new Tuple<string, int>("cn-lite", 1) },
{ "cn-lite/2", new Tuple<string, int>("cn-lite", 2) }
{ "cn-lite/2", new Tuple<string, int>("cn-lite", 2) },
{ "cn-pico/trtl", new Tuple<string, int>("cn-pico", 2) },
{ "cn/half", new Tuple<string, int>("cn-half", 2) }
};
public static bool ValidAlgorithm(string algo)
{
return lookup.ContainsKey(algo);
}
public static bool NormalizeAlgorithmAndVariant(JsonData job)
{
int possibleHeight = 0;
if (job.ContainsKey("height"))
{
Int32.TryParse(job["height"].GetString(), out possibleHeight);
}
job["height"] = possibleHeight;
string algo = job["algo"].GetString().ToLower();
if (algo == "cn" || algo == "cryptonight")
{
int possibleVariant = -1;
if (job.ContainsKey("variant"))
{
Int32.TryParse(job["variant"].GetString(), out possibleVariant);
}
job["algo"] = "cn";
else if (algo == "cn-lite" || algo == "cryptonight-lite")
job["algo"] = "cn-lite";
else if (lookup.ContainsKey(algo))
job["variant"] = possibleVariant;
}
if (lookup.ContainsKey(algo))
{
var tuple = lookup[algo];
job["algo"] = tuple.Item1;

+ 236
- 217
server/Server/Fleck/WebSocketConnection.cs View File

@ -29,268 +29,287 @@ using System.Threading.Tasks;
namespace Fleck
{
public class WebSocketConnection : IWebSocketConnection
{
public WebSocketConnection(ISocket socket, Action<IWebSocketConnection> initialize, Func<byte[], WebSocketHttpRequest> parseRequest, Func<WebSocketHttpRequest, IHandler> handlerFactory, Func<IEnumerable<string>, string> negotiateSubProtocol)
public class WebSocketConnection : IWebSocketConnection
{
Socket = socket;
OnOpen = () => { };
OnClose = () => { };
OnMessage = x => { };
OnBinary = x => { };
OnPing = x => SendPong(x);
OnPong = x => { };
OnError = x => { };
_initialize = initialize;
_handlerFactory = handlerFactory;
_parseRequest = parseRequest;
_negotiateSubProtocol = negotiateSubProtocol;
}
public WebSocketConnection(ISocket socket, Action<IWebSocketConnection> initialize, Func<byte[], WebSocketHttpRequest> parseRequest, Func<WebSocketHttpRequest, IHandler> handlerFactory, Func<IEnumerable<string>, string> negotiateSubProtocol)
{
Socket = socket;
OnOpen = () => { };
OnClose = () => { };
OnMessage = x => { };
OnBinary = x => { };
OnPing = x => SendPong(x);
OnPong = x => { };
OnError = x => { };
_initialize = initialize;
_handlerFactory = handlerFactory;
_parseRequest = parseRequest;
_negotiateSubProtocol = negotiateSubProtocol;
}
public ISocket Socket { get; set; }
public ISocket Socket { get; set; }
private readonly Action<IWebSocketConnection> _initialize;
private readonly Func<WebSocketHttpRequest, IHandler> _handlerFactory;
private readonly Func<IEnumerable<string>, string> _negotiateSubProtocol;
readonly Func<byte[], WebSocketHttpRequest> _parseRequest;
private readonly Action<IWebSocketConnection> _initialize;
private readonly Func<WebSocketHttpRequest, IHandler> _handlerFactory;
private readonly Func<IEnumerable<string>, string> _negotiateSubProtocol;
readonly Func<byte[], WebSocketHttpRequest> _parseRequest;
public IHandler Handler { get; set; }
public IHandler Handler { get; set; }
private bool _opened = false;
private bool _closing;
private bool _closed;
private const int ReadSize = 1024 * 4;
private bool _opened = false;
private bool _closing;
private bool _closed;
private const int ReadSize = 1024 * 4;
public Action OnOpen { get; set; }
public Action OnOpen { get; set; }
public Action OnClose { get; set; }
public Action OnClose { get; set; }
public Action<string> OnMessage { get; set; }
public Action<string> OnMessage { get; set; }
public Action<byte[]> OnBinary { get; set; }
public Action<byte[]> OnBinary { get; set; }
public Action<byte[]> OnPing { get; set; }
public Action<byte[]> OnPing { get; set; }
public Action<byte[]> OnPong { get; set; }
public Action<byte[]> OnPong { get; set; }
public Action<Exception> OnError { get; set; }
public Action<Exception> OnError { get; set; }
public IWebSocketConnectionInfo ConnectionInfo { get; private set; }
public IWebSocketConnectionInfo ConnectionInfo { get; private set; }
public bool IsAvailable {
get { return !_closing && !_closed && Socket.Connected; }
}
public bool IsAvailable
{
get { return !_closing && !_closed && Socket.Connected; }
}
public Task Send(string message)
{
return Send(message, Handler.FrameText);
}
public Task Send(string message)
{
return Send(message, Handler.FrameText);
}
public Task Send(byte[] message)
{
return Send(message, Handler.FrameBinary);
}
public Task Send(byte[] message)
{
return Send(message, Handler.FrameBinary);
}
public Task SendPing(byte[] message)
{
return Send(message, Handler.FramePing);
}
public Task SendPing(byte[] message)
{
return Send(message, Handler.FramePing);
}
public Task SendPong(byte[] message)
{
return Send(message, Handler.FramePong);
}
public Task SendPong(byte[] message)
{
return Send(message, Handler.FramePong);
}
private Task Send<T>(T message, Func<T, byte[]> createFrame)
{
if (Handler == null)
throw new InvalidOperationException("Cannot send before handshake");
if (!IsAvailable)
{
const string errorMessage = "Data sent while closing or after close. Ignoring.";
FleckLog.Warn(errorMessage);
var taskForException = new TaskCompletionSource<object>();
taskForException.SetException(new ConnectionNotAvailableException(errorMessage));
return taskForException.Task;
}
var bytes = createFrame(message);
return SendBytes(bytes);
}
private Task Send<T>(T message, Func<T, byte[]> createFrame)
{
if (Handler == null)
throw new InvalidOperationException("Cannot send before handshake");
if (!IsAvailable)
{
const string errorMessage = "Data sent while closing or after close. Ignoring.";
FleckLog.Warn(errorMessage);
byte[] buffer;
var taskForException = new TaskCompletionSource<object>();
taskForException.SetException(new ConnectionNotAvailableException(errorMessage));
return taskForException.Task;
}
public void StartReceiving()
{
var bytes = createFrame(message);
return SendBytes(bytes);
}
List<byte> data = new List<byte>(ReadSize);
buffer = new byte[ReadSize];
Task.Run(async delegate
{
await Task.Delay(TimeSpan.FromSeconds(20));
byte[] buffer;
if (!_closed && !_opened) {
Console.WriteLine("Closing/Disposing WebSocketConnection. Not able to perform handshake within 20s.");
CloseSocket ();
}
public void StartReceiving()
{
});
List<byte> data = new List<byte>(ReadSize);
buffer = new byte[ReadSize];
Read(data, buffer);
}
Task.Run(async delegate
{
await Task.Delay(TimeSpan.FromSeconds(20));
public void Close()
{
Close(WebSocketStatusCodes.NormalClosure);
}
public void Close(int code)
{
if (!_closed && !_opened)
{
Console.WriteLine("Closing/Disposing WebSocketConnection. Not able to perform handshake within 20s.");
CloseSocket();
}
//try{ CloseSocket();} catch{ // added by tl
// }
});
if (!IsAvailable)
return;
_closing = true;
Read(data, buffer);
}
if (Handler == null) {
CloseSocket();
return;
}
public void Close()
{
Close(WebSocketStatusCodes.NormalClosure);
}
var bytes = Handler.FrameClose(code);
if (bytes.Length == 0)
CloseSocket();
else
SendBytes(bytes, CloseSocket);
}
public void Close(int code)
{
public void CreateHandler(IEnumerable<byte> data)
{
var request = _parseRequest(data.ToArray());
if (request == null)
return;
Handler = _handlerFactory(request);
if (Handler == null)
return;
var subProtocol = _negotiateSubProtocol(request.SubProtocols);
ConnectionInfo = WebSocketConnectionInfo.Create(request, Socket.RemoteIpAddress, Socket.RemotePort, subProtocol);
_initialize(this);
var handshake = Handler.CreateHandshake(subProtocol);
SendBytes(handshake, () => { OnOpen(); _opened = true; });
}
//try{ CloseSocket();} catch{ // added by tl
// }
private void Read(List<byte> data, byte[] buffer)
{
if (!IsAvailable)
return;
Socket.Receive(buffer, r =>
{
if (r <= 0) {
FleckLog.Debug("0 bytes read. Closing.");
CloseSocket();
return;
if (!IsAvailable)
return;
_closing = true;
if (Handler == null)
{
CloseSocket();
return;
}
var bytes = Handler.FrameClose(code);
if (bytes.Length == 0)
CloseSocket();
else
SendBytes(bytes, CloseSocket);
}
FleckLog.Debug(r + " bytes read");
var readBytes = buffer.Take(r);
if (Handler != null) {
Handler.Receive(readBytes);
} else {
data.AddRange(readBytes);
CreateHandler(data);
public void CreateHandler(IEnumerable<byte> data)
{
var request = _parseRequest(data.ToArray());
if (request == null)
return;
Handler = _handlerFactory(request);
if (Handler == null)
return;
var subProtocol = _negotiateSubProtocol(request.SubProtocols);
ConnectionInfo = WebSocketConnectionInfo.Create(request, Socket.RemoteIpAddress, Socket.RemotePort, subProtocol);
_initialize(this);
var handshake = Handler.CreateHandshake(subProtocol);
SendBytes(handshake, () => { OnOpen(); _opened = true; });
}
Read(data, buffer);
},
HandleReadError);
}
private void Read(List<byte> data, byte[] buffer)
{
if (!IsAvailable)
return;
Socket.Receive(buffer, r =>
{
if (r <= 0)
{
FleckLog.Debug("0 bytes read. Closing.");
CloseSocket();
return;
}
FleckLog.Debug(r + " bytes read");
var readBytes = buffer.Take(r);
if (Handler != null)
{
Handler.Receive(readBytes);
}
else
{
data.AddRange(readBytes);
CreateHandler(data);
}
Read(data, buffer);
},
HandleReadError);
}
private void HandleReadError(Exception e)
{
if (e is AggregateException) {
var agg = e as AggregateException;
HandleReadError(agg.InnerException);
return;
}
if (e is ObjectDisposedException) {
FleckLog.Debug("Swallowing ObjectDisposedException", e);
CloseSocket();
return;
}
OnError(e);
if (e is HandshakeException) {
FleckLog.Debug("Error while reading", e);
// why no close here?? TL
//Close(WebSocketStatusCodes.ProtocolError);
CloseSocket();
} else if (e is WebSocketException) {
FleckLog.Debug("Error while reading", e);
//Close(((WebSocketException)e).StatusCode);
CloseSocket();
} else if (e is SubProtocolNegotiationFailureException) {
FleckLog.Debug(e.Message);
CloseSocket();
//Close(WebSocketStatusCodes.ProtocolError);
} else if (e is IOException) {
FleckLog.Debug("Error while reading", e);
CloseSocket();
//Close(WebSocketStatusCodes.AbnormalClosure);
} else {
FleckLog.Error("Application Error", e);
CloseSocket();
//Close(WebSocketStatusCodes.InternalServerError);
}
}
private void HandleReadError(Exception e)
{
if (e is AggregateException)
{
var agg = e as AggregateException;
HandleReadError(agg.InnerException);
return;
}
if (e is ObjectDisposedException)
{
FleckLog.Debug("Swallowing ObjectDisposedException", e);
CloseSocket();
return;
}
OnError(e);
if (e is HandshakeException)
{
FleckLog.Debug("Error while reading", e);
// why no close here?? TL
//Close(WebSocketStatusCodes.ProtocolError);
CloseSocket();
}
else if (e is WebSocketException)
{
FleckLog.Debug("Error while reading", e);
//Close(((WebSocketException)e).StatusCode);
CloseSocket();
}
else if (e is SubProtocolNegotiationFailureException)
{
FleckLog.Debug(e.Message);
CloseSocket();
//Close(WebSocketStatusCodes.ProtocolError);
}
else if (e is IOException)
{
FleckLog.Debug("Error while reading", e);
CloseSocket();
//Close(WebSocketStatusCodes.AbnormalClosure);
}
else
{
FleckLog.Error("Application Error", e);
CloseSocket();
//Close(WebSocketStatusCodes.InternalServerError);
}
}
private Task SendBytes(byte[] bytes, Action callback = null)
{
return Socket.Send(bytes, () =>
{
FleckLog.Debug("Sent " + bytes.Length + " bytes");
if (callback != null)
callback();
},
e =>
{
if (e is IOException)
FleckLog.Debug("Failed to send. Disconnecting.", e);
else
FleckLog.Info("Failed to send. Disconnecting.", e);
CloseSocket();
});
}
private Task SendBytes(byte[] bytes, Action callback = null)
{
return Socket.Send(bytes, () =>
{
FleckLog.Debug("Sent " + bytes.Length + " bytes");
if (callback != null)
callback();
},
e =>
{
if (e is IOException)
FleckLog.Debug("Failed to send. Disconnecting.", e);
else
FleckLog.Info("Failed to send. Disconnecting.", e);
CloseSocket();
});
}
public void CloseSocket()
{
_closing = true;
if (!_closed) {
_closed = true;
OnClose ();
}
public void CloseSocket()
{
_closing = true;
Socket.Close();
Socket.Dispose();
_closing = false;
if (!_closed)
{
_closed = true;
OnClose();
}
buffer = null;
Socket.Close();
Socket.Dispose();
_closing = false;
}
buffer = null;
}
}
}
}

+ 17
- 36
server/Server/PoolConnection.cs View File

@ -58,8 +58,6 @@ namespace Server
public CcHashset<string> LastSolved;
public string DefaultAlgorithm = "cn";
public int DefaultVariant = -1;
public CcHashset<Client> WebClients = new CcHashset<Client>();
@ -110,7 +108,7 @@ namespace Server
string blob = data["blob"].GetString();
string target = data["target"].GetString();
if (blob.Length < 152 || blob.Length > 180) return false;
if (blob.Length < 152 || blob.Length > 220) return false;
if (target.Length != 8) return false;
if (!Regex.IsMatch(blob, MainClass.RegexIsHex)) return false;
@ -121,7 +119,6 @@ namespace Server
private static void ReceiveCallback(IAsyncResult result)
{
PoolConnection mypc = result.AsyncState as PoolConnection;
TcpClient client = mypc.TcpClient;
@ -141,7 +138,6 @@ namespace Server
{
if (bytesread == 0) // disconnected
{
// slow that down a bit to avoid negative feedback loop
Task.Run(async delegate
@ -158,7 +154,6 @@ namespace Server
json = Encoding.ASCII.GetString(mypc.ReceiveBuffer, 0, bytesread);
networkStream.BeginRead(mypc.ReceiveBuffer, 0, mypc.ReceiveBuffer.Length, new AsyncCallback(ReceiveCallback), mypc);
}
catch { return; }
@ -213,9 +208,12 @@ namespace Server
}
// extended stratum
if (!lastjob.ContainsKey("variant")) lastjob.Add("variant", mypc.DefaultVariant);
if (!lastjob.ContainsKey("algo")) lastjob.Add("algo", mypc.DefaultAlgorithm);
AlgorithmHelper.NormalizeAlgorithmAndVariant(lastjob);
if (!AlgorithmHelper.NormalizeAlgorithmAndVariant(lastjob))
{
CConsole.ColorWarning(() => Console.WriteLine("Do not understand algorithm/variant!"));
return;
}
mypc.LastJob = lastjob;
mypc.LastInteraction = DateTime.Now;
@ -227,7 +225,6 @@ namespace Server
{
ReceiveJob(ev, mypc.LastJob, mypc.LastSolved);
}
}
else if (msg.ContainsKey("method") && msg["method"].GetString() == "job")
{
@ -244,9 +241,12 @@ namespace Server
}
// extended stratum
if (!lastjob.ContainsKey("variant")) lastjob.Add("variant", mypc.DefaultVariant);
if (!lastjob.ContainsKey("algo")) lastjob.Add("algo", mypc.DefaultAlgorithm);
AlgorithmHelper.NormalizeAlgorithmAndVariant(lastjob);
if (!AlgorithmHelper.NormalizeAlgorithmAndVariant(lastjob))
{
CConsole.ColorWarning(() => Console.WriteLine("Do not understand algorithm/variant!"));
return;
}