def breakDownWallet(walletPath, fragSize): print 'breaking down wallet in packs of %d addresses' % fragSize print 'reading wallet' theWallet = PyBtcWallet().readWalletFile(walletPath) nAddresses = len(theWallet.addrMap) addrIndex = 0 wltIndex = 1 if walletPath[-7:] == '.wallet': newDir = walletPath[:-7] else: newDir = os.path.dirname(walletPath) if not os.path.exists(newDir): os.mkdir(newDir) rootAddr = theWallet.addrMap['ROOT'] withEncrypt = theWallet.useEncryption addrIter = theWallet.addrMap.iteritems() while nAddresses > 0: print 'breaking down wallet from address #%d to #%d' % ( addrIndex, addrIndex + fragSize - 1) nAddresses -= fragSize newWalletPath = os.path.join(newDir, 'armory_wlt_%05d_.wallet' % wltIndex) wltIndex += 1 walletFragment = PyBtcWallet() createNewWallet(walletFragment, rootAddr, newWalletPath, withEncrypt, theWallet.kdf) fileData = BinaryPacker() i = 0 try: while i < fragSize: addrItem = addrIter.next()[1] addrItem.chainIndex = -2 fileData.put( BINARY_CHUNK, '\x00' + addrItem.addrStr20 + addrItem.serialize()) #walletFragment.walletFileSafeUpdate([[WLT_UPDATE_ADD, \ #WLT_DATATYPE_KEYDATA, addrItem.addrStr20, addrItem]]) i += 1 except StopIteration: pass walletFile = open(newWalletPath, 'ab') walletFile.write(fileData.getBinaryString()) walletFile.close() addrIndex += i print 'Done'
def breakDownWallet(walletPath, fragSize): print 'breaking down wallet in packs of %d addresses' % fragSize print 'reading wallet' theWallet = PyBtcWallet().readWalletFile(walletPath) nAddresses = len(theWallet.addrMap) addrIndex = 0 wltIndex = 1 if walletPath[-7:] == '.wallet': newDir = walletPath[:-7] else: newDir = os.path.dirname(walletPath) if not os.path.exists(newDir): os.mkdir(newDir) rootAddr = theWallet.addrMap['ROOT'] withEncrypt = theWallet.useEncryption addrIter = theWallet.addrMap.iteritems() while nAddresses > 0 : print 'breaking down wallet from address #%d to #%d' % (addrIndex, addrIndex +fragSize-1) nAddresses -= fragSize newWalletPath = os.path.join(newDir, 'armory_wlt_%05d_.wallet' % wltIndex) wltIndex += 1 walletFragment = PyBtcWallet() createNewWallet(walletFragment, rootAddr, newWalletPath, withEncrypt, theWallet.kdf) fileData = BinaryPacker() i=0 try: while i < fragSize: addrItem = addrIter.next()[1] addrItem.chainIndex = -2 fileData.put(BINARY_CHUNK, '\x00' + addrItem.addrStr20 + addrItem.serialize()) #walletFragment.walletFileSafeUpdate([[WLT_UPDATE_ADD, \ #WLT_DATATYPE_KEYDATA, addrItem.addrStr20, addrItem]]) i += 1 except StopIteration: pass walletFile = open(newWalletPath, 'ab') walletFile.write(fileData.getBinaryString()) walletFile.close() addrIndex += i print 'Done'
def getTxInStr(txInPrevHash, txInPrevTxOutHashIdx, txInScrLen, txInScr, \ txInSeqNum): '''Function that creates a transaction input from the function inputs.''' txInBin = BinaryPacker() txInBin.put(BINARY_CHUNK, txInPrevHash) txInBin.put(UINT32, txInPrevTxOutHashIdx) txInBin.put(VAR_INT, txInScrLen) txInBin.put(BINARY_CHUNK, txInScr) txInBin.put(UINT32, txInSeqNum) return txInBin.getBinaryString()
def getTxOutStr(txOutVal, txOutScrLen, txOutScr): '''Function that creates a transaction output from the function inputs.''' txOutBin = BinaryPacker() txOutBin.put(UINT64, txOutVal) txOutBin.put(VAR_INT, txOutScrLen) txOutBin.put(BINARY_CHUNK, txOutScr) return txOutBin.getBinaryString()
def testBinaryPacker(self): UNKNOWN_TYPE = 100 TEST_FLOAT = 1.23456789 TEST_UINT = 0xff TEST_INT = -1 TEST_VARINT = 78 TEST_STR = 'abc' TEST_BINARY_PACKER_STR = hex_to_binary('ffff00ff000000ff00000000000000ffffffffffffffffffffffffffffff4e0361626352069e3fffffffffffff00') FS_FOR_3_BYTES = '\xff\xff\xff' bp = BinaryPacker() bp.put(UINT8, TEST_UINT) bp.put(UINT16, TEST_UINT) bp.put(UINT32, TEST_UINT) bp.put(UINT64, TEST_UINT) bp.put(INT8, TEST_INT) bp.put(INT16, TEST_INT) bp.put(INT32, TEST_INT) bp.put(INT64, TEST_INT) bp.put(VAR_INT, TEST_VARINT) bp.put(VAR_STR, TEST_STR) bp.put(FLOAT, TEST_FLOAT) bp.put(BINARY_CHUNK, FS_FOR_3_BYTES) bp.put(BINARY_CHUNK, FS_FOR_3_BYTES, 4) self.assertRaises(PackerError, bp.put, UNKNOWN_TYPE, TEST_INT) self.assertRaises(PackerError, bp.put, BINARY_CHUNK, FS_FOR_3_BYTES, 2) self.assertEqual(bp.getSize(), len(TEST_BINARY_PACKER_STR)) ts = bp.getBinaryString() self.assertEqual(ts, TEST_BINARY_PACKER_STR) bu = BinaryUnpacker(ts) self.assertEqual(bu.get(UINT8), TEST_UINT) self.assertEqual(bu.get(UINT16), TEST_UINT) self.assertEqual(bu.get(UINT32), TEST_UINT) self.assertEqual(bu.get(UINT64), TEST_UINT) self.assertEqual(bu.get(INT8), TEST_INT) self.assertEqual(bu.get(INT16), TEST_INT) self.assertEqual(bu.get(INT32), TEST_INT) self.assertEqual(bu.get(INT64), TEST_INT) self.assertEqual(bu.get(VAR_INT), TEST_VARINT) self.assertEqual(bu.get(VAR_STR), TEST_STR) self.assertAlmostEqual(bu.get(FLOAT), TEST_FLOAT, 2) self.assertEqual(bu.get(BINARY_CHUNK, 3), FS_FOR_3_BYTES) self.assertEqual(bu.get(BINARY_CHUNK, 4), FS_FOR_3_BYTES+"\x00") self.assertRaises(UnpackerError, bu.get, BINARY_CHUNK, 1) self.assertRaises(UnpackerError, bu.get, UNKNOWN_TYPE) self.assertRaises(UnpackerError, bu.get, BINARY_CHUNK, 1)
def processTxOutScr(txOutScr, blkHash, blkPos, txIdx, txOutIdx): '''Function processing a TxOut script.''' # Proceed only if there's data to read. retVal = txOutScr txOutScrUnpack = BinaryUnpacker(txOutScr) txOutAddress = BinaryPacker() txType = TxType.unknownTx txOutScrSize = txOutScrUnpack.getRemainingSize() if(txOutScrSize > 0): # Read the initial byte and determine what TxOut type it is. initByte = txOutScrUnpack.get(BINARY_CHUNK, 1) # 0x21/0x41 = Pay2PubKey if(initByte == '\x21' or initByte == '\x41'): # Make sure it's a valid pub key before declaring it valid. pkLen = isPubKey(txOutScrUnpack) if(pkLen != 0): # Save the pub key. txOutKey = txOutScrUnpack.get(BINARY_CHUNK, pkLen) txOutAddress.put(BINARY_CHUNK, txOutKey) txType = TxType.p2pKey # OP_DUP = Pay2PubKeyHash elif(initByte == OP_DUP): # HACK ALERT: Some bright bulb has created OP_* TxOuts that have # nothing but the OP_* code. Check the remaining size upfront. # (Checking after every read is more robust, really. I'm just lazy # and don't want to retrofit this chunk of code. :) ) if(txOutScrUnpack.getRemainingSize() > 0): hashByte = txOutScrUnpack.get(BINARY_CHUNK, 1) if(hashByte == OP_HASH160): hashSize = txOutScrUnpack.get(BINARY_CHUNK, 1) hashRemSize = txOutScrUnpack.getRemainingSize() if(hashSize == '\x14' and \ hashRemSize >= binary_to_int(hashSize)): txOutHash = txOutScrUnpack.get(BINARY_CHUNK, \ binary_to_int(hashSize)) # Save the hash. txOutAddress.put(BINARY_CHUNK, txOutHash) eqVerByte = txOutScrUnpack.get(BINARY_CHUNK, 1) if(eqVerByte == OP_EQUALVERIFY): checkSigByte = txOutScrUnpack.get(BINARY_CHUNK, 1) if(checkSigByte == OP_CHECKSIG): txType = TxType.p2pHash # OP_HASH160 = Pay2ScriptHash elif(initByte == OP_HASH160): hashSize = txOutScrUnpack.get(BINARY_CHUNK, 1) hashRemSize = txOutScrUnpack.getRemainingSize() if(hashSize == '\x14' and hashRemSize >= binary_to_int(hashSize)): txOutHash = txOutScrUnpack.get(BINARY_CHUNK, \ binary_to_int(hashSize)) # Save the hash. txOutAddress.put(BINARY_CHUNK, txOutHash) eqByte = txOutScrUnpack.get(BINARY_CHUNK, 1) if(eqByte == OP_EQUAL): txType = TxType.p2sh # OP_1/2/3 = MultiSig elif(initByte == OP_1 or initByte == OP_2 or initByte == OP_3): validKeys = True readByte = 0 numKeys = 0 # HACK ALERT 1: Some scripts are weird and initially appear to be # multi-sig but really aren't. We should compensate. One particular # way is to require at least 36 bytes (assume 1-of-1 w/ compressed # key) beyond the initial byte. # # HACK ALERT 2: There are some multisig TxOuts that, for unknown # reasons have things like compressed keys that where the first byte # is 0x00, not 0x02 or 0x03. For now, we just mark them as unknown # Tx and move on. if(txOutScrUnpack.getRemainingSize() >= 36): readByte = txOutScrUnpack.get(BINARY_CHUNK, 1) while((readByte == '\x21' or readByte == '\x41') and numKeys < 3 and validKeys == True): pkLen = isPubKey(txOutScrUnpack) if(pkLen != 0): txOutKey = txOutScrUnpack.get(BINARY_CHUNK, pkLen) # Save the key. txOutAddress.put(BINARY_CHUNK, txOutKey) numKeys += 1 readByte = txOutScrUnpack.get(BINARY_CHUNK, 1) else: validKeys = False else: validKeys = False if(validKeys == True): if((readByte == OP_1 or readByte == OP_2 or readByte == OP_3) \ and binary_to_int(initByte) <= binary_to_int(readByte)): cmsByte = txOutScrUnpack.get(BINARY_CHUNK, 1) if(cmsByte == OP_CHECKMULTISIG): txType = TxType.multiSig # OP_RETURN = Arbitrary data attached to a Tx. # Official as of BC-Core 0.9. https://bitcoinfoundation.org/blog/?p=290 # and https://github.com/bitcoin/bitcoin/pull/2738 have the details of # the initial commit, with https://github.com/bitcoin/bitcoin/pull/3737 # having the revision down to 40 bytes. elif(initByte == OP_RETURN): # If the 1st byte is OP_RETURN, as of BC-Core v0.9, there can be # arbitrary data placed afterwards. This makes the TxOut immediately # prunable, meaning it can never be used as a TxIn. (It can still be # spent, mind you.) The final BC-Core 0.9 only accepts <=40 bytes, # but preview versions accepted <=80. In theory, any amount of data # is valid, but miners won't accept non-standard amounts by default. # # Anyway, since it's arbitrary, we don't care what's present and # just assume it's valid. Save all the data as the TxOut address. opRetData = txOutScrUnpack.get(BINARY_CHUNK, \ txOutScrUnpack.getRemainingSize()) # Save the data. txOutAddress.put(BINARY_CHUNK, opRetData) txType = TxType.opReturn # Everything else isn't standard. For now, we'll do nothing. # else: # print("DEBUG: 1st BYTE (TxOut) IS TOTALLY UNKNOWN!!! BYTE={0}".format(binary_to_hex(initByte))) # Could it be that there is no TxOut script? Is this legal? Let's take a # peek and write some debug code. else: print("DEBUG: At block {0}, we have an empty TxOut script!".format(blkPos)) # If we have a known TxOut type, we'll return an address instead of the # entire TxOut script. Note that it's unlikely but possible that an address # value for, say, OP_RETURN could match a pub key hash. In production code, # we'd want to add extra data to ensure that no collisions have occurred # (e.g., 2 different addresses are sharing a common balance), or possibly # use a different approach altogether. if(txType != TxType.unknownTx): retVal = txOutAddress.getBinaryString() if(txType == TxType.p2pKey): step1 = hash160(retVal) step2 = hash160_to_addrStr(step1) retVal = base58_to_binary(step2) elif(txType == TxType.p2pHash): step1 = hash160_to_addrStr(retVal) retVal = base58_to_binary(step1) return retVal
def testBinaryPacker(self): UNKNOWN_TYPE = 100 TEST_FLOAT = 1.23456789 TEST_UINT = 0xff TEST_INT = -1 TEST_VARINT = 78 TEST_STR = 'abc' TEST_BINARY_PACKER_STR = hex_to_binary( 'ffff00ff000000ff00000000000000ffffffffffffffffffffffffffffff4e0361626352069e3fffffffffffff00' ) FS_FOR_3_BYTES = '\xff\xff\xff' bp = BinaryPacker() bp.put(UINT8, TEST_UINT) bp.put(UINT16, TEST_UINT) bp.put(UINT32, TEST_UINT) bp.put(UINT64, TEST_UINT) bp.put(INT8, TEST_INT) bp.put(INT16, TEST_INT) bp.put(INT32, TEST_INT) bp.put(INT64, TEST_INT) bp.put(VAR_INT, TEST_VARINT) bp.put(VAR_STR, TEST_STR) bp.put(FLOAT, TEST_FLOAT) bp.put(BINARY_CHUNK, FS_FOR_3_BYTES) bp.put(BINARY_CHUNK, FS_FOR_3_BYTES, 4) self.assertRaises(PackerError, bp.put, UNKNOWN_TYPE, TEST_INT) self.assertRaises(PackerError, bp.put, BINARY_CHUNK, FS_FOR_3_BYTES, 2) self.assertEqual(bp.getSize(), len(TEST_BINARY_PACKER_STR)) ts = bp.getBinaryString() self.assertEqual(ts, TEST_BINARY_PACKER_STR) bu = BinaryUnpacker(ts) self.assertEqual(bu.get(UINT8), TEST_UINT) self.assertEqual(bu.get(UINT16), TEST_UINT) self.assertEqual(bu.get(UINT32), TEST_UINT) self.assertEqual(bu.get(UINT64), TEST_UINT) self.assertEqual(bu.get(INT8), TEST_INT) self.assertEqual(bu.get(INT16), TEST_INT) self.assertEqual(bu.get(INT32), TEST_INT) self.assertEqual(bu.get(INT64), TEST_INT) self.assertEqual(bu.get(VAR_INT), TEST_VARINT) self.assertEqual(bu.get(VAR_STR), TEST_STR) self.assertAlmostEqual(bu.get(FLOAT), TEST_FLOAT, 2) self.assertEqual(bu.get(BINARY_CHUNK, 3), FS_FOR_3_BYTES) self.assertEqual(bu.get(BINARY_CHUNK, 4), FS_FOR_3_BYTES + "\x00") self.assertRaises(UnpackerError, bu.get, BINARY_CHUNK, 1) self.assertRaises(UnpackerError, bu.get, UNKNOWN_TYPE) self.assertRaises(UnpackerError, bu.get, BINARY_CHUNK, 1) # Running tests with "python <module name>" will NOT work for any Armory tests # You must run tests with "python -m unittest <module name>" or run all tests with "python -m unittest discover" # if __name__ == "__main__": # unittest.main()
def getTxHash(txVer, numTxIn, txInList, numTxOut, txOutList, txLockTime): '''Function that creates a 2xSHA256 hash of a transaction.''' txHashBin = BinaryPacker() txHashBin.put(UINT32, txVer) txHashBin.put(VAR_INT, numTxIn) for txIn in txInList: txHashBin.put(BINARY_CHUNK, txIn) txHashBin.put(VAR_INT, numTxOut) for txOut in txOutList: txHashBin.put(BINARY_CHUNK, txOut) txHashBin.put(UINT32, txLockTime) return hash256(txHashBin.getBinaryString())
def testBinaryPacker(self): UNKNOWN_TYPE = 100 TEST_FLOAT = 1.23456789 TEST_UINT = 0xff TEST_INT = -1 TEST_VARINT = 78 TEST_STR = 'abc' TEST_BINARY_PACKER_STR = hex_to_binary( 'ffff00ff000000ff00000000000000ffffffffffffffffffffffffffffff4e0361626352069e3fffffffffffff00' ) FS_FOR_3_BYTES = '\xff\xff\xff' bp = BinaryPacker() bp.put(UINT8, TEST_UINT) bp.put(UINT16, TEST_UINT) bp.put(UINT32, TEST_UINT) bp.put(UINT64, TEST_UINT) bp.put(INT8, TEST_INT) bp.put(INT16, TEST_INT) bp.put(INT32, TEST_INT) bp.put(INT64, TEST_INT) bp.put(VAR_INT, TEST_VARINT) bp.put(VAR_STR, TEST_STR) bp.put(FLOAT, TEST_FLOAT) bp.put(BINARY_CHUNK, FS_FOR_3_BYTES) bp.put(BINARY_CHUNK, FS_FOR_3_BYTES, 4) self.assertRaises(PackerError, bp.put, UNKNOWN_TYPE, TEST_INT) self.assertRaises(PackerError, bp.put, BINARY_CHUNK, FS_FOR_3_BYTES, 2) self.assertEqual(bp.getSize(), len(TEST_BINARY_PACKER_STR)) ts = bp.getBinaryString() self.assertEqual(ts, TEST_BINARY_PACKER_STR) bu = BinaryUnpacker(ts) self.assertEqual(bu.get(UINT8), TEST_UINT) self.assertEqual(bu.get(UINT16), TEST_UINT) self.assertEqual(bu.get(UINT32), TEST_UINT) self.assertEqual(bu.get(UINT64), TEST_UINT) self.assertEqual(bu.get(INT8), TEST_INT) self.assertEqual(bu.get(INT16), TEST_INT) self.assertEqual(bu.get(INT32), TEST_INT) self.assertEqual(bu.get(INT64), TEST_INT) self.assertEqual(bu.get(VAR_INT), TEST_VARINT) self.assertEqual(bu.get(VAR_STR), TEST_STR) self.assertAlmostEqual(bu.get(FLOAT), TEST_FLOAT, 2) self.assertEqual(bu.get(BINARY_CHUNK, 3), FS_FOR_3_BYTES) self.assertEqual(bu.get(BINARY_CHUNK, 4), FS_FOR_3_BYTES + "\x00") self.assertRaises(UnpackerError, bu.get, BINARY_CHUNK, 1) self.assertRaises(UnpackerError, bu.get, UNKNOWN_TYPE) self.assertRaises(UnpackerError, bu.get, BINARY_CHUNK, 1)