def testHandleTxsManyNonconflictingTxs(self): many = 30 baseTx = Transaction() for i in range(many): baseTx.addOutput(1.0, scroogePubK) baseTx.finalize() # Add these outputs to the pool anyway pool = UTXOPool() addTx(pool, baseTx) # Make a bunch of transactions that depend on that one. possibleTxs = [] for i in range(many): tx = Transaction() tx.addInput(baseTx.getHash(), i) tx.addOutput(0.5, alicePubK) signInput(tx, scroogePrivK, 0) tx.finalize() possibleTxs.append(tx) handler = MaxFeeTxHandler(pool) ret = handler.handleTxs(possibleTxs) self.assertEquals(len(ret), many, 'should return all transactions')
def makeConflictingTree(root, pubK, priK, depth): ret = [] if depth > 0: prevOut = root.getOutput(0) prevVal = prevOut.value for i in range(2): tx = Transaction() tx.addInput(root.getHash(), 0) tx.addOutput(prevVal * 0.3 * (i + 1), pubK) signInput(tx, priK, 0) tx.finalize() ret.append(tx) for tx2 in makeConflictingTree(tx, pubK, priK, depth - 1): ret.append(tx2) return ret
def isValidTx(tx, utxoPool, pks): verifyTransaction = Transaction() #* Returns true if #* (1) all outputs claimed by tx are in the current UTXO pool, #* (4) all of tx’s output values are non-negative, and txOutSum = 0 for txInputInd in range(0, tx.getOutputLen()): txOut = tx.getOutput(txInputInd) txOutValue = txOut.value if txOutValue is None and not utxoPool.contains( txOutValue) and txOutValue < 0: return False else: txOutSum += txOutValue verifyTransaction.addOutput(txOut.value, txOut.address) #* (3) no UTXO is claimed multiple times by tx, meaning that an input cannot occur twice inputTracker = set() #* (2) the signatures on each input of tx are valid, #* (5) the sum of tx’s input values is greater than or equal to the sum of its output values; and false otherwise. txInSum = 0 for txInputInd in range(0, tx.getInputLen()): txIn = tx.getInput(txInputInd) prevTxOut = utxoPool.search(txIn.prevTxHash, txIn.outputIndex) if prevTxOut is None: return False if (txIn.prevTxHash, txIn.signature) in inputTracker: return False else: inputTracker.add((txIn.prevTxHash, txIn.signature)) verifyTransaction.addInput(txIn.prevTxHash, txIn.outputIndex) m = hashlib.sha256() m.update(str.encode( verifyTransaction.getRawDataToSign(txInputInd))) hm = int(m.hexdigest(), 16) #HM is same, pk is correct as it is passed in so signatures should match if not verify(pks[txInputInd], txIn.signature, hm): return False if prevTxOut.value is None or prevTxOut.value < 0: return False else: txInSum += prevTxOut.value return (txInSum >= txOutSum)
def test_5(self): possibleTxs = [] print("Test 5: test isValidTx() with some invalid transactions") nPeople = 10 people = [] #new list of KeyPair for i in range(nPeople): sk,pk = genkeys(n,p,g) people.append((sk,pk)) # Create a pool and an index into key pairs utxoPool = UTXOPool() utxoToKeyPair = {} keyPairAtIndex = {} nUTXOTx=10 maxUTXOTxOutput = 5 maxInput = 3 maxValue = 100 for i in range(nUTXOTx): num = random.randint(1,maxUTXOTxOutput) tx = Transaction() # add num randonm outputs to tx for j in range(num): # pick a random public address rIndex = random.randint(0,len(people)-1) #print("Index",rIndex); print(people[rIndex]) addr = people[rIndex][1] #(sk,pk) value = random.random() * maxValue tx.addOutput(value, addr); keyPairAtIndex[j] = people[rIndex] tx.finalize() # add all num tx outputs to utxo pool for j in range(num): ut = UTXO(tx.getHash(), j) utxoPool.addUTXO(ut, tx.getOutput(j)) utxoToKeyPair[ut] = keyPairAtIndex[j] print("Len of utxoSet", len(utxoPool.getAllUTXO())) maxValidInput = min(maxInput, len(utxoPool.getAllUTXO())) nTxPerTest= 11 maxValidInput = 2 maxOutput = 3 passes = True txHandler = TxHandler(utxoPool) for i in range(nTxPerTest): tx = Transaction() uncorrupted = True utxoAtIndex = {} nInput = random.randint(1,maxValidInput+ 1) inputValue = 0.0 # We're using this as our sample space to pull UTXOs from for # a Transaction's input. This is helpful because it ensures that # we never introduce a duplicate UTXO for an input of a valid Transaction. utxoSet = set(utxoPool.getAllUTXO()) for j in range(nInput): utxo = random.sample(utxoSet, 1)[0] tx.addInput(utxo.getTxHash(), utxo.getIndex()) utxoSet.remove(utxo) # See comment in test_1.py inputValue += utxoPool.getTxOutput(utxo).value utxoAtIndex[j] = utxo nOutput = random.randint(1,maxOutput) outputValue = 0.0 for j in range(nOutput): value = random.random()*(maxValue) if (outputValue + value > inputValue): break rIndex = random.randint(0,len(people)-1) addr = people[rIndex][1] tx.addOutput(value, addr) outputValue += value pCorrupt = 0.5 for j in range(nInput): m=hashlib.sha256() m.update(str.encode(tx.getRawDataToSign(j))) hm = int(m.hexdigest(),16) if (random.random() < pCorrupt): hm += 1 uncorrupted = False keyPair = utxoToKeyPair[utxoAtIndex[j]] tx.addSignature(sign(keyPair[0], hm,p,g), j) tx.finalize() possibleTxs.append(tx) if (txHandler.isValidTx(tx) != uncorrupted): passes = False self.assertTrue(passes) valid_txs = txHandler.handleTxs(possibleTxs) is_valid = False for tx in valid_txs: if txHandler.isValidTx(tx): is_valid = True else: break self.assertTrue(is_valid)
def test_2(self): print("Test 2: test isValidTx() with some invalid transactions") nPeople = 10 people = [] #new List RSAKeyPair for i in range(nPeople): sk, pk = genkeys(n, p, g) people.append((sk, pk)) # Create a pool and an index into key pairs utxoPool = UTXOPool() utxoToKeyPair = {} keyPairAtIndex = {} nUTXOTx = 10 maxUTXOTxOutput = 5 maxInput = 3 maxValue = 100 for i in range(nUTXOTx): num = random.randint(1, maxUTXOTxOutput) tx = Transaction() # add num randonm outputs to tx for j in range(num): # pick a random public address rIndex = random.randint(0, len(people) - 1) #print("Index",rIndex); print(people[rIndex]) addr = people[rIndex][1] #(sk,pk) value = random.random() * maxValue tx.addOutput(value, addr) keyPairAtIndex[j] = people[rIndex] tx.finalize() # add all num tx outputs to utxo pool for j in range(num): ut = UTXO(tx.hash, j) utxoPool.addUTXO(ut, tx.getOutput(j)) utxoToKeyPair[ut] = keyPairAtIndex[j] utxoSet = utxoPool.getAllUTXO() print("Len of utxoSet", len(utxoSet)) maxValidInput = min(maxInput, len(utxoSet)) nTxPerTest = 11 maxValidInput = 2 maxOutput = 3 passes = True for i in range(nTxPerTest): tx = Transaction() uncorrupted = True utxoAtIndex = {} nInput = random.randint(1, maxValidInput + 1) inputValue = 0.0 for j in range(nInput): utxo = random.sample(utxoSet, 1)[0] tx.addInput(utxo.getTxHash(), utxo.getIndex()) inputValue += utxoPool.getTxOutput(utxo).value utxoAtIndex[j] = utxo nOutput = random.randint(1, maxOutput) outputValue = 0.0 for j in range(nOutput): value = random.random() * (maxValue) if (outputValue + value > inputValue): break rIndex = random.randint(0, len(people) - 1) addr = people[rIndex][1] tx.addOutput(value, addr) outputValue += value pCorrupt = 0.1 for j in range(nInput): m = hashlib.sha256() m.update(str.encode(tx.getRawDataToSign(j))) hm = int(m.hexdigest(), 16) if (random.random() < pCorrupt): hm += 1 uncorrupted = False keyPair = utxoToKeyPair[utxoAtIndex[j]] tx.addSignature(sign(keyPair[0], hm, p, g, q), j) tx.finalize() if (isValidTx(tx, utxoPool) != uncorrupted): passes = False self.assertTrue(passes)
def test_3(self): print("Test 3: test isValidTx() with transactions containing signatures using incorrect private keys") nPeople = 10 people = [] #new List RSAKeyPair for i in range(nPeople): sk,pk = genkeys(n,p,g) people.append((sk,pk)) # Create a pool and an index into key pairs utxoPool = UTXOPool() utxoToKeyPair = {} keyPairAtIndex = {} nUTXOTx=10 maxUTXOTxOutput = 5 maxInput = 3 maxValue = 100 for i in range(nUTXOTx): num = random.randint(1,maxUTXOTxOutput) tx = Transaction() # add num randonm outputs to tx for j in range(num): # pick a random public address rIndex = random.randint(0,len(people)-1) addr = people[rIndex][1] #(sk,pk) value = random.random() * maxValue tx.addOutput(value, addr); keyPairAtIndex[j] = people[rIndex] tx.finalize() # add all num tx outputs to utxo pool for j in range(num): ut = UTXO(tx.getHash(), j) utxoPool.addUTXO(ut, tx.getOutput(j)) utxoToKeyPair[ut] = keyPairAtIndex[j] utxoSet = utxoPool.getAllUTXO() print("Len of utxoSet", len(utxoSet)) maxValidInput = min(maxInput, len(utxoSet)) nTxPerTest= 1000 maxValidInput = 2 maxOutput = 3 passes = True for i in range(nTxPerTest): pks = [] usedInputs = set() tx = Transaction() uncorrupted = True utxoAtIndex = {} nInput = random.randint(1,maxValidInput+ 1) inputValue = 0.0 for j in range(nInput): utxo = random.sample(utxoSet, 1)[0] while ((utxo.getTxHash(), utxo.getIndex()) in usedInputs): utxo = random.sample(utxoSet, 1)[0] usedInputs.add((utxo.getTxHash(), utxo.getIndex())) tx.addInput(utxo.getTxHash(), utxo.getIndex()) inputValue += utxoPool.getTxOutput(utxo).value utxoAtIndex[j] = utxo nOutput = random.randint(1,maxOutput) outputValue = 0.0 for j in range(nOutput): value = random.random()*(maxValue) if (outputValue + value > inputValue): break rIndex = random.randint(0,len(people)-1) addr = people[rIndex][1] tx.addOutput(value, addr) outputValue += value pCorrupt = 0.5 for j in range(nInput): m=hashlib.sha256() m.update(str.encode(tx.getRawDataToSign(j))) hm = int(m.hexdigest(),16) keyPair = utxoToKeyPair[utxoAtIndex[j]] if (random.random() < pCorrupt): randomKeyPair = random.randint(0,nPeople-1) while people[randomKeyPair] == keyPair: randomKeyPair = random.randint(0, nPeople - 1) keyPair = people[randomKeyPair] uncorrupted = False tx.addSignature(sign(keyPair[0], hm,p,g), j) pks.append(utxoToKeyPair[utxoAtIndex[j]][1]) tx.finalize() if (txHandler.isValidTx(tx,utxoPool,pks) != uncorrupted): passes = False self.assertTrue(passes)
alicePubK = CryptoUtil.getPublicKey(keyPair) # Make a genesis transaction genesis = Transaction() genesisValue = 25.0 genesis.addOutput(genesisValue, scroogePubK) genesis.finalize() # Make an initial pool initialPool = UTXOPool() utxo = UTXO(genesis.getHash(), 0) initialPool.addUTXO(utxo, genesis.getOutput(0)) # Note this transaction is not valid -- one of its inputs does not exist tx2in2out = Transaction() tx2in2out.addInput(genesis.hash, 0) tx2in2out.addInput(genesis.hash, 1) tx2in2out.addOutput(10.0, scroogePubK) tx2in2out.addOutput(15.0, scroogePubK) raw0 = tx2in2out.getRawDataToSign(0) sig0 = CryptoUtil.signMessage(scroogePrivK, raw0) tx2in2out.addSignature(sig0, 0) raw1 = tx2in2out.getRawDataToSign(1) sig1 = CryptoUtil.signMessage(scroogePrivK, raw1) tx2in2out.addSignature(sig1, 1) tx2in2out.finalize() txValid = Transaction() txValid.addInput(genesis.getHash(), 0) txValid.addOutput(10.0, scroogePubK) sig = CryptoUtil.signMessage(scroogePrivK, txValid.getRawDataToSign(0))
def test_3(self): print( "Test 3: test isValidTx() with transactions containing signatures using incorrect private keys" ) nPeople = 10 people = [] #new List RSAKeyPair for i in range(nPeople): sk, pk = genkeys(n, p, g) people.append((sk, pk)) # Create a pool and an index into key pairs utxoPool = UTXOPool() utxoToKeyPair = {} keyPairAtIndex = {} nUTXOTx = 10 maxUTXOTxOutput = 5 maxInput = 3 maxValue = 100 for i in range(nUTXOTx): num = random.randint(1, maxUTXOTxOutput) tx = Transaction() # add num randonm outputs to tx for j in range(num): # pick a random public address rIndex = random.randint(0, len(people) - 1) #print("Index",rIndex); print(people[rIndex]) addr = people[rIndex][1] #(sk,pk) value = random.random() * maxValue tx.addOutput(value, addr) keyPairAtIndex[j] = people[rIndex] tx.finalize() # add all num tx outputs to utxo pool for j in range(num): ut = UTXO(tx.getHash(), j) utxoPool.addUTXO(ut, tx.getOutput(j)) utxoToKeyPair[ut] = keyPairAtIndex[j] print("Len of utxoSet", len(utxoPool.getAllUTXO())) maxValidInput = min(maxInput, len(utxoPool.getAllUTXO())) nTxPerTest = 11 maxValidInput = 2 maxOutput = 3 passes = True txHandler = TxHandler(utxoPool) for i in range(nTxPerTest): tx = Transaction() uncorrupted = True utxoAtIndex = {} nInput = random.randint(1, maxValidInput + 1) inputValue = 0.0 # We're using this as our sample space to pull UTXOs from for # a Transaction's input. This is helpful because it ensures that # we never introduce a duplicate UTXO for an input of a valid Transaction. utxoSet = set(utxoPool.getAllUTXO()) for j in range(nInput): utxo = random.sample(utxoSet, 1)[0] tx.addInput(utxo.getTxHash(), utxo.getIndex()) utxoSet.remove(utxo) # See comment in test_1.py inputValue += utxoPool.getTxOutput(utxo).value utxoAtIndex[j] = utxo nOutput = random.randint(1, maxOutput) outputValue = 0.0 for j in range(nOutput): value = random.random() * (maxValue) if (outputValue + value > inputValue): break rIndex = random.randint(0, len(people) - 1) addr = people[rIndex][1] tx.addOutput(value, addr) outputValue += value pCorrupt = 0.5 for j in range(nInput): m = hashlib.sha256() m.update(str.encode(tx.getRawDataToSign(j))) hm = int(m.hexdigest(), 16) keyPair = utxoToKeyPair[utxoAtIndex[j]] if (random.random() < pCorrupt): # Attempt to corrupt the signature. potential_key_pair = people[random.randint(0, nPeople - 1)] if potential_key_pair[0] is not keyPair[0]: # Only consider _different_ randomly chosen keys # as "corrupted". keyPair = potential_key_pair uncorrupted = False tx.addSignature(sign(keyPair[0], hm, p, g), j) tx.finalize() if (txHandler.isValidTx(tx) != uncorrupted): print("Corrupted keys:", not uncorrupted) print("isValidTx got:", txHandler.isValidTx(tx)) passes = False self.assertTrue(passes)
def test_4(self): possibleTxs = [] print("Test 4: test isValidTx() with valid transactions") nPeople = 10 people = [] #new List DigSigKeyPair # Create |nPeople| pairs of {secret, public} keys. for i in range(nPeople): sk, pk = genkeys(n, p, g) people.append((sk, pk)) # Create a pool and an index into key pairs utxoPool = UTXOPool() utxoToKeyPair = {} # {UTXO: (sk, pk)} # |keyPairAtIndex| maps {idx: (sk, pk)}, so we # can know a person's keys for a given # Transaction.Output in a Transaction. keyPairAtIndex = {} nUTXOTx = 10 maxUTXOTxOutput = 5 maxInput = 3 maxValue = 100 # For num Transaction()s: for i in range(nUTXOTx): num = random.randint(1, maxUTXOTxOutput) tx = Transaction() # Add num random outputs to tx. for j in range(num): # Pick a random public address and transaction value. rIndex = random.randint(0, len(people) - 1) #print("Index",rIndex); print(people[rIndex]) addr = people[rIndex][1] #(sk,pk) value = random.random() * maxValue tx.addOutput(value, addr) keyPairAtIndex[j] = people[rIndex] tx.finalize() # Add all of the Transaction's outputs to UTXOPool. for j in range(num): ut = UTXO(tx.getHash(), j) utxoPool.addUTXO(ut, tx.getOutput(j)) utxoToKeyPair[ut] = keyPairAtIndex[j] # At this point we have a UTXOPool with all of the generated UTXOs # in the form of: # {UTXO(TransactionHash, TransactionIndex): Transaction.Output}, # as well as a map {UTXO: (sk, pk)}, so we can book-keep the # identities of the UTXO "authors" for testing. print("Len of utxoSet", len(utxoPool.getAllUTXO())) maxValidInput = min(maxInput, len(utxoPool.getAllUTXO())) nTxPerTest = 11 maxValidInput = 2 maxOutput = 3 passes = True txHandler = TxHandler(utxoPool) for i in range(nTxPerTest): tx = Transaction() # Create a new Transaction. utxoAtIndex = {} nInput = random.randint(1, maxValidInput + 1) inputValue = 0.0 # We're using this as our sample space to pull UTXOs from for # a Transaction's input. This is helpful because it ensures that # we never introduce a duplicate UTXO for an input of a valid Transaction. utxoSet = set(utxoPool.getAllUTXO()) # Add a bunch of inputs to |tx|. for j in range(nInput): # Choose a random UTXO to fund the input. # There is a non-zero chance that this could actually # pick duplicate UTXOs for a Transaction's input, thus # breaking the test. utxo = random.sample(utxoSet, 1)[0] tx.addInput(utxo.getTxHash(), utxo.getIndex()) utxoSet.remove( utxo) # Ensure we do not pick this UTXO as another input. inputValue += utxoPool.getTxOutput(utxo).value utxoAtIndex[j] = utxo nOutput = random.randint(1, maxOutput) outputValue = 0.0 # Add a bunch of outputs to |tx|, as long as the sum of # all of the outputs <= the sum of the inputs. for j in range(nOutput): value = random.random() * (maxValue) if (outputValue + value > inputValue): # Keep the transaction valid. break # Pick a random person to send the $ to. rIndex = random.randint(0, len(people) - 1) addr = people[rIndex][1] # Random person's public key address. tx.addOutput(value, addr) outputValue += value # Sign each input of the transaction. for j in range(nInput): m = hashlib.sha256() m.update(str.encode(tx.getRawDataToSign(j))) hm = int(m.hexdigest(), 16) signature = sign(utxoToKeyPair[utxoAtIndex[j]][0], hm, p, g) tx.addSignature(signature, j) # Compute overall transaction hash. tx.finalize() possibleTxs.append(tx) if (not txHandler.isValidTx(tx)): passes = False self.assertTrue(passes) valid_txs = txHandler.handleTxs(possibleTxs) is_valid = False for tx in valid_txs: if txHandler.isValidTx(tx): is_valid = True else: break self.assertTrue(is_valid)
def test_1(self): print("Test 1: test isValidTx() with valid transactions") nPeople = 10 people = [] #new List DigSigKeyPair for i in range(nPeople): sk, pk = genkeys(n, p, g) people.append((sk, pk)) # Create a pool and an index into key pairs utxoPool = UTXOPool() utxoToKeyPair = {} keyPairAtIndex = {} nUTXOTx = 10 maxUTXOTxOutput = 5 maxInput = 3 maxValue = 100 for i in range(nUTXOTx): num = random.randint(1, maxUTXOTxOutput) tx = Transaction() # add num randonm outputs to tx for j in range(num): # pick a random public address rIndex = random.randint(0, len(people) - 1) #print("Index",rIndex); print(people[rIndex]) addr = people[rIndex][1] #(sk,pk) value = random.random() * maxValue tx.addOutput(value, addr) keyPairAtIndex[j] = people[rIndex] tx.finalize() # add all num tx outputs to utxo pool for j in range(num): ut = UTXO(tx.getHash(), j) utxoPool.addUTXO(ut, tx.getOutput(j)) utxoToKeyPair[ut] = keyPairAtIndex[j] utxoSet = utxoPool.getAllUTXO() print("Len of utxoSet", len(utxoSet)) maxValidInput = min(maxInput, len(utxoSet)) nTxPerTest = 11 maxValidInput = 2 maxOutput = 3 passes = True for i in range(nTxPerTest): tx = Transaction() utxoAtIndex = {} nInput = random.randint(1, maxValidInput + 1) inputValue = 0.0 for j in range(nInput): utxo = random.sample(utxoSet, 1)[0] tx.addInput(utxo.getTxHash(), utxo.getIndex()) inputValue += utxoPool.getTxOutput(utxo).value utxoAtIndex[j] = utxo nOutput = random.randint(1, maxOutput) outputValue = 0.0 for j in range(nOutput): value = random.random() * (maxValue) if (outputValue + value > inputValue): break rIndex = random.randint(0, len(people) - 1) addr = people[rIndex][1] tx.addOutput(value, addr) outputValue += value for j in range(nInput): tx.addSignature( sign(utxoToKeyPair[utxoAtIndex[j]][0], tx.getRawDataToSign(j), p, g), j) tx.finalize() if (not txHandler.isValidTx(tx)): passes = False self.assertTrue(passes)
def testHandleTxsMessy(self): # Make the initial pool pool = UTXOPool() # Make the base transaction and put in pool baseTx = Transaction() baseTx.addOutput(10, scroogePubK) baseTx.addOutput(10, scroogePubK) baseTx.finalize() addTx(pool, baseTx) # Transaction A txA = Transaction() txA.addInput(baseTx.getHash(), 0) # 10 in, conflicts with B and C txA.addOutput(7, scroogePubK) # 7 out, fee = 3 signInput(txA, scroogePrivK, 0) txA.finalize() # Transaction B txB = Transaction() txB.addInput(baseTx.getHash(), 0) # 10 in, conflicts with A and C txB.addOutput(9, scroogePubK) # 9 out, fee = 1 signInput(txB, scroogePrivK, 0) txB.finalize() # Transaction C txC = Transaction() txC.addInput(baseTx.getHash(), 0) # 10 in, conflicts with A and B txC.addInput(baseTx.getHash(), 1) # 10 more in, conflicts with D txC.addOutput(5, scroogePubK) txC.addOutput(14, scroogePubK) # 19 out, fee = 1 signInput(txC, scroogePrivK, 0) signInput(txC, scroogePrivK, 1) txC.finalize() # Transaction D txD = Transaction() txD.addInput(baseTx.getHash(), 1) # 10 in, conflicts with C txD.addOutput(9, scroogePubK) # 9 out, fee = 1 signInput(txD, scroogePrivK, 0) txD.finalize() # Transaction E txE = Transaction() txE.addInput(txA.getHash(), 0) # 7 in, from A txE.addOutput(6, scroogePubK) # 6 out, fee = 1 signInput(txE, scroogePrivK, 0) txE.finalize() # Transaction F txF = Transaction() txF.addInput(txC.getHash(), 0) # 5 in, from C txF.addOutput(4, scroogePubK) # 4 out, fee = 1 signInput(txF, scroogePrivK, 0) txF.finalize() # Transaction G txG = Transaction() txG.addInput(txB.getHash(), 0) # 9 in, from B txG.addInput(txC.getHash(), 1) # 14 in, from C txG.addOutput(8, scroogePubK) # 8 out, fee = 15 signInput(txG, scroogePrivK, 0) signInput(txG, scroogePrivK, 1) txG.finalize() # Transaction H txH = Transaction() txH.addInput(baseTx.getHash(), 0) # 10 in txH.addOutput(4, scroogePubK) # 4 out, fee = 6 signInput(txH, alicePrivK, 0) # But invalid, since wrong signature txH.finalize() # Transaction I txI = Transaction() txI.addInput(baseTx.getHash(), 2) # no such input, so invalid txI.addOutput(4, scroogePubK) # 4 out, fee = NA signInput(txI, scroogePrivK, 0) txI.finalize() # Transaction J txJ = Transaction() txJ.addInput(txI.getHash(), 0) # 4 in (if txI were valid) txJ.addOutput(3, scroogePubK) # 3 out, fee = 1 signInput(txJ, scroogePrivK, 0) txJ.finalize() # Make the proposed transaction list proposedTxs = [txA, txB, txC, txD, txE, txF, txG, txH, txI, txJ] handler = MaxFeeTxHandler(pool) acceptedHashes = sorted([tx.getHash() for tx in handler.handleTxs(proposedTxs)]) expectedHashes = sorted([tx.getHash() for tx in [txA, txD, txE]]) self.assertEqual(acceptedHashes, expectedHashes, 'wrong set') # Make another proposed transaction list proposedTxs = [txB, txD, txH, txC, txF, txG, txI, txA, txE, txJ] handler = MaxFeeTxHandler(pool) acceptedHashes = sorted([tx.getHash() for tx in handler.handleTxs(proposedTxs)]) self.assertEqual(acceptedHashes, expectedHashes, 'wrong set') # Make another proposed transaction list proposedTxs = [txC, txJ, txF, txD, txB, txE, txG, txH, txI, txA] handler = MaxFeeTxHandler(pool) acceptedHashes = sorted([tx.getHash() for tx in handler.handleTxs(proposedTxs)]) self.assertEqual(acceptedHashes, expectedHashes, 'wrong set')
def testMethods(self): genesis = Block(b'', scroogePubKey) genesis.finalize() blockChain = BlockChain(genesis) blockHandler = BlockHandler(blockChain) # Genesis block test self.assertEqual(genesis.getHash(), blockChain.getMaxHeightBlock().getHash(), \ 'genesis should be max height block') self.assertEqual(blockChain.getMaxHeightBlock(), genesis, \ 'genesis should be max height block') self.assertEqual(len(blockChain.getMaxHeightUTXOPool().getAllUTXO()), 1, \ 'UTXOPool should have one output') self.assertEqual(len(blockChain.getTransactionPool().getTransactions()), 0, \ 'transaction pool should be empty') # Spend the genesis coinbase transaction in many outputs tx = Transaction() tx.addInput(genesis.getCoinbase().getHash(), 0) numGenOuts = int(COINBASE) for i in range(numGenOuts): tx.addOutput(1.0, scroogePubKey) signInput(tx, scroogePriKey, 0) tx.finalize() # Add one transaction test. No block has been added. blockHandler.processTx(tx) self.assertEqual(blockChain.getMaxHeightBlock(), genesis, \ 'genesis should be max height block') self.assertEqual(len(blockChain.getMaxHeightUTXOPool().getAllUTXO()), 1, \ 'UTXOPool should have one output') self.assertEqual(len(blockChain.getTransactionPool().getTransactions()), 1, \ 'transaction pool should have one entry') self.assertIsNotNone(blockChain.getTransactionPool().getTransaction(tx.getHash()), \ 'tx should be in txPool') # Build out the chain depth = 15; chainedBlocks = [] for i in range(depth): # Spend the new outputs, one tx per block tx2 = Transaction() tx2.addInput(tx.getHash(), i) tx2.addOutput(1.0, scroogePubKey) signInput(tx2, scroogePriKey, 0) tx2.finalize() blockHandler.processTx(tx2); chainedBlocks.append(blockHandler.createBlock(scroogePubKey)) # Deep chain test self.assertIsNotNone(chainedBlocks[i], 'ith block should exist') self.assertEqual(blockChain.getMaxHeightBlock(), chainedBlocks[i], \ 'ith block should be max height') self.assertEqual(len(blockChain.getMaxHeightUTXOPool().getAllUTXO()), numGenOuts + i + 1, \ 'wrong number UTXOs when i = ' + str(i)) self.assertEqual(len(blockChain.getTransactionPool().getTransactions()), 0, \ 'txPool should be empty') # Remember the current max height block maxBlockBefore = blockChain.getMaxHeightBlock() # Make another block on the deepest block that should still work sideBlock = Block(chainedBlocks[depth - CUT_OFF_AGE - 1].getHash(), scroogePubKey) sideBlock.finalize() retVal = blockChain.addBlock(sideBlock) # Add valid side chain block test self.assertTrue(retVal, 'side block should have been added') self.assertEqual(blockChain.getMaxHeightBlock(), maxBlockBefore, \ 'max height block should not have changed') # Make another block that is too deep tooDeep = Block(chainedBlocks[depth - CUT_OFF_AGE - 2].getHash(), scroogePubKey) tooDeep.finalize() retVal2 = blockChain.addBlock(tooDeep) # Too deep test self.assertFalse(retVal2, 'too deep block should not be added') # Build on the side chain prevBlock = sideBlock for i in range(CUT_OFF_AGE - 1): # Spend the previous coinbase transaction tx2 = Transaction(); tx2.addInput(prevBlock.getCoinbase().getHash(), 0) tx2.addOutput(10.0, scroogePubKey) signInput(tx2, scroogePriKey, 0) tx2.finalize(); newBlock = Block(prevBlock.getHash(), scroogePubKey) newBlock.addTransaction(tx2) newBlock.finalize() retVal3 = blockChain.addBlock(newBlock) self.assertTrue(retVal3, 'side blocks should be added') prevBlock = newBlock # The side chain should be the same length as the previous chain # and so the max height block should not have changed. self.assertEqual(blockChain.getMaxHeightBlock(), maxBlockBefore, \ 'max height block should not be changed') # Now add another to the side chain, making it the new highest # Spend the previous coinbase transaction tx3 = Transaction() tx3.addInput(prevBlock.getCoinbase().getHash(), 0) tx3.addOutput(10.0, scroogePubKey) signInput(tx3, scroogePriKey, 0) tx3.finalize() newBlock = Block(prevBlock.getHash(), scroogePubKey) newBlock.addTransaction(tx3) newBlock.finalize() retVal3 = blockChain.addBlock(newBlock) self.assertTrue(retVal3, 'block should be added') self.assertNotEqual(blockChain.getMaxHeightBlock(), maxBlockBefore, \ 'max height block should be changed') self.assertEqual(blockChain.getMaxHeightBlock(), newBlock, \ 'max height block should be the new block')