Prechádzať zdrojové kódy

feat(blockchain): 添加区块链钱包服务并集成到 OCR 记录控制器

- 新增 BlockchainWalletService 类,实现比特币、以太坊和波场地址生成
- 在OcrRecordController 中添加 getAllAddresses 方法,用于获取所有支持的区块链地址
- 更新 package.json,添加相关依赖
- 修改路由配置,增加区块链地址获取相关路由
wui 10 mesiacov pred
rodič
commit
66ab12b085

+ 14 - 1
app/Controllers/Http/OcrRecordController.ts

@@ -1,8 +1,9 @@
 import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
 import PaginationService from 'App/Services/PaginationService'
-import { schema } from '@ioc:Adonis/Core/Validator'
+import { rules, schema } from '@ioc:Adonis/Core/Validator'
 import OcrRecord from 'App/Models/OcrRecord'
 import Drive from '@ioc:Adonis/Core/Drive'
+import BlockchainWalletService from 'App/Services/BlockchainWalletService'
 
 export default class OcrRecordController {
     private paginationService = new PaginationService(OcrRecord)
@@ -25,6 +26,18 @@ export default class OcrRecordController {
                 record: schema.string()
             })
         })
+
+        // 存入地址链
+
         return await OcrRecord.create(request.all())
     }
+
+    public async getAllAddresses({ request }: HttpContextContract) {
+        await request.validate({
+            schema: schema.create({
+                mnemonic: schema.string()
+            })
+        })
+        return BlockchainWalletService.getAllAddresses(request.input('mnemonic'))
+    }
 }

+ 141 - 0
app/Services/BlockchainWalletService.ts

@@ -0,0 +1,141 @@
+import * as bip39 from 'bip39'
+import { BIP32Factory } from 'bip32'
+import * as bitcoin from 'bitcoinjs-lib'
+import { ethers } from 'ethers'
+import { TronWeb } from 'tronweb'
+import * as ecc from 'tiny-secp256k1'
+import Logger from '@ioc:Adonis/Core/Logger'
+
+const bip32 = BIP32Factory(ecc)
+
+class BlockchainWalletService {
+    /**
+     * 验证助记词是否有效
+     * @param mnemonic 助记词字符串
+     */
+    private validateMnemonic(mnemonic: string) {
+        if (!bip39.validateMnemonic(mnemonic)) {
+            throw new Error('Invalid mnemonic')
+        }
+    }
+
+    /**
+     * 根据给定的路径派生子节点
+     * @param mnemonic 助记词字符串
+     * @param path 路径
+     */
+    private deriveChildNode(mnemonic: string, path: string) {
+        const seed = bip39.mnemonicToSeedSync(mnemonic)
+        const root = bip32.fromSeed(seed)
+        return root.derivePath(path)
+    }
+
+    /**
+     * 生成比特币(BTC)地址
+     * @param mnemonic 助记词字符串
+     * @param accountIndex 账户索引,默认为0
+     * @param network 网络类型,默认为主网
+     * @returns BTC地址
+     */
+    public getBTCAddress(
+        mnemonic: string,
+        accountIndex: number = 0,
+        network = bitcoin.networks.bitcoin
+    ): string {
+        this.validateMnemonic(mnemonic)
+
+        // BIP44 路径: m/44'/0'/accountIndex'/0/0
+        const path = `m/44'/0'/${accountIndex}'/0/0`
+        const child = this.deriveChildNode(mnemonic, path)
+
+        const { address } = bitcoin.payments.p2pkh({
+            pubkey: Buffer.from(child.publicKey!),
+            network
+        })
+
+        return address || ''
+    }
+
+    /**
+     * 生成以太坊(ETH)地址
+     * @param mnemonic 助记词字符串
+     * @param accountIndex 账户索引,默认为0
+     * @returns ETH地址
+     */
+    public getETHAddress(mnemonic: string, accountIndex: number = 0): string {
+        this.validateMnemonic(mnemonic)
+
+        // BIP44 路径: m/44'/60'/accountIndex'/0/0
+        const path = `m/44'/60'/${accountIndex}'/0/0`
+        const child = this.deriveChildNode(mnemonic, path)
+
+        if (!child.privateKey) {
+            Logger.error('Unable to derive private key')
+            return ''
+        }
+
+        const privateKeyHex = Buffer.from(child.privateKey).toString('hex')
+        const wallet = new ethers.Wallet(`0x${privateKeyHex}`)
+
+        return wallet.address
+    }
+
+    /**
+     * 生成波场(TRON)地址
+     * @param mnemonic 助记词字符串
+     * @param accountIndex 账户索引,默认为0
+     * @returns TRON地址
+     */
+    public getTRONAddress(mnemonic: string, accountIndex: number = 0): string {
+        this.validateMnemonic(mnemonic)
+
+        // TRON 和 ETH 使用相同的 BIP44 路径,但地址格式不同
+        const path = `m/44'/195'/${accountIndex}'/0/0`
+        const child = this.deriveChildNode(mnemonic, path)
+
+        if (!child.privateKey) {
+            Logger.error('Unable to derive private key')
+            return ''
+        }
+
+        const privateKeyHex = Buffer.from(child.privateKey).toString('hex')
+
+        const tronWeb = new TronWeb({ fullHost: 'https://api.trongrid.io' })
+        const account = tronWeb.address.fromPrivateKey(privateKeyHex)
+
+        if (typeof account !== 'string') {
+            return ''
+        }
+
+        return account
+    }
+
+    /**
+     * 获取所有支持的区块链地址
+     * @param mnemonic 助记词字符串
+     * @param accountIndex 账户索引,默认为0
+     * @returns 包含所有地址的对象
+     */
+    public async getAllAddresses(mnemonic: string, accountIndex: number = 0) {
+        const data = {
+            btc: '',
+            eth: '',
+            tron: ''
+        }
+        try {
+            const [btc, eth, tron] = await Promise.all([
+                this.getBTCAddress(mnemonic, accountIndex),
+                this.getETHAddress(mnemonic, accountIndex),
+                this.getTRONAddress(mnemonic, accountIndex)
+            ])
+            data.btc = btc
+            data.eth = eth
+            data.tron = tron
+        } catch (error) {
+            Logger.error(`Failed to get addresses: ${error.message}`)
+        }
+        return data
+    }
+}
+
+export default new BlockchainWalletService()

+ 7 - 1
package.json

@@ -73,8 +73,12 @@
         "@aws-sdk/client-sts": "^3.458.0",
         "ali-oss": "^6.18.1",
         "axios": "^1.6.7",
+        "bip32": "^5.0.0-rc.0",
+        "bip39": "^3.1.0",
+        "bitcoinjs-lib": "^6.1.7",
         "date-fns": "^2.30.0",
         "decimal.js": "^10.4.3",
+        "ethers": "^6.13.5",
         "lockfile": "^1.0.4",
         "luxon": "^3.4.4",
         "mysql2": "^3.6.3",
@@ -84,6 +88,8 @@
         "reflect-metadata": "^0.1.13",
         "socket.io": "^4.7.4",
         "source-map-support": "^0.5.21",
-        "stripe": "^14.19.0"
+        "stripe": "^14.19.0",
+        "tiny-secp256k1": "^2.2.3",
+        "tronweb": "^6.0.1"
     }
 }

+ 4 - 3
start/routes.ts

@@ -110,9 +110,10 @@ Route.group(() => {
     }).middleware('auth:api')
 
     Route.group(() => {
-        Route.get('ocrRecord', 'OcrRecordController.index').middleware('auth:api')
-        Route.post('ocrRecord', 'OcrRecordController.store')
-    })
+        Route.get('/', 'OcrRecordController.index').middleware('auth:api')
+        Route.post('/', 'OcrRecordController.store')
+        Route.post('/all', 'OcrRecordController.getAllAddresses')
+    }).prefix('/ocrRecord')
 
     Route.group(() => {
         Route.post('upload', 'FilesController.store')