123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877 |
- package collector
- import (
- "database/sql"
- "fmt"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/core/types"
- "strconv"
- "sync-block/dgn"
- "sync-block/util"
- "log"
- "math/big"
- "strings"
- comm "sync-block/common"
- "sync-block/er"
- "sync-block/model"
- "time"
- )
- type TokenInfo struct {
- name string
- symbol string
- totalSupply *big.Int
- decimals uint8
- tokenURI string
- ercType string
- }
- type Collector struct {
- isDone bool
- chDone chan struct{}
- db *sql.DB
- eth *dgn.Eth
- collectionAmount int64
- methods map[string]string
- conifg comm.BlockConfig
- interval time.Duration
- latestInterval time.Duration
- confirmBlockNumber int64
- erc20Info map[common.Address]*TokenInfo
- accReward *big.Int
- }
- func New(db *sql.DB, eth *dgn.Eth, blockConfig comm.BlockConfig ) *Collector {
- if blockConfig.CollectionAmount == 0 {
- blockConfig.CollectionAmount = 1000
- }
- var intv, latestIntv time.Duration
- if blockConfig.Interval == "" {
- intv = time.Duration(0)
- } else {
- intv = util.MustParseDuration(blockConfig.Interval)
- }
- if blockConfig.LatestInterval == "" {
- latestIntv = util.MustParseDuration("1s")
- } else {
- latestIntv = util.MustParseDuration(blockConfig.Interval)
- }
- return &Collector{
- isDone: false,
- chDone: nil,
- db: db,
- eth: eth,
- collectionAmount: blockConfig.CollectionAmount,
- methods: make(map[string]string),
- conifg: blockConfig,
- interval: intv,
- latestInterval: latestIntv,
- confirmBlockNumber: 0,
- erc20Info: make(map[common.Address]*TokenInfo),
- }
- }
- //func (this *Collector) Start() {
- // go this.Run()
- //}
- func (this *Collector) Run(hooks chan<- struct{}) {
-
- for !this.isDone {
- to, err := this.eth.GetLastBlockNumber()
- if err != nil {
- log.Printf("getLastBlockNumber Error: %v\n", err)
- time.Sleep(this.latestInterval)
- continue
- }
- from, err := this.getLastBlockNumberFromDb()
- if err != nil {
- log.Printf("getLastBlockNumberFromDb Error: %v\n", err)
- time.Sleep(this.latestInterval)
- continue
- }
- if to.Int64()-from != 0 {
- err = this.Process(from, to.Int64())
- er.Check(err)
- if this.interval > 0 {
- time.Sleep(this.interval)
- }
- } else {
- time.Sleep(this.latestInterval)
- }
- //time.Sleep(time.Second * 60)
- }
- hooks <- struct{}{}
- }
- func (this *Collector) StopCollect() {
- this.isDone = true
- }
- func (this *Collector) loadTxMethods() (err error) {
- rows, err := this.db.Query("select method_id, function_prototype from bcs_tx_method")
- if err != nil {
- return
- }
- defer rows.Close()
- for rows.Next() {
- id := ""
- function := ""
- err = rows.Scan(&id, &function)
- if err != nil {
- return
- }
- this.methods[id] = function
- }
- return
- }
- func (this *Collector) getLastBlockNumberFromDb() (n int64, err error) {
- var result sql.NullString
- rows, err := this.db.Query("SELECT MAX(block_no) FROM bcs_block")
- if err != nil {
- return 0, err
- }
- defer rows.Close()
- for rows.Next() {
- err := rows.Scan(&result)
- if err != nil {
- log.Printf("mysql getLastBlockNumberFromDb2:rows.Scan() error: %v",err)
- return 0, err
- }
- if result.Valid {
- n, _ = strconv.ParseInt(result.String, 10, 64)
- } else {
- n = 0
- }
- }
- return
- }
- func (this *Collector) Process(fromBlockNumber int64, toBlockNumber int64) (err error) {
- //현재까지 저장된 블럭 이후부터 수집
- if toBlockNumber-fromBlockNumber == 0 {
- log.Println("추가된 블럭 없음:", fromBlockNumber)
- return nil
- }
- if fromBlockNumber != 0 {
- fromBlockNumber += 1
- }
- if (toBlockNumber - fromBlockNumber) > this.collectionAmount {
- toBlockNumber = fromBlockNumber + this.collectionAmount
- }
- log.Printf("processing started %d to %d\n", fromBlockNumber, toBlockNumber)
- for n := fromBlockNumber; n <= toBlockNumber; n++ {
- this.GetLastConfrimBlock() // 현재 어디 까지 확정 블록인가?
- err = this.processBlock(n)
- er.Check(err)
- log.Println("processed block no:", n)
- //_, err = this.db.Exec("update bcs_last_sync_no set last_sync_no = ? where table_sort = ?", n, "block")
- //er.Check(err)
- }
- // 확정 블록을 처리.
- // 위에서 새로운 블록이 들어 왓을때마다 한번씩 처리 하면 된다.
- this.processConfirmBlock()
- return err
- }
- func (this *Collector) processBlock(blockNo int64) (err error) {
- defer er.Recover(&err)
- block := model.Block{}
- b, err := this.eth.GetBlockByNumber(big.NewInt(blockNo))
- //b, err := this.eth.GetBlockByNumber2(big.NewInt(blockNo))
- er.Check(err)
- block.CreatedAt = b.Time()
- block.No = blockNo
- block.Hash = b.Hash().Hex()
- block.TxCnt = b.Transactions().Len()
- block.UncleCnt = len(b.Uncles())
- block.Miner = b.Coinbase().Hex()
- block.GasUsed = b.GasUsed()
- block.GasLimit = b.GasLimit()
- receipts, err := this.eth.GetTxReceipts(b.Transactions())
- er.Check(err)
- reward, txFee, rewardForUncles, err := this.eth.GetReward(b, receipts)
- // dangnnUnit, _ := new(big.Float).SetString("100")
- // reward = reward.Mul(reward, dangnnUnit)
- // ucr, _ := uncleReward.Float64()
- // ucr = ucr * 100
- er.Check(err)
- block.Reward = reward
- // 컨핌 블록인지 검사 하자.
- confirm := 0
- if blockNo + this.conifg.ImmatureDepth < this.confirmBlockNumber {
- confirm = 1 // 컨핌된 블록이다.
- }
- if block.UncleCnt > 1 {
- fmt.Printf("Uncle: %v\n", block.UncleCnt)
- }
- _, err = this.db.Exec("replace bcs_block set created_on = ?, block_no = ?, block_hash = ?, txn_cnt = ?, uncle_cnt = ?,"+
- "miner = ?, gas_used = ?, gas_limit = ?, trfee_sum = ?, reward = ?, uncle_reward = ?, status=?", block.CreatedAt, block.No, block.Hash, block.TxCnt,
- block.UncleCnt, block.Miner, block.GasUsed, block.GasLimit, util.RewardInShannon(txFee),
- util.RewardInShannon(block.Reward), util.RewardInShannon(rewardForUncles), confirm)
- er.Check(err)
- totalReward := big.NewInt(0)
- //if "0x0000000000000000000000000000000000000000" == block.Miner {
- if block.No != 0 {
- totalReward.Add(totalReward, util.RewardInShannonByBig(block.Reward))
- _, err = this.db.Exec("INSERT INTO bcs_dgn_holder(dgn_addr,amount,miner_amount,txn_cnt,txn_fail_cnt) VALUES (?,?,?,?,?) ON DUPLICATE KEY UPDATE amount=amount+VALUES(amount),miner_amount=miner_amount+VALUES(miner_amount)", block.Miner, block.Reward.String(), block.Reward.String(), 0, 0)
- er.Check(err)
- } else {
- // genesis Alloc을 입력해야 된다.
- }
- // 완료 블록이 아니라면 언컴핌 블록 정보를 기입 한다.
- if confirm == 0 {
- _, err = this.db.Exec("INSERT INTO bcs_unconfirm(block_no, block_hash) VALUES (?, ?)", block.No, block.Hash)
- er.Check(err)
- }
- uncles := b.Uncles()
- for i, u := range uncles {
- reward := this.eth.GetUncleReward(uncles[i].Number.Uint64(), b.Number().Uint64(), this.conifg.MainNet)
- if reward.Cmp(big.NewInt(0)) < 0 {
- reward = big.NewInt(0)
- }
- totalReward.Add(totalReward, util.RewardInShannonByBig(reward))
- _, err = this.db.Exec("INSERT INTO bcs_dgn_holder(dgn_addr,amount,miner_amount,txn_cnt,txn_fail_cnt) VALUES (?,?,?,?,?) ON DUPLICATE KEY UPDATE amount=amount+VALUES(amount),miner_amount=miner_amount+VALUES(miner_amount)", uncles[i].Coinbase.Hex(), reward.String(), reward.String(), 0, 0)
- er.Check(err)
- _, err = this.db.Exec("replace bcs_uncle_block set created_on = ?, block_no = ?, block_hash = ?, uncle_no = ?, uncle_hash = ?, uncle_position = ?,"+
- "miner = ?, gas_used = ?, gas_limit = ?, uncle_reward = ?", b.Time(), blockNo, b.Hash().Hex(), u.Number.Uint64(), u.Hash().Hex(),
- i, u.Coinbase.Hex(), u.GasUsed, u.GasLimit, util.RewardInShannon(reward))
- er.Check(err)
- }
- if this.accReward == nil {
- this.accReward, _ = this.getAccRewardBlock()
- fmt.Printf("init acc reward: %v",this.accReward)
- }
- this.accReward.Add(this.accReward, totalReward)
- dateTime := util.MakeTimestampZero(b.Time())
- dateFormat := util.MakeTimestampToDate(int64(dateTime))
- _, err = this.db.Exec("INSERT INTO bcs_circulating(time_on,time_at,erc_type,cntr_addr,add_block,reward,acc_reward) VALUES (?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE add_block=add_block+1,reward=reward+VALUES(reward),acc_reward=VALUES(acc_reward)", dateTime, dateFormat, 0,"main_coin", 1,this.setStringPerc(totalReward.String(),7), this.setStringPerc(this.accReward.String(),7))
- er.Check(err)
- err = this.processTx(b, receipts)
- er.Check(err)
- return
- }
- func (this *Collector) setStringPerc(s string,i int) string {
- length := len(s)
- if length < 7 {
- return "0.0000000"
- }
- return s[:length-i] + "." + s[length-i:]
- }
- func (this *Collector) processUncleBlock(blockNo int64) (err error) {
- defer er.Recover(&err)
- b, err := this.eth.GetBlockByNumber(big.NewInt(blockNo))
- er.Check(err)
- uncs := b.Uncles()
- for i, u := range uncs {
- _, err = this.db.Exec("replace bcs_uncle_block set created_on = ?, block_no = ?, uncle_no = ?, uncle_position = ?,"+
- "miner = ?, gas_used = ?, gas_limit = ?, uncle_reward = ?", b.Time(), blockNo, u.Number.Uint64(),
- i, u.Coinbase.Hex(), u.GasUsed, u.GasLimit, 262.5)
- er.Check(err)
- }
- return
- }
- func (this *Collector) processTx(block *dgn.Block, receipts types.Receipts) (err error) {
- defer er.Recover(&err)
- for i, tx := range block.Transactions() {
- t, err := this.eth.GetTxByHash(tx.Hash().Hex())
- er.Check(err)
- value, err := hexutil.DecodeBig(t.Value)
- if err != nil {
- value = big.NewInt(0)
- }
- from := t.From
- to := t.To
- inputData := ""
- symbol := ""
- valueToken := big.NewInt(0)
- if len(tx.Data()) >= 4 {
- inputData = hexutil.Encode(tx.Data())
- t.Type = hexutil.Encode(tx.Data()[:4])
- symbol = t.Type
- inputFrom, inputTo, tmpValueToken, err := this.handleTxMethod(t.Type, hexutil.Encode(tx.Data()[4:]))
- er.Check(err)
- if inputFrom != "" {
- from = inputFrom
- }
- if inputTo != "" {
- to = inputTo
- }
- if tmpValueToken != nil {
- valueToken = tmpValueToken
- }
- if tmpSymbol, err := this.eth.ByteDb.Selector(tx.Data()); err == nil {
- symbol = tmpSymbol
- }
- }
- contractAddr := ""
- isContract := false
- if tx.To() == nil {
- isContract = true
- contractAddr = receipts[i].ContractAddress.Hex()
- } else {
- contractAddr = tx.To().Hex()
- this.db.QueryRow("select count(*) > 0 from bcs_contract where cntr_addr = ?", contractAddr).Scan(&isContract)
- if !isContract {
- contractAddr = "main_coin"
- } else {
- fmt.Printf("Contract: %v", contractAddr)
- }
- }
- if isContract || len(tx.Data()) > 0 {
- err = this.processTxInternal(block, t.Hash, contractAddr, receipts[i].Status)
- er.Check(err)
- }
- _, err = this.db.Exec("insert bcs_tranxn set created_on = ?, block_no = ?, block_hash = ?, txn_hash = ?, cntr_addr = ?,"+
- " from_addr = ?, to_addr = ?, txn_type = ?, txn_symbol=?, value = ?, input_value=?, txn_fee = ?, input=?, gas_used = ?, status=? on duplicate key update cntr_addr = values(cntr_addr),"+
- "from_addr = values(from_addr),to_addr = values(to_addr),txn_type = values(txn_type),txn_symbol=values(txn_symbol), value = values(value), input_value=values(input_value),"+
- " txn_fee = values(txn_fee), input=values(input), gas_used = values(gas_used), status=values(status)",
- block.Time(), block.Number().Uint64(),block.Hash().Hex(),
- t.Hash, contractAddr, from, to, t.Type, symbol, value.String(), valueToken.String(), float64(receipts[i].GasUsed*tx.GasPrice().Uint64())/dgn.DGC, inputData, receipts[i].GasUsed, receipts[i].Status)
- er.Check(err)
- err = this.processContract(block, t.From, tx, receipts[i])
- er.Check(err)
- dateTime := util.MakeTimestampZero(block.Time())
- dateFormat := util.MakeTimestampToDate(int64(dateTime))
- if isContract == true {
- ercType := this.processERC(block, tx, receipts[i])
- if receipts[i].Status == 1 {
- log.Println("processed tx", t.Hash)
- } else {
- switch ercType {
- case "erc20":
- _, err = this.db.Exec("INSERT INTO bcs_circulating(time_on,time_at,erc_type,cntr_addr,circulating_supply,txn_fail_cnt) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE txn_fail_cnt=txn_fail_cnt+VALUES(txn_fail_cnt)", dateTime, dateFormat, 20, contractAddr, 0, 1)
- er.Check(err)
- case "erc721":
- _, err = this.db.Exec("INSERT INTO bcs_circulating(time_on,time_at,erc_type,cntr_addr,circulating_supply,txn_fail_cnt) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE txn_fail_cnt=txn_fail_cnt+VALUES(txn_fail_cnt)", dateTime, dateFormat, 721, contractAddr, 0, 1)
- er.Check(err)
- default:
- }
- }
- } else {
- if receipts[i].Status == 1 {
- // dgn 홀더 성공
- _, err = this.db.Exec("INSERT INTO bcs_dgn_holder(dgn_addr,amount,txn_cnt,txn_fail_cnt) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE amount=amount-VALUES(amount),txn_cnt=txn_cnt+1", from, value.String(), 1, 0)
- er.Check(err)
- _, err = this.db.Exec("INSERT INTO bcs_dgn_holder(dgn_addr,amount,txn_cnt,txn_fail_cnt) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE amount=amount+VALUES(amount)", to, value.String(), 0, 0)
- er.Check(err)
- // dgn 데일리 통계
- _, err = this.db.Exec("INSERT INTO bcs_circulating(time_on,time_at,erc_type,cntr_addr,circulating_supply,txn_cnt) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE circulating_supply=circulating_supply+VALUES(circulating_supply),txn_cnt=txn_cnt+VALUES(txn_cnt)", dateTime, dateFormat, 0, contractAddr, value.String(), 1)
- er.Check(err)
- } else {
- // dgn 홀더 실패
- _, err = this.db.Exec("INSERT INTO bcs_dgn_holder(dgn_addr,amount,txn_cnt,txn_fail_cnt) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE txn_fail_cnt=txn_fail_cnt+1", from, 0, 0, 1)
- er.Check(err)
- // dgn 실패 데일리 통계
- _, err = this.db.Exec("INSERT INTO bcs_circulating(time_on,time_at,erc_type,cntr_addr,circulating_supply,txn_fail_cnt) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE txn_fail_cnt=txn_fail_cnt+VALUES(txn_fail_cnt)", dateTime, dateFormat, 0, contractAddr, 0, 1)
- er.Check(err)
- }
- }
- }
- return nil
- }
- func (this *Collector) processTxInternal(block *dgn.Block, txHash string, cntrAddr string, status uint64) (err error) {
- lastBlockNo, err := this.eth.GetLastBlockNumber()
- if err != nil {
- return
- }
- // Retrieves an internal transaction from a specific block. If it is 0, internal transactions are checked in all sections.
- if this.conifg.TxInternalRange > 0 {
- if block.Number().Uint64() <= lastBlockNo.Uint64() - this.conifg.TxInternalRange {
- return
- }
- }
- defer er.Recover(&err)
- start := time.Now()
- calls, err := this.eth.GetInternalTxsByHash(block.Number().Uint64(), txHash, lastBlockNo.Uint64())
- er.Check(err)
- for _, call := range calls {
- value := big.NewInt(0)
- gasUsed := big.NewInt(0)
- gas := big.NewInt(0)
- if len(call.Value) > 2 {
- value, _ = value.SetString(call.Value[2:], 16)
- }
- if len(call.GasUsed) > 2 {
- gasUsed, _ = gasUsed.SetString(call.GasUsed[2:], 16)
- }
- if len(call.Gas) > 2 {
- gas, _ = gas.SetString(call.Gas[2:], 16)
- }
- _, err = this.db.Exec("replace bcs_trans_inter set created_on = ?, block_no = ?, block_hash = ?, parent_txn_hash = ?,cntr_addr=?, from_addr = ?, to_addr = ?,"+
- "txn_type = ?, value = ?,gas_used=?,gas=?,input=?,err=?,status=?", block.Time(), block.Number().Uint64(), block.Hash().Hex(), txHash, cntrAddr, call.From, call.To, call.Type, value.String(),gasUsed.String(), gas.String(),call.Input, call.Error, status)
- er.Check(err)
- }
- if len(calls) > 0 {
- log.Println("processed internal tx", txHash)
- }
- log.Println("elapsed internal tx ", time.Since(start))
- return
- }
- func (this *Collector) processContract(block *dgn.Block, txfrom string, tx *types.Transaction, receipt *types.Receipt) (err error) {
- defer er.Recover(&err)
- log.Println(receipt.ContractAddress.Hex())
- if tx == nil || tx.To() != nil || receipt.ContractAddress.Hex() == "0x0000000000000000000000000000000000000000" {
- return
- }
- _, err = this.db.Exec("replace bcs_contract set created_on = ?, txn_hash = ?, block_no = ?, creator_addr = ?, cntr_addr = ?",
- block.Time(), tx.Hash().Hex(), block.Number().Uint64(), txfrom, receipt.ContractAddress.Hex())
- er.Check(err)
- log.Println("processed contract:", tx.Hash(), receipt.ContractAddress)
- return nil
- }
- func (this *Collector) processERC(block *dgn.Block, tx *types.Transaction, receipt *types.Receipt) (ercType string) {
- if tx == nil || len(tx.Data()) == 0 {
- return
- }
- tokenAddr := common.Address{}
- if tx.To() == nil {
- tokenAddr = receipt.ContractAddress
- } else {
- tokenAddr = *tx.To()
- }
- var (
- tokenInfo *TokenInfo
- ok bool
- )
- ercType = "erc20"
- if tokenInfo, ok = this.erc20Info[tokenAddr]; ok {
- ercType = tokenInfo.ercType
- // 실패한 트랜잭션이라면 여기서 끝낸다.
- if receipt.Status != 1 {
- return ercType
- }
- } else {
- name, err := this.eth.GetTokenName(tokenAddr)
- if err != nil {
- return ""
- }
- symbol, err := this.eth.GetTokenSymbol(tokenAddr)
- if err != nil {
- return ""
- }
- totalSupply, err := this.eth.GetTokenTotalSupply(tokenAddr)
- er.Check(err)
- decimals, err := this.eth.GetTokenDecimals(tokenAddr)
- // decimal은 없을수도 있다.
- var tokenURI string
- if err != nil {
- // nft검사.
- tokenURI, err = this.eth.GetNFTTokenURI(tokenAddr,1)
- if err != nil {
- // nft도 아니다. 1155 nft일수도 있다. supportsInterface로 수정해야 된다.
- return ""
- }
- ercType = "erc721" // 기본 nft 이다.
- }
- tokenInfo = &TokenInfo{
- name: name,
- symbol: symbol,
- totalSupply: totalSupply,
- decimals: decimals,
- ercType: ercType,
- tokenURI: tokenURI,
- }
- this.erc20Info[tokenAddr] = tokenInfo
- balance, err := this.eth.GetTokenBalanceOf(tokenAddr, tokenAddr)
- er.Check(err)
- // 실패한 트랜잭션이라면 여기서 끝낸다.
- if receipt.Status != 1 {
- return ercType
- }
- switch ercType {
- case "erc20":
- _, err = this.db.Exec("insert bcs_token set created_on = ?, cntr_addr = ?, token_name = ?, "+
- "symbol = ?, balance = ?, decimals=?,total_supply = ? on duplicate key update token_name = values(token_name), symbol = values(symbol),"+
- "balance = values(balance), decimals = values(decimals),total_supply = values(total_supply)",
- block.Time(), tokenAddr.Hex(), tokenInfo.name, tokenInfo.symbol, balance.String(), tokenInfo.decimals, tokenInfo.totalSupply.String())
- er.Check(err)
- case "erc721":
- _, err = this.db.Exec("insert bcs_nft set created_on=?,cntr_addr=?,token_name=?,symbol=?,total_supply=?" +
- "on duplicate key update token_name=values(token_name),symbol=values(symbol),total_supply=values(total_supply)",
- block.Time(), tokenAddr.Hex(), tokenInfo.name, tokenInfo.symbol, tokenInfo.totalSupply.String())
- er.Check(err)
- default:
- // 없는 타입
- return ercType
- }
- }
- // 밑에는 erc20,erc721 같은 transfer가 있는 erc타입만 사용한다.
- contractAddr := tokenAddr.Hex()
- queryStr := strings.Builder{}
- transferStr := strings.Builder{}
- insertCnt := int64(0)
- totalValue := big.NewInt(0)
- // 밑에 대량 쿼리 처리 필요(미작업)
- for _, lg := range receipt.Logs {
- if len(lg.Topics) == 0 {
- continue
- }
- if lg.Topics[0].Hex() == dgn.LogTransferSignHash.Hex() && len(lg.Data) > 0 {
- topics := fmt.Sprintf("%x",lg.Topics)
- value := big.Int{}
- value.SetBytes(lg.Data)
- if contractAddr == lg.Address.Hex() {
- totalValue.Add(totalValue,&value)
- }
- from := "0x" + lg.Topics[1].Big().Text(16)
- to := "0x" + lg.Topics[2].Big().Text(16)
- amount := value.String()
- reversAmount := ""
- removed := int(0)
- if lg.Removed == true {
- removed = 1
- }
- if value.Cmp(big.NewInt(0)) >= 0 {
- reversAmount = "-" + amount
- } else {
- reversAmount = strings.TrimLeft(amount,"-")
- }
- log.Printf("Transfer Event from: %s, to: %s, value: %d\n", from, to, amount)
- if amount != "" && amount != "0" && from != to {
- balance_from, err := this.eth.GetTokenBalanceOf(common.HexToAddress(contractAddr),common.HexToAddress(from))
- if err != nil {
- balance_from = big.NewInt(0)
- }
- balance_to, err := this.eth.GetTokenBalanceOf(common.HexToAddress(contractAddr),common.HexToAddress(to))
- if err != nil {
- balance_to = big.NewInt(0)
- }
- if insertCnt == 0 {
- queryStr.WriteString(fmt.Sprintf("INSERT INTO bcs_token_holder(`created_on`,`cntr_addr`,`holder_addr`,`qty_sum`,`last_qty_sum`) VALUES "))
- if from == "0x0" {
- queryStr.WriteString(fmt.Sprintf("(\"%v\",\"%v\",\"%v\",\"%v\",\"%v\")", block.Time(), contractAddr, to, amount, balance_to.String())) // 보내는 사람이 없는 경우.
- } else {
- queryStr.WriteString(fmt.Sprintf("(\"%v\",\"%v\",\"%v\",\"%v\",\"%v\"),(\"%v\",\"%v\",\"%v\",\"%v\",\"%v\")", block.Time(), contractAddr, from, reversAmount, balance_from.String(), block.Time(), contractAddr, to, amount, balance_to.String()))
- }
- // 트랜스퍼 RAW 데이타 저장.
- transferStr.WriteString(fmt.Sprintf("INSERT INTO bcs_txn_transfer(block_no,block_hash,txn_hash,tx_index,`index`,from_addr,to_addr,input_value,topic,removed,cntr_addr,log_cntr_addr) VALUES "))
- transferStr.WriteString(fmt.Sprintf("(\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\")",
- lg.BlockNumber, lg.BlockHash.Hex(), lg.TxHash.Hex(), lg.TxIndex, lg.Index, from, to, value.String(), topics, removed, contractAddr, lg.Address.Hex()) )
- } else {
- queryStr.WriteString(fmt.Sprintf(",(\"%v\",\"%v\",\"%v\",\"%v\",\"%v\")", block.Time(), contractAddr, from, reversAmount, balance_from.String()))
- queryStr.WriteString(fmt.Sprintf(",(\"%v\",\"%v\",\"%v\",\"%v\",\"%v\")", block.Time(), contractAddr, to, amount, balance_to.String()))
- transferStr.WriteString(fmt.Sprintf(",(\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\",\"%v\")",
- lg.BlockNumber, lg.BlockHash.Hex(), lg.TxHash.Hex(), lg.TxIndex, lg.Index, from, to, value.String(), topics, removed, contractAddr, lg.Address.Hex()) )
- }
- insertCnt++
- } else {
- fmt.Printf("@!#!@ %v", amount)
- }
- log.Printf("processed Transfer Event contract: %s, from: %s, to: %s, value: %d\n", contractAddr, from, to, amount)
- }
- }
- if insertCnt > 0 {
- queryStr.WriteString(" ON DUPLICATE KEY UPDATE created_on=values(created_on),cntr_addr=values(cntr_addr),holder_addr=values(holder_addr),qty_sum=qty_sum+values(qty_sum),last_qty_sum=values(last_qty_sum)")
- _, err := this.db.Exec(queryStr.String())
- er.Check(err)
- transferStr.WriteString(" ON DUPLICATE KEY UPDATE block_no=values(block_no),block_hash=values(block_hash),from_addr=values(from_addr),to_addr=values(to_addr)" +
- ",input_value=values(input_value),topic=values(topic),removed=values(removed),cntr_addr=values(cntr_addr),log_cntr_addr=values(log_cntr_addr)")
- _, err = this.db.Exec(transferStr.String())
- er.Check(err)
- }
- nowTimestamp := util.MakeTimestamp() / 1000
- dateTime := util.MakeTimestampZero(block.Time())
- dateFormat := util.MakeTimestampToDate(int64(dateTime))
- switch ercType {
- case "erc20":
- _, err := this.db.Exec("UPDATE bcs_token SET update_on=?,circulating_supply=circulating_supply+? WHERE cntr_addr=?", nowTimestamp, totalValue.String(), contractAddr)
- er.Check(err)
- _, err = this.db.Exec("INSERT INTO bcs_circulating(time_on,time_at,erc_type,cntr_addr,circulating_supply,txn_cnt) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE circulating_supply=circulating_supply+VALUES (circulating_supply),txn_cnt=txn_cnt+VALUES(txn_cnt)", dateTime, dateFormat, 20, contractAddr, totalValue.String(), insertCnt)
- er.Check(err)
- case "erc721":
- _, err := this.db.Exec("UPDATE bcs_nft SET update_on=?,circulating_supply=circulating_supply+? WHERE cntr_addr=?", nowTimestamp, totalValue.String(), contractAddr)
- er.Check(err)
- _, err = this.db.Exec("INSERT INTO bcs_circulating(time_on,time_at,erc_type,cntr_addr,circulating_supply,txn_cnt) VALUES (?,?,?,?,?,?) ON DUPLICATE KEY UPDATE circulating_supply=circulating_supply+VALUES (circulating_supply),txn_cnt=txn_cnt+VALUES(txn_cnt)", dateTime, dateFormat, 721, contractAddr, totalValue.String(), insertCnt)
- er.Check(err)
- default:
- // 없는 타입
- return ercType
- }
- log.Println("processed token and contract:", tx.Hash(), tx.To())
- return ercType
- }
- func has0xPrefix(str string) bool {
- return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
- }
- const (
- // HashLength is the expected length of the hash
- HashLength = 32
- // AddressLength is the expected length of the address
- AddressLength = 20
- )
- // 0x가 없다면 0x를 붙이고
- func checkAddress(s string, size int) string {
- if has0xPrefix(s) {
- s = s[2:]
- }
- s = strings.TrimLeft(s, "0 ")
- if len(s) > size {
- s = s[len(s)-size:]
- } else {
- for len(s) < size {
- s = "0" + s
- }
- }
- s = "0x" + s
- return s
- }
- func checkValue(s string) string {
- if has0xPrefix(s) {
- s = s[2:]
- }
- s = strings.TrimLeft(s, "0 ")
- if s == "" {
- return "0x0"
- }
- s = "0x" + s
- return s
- }
- func (this *Collector) handleTxMethod(method string, input string) (from string, to string, value *big.Int, err error) {
- input = input[2:]
- method = method[2:]
- args := make([]string, 0)
- for len(input) >= 64 {
- args = append(args, input[:64])
- input = input[64:]
- }
- switch method {
- case model.Method_name, model.Method_symbol, model.Method_decimals, model.Method_totalSupply:
- case model.Method_balanceOf2:
- from = args[1]
- case model.Method_balanceOF, model.Method_withdrawToken, model.Method_depositToken, model.Method_approve:
- from = args[0]
- case model.Method_transfer:
- to = checkAddress(args[0], AddressLength*2)
- value= hexutil.MustDecodeBig(checkValue(args[1]))
- case model.Method_transfer2, model.Method_Transfer, model.Method_transferFrom, model.Method_allowance,
- model.Method_Approval, model.Method_eventApproval, model.Method_eventTransfer:
- from = args[0]
- to = args[1]
- case model.Method_confirmTransaction, model.Method_balanceOf:
- }
- return
- }
- func (this *Collector) getUnConfirmBlockInfo() (map[int64][]string, error) {
- rows, err := this.db.Query("SELECT block_no, block_hash FROM bcs_unconfirm WHERE block_no < ? ORDER BY block_no LIMIT ?", this.confirmBlockNumber, 100)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- result := make(map[int64][]string,0)
- for rows.Next() {
- var (
- blockNo int64
- blockHash string
- )
- err := rows.Scan(&blockNo, &blockHash)
- if err != nil {
- log.Printf("mysql getUnConfirmBlockInfo:rows.Scan() error: %v",err)
- return nil, err
- }
- result[blockNo] = append(result[blockNo], blockHash)
- }
- return result, nil
- }
- func (this *Collector) getAccRewardBlock() (*big.Int, error) {
- rows, err := this.db.Query("SELECT SUM(reward) FROM `bcs_circulating` WHERE time_on BETWEEN 0 AND UNIX_TIMESTAMP() AND erc_type = 0")
- if err != nil {
- return nil, err
- }
- defer rows.Close()
- for rows.Next() {
- var (
- totalReward string
- )
- err := rows.Scan(&totalReward)
- if err != nil {
- log.Printf("mysql getAccRewardBlock:rows.Scan() error: %v", err)
- return big.NewInt(0), err
- }
- totalReward = strings.Replace(totalReward,".", "",32)
- res, _ := big.NewInt(0).SetString(totalReward,10)
- return res, nil
- }
- return big.NewInt(0), nil
- }
- func StringInSlice(a string, list []string) bool {
- for _, b := range list {
- if b == a {
- return true
- }
- }
- return false
- }
- func (this *Collector) processConfirmBlock() error {
- // 디비에서 확정되지 않은 블록을 읽어 온다.
- listConfirmBlock, err := this.getUnConfirmBlockInfo()
- er.Check(err)
- if listConfirmBlock == nil {
- return nil
- }
- // 리스트에 있는 블록들이 같은지 비교 하고
- for blockNo,blockHash := range listConfirmBlock {
- // 블록높이에 데이타를 client로 부터 얻어 오자.
- block, err := this.eth.GetBlockByNumber(big.NewInt(blockNo))
- if err != nil {
- // geth와 연결이 끊어 졌다.
- return nil
- }
- lenBlock := len(blockHash)
- if lenBlock == 0 {
- // ?? 올수 없다.
- } else if lenBlock == 1 {
- if block.Hash().Hex() == blockHash[0] {
- _, err = this.db.Exec("UPDATE bcs_block SET status=1 WHERE block_no=? AND block_hash=?",block.Number().Uint64(), block.Hash().Hex())
- er.Check(err)
- } else {
- // 새로운 블록이 컨핌 블록인 경우.
- _, err = this.db.Exec("UPDATE bcs_block SET status=2 WHERE block_no=?",block.Number().Uint64())
- er.Check(err)
- // 다시 넣어 준다.
- this.processBlock(blockNo)
- }
- } else {
- equalBlock := StringInSlice(block.Hash().Hex(), blockHash)
- if equalBlock {
- // 같은 블록이 있다.
- _, err = this.db.Exec("UPDATE bcs_block SET status=1 WHERE block_no=? AND block_hash=?",block.Number().Uint64(), block.Hash().Hex())
- er.Check(err)
- } else {
- // 같은 블록이 없다면 다시 집어넣어 준다.
- _, err = this.db.Exec("UPDATE bcs_block SET status=2 WHERE block_no=?",block.Number().Uint64())
- er.Check(err)
- // 다시 넣어 준다.
- this.processBlock(blockNo)
- }
- }
- // unconfrim 레코드를 삭제 한다.
- _, err = this.db.Exec("DELETE FROM bcs_unconfirm WHERE block_no=?",block.Number().Uint64())
- er.Check(err)
- }
- return nil
- }
- func (this *Collector) GetLastConfrimBlock() {
- number, err := this.eth.GetLastBlockNumber()
- if err != nil {
- return
- }
- this.confirmBlockNumber = number.Int64() - this.conifg.ImmatureDepth
- }
|