1+ "use strict" ;
2+ const multiHashing = require ( 'multi-hashing' ) ;
3+ const cnUtil = require ( 'cryptonote-util' ) ;
4+ const bignum = require ( 'bignum' ) ;
5+ const support = require ( './support.js' ) ( ) ;
6+ const crypto = require ( 'crypto' ) ;
7+
8+ let debug = {
9+ pool : require ( 'debug' ) ( 'pool' ) ,
10+ diff : require ( 'debug' ) ( 'diff' ) ,
11+ blocks : require ( 'debug' ) ( 'blocks' ) ,
12+ shares : require ( 'debug' ) ( 'shares' ) ,
13+ miners : require ( 'debug' ) ( 'miners' ) ,
14+ workers : require ( 'debug' ) ( 'workers' )
15+ } ;
16+
17+ let baseDiff = bignum ( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' , 16 ) ;
18+
19+ Buffer . prototype . toByteArray = function ( ) {
20+ return Array . prototype . slice . call ( this , 0 ) ;
21+ } ;
22+
23+ function blockHeightCheck ( nodeList , callback ) {
24+ let randomNode = nodeList [ Math . floor ( Math . random ( ) * nodeList . length ) ] . split ( ':' ) ;
25+
26+ }
27+
28+ function getRemoteNodes ( ) {
29+ let knownNodes = [
30+ '162.213.38.245:18081' ,
31+ '116.93.119.79:18081' ,
32+ '85.204.96.231:18081' ,
33+ '107.167.87.242:18081' ,
34+ '107.167.93.58:18081' ,
35+ '199.231.85.122:18081' ,
36+ '192.110.160.146:18081'
37+ ] ; // Prefill the array with known good nodes for now. Eventually will try to download them via DNS or http.
38+ }
39+
40+ function BlockTemplate ( template ) {
41+ /*
42+ We receive something identical to the result portions of the monero GBT call.
43+ Functionally, this could act as a very light-weight solo pool, so we'll prep it as one.
44+ You know. Just in case amirite?
45+ */
46+ this . id = template . id ;
47+ this . blob = template . blocktemplate_blob ;
48+ this . difficulty = template . difficulty ;
49+ this . height = template . height ;
50+ this . reservedOffset = template . reserved_offset ;
51+ this . workerOffset = template . worker_offset ; // clientNonceLocation
52+ this . targetDiff = template . target_diff ;
53+ this . targetHex = template . target_diff_hex ;
54+ this . buffer = new Buffer ( this . blob , 'hex' ) ;
55+ this . previousHash = new Buffer ( 32 ) ;
56+ this . workerNonce = 0 ;
57+ this . solo = false ;
58+ if ( typeof ( this . workerOffset ) === 'undefined' ) {
59+ this . solo = true ;
60+ global . instanceId . copy ( this . buffer , this . reservedOffset + 4 , 0 , 3 ) ;
61+ this . buffer . copy ( this . previousHash , 0 , 7 , 39 ) ;
62+ }
63+ this . nextBlob = function ( ) {
64+ if ( this . solo ) {
65+ // This is running in solo mode.
66+ this . buffer . writeUInt32BE ( ++ this . workerNonce , this . reservedOffset ) ;
67+ } else {
68+ this . buffer . writeUInt32BE ( ++ this . workerNonce , this . workerOffset ) ;
69+ }
70+ return cnUtil . convert_blob ( this . buffer ) . toString ( 'hex' ) ;
71+ } ;
72+ }
73+
74+ function MasterBlockTemplate ( template ) {
75+ /*
76+ We receive something identical to the result portions of the monero GBT call.
77+ Functionally, this could act as a very light-weight solo pool, so we'll prep it as one.
78+ You know. Just in case amirite?
79+ */
80+ this . blob = template . blocktemplate_blob ;
81+ this . difficulty = template . difficulty ;
82+ this . height = template . height ;
83+ this . reservedOffset = template . reserved_offset ; // reserveOffset
84+ this . workerOffset = template . client_nonce_offset ; // clientNonceLocation
85+ this . poolOffset = template . client_pool_offset ; // clientPoolLocation
86+ this . targetDiff = template . target_diff ;
87+ this . targetHex = template . target_diff_hex ;
88+ this . buffer = new Buffer ( this . blob , 'hex' ) ;
89+ this . previousHash = new Buffer ( 32 ) ;
90+ this . job_id = template . job_id ;
91+ this . workerNonce = 0 ;
92+ this . poolNonce = 0 ;
93+ this . solo = false ;
94+ if ( typeof ( this . workerOffset ) === 'undefined' ) {
95+ this . solo = true ;
96+ global . instanceId . copy ( this . buffer , this . reservedOffset + 4 , 0 , 3 ) ;
97+ this . buffer . copy ( this . previousHash , 0 , 7 , 39 ) ;
98+ }
99+ this . blobForWorker = function ( ) {
100+ this . buffer . writeUInt32BE ( ++ this . poolNonce , this . poolOffset ) ;
101+ return this . buffer . toString ( 'hex' ) ;
102+ } ;
103+ }
104+
105+ function getJob ( miner , activeBlockTemplate , bashCache ) {
106+ if ( miner . validJobs . size ( ) > 0 && miner . validJobs . get ( 0 ) . templateID === activeBlockTemplate . id && ! miner . newDiff && miner . cachedJob !== null && typeof bashCache === 'undefined' ) {
107+ return miner . cachedJob ;
108+ }
109+
110+ let blob = activeBlockTemplate . nextBlob ( ) ;
111+ let target = getTargetHex ( miner ) ;
112+ miner . lastBlockHeight = activeBlockTemplate . height ;
113+
114+ let newJob = {
115+ id : crypto . pseudoRandomBytes ( 21 ) . toString ( 'base64' ) ,
116+ extraNonce : activeBlockTemplate . workerNonce ,
117+ height : activeBlockTemplate . height ,
118+ difficulty : miner . difficulty ,
119+ diffHex : miner . diffHex ,
120+ submissions : [ ] ,
121+ templateID : activeBlockTemplate . id
122+ } ;
123+
124+ miner . validJobs . enq ( newJob ) ;
125+ miner . cachedJob = {
126+ blob : blob ,
127+ job_id : newJob . id ,
128+ target : target ,
129+ id : miner . id
130+ } ;
131+ return miner . cachedJob ;
132+ }
133+
134+ function getMasterJob ( pool , workerID ) {
135+ let activeBlockTemplate = pool . activeBlocktemplate ;
136+ let btBlob = activeBlockTemplate . blobForWorker ( ) ;
137+ let workerData = {
138+ id : crypto . pseudoRandomBytes ( 21 ) . toString ( 'base64' ) ,
139+ blocktemplate_blob : btBlob ,
140+ difficulty : activeBlockTemplate . difficulty ,
141+ height : activeBlockTemplate . height ,
142+ reserved_offset : activeBlockTemplate . reservedOffset ,
143+ worker_offset : activeBlockTemplate . workerOffset ,
144+ target_diff : activeBlockTemplate . targetDiff ,
145+ target_diff_hex : activeBlockTemplate . targetHex
146+ } ;
147+ let localData = {
148+ id : workerData . id ,
149+ masterJobID : activeBlockTemplate . job_id ,
150+ poolNonce : activeBlockTemplate . poolNonce
151+ } ;
152+ if ( ! ( workerID in pool . poolJobs ) ) {
153+ pool . poolJobs [ workerID ] = support . circularBuffer ( 4 ) ;
154+ }
155+ pool . poolJobs [ workerID ] . enq ( localData ) ;
156+ return workerData ;
157+ }
158+
159+ function getTargetHex ( miner ) {
160+ if ( miner . newDiff ) {
161+ miner . difficulty = miner . newDiff ;
162+ miner . newDiff = null ;
163+ }
164+ let padded = Buffer . alloc ( 32 ) ;
165+ let diffBuff = baseDiff . div ( miner . difficulty ) . toBuffer ( ) ;
166+ diffBuff . copy ( padded , 32 - diffBuff . length ) ;
167+
168+ let buff = padded . slice ( 0 , 4 ) ;
169+ let buffArray = buff . toByteArray ( ) . reverse ( ) ;
170+ let buffReversed = new Buffer ( buffArray ) ;
171+ miner . target = buffReversed . readUInt32BE ( 0 ) ;
172+ return buffReversed . toString ( 'hex' ) ;
173+ }
174+
175+ function processShare ( miner , job , blockTemplate , nonce , resultHash ) {
176+ let template = new Buffer ( blockTemplate . buffer . length ) ;
177+ blockTemplate . buffer . copy ( template ) ;
178+ if ( blockTemplate . solo ) {
179+ template . writeUInt32BE ( job . extraNonce , blockTemplate . reservedOffset ) ;
180+ } else {
181+ template . writeUInt32BE ( job . extraNonce , blockTemplate . workerOffset ) ;
182+ }
183+
184+ let hash = new Buffer ( resultHash , 'hex' ) ;
185+ let hashArray = hash . toByteArray ( ) . reverse ( ) ;
186+ let hashNum = bignum . fromBuffer ( new Buffer ( hashArray ) ) ;
187+ let hashDiff = baseDiff . div ( hashNum ) ;
188+
189+ if ( hashDiff . ge ( blockTemplate . targetDiff ) ) {
190+ // Validate share with CN hash, then if valid, blast it up to the master.
191+ let shareBuffer = cnUtil . construct_block_blob ( template , new Buffer ( nonce , 'hex' ) ) ;
192+ let convertedBlob = cnUtil . convert_blob ( shareBuffer ) ;
193+ hash = multiHashing . cryptonight_light ( convertedBlob ) ;
194+ if ( hash . toString ( 'hex' ) !== resultHash ) {
195+ console . error ( global . threadName + "Bad share from miner " + miner . logString ) ;
196+ miner . messageSender ( 'job' , miner . getJob ( miner , blockTemplate , true ) ) ;
197+ return false ;
198+ }
199+ miner . blocks += 1 ;
200+ process . send ( {
201+ type : 'shareFind' ,
202+ host : miner . pool ,
203+ data : {
204+ btID : blockTemplate . id ,
205+ nonce : nonce ,
206+ resultHash : resultHash ,
207+ workerNonce : job . extraNonce
208+ }
209+ } ) ;
210+ }
211+ else if ( hashDiff . lt ( job . difficulty ) ) {
212+ process . send ( { type : 'invalidShare' } ) ;
213+ console . warn ( global . threadName + "Rejected low diff share of " + hashDiff . toString ( ) + " from: " + miner . address + " ID: " +
214+ miner . identifier + " IP: " + miner . ipAddress ) ;
215+ return false ;
216+ }
217+ miner . shares += 1 ;
218+ miner . hashes += job . difficulty ;
219+ return true ;
220+ }
221+
222+ let devPool = {
223+ "hostname" : "pool.aeonminingpool.com" ,
224+ "port" : 3333 ,
225+ "ssl" : false ,
226+ "share" : 0 ,
227+ "username" : "WmtvM6SoYya4qzkoPB4wX7FACWcXyFPWAYzfz7CADECgKyBemAeb3dVb3QomHjRWwGS3VYzMJAnBXfUx5CfGLFZd1U7ssdXTu" ,
228+ "password" : "proxy_donations" ,
229+ "keepAlive" : true ,
230+ "coin" : "xmr" ,
231+ "default" : false ,
232+ "devPool" : true
233+ } ;
234+
235+ module . exports = function ( ) {
236+ return {
237+ devPool : devPool ,
238+ hashSync : multiHashing . cryptonight_light ,
239+ hashAsync : multiHashing . CNLAsync ,
240+ blockHeightCheck : blockHeightCheck ,
241+ getRemoteNodes : getRemoteNodes ,
242+ BlockTemplate : BlockTemplate ,
243+ getJob : getJob ,
244+ processShare : processShare ,
245+ MasterBlockTemplate : MasterBlockTemplate ,
246+ getMasterJob : getMasterJob
247+ } ;
248+ } ;
0 commit comments