Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #difficulty adjustment block - 479808, 477792
- import time, datetime, calendar
- from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
- from numpy import median
- import os.path, pickle, random
- class btcBlockchain():
- # last common block between bch and bitcoin: 478558
- def __init__(self, initialTime):
- self.currentSimTime = initialTime
- self.lastCommonBlock = 478558
- self.referenceDiff = 860221984436.2223 #difficulty at block 478558; assume the hash power at this block represents 100% of available hash power
- self.avg = 600 #average block interval in seconds
- #block chain is stored as list of dictionaries
- #fetch the historical blocks between 477792 and 478558
- #Each block is defined as the following stats: 'time', 'difficulty', 'height', 'mediantime'
- self.blocks = self._getHistBlocks(477792, self.lastCommonBlock)
- def update(self, newTime, minerSupport = 0.5):
- """
- newTime - updated simulation time
- minerSupport - portion of total hashpower supporting this chain (e.g., 0.9)
- Check if a new block should be 'issued' based on the new simulation time. If so, generate block and potentially
- adjust the difficulty.
- RReturn a code indicating 'none' (no difficulty adjust), 'yes ' (regular difficulty adjust), or 'emer' (emergency diff adjust, bch only)
- """
- self.currentSimTime = newTime
- #the current average block interval is proportional to the current difficulty, the reference difficulty, and minerSupport
- averageBlockInterval = (10.0 * 60) * (self.blocks[-1]['difficulty'] / self.referenceDiff) / minerSupport
- self.avg = averageBlockInterval
- timestamp = calendar.timegm(self.currentSimTime.timetuple())
- code = 'none'
- if self.blocks[-1]['time'] + averageBlockInterval < timestamp: #add a new block?
- newBlockTime = self.blocks[-1]['time'] + averageBlockInterval
- self.blocks.append(self._createNewBlock(newBlockTime))
- code = self._adjustDifficulty()
- return code
- def returnDifficulty(self):
- return self.blocks[-1]['difficulty']
- def _adjustDifficulty(self):
- """Determine whether tip block is a difficulty adjustment block. IF so, adjust difficulty.
- Return a code indicating 'none' (no difficulty adjust), 'yes ' (regular difficulty adjust), or 'emer' (emergency diff adjust, bch only)"""
- twoWeeks = 24 * 14 * 60 * 60 #two weeks in seconds
- code = 'none'
- if self.blocks[-1]['height'] % 2016 == 0:
- elapsedTime = self.blocks[-1]['mediantime'] - self.blocks[-2016]['mediantime']
- adjustFactor = elapsedTime / twoWeeks
- #adjustFactor is limited to between .25 and 4
- adjustFactor = max(adjustFactor, .25)
- adjustFactor = min(adjustFactor, 4.0)
- self.blocks[-1]['difficulty'] = self.blocks[-1]['difficulty'] / adjustFactor
- code = 'yes '
- return code
- def _getHistBlocks(self, startBlock, endBlock):
- """Fetches the blocks between startBlock and endBlock (inclusive) from the bitcoin rpc-json interface."""
- if os.path.isfile('historicalBlocks.pkl'):
- blocks = pickle.load(open('historicalBlocks.pkl', 'rb'))
- else:
- rpc_user = 'XXXX'; rpc_password = 'XXXX'
- rpc_connection = AuthServiceProxy("http://%s:%s@127.0.0.1:8332"%(rpc_user, rpc_password))
- blocks = []
- for h in range(startBlock, endBlock + 1):
- blockHash = rpc_connection.getblockhash(h)
- b = rpc_connection.getblock(blockHash)
- block = {'time': b['time'], 'mediantime': b['mediantime'], 'difficulty': float(b['difficulty']), 'height':b['height'] }
- blocks.append(block)
- pickle.dump(blocks, open('historicalBlocks.pkl', 'wb'))
- return blocks
- def _createNewBlock(self, blockTime):
- b = {'time': blockTime, 'difficulty': self.blocks[-1]['difficulty'],
- 'height': self.blocks[-1]['height'] + 1}
- #median time is median of last 11 blocks
- v = [blockTime]
- for idx in range(-10, 0):
- v.append(self.blocks[idx]['time'])
- b['mediantime'] = median(v)
- return b
- class bchBlockchain(btcBlockchain):
- def _adjustDifficulty(self):
- """Determine whether tip block is a difficulty adjustment block. IF so, adjust difficulty.
- Return a code indicating 'none' (no difficulty adjust), 'yes ' (regular difficulty adjust), or 'emer' (emergency diff adjust, bch only)"""
- code = 'none'
- twoWeeks = 24 * 14 * 60 * 60 #two weeks in seconds
- if self.blocks[-1]['height'] % 2016 == 0:
- elapsedTime = self.blocks[-1]['mediantime'] - self.blocks[-2016]['mediantime']
- adjustFactor = elapsedTime / twoWeeks
- #adjustFactor is limited to between .25 and 4
- adjustFactor = max(adjustFactor, .25)
- adjustFactor = min(adjustFactor, 4.0)
- self.blocks[-1]['difficulty'] = self.blocks[-1]['difficulty'] / adjustFactor
- code = 'yes '
- #apply BCH emergency adjust rules
- # * Median Time Past of the current block and the Median Time Past of 6 blocks before has to be greater than 12 hours.
- # * If so, it gets 20% easier to create proof of work. In other words, miners can find blocks 20% easier.
- interval = (self.blocks[-1]['mediantime'] - self.blocks[-7]['mediantime'])
- if interval > 12 * 60 * 60:
- self.blocks[-1]['difficulty'] = self.blocks[-1]['difficulty'] * 0.80
- code = 'emer'
- return code
- if __name__ == "__main__":
- #Simulation Parameters
- btcLoyalMiners = 0.5 #portion of miners that will always mine BTC
- bchLoyalMiners = 0.1 #portion of miners that will always mine BCH
- exchangeRate = 0.2 #exchange rate - BCH/BTC
- startTime = datetime.datetime(2017, 8, 1, 13, 16, 14) #time of last common block
- simDuration = datetime.timedelta(days = 240)
- simIncrement = datetime.timedelta(minutes = 5) #simulation resolution
- #end simulation parameters
- profitMaximizingMiners = 1.0 - btcLoyalMiners - bchLoyalMiners
- btcChain = btcBlockchain(startTime)
- bchChain = bchBlockchain(startTime)
- currentTime = startTime
- iters = 0
- while currentTime < startTime + simDuration:
- #These bit was when I was trying to simulate changes to the exchange rate via some form of random walk
- #vary exchange rate every hour
- #hourlyPercentChangeMax = 2.0
- #if currentTime.hour != (currentTime + simIncrement).hour:
- #randomPercentChange = (random.random() * hourlyPercentChangeMax*2) - hourlyPercentChangeMax
- #exchangeRate = exchangeRate * (1.0 - randomPercentChange/100.0)
- currentTime += simIncrement
- #determine most profitable chain
- if btcChain.returnDifficulty() * (1.0 - exchangeRate) > bchChain.returnDifficulty() * exchangeRate:
- #btc chain is more profitable
- btcDiffAdj = btcChain.update(currentTime, minerSupport = btcLoyalMiners + profitMaximizingMiners)
- bchDiffAdj = bchChain.update(currentTime, minerSupport = bchLoyalMiners)
- else:
- #bch chain is more profitable
- btcDiffAdj = btcChain.update(currentTime, minerSupport = btcLoyalMiners)
- bchDiffAdj = bchChain.update(currentTime, minerSupport = bchLoyalMiners + profitMaximizingMiners)
- #Output some useful information
- bchHeight = bchChain.blocks[-1]['height']
- bchAvgInterval = bchChain.avg/60.0
- bchDate = bchChain.blocks[-1]['time']
- bchDifficulty = bchChain.blocks[-1]['difficulty']
- btcHeight = btcChain.blocks[-1]['height']
- btcAvgInterval = btcChain.avg/60.0
- btcDate = btcChain.blocks[-1]['time']
- btcDifficulty = btcChain.blocks[-1]['difficulty']
- if btcDiffAdj != 'none' or bchDiffAdj != 'none':
- print btcDiffAdj, bchDiffAdj, btcChain.currentSimTime, round(btcAvgInterval, 1), round(bchAvgInterval,1), exchangeRate
- iters += 1
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement