Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions lib/bitcoin_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import lib.logger
log = lib.logger.get_logger('bitcoin_rpc')

gbt_known_rules = ["segwit"]

class BitcoinRPC(object):

def __init__(self, host, port, username, password):
Expand Down Expand Up @@ -129,7 +131,7 @@ def getinfo(self):
@defer.inlineCallbacks
def getblocktemplate(self):
try:
resp = (yield self._call('getblocktemplate', [{}]))
resp = (yield self._call('getblocktemplate', [{"rules": gbt_known_rules}]))
defer.returnValue(json.loads(resp)['result'])
# if internal server error try getblocktemplate without empty {} # ppcoin
except Exception as e:
Expand All @@ -138,15 +140,20 @@ def getblocktemplate(self):
defer.returnValue(json.loads(resp)['result'])
else:
raise

@defer.inlineCallbacks
def prevhash(self):
resp = (yield self._call('getwork', []))
try:
defer.returnValue(json.loads(resp)['result']['data'][8:72])
resp = (yield self._call('getbestblockhash', []))
defer.returnValue(json.loads(resp)['result'])
# if internal server error try getblocktemplate without empty {} # ppcoin
except Exception as e:
log.exception("Cannot decode prevhash %s" % str(e))
raise
if (str(e) == "500 Internal Server Error"):
resp = (yield self._call('getwork', []))
defer.returnValue(util.reverse_hash(json.loads(resp)['result']['data'][8:72]))
else:
log.exception("Cannot decode prevhash %s" % str(e))
raise

@defer.inlineCallbacks
def validateaddress(self, address):
Expand Down
91 changes: 83 additions & 8 deletions lib/block_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from coinbasetx import CoinbaseTransactionPOW
from coinbasetx import CoinbaseTransactionPOS
from coinbasetx import CoinbaseTransaction
from Crypto.Hash import SHA256

import lib.logger
log = lib.logger.get_logger('block_template')

Expand All @@ -19,6 +21,21 @@
# provided from coinbaser
import settings

if settings.COINDAEMON_ALGO == 'scrypt':
import ltc_scrypt

witness_nonce = b'\0' * 0x20
witness_magic = b'\xaa\x21\xa9\xed'

class TxBlob(object):
def __init__(self):
self.data = ''
def serialize(self):
return self.data
def deserialize(self, data):
self.data = data


class BlockTemplate(halfnode.CBlock):
'''Template is used for generating new jobs for clients.
Let's iterate extranonce1, extranonce2, ntime and nonce
Expand All @@ -40,6 +57,7 @@ def __init__(self, timestamper, coinbaser, job_id):
self.timedelta = 0
self.curtime = 0
self.target = 0
self.witness = 0
#self.coinbase_hex = None
self.merkletree = None

Expand All @@ -52,17 +70,36 @@ def __init__(self, timestamper, coinbaser, job_id):

def fill_from_rpc(self, data):
'''Convert getblocktemplate result into BlockTemplate instance'''

#txhashes = [None] + [ binascii.unhexlify(t['hash']) for t in data['transactions'] ]
txhashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ]
mt = merkletree.MerkleTree(txhashes)

commitment = None
txids = []
hashes = [None] + [ util.ser_uint256(int(t['hash'], 16)) for t in data['transactions'] ]
try:
txids = [None] + [ util.ser_uint256(int(t['txid'], 16)) for t in data['transactions'] ]
mt = merkletree.MerkleTree(txids)
except KeyError:
mt = merkletree.MerkleTree(hashes)

wmt = merkletree.MerkleTree(hashes).withFirst(binascii.unhexlify('0000000000000000000000000000000000000000000000000000000000000000'))
self.witness = SHA256.new(SHA256.new(wmt + witness_nonce).digest()).digest()
commitment = b'\x6a' + struct.pack(">b", len(self.witness) + len(witness_magic)) + witness_magic + self.witness
try:
default_witness = data['default_witness_commitment']
commitment_check = binascii.unhexlify(default_witness)
if(commitment != commitment_check):
print("calculated witness does not match supplied one! This block probably will not be accepted!")
commitment = commitment_check
except KeyError:
pass
self.witness = commitment[6:]

if settings.COINDAEMON_Reward == 'POW':
coinbase = CoinbaseTransactionPOW(self.timestamper, self.coinbaser, data['coinbasevalue'],
data['coinbaseaux']['flags'], data['height'],
data['coinbaseaux']['flags'], data['height'], commitment,
settings.COINBASE_EXTRAS)
else:
coinbase = CoinbaseTransactionPOS(self.timestamper, self.coinbaser, data['coinbasevalue'],
data['coinbaseaux']['flags'], data['height'],
data['coinbaseaux']['flags'], data['height'], commitment,
settings.COINBASE_EXTRAS, data['curtime'])

self.height = data['height']
Expand All @@ -76,8 +113,8 @@ def fill_from_rpc(self, data):
self.vtx = [ coinbase, ]

for tx in data['transactions']:
t = halfnode.CTransaction()
t.deserialize(StringIO.StringIO(binascii.unhexlify(tx['data'])))
t = TxBlob()
t.deserialize(binascii.unhexlify(tx['data']))
self.vtx.append(t)

self.curtime = data['curtime']
Expand Down Expand Up @@ -158,3 +195,41 @@ def finalize(self, merkle_root_int, extranonce1_bin, extranonce2_bin, ntime, non
self.nNonce = nonce
self.vtx[0].set_extranonce(extranonce1_bin + extranonce2_bin)
self.sha256 = None # We changed block parameters, let's reset sha256 cache
if settings.COINDAEMON_ALGO == 'scrypt':
self.scrypt = None

def serialize(self):
r = []
r.append(struct.pack("<i", self.nVersion))
r.append(util.ser_uint256(self.hashPrevBlock))
r.append(util.ser_uint256(self.hashMerkleRoot))
r.append(struct.pack("<I", self.nTime))
r.append(struct.pack("<I", self.nBits))
r.append(struct.pack("<I", self.nNonce))
r.append(util.ser_vector(self.vtx))
return ''.join(r)

def is_valid(self):
if settings.COINDAEMON_ALGO == 'scrypt':
self.calc_scrypt()
else:
self.calc_sha256()
target = util.uint256_from_compact(self.nBits)
if settings.COINDAEMON_ALGO == 'scrypt' and self.scrypt > self.target:
return False
elif self.sha256 > self.target:
return False
hashes = []
hashes.append(b'\0' * 0x20)
for tx in self.vtx[1:]:
hashes.append(SHA256.new(SHA256.new(tx.serialize()).digest()).digest())
while len(hashes) > 1:
newhashes = []
for i in xrange(0, len(hashes), 2):
i2 = min(i+1, len(hashes)-1)
newhashes.append(SHA256.new(SHA256.new(hashes[i] + hashes[i2]).digest()).digest())
hashes = newhashes
calcwitness = SHA256.new(SHA256.new(hashes[0] + witness_nonce).digest()).digest()
if calcwitness != self.witness:
return False
return True
2 changes: 1 addition & 1 deletion lib/block_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def run(self):
current_prevhash = None

log.info("Checking for new block.")
prevhash = util.reverse_hash((yield self.bitcoin_rpc.prevhash()))
prevhash = yield self.bitcoin_rpc.prevhash()
if prevhash and prevhash != current_prevhash:
log.info("New block! Prevhash: %s" % prevhash)
update = True
Expand Down
19 changes: 15 additions & 4 deletions lib/coinbasetx.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class CoinbaseTransactionPOW(halfnode.CTransaction):
extranonce_placeholder = struct.pack(extranonce_type, int('f000000ff111111f', 16))
extranonce_size = struct.calcsize(extranonce_type)

def __init__(self, timestamper, coinbaser, value, flags, height, data):
def __init__(self, timestamper, coinbaser, value, flags, height, commitment, data):
super(CoinbaseTransactionPOW, self).__init__()
log.debug("Got to CoinBaseTX")
#self.extranonce = 0
Expand All @@ -43,7 +43,12 @@ def __init__(self, timestamper, coinbaser, value, flags, height, data):
self.strTxComment = "http://github.com/ahmedbodi/stratum-mining"
self.vin.append(tx_in)
self.vout.append(tx_out)


if(commitment):
txout_commitment = halfnode.CTxOut()
txout_commitment.nValue = 0
txout_commitment.scriptPubKey = commitment
self.vout.append(txout_commitment)
# Two parts of serialized coinbase, just put part1 + extranonce + part2 to have final serialized tx
self._serialized = super(CoinbaseTransactionPOW, self).serialize().split(self.extranonce_placeholder)

Expand All @@ -63,7 +68,7 @@ class CoinbaseTransactionPOS(halfnode.CTransaction):
extranonce_placeholder = struct.pack(extranonce_type, int('f000000ff111111f', 16))
extranonce_size = struct.calcsize(extranonce_type)

def __init__(self, timestamper, coinbaser, value, flags, height, data, ntime):
def __init__(self, timestamper, coinbaser, value, flags, height, commitment, data, ntime):
super(CoinbaseTransactionPOS, self).__init__()
log.debug("Got to CoinBaseTX")
#self.extranonce = 0
Expand Down Expand Up @@ -137,7 +142,13 @@ def __init__(self, timestamper, coinbaser, value, flags, height, data, ntime):
self.nTime = ntime
self.vin.append(tx_in)
self.vout.append(tx_out)


if(commitment):
txout_commitment = halfnode.CTxOut()
txout_commitment.nValue = 0
txout_commitment.scriptPubKey = commitment
self.vout.append(txout_commitment)

# Two parts of serialized coinbase, just put part1 + extranonce + part2 to have final serialized tx
self._serialized = super(CoinbaseTransaction, self).serialize().split(self.extranonce_placeholder)

Expand Down
19 changes: 8 additions & 11 deletions lib/template_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,24 +223,21 @@ def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce
else:
if len(nonce) != 8:
raise SubmitException("Incorrect size of nonce. Expected 8 chars")


# Convert from hex to binary
extranonce2_bin = binascii.unhexlify(extranonce2)
ntime_bin = binascii.unhexlify(ntime)
nonce_bin = binascii.unhexlify(nonce)

# Check for duplicated submit
if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce):
if not job.register_submit(extranonce1_bin, extranonce2_bin, ntime_bin, nonce_bin):
log.info("Duplicate from %s, (%s %s %s %s)" % \
(worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce))
raise SubmitException("Duplicate share")

# Now let's do the hard work!
# ---------------------------

# 0. Some sugar
extranonce2_bin = binascii.unhexlify(extranonce2)
ntime_bin = binascii.unhexlify(ntime)
nonce_bin = binascii.unhexlify(nonce)
if settings.COINDAEMON_ALGO == 'riecoin':
ntime_bin = (''.join([ ntime_bin[(1-i)*4:(1-i)*4+4] for i in range(0, 2) ]))
nonce_bin = (''.join([ nonce_bin[(7-i)*4:(7-i)*4+4] for i in range(0, 8) ]))


# 1. Build coinbase
coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin)
coinbase_hash = util.doublesha(coinbase_bin)
Expand Down