def test_findMultipleEntities(self): g = Generator() airdropAmount1 = 777 recipients1 = g.getExistingAddresses(20) recipientsAggregated1 = recipients1[:15] airdropAmount2 = 1000 recipients2 = g.getExistingAddresses(100) recipientsAggregated2 = recipients2[:30] collectors = g.getExistingAddresses(2) g.addAirdrop('0xdistributor1', recipients1, airdropAmount1) g.addAggregation(recipientsAggregated1, collectors[0], airdropAmount1) g.addAirdrop('0xdistributor2', recipients2, airdropAmount2) g.addAggregation(recipientsAggregated2, collectors[1], airdropAmount2) transfers = g.getGraphEdgeDF() result = findEntities(transfers, minRecipients=10, maxBlockDiff=2, minAggregations=10) # there are (15 + 1) + (30 + 1) addresses in the entity table self.assertEqual(len(result), 15 + 1 + 30 + 1) # they belong to two different entities self.assertEqual(result.entity.nunique(), 2)
def test_findNoEntity(self): result = findEntities(self.transfers, minRecipients=10, maxBlockDiff=2, minAggregations=10) # there are no addresses in the entity table self.assertEqual(len(result), 0)
def test_findSingleEntityInTwoNetworks(self): g1 = Generator(address="0xFirstNetwork") airdropAmount1 = 777 recipients1 = g1.getExistingAddresses(100) # first airdrop has one aggregation to 15 addresses recipientsAggregated1 = recipients1[:15] g2 = Generator(address="0xSecondNetwork") airdropAmount2 = 1000 # second airdrop has 50 recipients of the first airdrop recipients2 = recipients1[:50] # note that the aggregation overlaps with addresses of the first one recipientsAggregated2 = recipients1[10:20] collectors = g1.getExistingAddresses(2) g1.addAirdrop('0xdistributor1', recipients1, airdropAmount1) g1.addAggregation(recipientsAggregated1, collectors[0], airdropAmount1) g2.addAirdrop('0xdistributor2', recipients2, airdropAmount2) g2.addAggregation(recipientsAggregated2, collectors[1], airdropAmount2) transfers = g1.getGraphEdgeDF().append(g2.getGraphEdgeDF()) result = findEntities(transfers, minRecipients=10, maxBlockDiff=2, minAggregations=10) # there are (15 + 1) + (30 + 1) addresses in the entity table self.assertEqual( len(result), len(set(recipientsAggregated1 + recipientsAggregated2)) + 2) # there is just one identified entity! self.assertEqual(result.entity.nunique(), 1)
def test_findSingleEntityGivenIDEX(self): # IDEX is not an active EOA, so we shouldn't find it g1 = Generator(address="0xFirstNetwork") airdropAmount1 = 777 recipients1 = g1.getExistingAddresses(100) # one aggregation of 20 addresses recipientsAggregated1 = recipients1[:20] # and another one that isn't a real one # instead, its individuals sending their tokens to IDEX (exchange) recipientsAggregated2 = recipients1[30:80] collector = g1.getExistingAddresses(1)[0] IDEX = "0x2a0c0dbecc7e4d658f48e01e3fa353f44050c208" g1.addAirdrop('0xdistributor1', recipients1, airdropAmount1) g1.addAggregation(recipientsAggregated1, collector, airdropAmount1) g1.addAggregation(recipientsAggregated2, IDEX, airdropAmount1) # the collector of the airdrop also sent the airdrop amount to IDEX # it could now seem like IDEX collected from many addresses g1.addTransfer('0xcollector1', IDEX, airdropAmount1) transfers = g1.getGraphEdgeDF() result = findEntities(transfers, minRecipients=10, maxBlockDiff=2, minAggregations=10) # there are (15 + 1) addresses in the entity table self.assertEqual(len(result), len(recipientsAggregated1) + 1) # there is just one identified entity! self.assertEqual(result.entity.nunique(), 1)
def test_findSingleEntity(self): result = findEntities(self.transfers, minRecipients=10, maxBlockDiff=2, minAggregations=10) # there are 16 (15+1) addresses in the entity table self.assertEqual(len(result), 16) # they are all the same entity self.assertEqual(result.entity.nunique(), 1)
def test_noEntitiesInEmptyGraph(self): transfers = pd.DataFrame(columns=[ 'address', 'blockNumber', 'eventName', 'source', 'target', 'amount', ]) result = findEntities(transfers, 1, 1, 1) self.assertEqual(len(result), 0)
def test_noEntitiesInZeroAmountAirdrop(self): from networkgenerator.generator import Generator g = Generator() airdropAmount = 0 recipients = g.generateNewAddresses(20) recipientsAggregated = recipients[:15] g.addAirdrop('0xdistributor', recipients, airdropAmount) g.addAggregation(recipientsAggregated, "0xcollector", airdropAmount) transfers = g.getGraphEdgeDF() result = findEntities(transfers, minRecipients=10, maxBlockDiff=2, minAggregations=10) self.assertEqual(len(result), 0)
def test_noEntityWhenForwardToExchangeContract(self): g = Generator(addressLength=2) airdropAmount = 7777 recipients = g.generateNewAddresses(20) recipientsAggregated = recipients[:15] IDEX = "0x2a0c0dbecc7e4d658f48e01e3fa353f44050c208" g.addAirdrop('0xdistributor', recipients, airdropAmount) g.addAggregation(recipientsAggregated, IDEX, airdropAmount) transfers = g.getGraphEdgeDF() result = findEntities(transfers, minRecipients=10, maxBlockDiff=2, minAggregations=10) # there are no addresses in the entity table self.assertEqual(len(result), 0)
def test_noEntityWhenForwardToDeposit(self): g = Generator(addressLength=2) airdropAmount = 7777 recipients = g.generateNewAddresses(20) depositAdresses = g.generateNewAddresses(15) g.addAirdrop('0xdistributor', recipients, airdropAmount) for i, depositAdress in enumerate(depositAdresses): # airdropped tokens are sent to exchange deposit addresses g.addTransfer(recipients[i], depositAdress, airdropAmount) # and then forwarded to the exchange itself g.addTransfer(depositAdress, "0xchange", airdropAmount) transfers = g.getGraphEdgeDF() result = findEntities(transfers, minRecipients=10, maxBlockDiff=2, minAggregations=10) # there are no addresses in the entity table self.assertEqual(len(result), 0)
def main(): if args.prepare: if (os.path.exists(config.dbFile)): if (query_yes_no("A database already exists, want to delete it?")): os.remove(config.dbFile) prepare() else: print("skipping prepare") else: prepare() if args.ethdeposit: triplets = getETHDepositTriplets(config.transactionsFile, config.blocksFile, config.exchangesFile, config.maxBlockDiff, config.maxETHDiff) triplets.to_csv("data/output/ethdeposittriplets.csv", index=False) print("Done.") if args.tokendeposit: print("Retrieving all token network addresses") cnx = sqlite3.connect(config.dbFile) tokenAddresses = pd.read_sql( "SELECT DISTINCT address FROM tokentransfers;", cnx).address print("Reading exchanges file") exchanges = pd.read_csv(config.exchangesFile) exchanges = exchanges[(exchanges.accountType == 'eoa') & (exchanges.type == 'Exchange')].address result_list = [] print("Starting analysis for each token network.") for address in tqdm(list(tokenAddresses)): transfers = pd.read_sql( "select * from tokentransfers WHERE address = '{}'".format( address), cnx) res = getTokenDepositTripletChunk(transfers, exchanges, config.maxBlockDiff) result_list.append(res) triplets = pd.concat(result_list) triplets.to_csv("data/output/tokendeposittriplets.csv", index=False) cnx.close() print("Done.") if args.airdrop: cnx = sqlite3.connect(config.dbFile) tokenAddresses = pd.read_sql( "SELECT address FROM (select address, count(*) AS count from tokentransfers GROUP BY address)a WHERE count >= {}" .format(config.minAirDropRecipients), cnx).address tokenAddressCount = len(tokenAddresses) result_list = [] for address in tqdm(list(tokenAddresses)): transfers = pd.read_sql( "select * from tokentransfers WHERE address = '{}'".format( address), cnx) res = findEntities(transfers, minRecipients=config.minAirDropRecipients, maxBlockDiff=config.maxMedianAirdropBlockDiff, minAggregations=config.minAggregations) res['network'] = address result_list.append(res) #print((num+1)/(tokenAddressCount), len(res)) result = pd.concat(result_list) result.to_csv("data/output/airdrop-entities.csv", index=False) cnx.close()
def test_findNothingWithHighRequirements(self): result = findEntities(self.transfers, minRecipients=21, maxBlockDiff=2, minAggregations=10) self.assertEqual(len(result), 0)