github.com/klaytn/klaytn@v1.10.2/datasync/chaindatafetcher/kas/repository_transactions.go (about) 1 // Copyright 2020 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package kas 18 19 import ( 20 "fmt" 21 "strings" 22 23 "github.com/klaytn/klaytn/blockchain" 24 "github.com/klaytn/klaytn/blockchain/types" 25 "github.com/klaytn/klaytn/common" 26 "github.com/klaytn/klaytn/common/hexutil" 27 ) 28 29 // TxFilteringTypes filters types which are only stored in KAS database. 30 var TxFilteringTypes = map[types.TxType]bool{ 31 types.TxTypeLegacyTransaction: true, 32 33 types.TxTypeValueTransfer: true, 34 types.TxTypeFeeDelegatedValueTransfer: true, 35 types.TxTypeFeeDelegatedValueTransferWithRatio: true, 36 37 types.TxTypeValueTransferMemo: true, 38 types.TxTypeFeeDelegatedValueTransferMemo: true, 39 types.TxTypeFeeDelegatedValueTransferMemoWithRatio: true, 40 41 types.TxTypeSmartContractDeploy: true, 42 types.TxTypeFeeDelegatedSmartContractDeploy: true, 43 types.TxTypeFeeDelegatedSmartContractDeployWithRatio: true, 44 45 types.TxTypeSmartContractExecution: true, 46 types.TxTypeFeeDelegatedSmartContractExecution: true, 47 types.TxTypeFeeDelegatedSmartContractExecutionWithRatio: true, 48 } 49 50 // transformToTxs transforms a chain event to transaction list according to the KAS database scheme. 51 func transformToTxs(event blockchain.ChainEvent) ([]*Tx, map[common.Address]struct{}) { 52 var txs []*Tx 53 updatedEOAs := make(map[common.Address]struct{}) 54 55 block := event.Block 56 head := block.Header() 57 receipts := event.Receipts 58 59 for idx, rawTx := range block.Transactions() { 60 txId := head.Number.Int64()*maxTxCountPerBlock*maxTxLogCountPerTx + int64(idx)*maxInternalTxCountPerTx 61 62 // from 63 var from common.Address 64 if rawTx.IsEthereumTransaction() { 65 signer := types.LatestSignerForChainID(rawTx.ChainId()) 66 from, _ = types.Sender(signer, rawTx) 67 } else { 68 from, _ = rawTx.From() 69 } 70 71 // to 72 to := rawTx.To() 73 if to == nil { 74 to = &common.Address{} 75 } 76 77 // value 78 value := hexutil.EncodeBig(rawTx.Value()) 79 80 // status 81 var status int 82 if receipts[idx].Status != types.ReceiptStatusSuccessful { 83 status = int(types.ReceiptStatusFailed) 84 } else { 85 status = int(receipts[idx].Status) 86 } 87 88 // fee payer 89 // fee ratio 90 var ( 91 feePayer []byte 92 feeRatio uint 93 ) 94 95 if rawTx.IsFeeDelegatedTransaction() { 96 payer, _ := rawTx.FeePayer() 97 feePayer = payer.Bytes() 98 99 ratio, ok := rawTx.FeeRatio() 100 if ok { 101 feeRatio = uint(ratio) 102 } 103 } 104 105 updatedEOAs[from] = struct{}{} 106 updatedEOAs[*to] = struct{}{} 107 108 tx := &Tx{ 109 Timestamp: head.Time.Int64(), 110 TransactionId: txId, 111 FromAddr: from.Bytes(), 112 ToAddr: to.Bytes(), 113 Value: value, 114 TransactionHash: rawTx.Hash().Bytes(), 115 Status: status, 116 TypeInt: int(rawTx.Type()), 117 GasPrice: rawTx.GasPrice().Uint64(), 118 GasUsed: receipts[idx].GasUsed, 119 FeePayer: feePayer, 120 FeeRatio: feeRatio, 121 } 122 123 txs = append(txs, tx) 124 } 125 return txs, updatedEOAs 126 } 127 128 // InsertTransactions inserts transactions in the given chain event into KAS database. 129 func (r *repository) InsertTransactions(event blockchain.ChainEvent) error { 130 txs, updatedEOAs := transformToTxs(event) 131 if err := r.insertTransactions(txs); err != nil { 132 logger.Error("Failed to insertTransactions", "err", err, "blockNumber", event.Block.NumberU64(), "numTxs", len(txs)) 133 return err 134 } 135 go r.InvalidateCacheEOAList(updatedEOAs) 136 return nil 137 } 138 139 // insertTransactions inserts the given transactions divided into chunkUnit because of the max number of placeholders. 140 func (r *repository) insertTransactions(txs []*Tx) error { 141 chunkUnit := maxPlaceholders / placeholdersPerTxItem 142 var chunks []*Tx 143 144 for txs != nil { 145 if placeholdersPerTxItem*len(txs) > maxPlaceholders { 146 chunks = txs[:chunkUnit] 147 txs = txs[chunkUnit:] 148 } else { 149 chunks = txs 150 txs = nil 151 } 152 153 if err := r.bulkInsertTransactions(chunks); err != nil { 154 return err 155 } 156 } 157 158 return nil 159 } 160 161 // bulkInsertTransactions inserts the given transactions in multiple rows at once. 162 func (r *repository) bulkInsertTransactions(txs []*Tx) error { 163 var valueStrings []string 164 var valueArgs []interface{} 165 166 for _, tx := range txs { 167 if _, ok := TxFilteringTypes[types.TxType(tx.TypeInt)]; ok { 168 valueStrings = append(valueStrings, "(?,?,?,?,?,?,?,?,?,?,?,?,?)") 169 170 valueArgs = append(valueArgs, tx.TransactionId) 171 valueArgs = append(valueArgs, tx.FromAddr) 172 valueArgs = append(valueArgs, tx.ToAddr) 173 valueArgs = append(valueArgs, tx.Value) 174 valueArgs = append(valueArgs, tx.TransactionHash) 175 valueArgs = append(valueArgs, tx.Status) 176 valueArgs = append(valueArgs, tx.Timestamp) 177 valueArgs = append(valueArgs, tx.TypeInt) 178 valueArgs = append(valueArgs, tx.GasPrice) 179 valueArgs = append(valueArgs, tx.GasUsed) 180 valueArgs = append(valueArgs, tx.FeePayer) 181 valueArgs = append(valueArgs, tx.FeeRatio) 182 valueArgs = append(valueArgs, tx.Internal) 183 } 184 } 185 186 if len(valueStrings) == 0 { 187 return nil 188 } 189 190 rawQuery := ` 191 INSERT INTO klay_transfers(transactionId, fromAddr, toAddr, value, transactionHash, status, timestamp, typeInt, gasPrice, gasUsed, feePayer, feeRatio, internal) 192 VALUES %s 193 ON DUPLICATE KEY 194 UPDATE transactionId=transactionId` 195 query := fmt.Sprintf(rawQuery, strings.Join(valueStrings, ",")) 196 197 if _, err := r.db.DB().Exec(query, valueArgs...); err != nil { 198 return err 199 } 200 201 return nil 202 }