send_universal_TESTING.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. import { Address, BitReader, BitString, Cell, TupleReader, beginCell, external, internal, parseTuple, storeMessage, toNano } from '@ton/core'
  2. import { KeyPair, getSecureRandomBytes, keyPairFromSeed, mnemonicToWalletKey } from '@ton/crypto'
  3. import axios from 'axios'
  4. // import { LiteClient, LiteRoundRobinEngine, LiteSingleEngine } from 'ton-lite-client'
  5. import { TonClient4, WalletContractV3R2 } from '@ton/ton';
  6. import { execSync } from 'child_process';
  7. import fs from 'fs'
  8. import { WalletContractV4 } from '@ton/ton';
  9. import dotenv from 'dotenv'
  10. import { givers100, givers1000 } from './givers'
  11. import arg from 'arg'
  12. import { LiteClient, LiteSingleEngine, LiteRoundRobinEngine } from 'ton-lite-client';
  13. import { getLiteClient, getTon4Client, getTon4ClientOrbs, getTonCenterClient } from './client';
  14. import { HighloadWalletV2 } from '@scaleton/highload-wallet';
  15. import { OpenedContract } from '@ton/core';
  16. import { BlockID } from 'ton-lite-client';
  17. dotenv.config({ path: 'config.txt.txt' })
  18. dotenv.config({ path: '.env.txt' })
  19. dotenv.config()
  20. dotenv.config({ path: 'config.txt' })
  21. const args = arg({
  22. '--givers': Number, // 100 1000 10000
  23. '--api': String, // lite, tonhub
  24. '--bin': String, // cuda, opencl or path to miner
  25. '--gpu': Number, // gpu id, default 0
  26. '--timeout': Number, // Timeout for mining in seconds
  27. '--allow-shards': Boolean, // if true - allows mining to other shards
  28. '-c': String, // blockchain config
  29. })
  30. let givers = givers1000
  31. if (args['--givers']) {
  32. const val = args['--givers']
  33. const allowed = [100, 1000]
  34. if (!allowed.includes(val)) {
  35. throw new Error('Invalid --givers argument')
  36. }
  37. switch (val) {
  38. case 100:
  39. givers = givers100
  40. console.log('Using givers 100')
  41. break
  42. case 1000:
  43. givers = givers1000
  44. console.log('Using givers 1 000')
  45. break
  46. // case 10000:
  47. // givers = givers10000
  48. // console.log('Using givers 10 000')
  49. // break
  50. }
  51. } else {
  52. console.log('Using givers 10 000')
  53. }
  54. let bin = '.\\pow-miner-cuda.exe'
  55. if (args['--bin']) {
  56. const argBin = args['--bin']
  57. if (argBin === 'cuda') {
  58. bin = '.\\pow-miner-cuda.exe'
  59. } else if (argBin === 'opencl' || argBin === 'amd') {
  60. bin = '.\\pow-miner-opencl.exe'
  61. } else {
  62. bin = argBin
  63. }
  64. }
  65. console.log('Using bin', bin)
  66. const gpu = args['--gpu'] ?? 0
  67. const timeout = args['--timeout'] ?? 5
  68. const allowShards = args['--allow-shards'] ?? false
  69. console.log('Using GPU', gpu)
  70. console.log('Using timeout', timeout)
  71. const mySeed = process.env.SEED as string
  72. const totalDiff = BigInt('115792089237277217110272752943501742914102634520085823245724998868298727686144')
  73. let bestGiver: { address: string, coins: number } = { address: '', coins: 0 }
  74. async function updateBestGivers(liteClient: TonClient4 | LiteClient, myAddress: Address) {
  75. const whitelistGivers = allowShards ? [...givers] : givers.filter((giver) => {
  76. const shardMaxDepth = 1
  77. const giverAddress = Address.parse(giver.address)
  78. const myShard = new BitReader(new BitString(myAddress.hash, 0, 1024)).loadUint(
  79. shardMaxDepth
  80. )
  81. const giverShard = new BitReader(new BitString(giverAddress.hash, 0, 1024)).loadUint(
  82. shardMaxDepth
  83. )
  84. if (myShard === giverShard) {
  85. return true
  86. }
  87. return false
  88. })
  89. console.log('Whitelist: ', whitelistGivers.length)
  90. if (liteClient instanceof TonClient4) {
  91. const lastInfo = await CallForSuccess(() => liteClient.getLastBlock())
  92. let newBestGiber: { address: string, coins: number } = { address: '', coins: 0 }
  93. await Promise.all(whitelistGivers.map(async (giver) => {
  94. const stack = await CallForSuccess(() => liteClient.runMethod(lastInfo.last.seqno, Address.parse(giver.address), 'get_pow_params', []))
  95. // const powStack = Cell.fromBase64(powInfo.result as string)
  96. // const stack = parseTuple(powStack)
  97. const reader = new TupleReader(stack.result)
  98. const seed = reader.readBigNumber()
  99. const complexity = reader.readBigNumber()
  100. const iterations = reader.readBigNumber()
  101. const hashes = totalDiff / complexity
  102. const coinsPerHash = giver.reward / Number(hashes)
  103. if (coinsPerHash > newBestGiber.coins) {
  104. newBestGiber = { address: giver.address, coins: coinsPerHash }
  105. }
  106. }))
  107. bestGiver = newBestGiber
  108. } else if (liteClient instanceof LiteClient) {
  109. const lastInfo = await liteClient.getMasterchainInfo()
  110. let newBestGiber: { address: string, coins: number } = { address: '', coins: 0 }
  111. await Promise.all(whitelistGivers.map(async (giver) => {
  112. const powInfo = await liteClient.runMethod(Address.parse(giver.address), 'get_pow_params', Buffer.from([]), lastInfo.last)
  113. const powStack = Cell.fromBase64(powInfo.result as string)
  114. const stack = parseTuple(powStack)
  115. const reader = new TupleReader(stack)
  116. const seed = reader.readBigNumber()
  117. const complexity = reader.readBigNumber()
  118. const iterations = reader.readBigNumber()
  119. const hashes = totalDiff / complexity
  120. const coinsPerHash = giver.reward / Number(hashes)
  121. if (coinsPerHash > newBestGiber.coins) {
  122. newBestGiber = { address: giver.address, coins: coinsPerHash }
  123. }
  124. }))
  125. bestGiver = newBestGiber
  126. }
  127. }
  128. async function getNextMaster(liteClient: TonClient4 | LiteClient) {
  129. if (liteClient instanceof LiteClient) {
  130. const info = await liteClient.getMasterchainInfo()
  131. const nextInfo = await liteClient.getMasterchainInfo({ awaitSeqno: info.last.seqno + 1 })
  132. return nextInfo.last
  133. } else {
  134. const info = await liteClient.getLastBlock()
  135. while (true) {
  136. try {
  137. const nextInfo = await liteClient.getBlock(info.last.seqno + 1)
  138. return nextInfo.shards.find(s => s.workchain === -1)
  139. }
  140. catch (e) {
  141. //
  142. }
  143. }
  144. // const nextInfo = await liteClient.getBlock(info.last.seqno + 1)
  145. // return info.last.seqno + 1
  146. }
  147. }
  148. async function getPowInfo(liteClient: TonClient4 | LiteClient, address: Address, lastInfoRoot?: any): Promise<[bigint, bigint, bigint]> {
  149. // console.log('getPowInfo', address, lastInfoRoot)
  150. if (liteClient instanceof TonClient4) {
  151. const lastInfo = lastInfoRoot ?? (await CallForSuccess(() => liteClient.getLastBlock())).last
  152. const powInfo = await CallForSuccess(() => liteClient.runMethod(lastInfo.seqno, address, 'get_pow_params', []))
  153. const reader = new TupleReader(powInfo.result)
  154. const seed = reader.readBigNumber()
  155. const complexity = reader.readBigNumber()
  156. const iterations = reader.readBigNumber()
  157. return [seed, complexity, iterations]
  158. } else if (liteClient instanceof LiteClient) {
  159. // console.log('lastInfoRoot', lastInfoRoot)
  160. const lastInfo = lastInfoRoot ?? (await liteClient.getMasterchainInfo()).last
  161. const powInfo = await liteClient.runMethod(address, 'get_pow_params', Buffer.from([]), lastInfo, { awaitSeqno: lastInfo.seqno, timeout: 100 })
  162. const powStack = Cell.fromBase64(powInfo.result as string)
  163. const stack = parseTuple(powStack)
  164. const reader = new TupleReader(stack)
  165. const seed = reader.readBigNumber()
  166. const complexity = reader.readBigNumber()
  167. const iterations = reader.readBigNumber()
  168. return [seed, complexity, iterations]
  169. }
  170. throw new Error('invalid client')
  171. }
  172. let go = true
  173. let i = 0
  174. async function main() {
  175. let liteClient: TonClient4 | LiteClient
  176. if (!args['--api']) {
  177. console.log('Using TonHub API')
  178. liteClient = await getTon4Client()
  179. } else {
  180. if (args['--api'] === 'lite') {
  181. console.log('Using LiteServer API')
  182. liteClient = await getLiteClient(args['-c'] ?? 'https://ton-blockchain.github.io/global.config.json')
  183. } else {
  184. console.log('Using TonHub API')
  185. liteClient = await getTon4Client()
  186. }
  187. }
  188. const keyPair = await mnemonicToWalletKey(mySeed.split(' '))
  189. const wallet = WalletContractV4.create({
  190. workchain: 0,
  191. publicKey: keyPair.publicKey
  192. })
  193. const walletv3r2 = WalletContractV3R2.create({
  194. workchain: 0,
  195. publicKey: keyPair.publicKey
  196. })
  197. if (args['--wallet'] === 'highload') {
  198. console.log('Using highload wallet', wallet.address.toString({ bounceable: false, urlSafe: true }))
  199. } else {
  200. console.log('Using v4r2 wallet', wallet.address.toString({ bounceable: false, urlSafe: true }))
  201. }
  202. const opened = liteClient.open(wallet)
  203. await updateBestGivers(liteClient, wallet.address)
  204. setInterval(() => {
  205. updateBestGivers(liteClient, wallet.address)
  206. }, 1000)
  207. while (go) {
  208. // console.log('waiting master')
  209. const nextMaster = await getNextMaster(liteClient)
  210. console.log('got next master')
  211. const giverAddress = bestGiver.address
  212. const [seed, complexity, iterations] = await getPowInfo(liteClient, Address.parse(giverAddress), nextMaster)
  213. console.log('got giver address', giverAddress)
  214. const randomName = (await getSecureRandomBytes(8)).toString('hex') + '.boc'
  215. const path = `bocs/${randomName}`
  216. const command = `${bin} -g ${gpu} -F 128 -t ${timeout} ${wallet.address.toString({ urlSafe: true, bounceable: true })} ${seed} ${complexity} ${iterations} ${giverAddress} ${path}`
  217. try {
  218. const output = execSync(command, { encoding: 'utf-8', stdio: "pipe" }); // the default is 'buffer'
  219. } catch (e) {
  220. }
  221. let mined: Buffer | undefined = undefined
  222. try {
  223. mined = fs.readFileSync(path)
  224. fs.rmSync(path)
  225. } catch (e) {
  226. //
  227. }
  228. if (!mined) {
  229. console.log(`${new Date()}: not mined`, seed, i++)
  230. }
  231. if (mined) {
  232. // const [newSeed] = await getPowInfo(liteClient, Address.parse(giverAddress))
  233. // if (newSeed !== seed) {
  234. // console.log('Mined already too late seed')
  235. // continue
  236. // }
  237. console.log(`${new Date()}: mined`, seed, i++)
  238. let w = opened as OpenedContract<WalletContractV4>
  239. let seqno = 0
  240. try {
  241. seqno = await CallForSuccess(() => w.getSeqno())
  242. } catch (e) {
  243. //
  244. }
  245. sendMinedBoc(wallet, seqno, keyPair, giverAddress, Cell.fromBoc(mined as Buffer)[0].asSlice().loadRef())
  246. // let seqnov3 = 0
  247. // try {
  248. // seqnov3 = await CallForSuccess(() => liteClient.open(walletv3r2).getSeqno())
  249. // } catch (e) {
  250. // //
  251. // }
  252. // sendMinedBoc(walletv3r2, seqnov3, keyPair, giverAddress, Cell.fromBoc(mined as Buffer)[0].asSlice().loadRef())
  253. // for (let j = 0; j < 5; j++) {
  254. // try {
  255. // await CallForSuccess(() => {
  256. // return w.sendTransfer({
  257. // seqno,
  258. // secretKey: keyPair.secretKey,
  259. // messages: [internal({
  260. // to: giverAddress,
  261. // value: toNano('0.05'),
  262. // bounce: true,
  263. // body: Cell.fromBoc(mined as Buffer)[0].asSlice().loadRef(),
  264. // })],
  265. // sendMode: 3 as any,
  266. // })
  267. // })
  268. // break
  269. // } catch (e) {
  270. // if (j === 4) {
  271. // throw e
  272. // }
  273. // //
  274. // }
  275. // }
  276. }
  277. }
  278. }
  279. main()
  280. async function sendMinedBoc(
  281. wallet: WalletContractV4,
  282. seqno: number,
  283. keyPair: KeyPair,
  284. giverAddress: string,
  285. boc: Cell
  286. ) {
  287. const liteServerClient = await getLiteClient(args['-c'] ?? 'https://ton-blockchain.github.io/global.config.json')
  288. const ton4Client = await getTon4Client()
  289. const tonOrbsClient = await getTon4ClientOrbs()
  290. const toncenterClient = await getTonCenterClient()
  291. const w1 = liteServerClient.open(wallet)
  292. const w2 = ton4Client.open(wallet)
  293. const w3 = tonOrbsClient.open(wallet)
  294. const w4 = toncenterClient.open(wallet)
  295. const wallets = [w1, w2, w3]
  296. // const transferBoc = w1.createTransfer({
  297. // seqno,
  298. // secretKey: keyPair.secretKey,
  299. // messages: [internal({
  300. // to: giverAddress,
  301. // value: toNano('0.05'),
  302. // bounce: true,
  303. // body: boc,
  304. // })],
  305. // sendMode: 3 as any,
  306. // })
  307. // console.log('send seqno', seqno)
  308. // const ext = external({
  309. // to: Address.parse(giverAddress),
  310. // body: transferBoc
  311. // })
  312. // const dataBoc = beginCell().store(storeMessage(ext)).endCell()
  313. // toncenterClient.sendFile(dataBoc.toBoc()).then(() => {
  314. // console.log('toncenter success')
  315. // }).catch(e => {
  316. // //
  317. // console.log('toncenter send error', e)
  318. // })
  319. // w4.sendTransfer({
  320. // seqno,
  321. // secretKey: keyPair.secretKey,
  322. // messages: [internal({
  323. // to: giverAddress,
  324. // value: toNano('0.05'),
  325. // bounce: true,
  326. // body: boc,
  327. // })],
  328. // sendMode: 3 as any,
  329. // })
  330. for (let i = 0; i < 3; i++) {
  331. for (const w of wallets) {
  332. w.sendTransfer({
  333. seqno,
  334. secretKey: keyPair.secretKey,
  335. messages: [internal({
  336. to: giverAddress,
  337. value: toNano('0.05'),
  338. bounce: true,
  339. body: boc,
  340. })],
  341. sendMode: 3 as any,
  342. }).catch(e => {
  343. //
  344. })
  345. }
  346. }
  347. }
  348. // Function to call ton api untill we get response.
  349. // Because testnet is pretty unstable we need to make sure response is final
  350. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  351. export async function CallForSuccess<T extends (...args: any[]) => any>(
  352. toCall: T,
  353. attempts = 20,
  354. delayMs = 100
  355. ): Promise<ReturnType<T>> {
  356. if (typeof toCall !== 'function') {
  357. throw new Error('unknown input')
  358. }
  359. let i = 0
  360. let lastError: unknown
  361. while (i < attempts) {
  362. try {
  363. const res = await toCall()
  364. return res
  365. } catch (err) {
  366. lastError = err
  367. i++
  368. await delay(delayMs)
  369. }
  370. }
  371. console.log('error after attempts', i)
  372. throw lastError
  373. }
  374. export function delay(ms: number) {
  375. return new Promise((resolve) => {
  376. setTimeout(resolve, ms)
  377. })
  378. }