Last active
February 10, 2024 19:27
-
-
Save ross-p/bd5d4258ac23319f363dc75c2b722dd9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Ethereum Account Scanner | |
* | |
* To run this, you need your own geth node, accepting RPC | |
* connections on a port you can access. | |
* | |
* Install pre-requisites: | |
* sudo npm install -g web3 | |
* | |
* Usage: | |
* nodejs ./ethereum-account-scanner.js | |
*/ | |
let Web3 = require('web3'); | |
//////////////////////////////////////////////////////////////////////////////// | |
// BEGIN CONFIGURATION SECTION | |
//////////////////////////////////////////////////////////////////////////////// | |
/** | |
* Location of your geth server | |
* | |
* It must be running with RPC enabled, and you must have access to | |
* connect to it through your and its firewall. | |
* | |
* @type {{host: string, port: number}} | |
*/ | |
let gethServer = { | |
host: '10.0.0.10', // change this to your geth hostname/IP | |
port: 8545 // change this to your geth RPC port | |
}; | |
/** | |
* Address of the Ethereum account you wish to scan | |
* | |
* Example: 0x0123456789012345678090123456789012345678 | |
* | |
* @type {string} | |
*/ | |
let wallet = 'YOUR_ACCOUNT_HERE'; // change this to your Ethereum account number | |
/** | |
* Which block to start scanning. | |
* | |
* You can start at block 0, but it will take FOREVER to scan, | |
* so you probably don't want to do that. | |
* | |
* Generally speaking on a dual-core CPU that runs both geth | |
* and this scanning client, I can scan ~ 300 blocks/second, | |
* but in so doing, the CPU maxed at 100%. | |
* | |
* @type {number} | |
*/ | |
let firstBlockNumber = 1909000; | |
/** | |
* Maximum number of threads to create. | |
* | |
* The higher you set this, the faster the scan will run. However if | |
* you set it too high, you will overload the geth server and/or your | |
* client machine and you may start getting networking errors. | |
* | |
* Generally speaking on a dual-core CPU that runs both geth | |
* and this scanning client, I can scan ~ 300 blocks/second, | |
* but in so doing, the CPU maxed at 100%. | |
* | |
* On the same dual-core CPU, settings higher than 200 threads | |
* actually SLOW DOWN the processing since the I/O overhead exceeds | |
* the capabilities of the machine. Your results may vary. | |
* | |
* @type {number} | |
*/ | |
let maxThreads = 200; | |
//////////////////////////////////////////////////////////////////////////////// | |
// END CONFIGURATION SECTION | |
//////////////////////////////////////////////////////////////////////////////// | |
/** | |
* Initialize Ethereum Web3 client (if we haven't already) | |
* @type {Web3} | |
*/ | |
if (typeof web3 !== 'undefined') { | |
web3 = new Web3(web3.currentProvider); | |
} else { | |
console.log(`Connecting to geth on RPC @ ${gethServer.host}:${gethServer.port}`); | |
// set the provider you want from Web3.providers | |
web3 = new Web3(new Web3.providers.HttpProvider(`http://${gethServer.host}:${gethServer.port}`)); | |
} | |
/** | |
* Scan an individual transaction | |
* | |
* This is called once for every transaction found between the | |
* starting block and the ending block. | |
* | |
* Do whatever you want with this transaction. | |
* | |
* NOTE- This is called asynchronously, so the txn/block you | |
* see here might have actually happened AFTER the txn/block | |
* you see the next time is is called. To determine | |
* synchronicity, you need to look at `block.timestamp` | |
* | |
* @param {Object} txn (See https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgettransaction) | |
* @param {Object} block The parent block of the transaction (See https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgetblock) | |
*/ | |
function scanTransactionCallback(txn, block) { | |
// console.log(JSON.stringify(block, null, 4)); | |
// console.log(JSON.stringify(txn, null, 4)); | |
if (txn.to === wallet) { | |
// A transaction credited ether into this wallet | |
var ether = web3.fromWei(txn.value, 'ether'); | |
console.log(`\r${block.timestamp} +${ether} from ${txn.from}`); | |
} else if (txn.from === wallet) { | |
// A transaction debitted ether from this wallet | |
var ether = web3.fromWei(txn.value, 'ether'); | |
console.log(`\r${block.timestamp} -${ether} to ${txn.to}`); | |
} | |
} | |
/** | |
* Scan an individual block | |
* | |
* This is called once for every block found between the | |
* starting block and the ending block. | |
* | |
* Here we just look for transactions in the block, and then | |
* we scan each of those. | |
* | |
* NOTE- This is called asynchronously, so the block you | |
* see here might have actually happened AFTER the block | |
* you see the next time this is called. To determine | |
* synchronicity, you need to look at `block.timestamp` | |
* | |
* @param {Object} block (See https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgetblock) | |
*/ | |
function scanBlockCallback(block) { | |
if (block.transactions) { | |
for (var i = 0; i < block.transactions.length; i++) { | |
var txn = block.transactions[i]; | |
scanTransactionCallback(txn, block); | |
} | |
} | |
} | |
/** | |
* Scan a range of blocks | |
* | |
* Spawn up to `maxThreads` threads to scan blocks in the | |
* range provided. | |
* | |
* Note that if you pass undefined for `stoppingBlock`, its | |
* value will be computed at the beginning of the function, | |
* so any blocks added during the scan will not be processed. | |
* | |
* @param {number|hex} startingBlock First block to scan. | |
* @param {number|hex} stoppingBlock (Optional) Last block to scan. If undefined, scan all blocks. | |
* @param {function} callback Function to call after this range has been fully scanned. | |
* It must accept these arguments: (error, lastScannedBlockNumber) | |
* @returns {number} Number of threads started. They will continue working asynchronously in the background. | |
*/ | |
function scanBlockRange(startingBlock, stoppingBlock, callback) { | |
// If they didn't provide an explicit stopping block, then read | |
// ALL of the blocks up to the current one. | |
if (typeof stoppingBlock === 'undefined') { | |
stoppingBlock = web3.eth.blockNumber; | |
} | |
// If they asked for a starting block that's after the stopping block, | |
// that is an error (or they're waiting for more blocks to appear, | |
// which hasn't yet happened). | |
if (startingBlock > stoppingBlock) { | |
return -1; | |
} | |
let blockNumber = startingBlock, | |
gotError = false, | |
numThreads = 0, | |
startTime = new Date(); | |
function getPercentComplete(bn) { | |
var t = stoppingBlock - startingBlock, | |
n = bn - startingBlock; | |
return Math.floor(n / t * 100, 2); | |
} | |
function exitThread() { | |
if (--numThreads == 0) { | |
var numBlocksScanned = 1 + stoppingBlock - startingBlock, | |
stopTime = new Date(), | |
duration = (stopTime.getTime() - startTime.getTime())/1000, | |
blocksPerSec = Math.floor(numBlocksScanned / duration, 2), | |
msg = `Scanned to block ${stoppingBlock} (${numBlocksScanned} in ${duration} seconds; ${blocksPerSec} blocks/sec).`, | |
len = msg.length, | |
numSpaces = process.stdout.columns - len, | |
spaces = Array(1+numSpaces).join(" "); | |
process.stdout.write("\r"+msg+spaces+"\n"); | |
if (callback) { | |
callback(gotError, stoppingBlock); | |
} | |
} | |
return numThreads; | |
} | |
function asyncScanNextBlock() { | |
// If we've encountered an error, stop scanning blocks | |
if (gotError) { | |
return exitThread(); | |
} | |
// If we've reached the end, don't scan more blocks | |
if (blockNumber > stoppingBlock) { | |
return exitThread(); | |
} | |
// Scan the next block and assign a callback to scan even more | |
// once that is done. | |
var myBlockNumber = blockNumber++; | |
// Write periodic status update so we can tell something is happening | |
if (myBlockNumber % maxThreads == 0 || myBlockNumber == stoppingBlock) { | |
var pctDone = getPercentComplete(myBlockNumber); | |
process.stdout.write(`\rScanning block ${myBlockNumber} - ${pctDone} %`); | |
} | |
// Async call to getBlock() means we can run more than 1 thread | |
// at a time, which is MUCH faster for scanning. | |
web3.eth.getBlock(myBlockNumber, true, (error, block) => { | |
if (error) { | |
// Error retrieving this block | |
gotError = true; | |
console.error("Error:", error); | |
} else { | |
scanBlockCallback(block); | |
asyncScanNextBlock(); | |
} | |
}); | |
} | |
var nt; | |
for (nt = 0; nt < maxThreads && startingBlock + nt <= stoppingBlock; nt++) { | |
numThreads++; | |
asyncScanNextBlock(); | |
} | |
return nt; // number of threads spawned (they'll continue processing) | |
} | |
// Scan all blocks from the starting block up to current, | |
// and then keep scanning forever. | |
scanBlockRange(firstBlockNumber, undefined); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ross@geth:~$ node ./ethereum-account-scanner.js | |
Connecting to geth on RPC @ 10.240.0.4:8545 | |
1468864992 +1.0062231070066566 from 0xea674fdde714fd979de3edf0f56aa9716b898ec8 | |
1468885219 +1.0328395604683791 from 0xea674fdde714fd979de3edf0f56aa9716b898ec8 | |
1468907893 +1.0454603977622094 from 0xea674fdde714fd979de3edf0f56aa9716b898ec8 | |
1468930523 +1.0598223082859195 from 0xea674fdde714fd979de3edf0f56aa9716b898ec8 | |
Scanned to block 1914297 (5298 in 16.111 seconds; 328 blocks/sec). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hi, you can ask me, how run this script for scanning transactions of my address?
If, i just run ethereum-account-scanner.js it scanned block and return count block with scannig