-
Notifications
You must be signed in to change notification settings - Fork 284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Major fixes & improvements for getwork #583
Changes from all commits
5928e80
23436f8
6d6dec5
68bdeb3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -361,29 +361,6 @@ class BlockTemplate { | |
this.coinbase = cb; | ||
} | ||
|
||
/** | ||
* Generate a random mask (2 bytes less than target). | ||
* @returns {Buffer[]} | ||
*/ | ||
|
||
randomMask() { | ||
const mask = random.randomBytes(32); | ||
|
||
for (let i = 0; i < this.target.length; i++) { | ||
mask[i] = 0; | ||
|
||
if (this.target[i] !== 0) { | ||
if (i + 1 < mask.length) | ||
mask[i + 1] = 0; | ||
break; | ||
} | ||
} | ||
|
||
const hash = BLAKE2b.multi(this.prevBlock, mask); | ||
|
||
return [mask, hash]; | ||
} | ||
|
||
/** | ||
* Create raw block header with given parameters. | ||
* @param {Buffer} extraNonce | ||
|
@@ -468,7 +445,7 @@ class BlockTemplate { | |
*/ | ||
|
||
toCoinbase() { | ||
return this.coinbase; | ||
return this.coinbase.clone(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense to return a clone here, but I can't find reference to this function - what's it used for? |
||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,7 +115,8 @@ class RPC extends RPCBase { | |
this.lastActivity = 0; | ||
this.boundChain = false; | ||
this.mask = Buffer.alloc(32, 0x00); | ||
this.maskMap = new BufferMap(); | ||
this.merkleMap = new BufferMap(); | ||
this.merkleList = []; | ||
this.pollers = []; | ||
|
||
this.init(); | ||
|
@@ -1127,36 +1128,35 @@ class RPC extends RPCBase { | |
* Mining | ||
*/ | ||
|
||
async handleWork(data) { | ||
async handleWork(data, mask) { | ||
const unlock = await this.locker.lock(); | ||
try { | ||
return await this._handleWork(data); | ||
return await this._handleWork(data, mask); | ||
} finally { | ||
unlock(); | ||
} | ||
} | ||
|
||
async _handleWork(data) { | ||
const attempt = this.attempt; | ||
|
||
if (!attempt) | ||
return [false, 'no-mining-job']; | ||
|
||
async _handleWork(data, mask) { | ||
if (data.length !== 256) | ||
return [false, 'invalid-data-length']; | ||
|
||
const hdr = Headers.fromMiner(data); | ||
const maskHash = blake2b.multi(hdr.prevBlock, mask); | ||
|
||
if (!hdr.maskHash().equals(maskHash)) | ||
return [false, 'bad-maskhash']; | ||
|
||
const attempt = this.merkleMap.get(hdr.witnessRoot); | ||
|
||
if (!attempt) | ||
return [false, 'stale']; | ||
|
||
if (!hdr.prevBlock.equals(attempt.prevBlock) | ||
|| hdr.bits !== attempt.bits) { | ||
return [false, 'stale']; | ||
} | ||
|
||
const mask = this.maskMap.get(hdr.maskHash()); | ||
|
||
if (!mask) | ||
return [false, 'stale']; | ||
|
||
const {nonce, time, extraNonce} = hdr; | ||
const proof = attempt.getProof(nonce, time, extraNonce, mask); | ||
|
||
|
@@ -1186,19 +1186,21 @@ class RPC extends RPCBase { | |
return [true, 'valid']; | ||
} | ||
|
||
async createWork(data) { | ||
async createWork() { | ||
const unlock = await this.locker.lock(); | ||
try { | ||
return await this._createWork(data); | ||
return await this._createWork(); | ||
} finally { | ||
unlock(); | ||
} | ||
} | ||
|
||
async _createWork() { | ||
const [mask, attempt] = await this.updateWork(); | ||
const attempt = await this.updateWork(); | ||
const time = attempt.time; | ||
const data = attempt.getHeader(0, time, consensus.ZERO_NONCE, mask); | ||
const nonce = consensus.ZERO_NONCE; | ||
const mask = consensus.ZERO_HASH; | ||
const data = attempt.getHeader(0, time, nonce, mask); | ||
|
||
return { | ||
network: this.network.type, | ||
|
@@ -1215,34 +1217,32 @@ class RPC extends RPCBase { | |
} | ||
|
||
async getWork(args, help) { | ||
if (help || args.length > 1) | ||
throw new RPCError(errs.MISC_ERROR, 'getwork ( "maskhash" )'); | ||
|
||
if (args.length === 1) { | ||
const valid = new Validator(args); | ||
const maskHash = valid.bhash(0); | ||
|
||
if (!maskHash) | ||
throw new RPCError(errs.TYPE_ERROR, 'Invalid mask hash.'); | ||
|
||
if (this.maskMap.has(maskHash)) | ||
return null; | ||
} | ||
if (help || args.length !== 0) | ||
throw new RPCError(errs.MISC_ERROR, 'getwork'); | ||
|
||
return this.createWork(); | ||
} | ||
|
||
async submitWork(args, help) { | ||
if (help || args.length !== 1) | ||
throw new RPCError(errs.MISC_ERROR, 'submitwork ( "data" )'); | ||
if (help || args.length < 1 || args.length > 2) | ||
throw new RPCError(errs.MISC_ERROR, 'submitwork ( "data" "mask" )'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
|
||
const valid = new Validator(args); | ||
const data = valid.buf(0); | ||
|
||
if (!data) | ||
throw new RPCError(errs.TYPE_ERROR, 'Invalid work data.'); | ||
|
||
return this.handleWork(data); | ||
let mask = consensus.ZERO_HASH; | ||
|
||
if (args.length === 2) { | ||
mask = valid.bhash(1); | ||
|
||
if (!mask) | ||
throw new RPCError(errs.TYPE_ERROR, 'Invalid mask.'); | ||
} | ||
|
||
return this.handleWork(data, mask); | ||
} | ||
|
||
async submitBlock(args, help) { | ||
|
@@ -1323,16 +1323,6 @@ class RPC extends RPCBase { | |
} | ||
} | ||
|
||
// BIP22 states that we can't have coinbasetxn | ||
// _and_ coinbasevalue in the same template. | ||
// The problem is, many clients _say_ they | ||
// support coinbasetxn when they don't (ckpool). | ||
// To make matters worse, some clients will | ||
// parse an undefined `coinbasevalue` as zero. | ||
// Because of all of this, coinbasetxn is | ||
// disabled for now. | ||
valueCap = true; | ||
|
||
if (txnCap && !valueCap) { | ||
if (this.miner.addresses.length === 0) { | ||
throw new RPCError(errs.MISC_ERROR, | ||
|
@@ -1488,9 +1478,11 @@ class RPC extends RPCBase { | |
vbrequired: 0, | ||
height: attempt.height, | ||
previousblockhash: attempt.prevBlock.toString('hex'), | ||
merkleroot: undefined, | ||
witnessroot: undefined, | ||
treeroot: attempt.treeRoot.toString('hex'), | ||
reservedroot: attempt.reservedRoot.toString('hex'), | ||
mask: attempt.randomMask()[0].toString('hex'), | ||
mask: consensus.ZERO_HASH.toString('hex'), // Compat. | ||
target: attempt.target.toString('hex'), | ||
bits: hex32(attempt.bits), | ||
noncerange: | ||
|
@@ -1509,7 +1501,7 @@ class RPC extends RPCBase { | |
coinbaseaux: { | ||
flags: attempt.coinbaseFlags.toString('hex') | ||
}, | ||
coinbasevalue: undefined, | ||
coinbasevalue: attempt.getReward(), | ||
coinbasetxn: undefined, | ||
claims: attempt.claims.map((claim) => { | ||
return { | ||
|
@@ -1544,14 +1536,10 @@ class RPC extends RPCBase { | |
// The client wants a coinbasetxn | ||
// instead of a coinbasevalue. | ||
if (coinbase) { | ||
const tx = attempt.toCoinbase(); | ||
const input = tx.inputs[0]; | ||
|
||
// Pop off the nonces. | ||
input.witness.pop(); | ||
input.witness.compile(); | ||
const tx = attempt.coinbase; | ||
|
||
tx.refresh(); | ||
json.merkleroot = attempt.merkleRoot.toString('hex'); | ||
json.witnessroot = attempt.witnessRoot.toString('hex'); | ||
Comment on lines
+1541
to
+1542
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a convenient feature - client can still modify TXs in the block before submitting? |
||
|
||
json.coinbasetxn = { | ||
data: tx.toHex(), | ||
|
@@ -1562,8 +1550,6 @@ class RPC extends RPCBase { | |
sigops: tx.getSigops() / scale | 0, | ||
weight: tx.getWeight() | ||
}; | ||
} else { | ||
json.coinbasevalue = attempt.getReward(); | ||
} | ||
|
||
return json; | ||
|
@@ -2565,7 +2551,6 @@ class RPC extends RPCBase { | |
|
||
this.attempt = null; | ||
this.lastActivity = 0; | ||
this.maskMap.clear(); | ||
this.pollers = []; | ||
|
||
for (const job of pollers) | ||
|
@@ -2578,31 +2563,32 @@ class RPC extends RPCBase { | |
|
||
this.boundChain = true; | ||
|
||
this.node.on('connect', () => { | ||
const refresh = () => { | ||
if (!this.attempt) | ||
return; | ||
|
||
this.refreshBlock(); | ||
}); | ||
this.merkleMap.clear(); | ||
this.merkleList.length = 0; | ||
}; | ||
|
||
this.node.on('connect', refresh); | ||
this.node.on('reset', refresh); | ||
|
||
if (!this.mempool) | ||
return; | ||
|
||
this.node.on('tx', () => { | ||
const tryRefresh = () => { | ||
if (!this.attempt) | ||
return; | ||
|
||
if (util.now() - this.lastActivity > 10) | ||
this.refreshBlock(); | ||
}); | ||
|
||
this.node.on('claim', () => { | ||
if (!this.attempt) | ||
return; | ||
}; | ||
|
||
if (util.now() - this.lastActivity > 10) | ||
this.refreshBlock(); | ||
}); | ||
this.node.on('tx', tryRefresh); | ||
this.node.on('claim', tryRefresh); | ||
this.node.on('airdrop', tryRefresh); | ||
} | ||
|
||
async getTemplate() { | ||
|
@@ -2634,11 +2620,7 @@ class RPC extends RPCBase { | |
|
||
this.miner.updateTime(attempt); | ||
|
||
const [mask, maskHash] = attempt.randomMask(); | ||
|
||
this.maskMap.set(maskHash, mask); | ||
|
||
return [mask, attempt]; | ||
return attempt; | ||
} | ||
|
||
if (this.miner.addresses.length === 0) { | ||
|
@@ -2647,15 +2629,16 @@ class RPC extends RPCBase { | |
} | ||
|
||
attempt = await this.miner.createBlock(); | ||
attempt.version = 0; | ||
|
||
const [mask, maskHash] = attempt.randomMask(); | ||
if (this.merkleMap.size >= 10) | ||
this.merkleMap.delete(this.merkleList.shift()); | ||
|
||
this.attempt = attempt; | ||
this.lastActivity = util.now(); | ||
this.maskMap.set(maskHash, mask); | ||
this.merkleMap.set(attempt.witnessRoot, attempt); | ||
this.merkleList.push(attempt.witnessRoot); | ||
|
||
return [mask, attempt]; | ||
return attempt; | ||
} | ||
|
||
async addBlock(block) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this effectively the same fix we did here?
5e20a5a