Advertisement
Guest User

Untitled

a guest
Aug 21st, 2017
622
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.64 KB | None | 0 0
  1. #difficulty adjustment block - 479808, 477792
  2.  
  3. import time, datetime, calendar
  4. from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
  5. from numpy import median
  6. import os.path, pickle, random
  7.  
  8. class btcBlockchain():
  9.     # last common block between bch and bitcoin: 478558
  10.     def __init__(self, initialTime):
  11.         self.currentSimTime = initialTime
  12.        
  13.         self.lastCommonBlock = 478558
  14.         self.referenceDiff = 860221984436.2223   #difficulty at block 478558; assume the hash power at this block represents 100% of available hash power
  15.         self.avg = 600 #average block interval in seconds
  16.        
  17.         #block chain is stored as list of dictionaries
  18.         #fetch the historical blocks between 477792 and 478558
  19.         #Each block is defined as the following stats: 'time', 'difficulty', 'height', 'mediantime'
  20.         self.blocks = self._getHistBlocks(477792, self.lastCommonBlock)
  21.                
  22.     def update(self, newTime, minerSupport = 0.5):
  23.         """
  24.        newTime - updated simulation time
  25.        minerSupport - portion of total hashpower supporting this chain (e.g., 0.9)
  26.        
  27.        Check if a new block should be 'issued' based on the new simulation time. If so, generate block and potentially
  28.        adjust the difficulty.
  29.        
  30.        RReturn a code indicating 'none' (no difficulty adjust), 'yes ' (regular difficulty adjust), or 'emer' (emergency diff adjust, bch only)
  31.        """
  32.         self.currentSimTime = newTime
  33.        
  34.         #the current average block interval is proportional to the current difficulty, the reference difficulty, and minerSupport
  35.         averageBlockInterval = (10.0 * 60) * (self.blocks[-1]['difficulty'] / self.referenceDiff) / minerSupport
  36.         self.avg = averageBlockInterval
  37.        
  38.         timestamp = calendar.timegm(self.currentSimTime.timetuple())
  39.         code = 'none'
  40.         if self.blocks[-1]['time'] + averageBlockInterval < timestamp:  #add a new block?
  41.             newBlockTime = self.blocks[-1]['time'] + averageBlockInterval
  42.             self.blocks.append(self._createNewBlock(newBlockTime))
  43.             code = self._adjustDifficulty()    
  44.         return code
  45.            
  46.    
  47.     def returnDifficulty(self):
  48.         return self.blocks[-1]['difficulty']
  49.            
  50.    
  51.     def _adjustDifficulty(self):
  52.         """Determine whether tip block is a difficulty adjustment block. IF so, adjust difficulty.
  53.        
  54.        Return a code indicating 'none' (no difficulty adjust), 'yes ' (regular difficulty adjust), or 'emer' (emergency diff adjust, bch only)"""
  55.        
  56.         twoWeeks = 24 * 14 * 60 * 60   #two weeks in seconds
  57.         code = 'none'
  58.         if self.blocks[-1]['height'] % 2016 == 0:
  59.             elapsedTime = self.blocks[-1]['mediantime'] - self.blocks[-2016]['mediantime']
  60.             adjustFactor = elapsedTime / twoWeeks
  61.            
  62.             #adjustFactor is limited to between .25 and 4
  63.             adjustFactor = max(adjustFactor, .25)
  64.             adjustFactor = min(adjustFactor, 4.0)
  65.            
  66.             self.blocks[-1]['difficulty']  = self.blocks[-1]['difficulty'] / adjustFactor
  67.             code = 'yes '
  68.         return code
  69.    
  70.            
  71.     def _getHistBlocks(self, startBlock, endBlock):
  72.         """Fetches the blocks between startBlock and endBlock (inclusive) from the bitcoin rpc-json interface."""
  73.         if os.path.isfile('historicalBlocks.pkl'):
  74.             blocks = pickle.load(open('historicalBlocks.pkl', 'rb'))
  75.         else:    
  76.             rpc_user = 'XXXX'; rpc_password = 'XXXX'
  77.             rpc_connection = AuthServiceProxy("http://%s:%s@127.0.0.1:8332"%(rpc_user, rpc_password))
  78.             blocks = []
  79.            
  80.             for h in range(startBlock, endBlock + 1):
  81.                 blockHash = rpc_connection.getblockhash(h)
  82.                 b = rpc_connection.getblock(blockHash)
  83.                 block = {'time': b['time'], 'mediantime': b['mediantime'], 'difficulty': float(b['difficulty']), 'height':b['height'] }
  84.                 blocks.append(block)
  85.             pickle.dump(blocks, open('historicalBlocks.pkl', 'wb'))
  86.         return blocks
  87.            
  88.  
  89.     def _createNewBlock(self, blockTime):
  90.         b = {'time': blockTime, 'difficulty': self.blocks[-1]['difficulty'],
  91.              'height': self.blocks[-1]['height'] + 1}
  92.        
  93.         #median time is median of last 11 blocks
  94.         v = [blockTime]
  95.         for idx in range(-10, 0):
  96.             v.append(self.blocks[idx]['time'])
  97.         b['mediantime'] = median(v)
  98.         return b
  99.        
  100. class bchBlockchain(btcBlockchain):  
  101.     def _adjustDifficulty(self):
  102.         """Determine whether tip block is a difficulty adjustment block. IF so, adjust difficulty.
  103.        
  104.        Return a code indicating 'none' (no difficulty adjust), 'yes ' (regular difficulty adjust), or 'emer' (emergency diff adjust, bch only)"""
  105.        
  106.         code = 'none'
  107.         twoWeeks = 24 * 14 * 60 * 60   #two weeks in seconds
  108.         if self.blocks[-1]['height'] % 2016 == 0:
  109.             elapsedTime = self.blocks[-1]['mediantime'] - self.blocks[-2016]['mediantime']
  110.             adjustFactor = elapsedTime / twoWeeks
  111.            
  112.             #adjustFactor is limited to between .25 and 4
  113.             adjustFactor = max(adjustFactor, .25)
  114.             adjustFactor = min(adjustFactor, 4.0)
  115.            
  116.             self.blocks[-1]['difficulty']  = self.blocks[-1]['difficulty'] / adjustFactor
  117.             code = 'yes '
  118.            
  119.         #apply BCH emergency adjust rules
  120.         #   * Median Time Past of the current block and the Median Time Past of 6 blocks before has to be greater than 12 hours.
  121.         #   * If so, it gets 20% easier to create proof of work. In other words, miners can find blocks 20% easier.        
  122.         interval = (self.blocks[-1]['mediantime'] - self.blocks[-7]['mediantime'])
  123.         if interval > 12 * 60 * 60:
  124.             self.blocks[-1]['difficulty'] = self.blocks[-1]['difficulty'] * 0.80
  125.             code = 'emer'
  126.         return code
  127.        
  128.        
  129. if __name__ == "__main__":
  130.    
  131.     #Simulation Parameters
  132.     btcLoyalMiners = 0.5 #portion of miners that will always mine BTC
  133.     bchLoyalMiners = 0.1     #portion of miners that will always mine BCH      
  134.     exchangeRate = 0.2   #exchange rate - BCH/BTC
  135.    
  136.     startTime = datetime.datetime(2017, 8, 1, 13, 16, 14)   #time of last common block
  137.     simDuration = datetime.timedelta(days = 240)  
  138.     simIncrement = datetime.timedelta(minutes = 5)   #simulation resolution
  139.     #end simulation parameters
  140.    
  141.     profitMaximizingMiners = 1.0 - btcLoyalMiners - bchLoyalMiners
  142.    
  143.     btcChain = btcBlockchain(startTime)
  144.     bchChain = bchBlockchain(startTime)
  145.    
  146.     currentTime = startTime
  147.     iters = 0
  148.     while currentTime < startTime + simDuration:
  149.         #These bit was when I was trying to simulate changes to the exchange rate via some form of random walk
  150.         #vary exchange rate every hour
  151.         #hourlyPercentChangeMax = 2.0
  152.         #if currentTime.hour != (currentTime + simIncrement).hour:
  153.             #randomPercentChange = (random.random() * hourlyPercentChangeMax*2) - hourlyPercentChangeMax
  154.             #exchangeRate = exchangeRate * (1.0 - randomPercentChange/100.0)
  155.                    
  156.         currentTime += simIncrement
  157.        
  158.         #determine most profitable chain
  159.         if btcChain.returnDifficulty() * (1.0 - exchangeRate) > bchChain.returnDifficulty() * exchangeRate:
  160.             #btc chain is more profitable
  161.             btcDiffAdj = btcChain.update(currentTime, minerSupport = btcLoyalMiners + profitMaximizingMiners)
  162.             bchDiffAdj = bchChain.update(currentTime, minerSupport = bchLoyalMiners)
  163.         else:
  164.             #bch chain is more profitable
  165.             btcDiffAdj = btcChain.update(currentTime, minerSupport = btcLoyalMiners)
  166.             bchDiffAdj = bchChain.update(currentTime, minerSupport = bchLoyalMiners + profitMaximizingMiners)  
  167.        
  168.    
  169.    
  170.         #Output some useful information
  171.        
  172.         bchHeight = bchChain.blocks[-1]['height']
  173.         bchAvgInterval = bchChain.avg/60.0
  174.         bchDate = bchChain.blocks[-1]['time']
  175.         bchDifficulty = bchChain.blocks[-1]['difficulty']
  176.        
  177.         btcHeight = btcChain.blocks[-1]['height']
  178.         btcAvgInterval = btcChain.avg/60.0
  179.         btcDate = btcChain.blocks[-1]['time']
  180.         btcDifficulty = btcChain.blocks[-1]['difficulty']  
  181.        
  182.         if btcDiffAdj != 'none' or bchDiffAdj != 'none':
  183.             print btcDiffAdj, bchDiffAdj, btcChain.currentSimTime, round(btcAvgInterval, 1), round(bchAvgInterval,1), exchangeRate
  184.                    
  185.         iters += 1
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement