github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/core/rawdb/tx3_local_database_util.go (about) 1 package rawdb 2 3 import ( 4 "bytes" 5 "fmt" 6 "github.com/intfoundation/intchain/common" 7 tdmTypes "github.com/intfoundation/intchain/consensus/ipbft/types" 8 "github.com/intfoundation/intchain/core/types" 9 intAbi "github.com/intfoundation/intchain/intabi/abi" 10 "github.com/intfoundation/intchain/intdb" 11 "github.com/intfoundation/intchain/params" 12 "github.com/intfoundation/intchain/rlp" 13 "github.com/intfoundation/intchain/trie" 14 ) 15 16 var ( 17 tx3Prefix = []byte("t") // tx3Prefix + chainId + txHash -> tx3 18 tx3LookupPrefix = []byte("k") // tx3LookupPrefix + chainId + txHash -> tx3 lookup metadata 19 tx3ProofPrefix = []byte("p") // tx3ProofPrefix + chainId + height -> proof data 20 ) 21 22 // TX3LookupEntry is a positional metadata to help looking up the tx3 proof content given only its chainId and hash. 23 type TX3LookupEntry struct { 24 BlockIndex uint64 25 TxIndex uint64 26 } 27 28 func GetTX3(db intdb.Reader, chainId string, txHash common.Hash) *types.Transaction { 29 key := append(tx3Prefix, append([]byte(chainId), txHash.Bytes()...)...) 30 bs, err := db.Get(key) 31 if len(bs) == 0 || err != nil { 32 return nil 33 } 34 35 tx, err := decodeTx(bs) 36 if err != nil { 37 return nil 38 } 39 40 return tx 41 } 42 43 func GetTX3ProofData(db intdb.Reader, chainId string, txHash common.Hash) *types.TX3ProofData { 44 // Retrieve the lookup metadata 45 hash, blockNumber, txIndex := GetTX3LookupEntry(db, chainId, txHash) 46 if hash == (common.Hash{}) { 47 return nil 48 } 49 50 encNum := encodeBlockNumber(blockNumber) 51 key := append(tx3ProofPrefix, append([]byte(chainId), encNum...)...) 52 bs, err := db.Get(key) 53 if len(bs) == 0 || err != nil { 54 return nil 55 } 56 57 var proofData types.TX3ProofData 58 err = rlp.DecodeBytes(bs, &proofData) 59 if err != nil { 60 return nil 61 } 62 63 var i int 64 for i = 0; i < len(proofData.TxIndexs); i++ { 65 if uint64(proofData.TxIndexs[i]) == txIndex { 66 break 67 } 68 } 69 if i >= len(proofData.TxIndexs) { // can't find the txIndex 70 return nil 71 } 72 73 ret := types.TX3ProofData{ 74 Header: proofData.Header, 75 TxIndexs: make([]uint, 1), 76 TxProofs: make([]*types.BSKeyValueSet, 1), 77 } 78 ret.TxIndexs[0] = proofData.TxIndexs[i] 79 ret.TxProofs[0] = proofData.TxProofs[i] 80 81 return &ret 82 } 83 84 func GetTX3LookupEntry(db intdb.Reader, chainId string, txHash common.Hash) (common.Hash, uint64, uint64) { 85 // Load the positional metadata from disk and bail if it fails 86 key := append(tx3LookupPrefix, append([]byte(chainId), txHash.Bytes()...)...) 87 bs, err := db.Get(key) 88 if len(bs) == 0 || err != nil { 89 return common.Hash{}, 0, 0 90 } 91 92 // Parse and return the contents of the lookup entry 93 var entry TX3LookupEntry 94 if err := rlp.DecodeBytes(bs, &entry); err != nil { 95 return common.Hash{}, 0, 0 96 } 97 return txHash, entry.BlockIndex, entry.TxIndex 98 } 99 100 func GetAllTX3ProofData(db intdb.Database) []*types.TX3ProofData { 101 var ret []*types.TX3ProofData 102 iter := db.NewIteratorWithPrefix(tx3ProofPrefix) 103 for iter.Next() { 104 key := iter.Key() 105 value := iter.Value() 106 107 if !bytes.HasPrefix(key, tx3ProofPrefix) { 108 break 109 } 110 111 var proofData *types.TX3ProofData 112 err := rlp.DecodeBytes(value, proofData) 113 if err != nil { 114 continue 115 } 116 ret = append(ret, proofData) 117 } 118 119 return ret 120 } 121 122 // WriteTX3ProofData serializes TX3ProofData into the database. 123 func WriteTX3ProofData(db intdb.Database, proofData *types.TX3ProofData) error { 124 header := proofData.Header 125 tdmExtra, err := tdmTypes.ExtractTendermintExtra(header) 126 if err != nil { 127 return err 128 } 129 130 chainId := tdmExtra.ChainID 131 if chainId == "" || chainId == params.MainnetChainConfig.IntChainId || chainId == params.TestnetChainConfig.IntChainId { 132 return fmt.Errorf("invalid child chain id: %s", chainId) 133 } 134 135 num := header.Number.Uint64() 136 encNum := encodeBlockNumber(num) 137 key1 := append(tx3ProofPrefix, append([]byte(chainId), encNum...)...) 138 bs, err := db.Get(key1) 139 if len(bs) == 0 || err != nil { // not exists yet. 140 bss, _ := rlp.EncodeToBytes(proofData) 141 if err := db.Put(key1, bss); err != nil { 142 return err 143 } 144 145 for i, txIndex := range proofData.TxIndexs { 146 if err := WriteTX3(db, chainId, header, txIndex, proofData.TxProofs[i]); err != nil { 147 return err 148 } 149 } 150 } else { // merge to the existing one. 151 var existProofData types.TX3ProofData 152 err = rlp.DecodeBytes(bs, &existProofData) 153 if err != nil { 154 return err 155 } 156 157 var update bool 158 for i, txIndex := range proofData.TxIndexs { 159 if !hasTxIndex(&existProofData, txIndex) { 160 if err := WriteTX3(db, chainId, header, txIndex, proofData.TxProofs[i]); err != nil { 161 return err 162 } 163 164 existProofData.TxIndexs = append(existProofData.TxIndexs, txIndex) 165 existProofData.TxProofs = append(existProofData.TxProofs, proofData.TxProofs[i]) 166 update = true 167 } 168 } 169 170 if update { 171 bss, _ := rlp.EncodeToBytes(existProofData) 172 if err := db.Put(key1, bss); err != nil { 173 return err 174 } 175 } 176 } 177 178 return nil 179 } 180 181 func hasTxIndex(proofData *types.TX3ProofData, target uint) bool { 182 for _, txIndex := range proofData.TxIndexs { 183 if txIndex == target { 184 return true 185 } 186 } 187 return false 188 } 189 190 func WriteTX3(db intdb.Writer, chainId string, header *types.Header, txIndex uint, txProofData *types.BSKeyValueSet) error { 191 keybuf := new(bytes.Buffer) 192 rlp.Encode(keybuf, txIndex) 193 val, _, err := trie.VerifyProof(header.TxHash, keybuf.Bytes(), txProofData) 194 if err != nil { 195 return err 196 } 197 198 var tx types.Transaction 199 err = rlp.DecodeBytes(val, &tx) 200 if err != nil { 201 return err 202 } 203 204 if intAbi.IsIntChainContractAddr(tx.To()) { 205 data := tx.Data() 206 function, err := intAbi.FunctionTypeFromId(data[:4]) 207 if err != nil { 208 return err 209 } 210 211 if function == intAbi.WithdrawFromChildChain { 212 txHash := tx.Hash() 213 key1 := append(tx3Prefix, append([]byte(chainId), txHash.Bytes()...)...) 214 bs, _ := rlp.EncodeToBytes(&tx) 215 if err = db.Put(key1, bs); err != nil { 216 return err 217 } 218 219 entry := TX3LookupEntry{ 220 BlockIndex: header.Number.Uint64(), 221 TxIndex: uint64(txIndex), 222 } 223 data, _ := rlp.EncodeToBytes(entry) 224 key2 := append(tx3LookupPrefix, append([]byte(chainId), txHash.Bytes()...)...) 225 if err := db.Put(key2, data); err != nil { 226 return err 227 } 228 } 229 } 230 231 return nil 232 } 233 234 func DeleteTX3(db intdb.Database, chainId string, txHash common.Hash) { 235 // Retrieve the lookup metadata 236 hash, blockNumber, txIndex := GetTX3LookupEntry(db, chainId, txHash) 237 if hash == (common.Hash{}) { 238 return 239 } 240 241 // delete the tx3 itself 242 key1 := append(tx3Prefix, append([]byte(chainId), txHash.Bytes()...)...) 243 db.Delete(key1) 244 245 // delete the tx3 lookup metadata 246 key2 := append(tx3LookupPrefix, append([]byte(chainId), txHash.Bytes()...)...) 247 db.Delete(key2) 248 249 encNum := encodeBlockNumber(blockNumber) 250 key3 := append(tx3ProofPrefix, append([]byte(chainId), encNum...)...) 251 bs, err := db.Get(key3) 252 if len(bs) == 0 || err != nil { 253 return 254 } 255 256 var proofData types.TX3ProofData 257 err = rlp.DecodeBytes(bs, &proofData) 258 if err != nil { 259 return 260 } 261 262 var i int 263 for i = 0; i < len(proofData.TxIndexs); i++ { 264 if uint64(proofData.TxIndexs[i]) == txIndex { 265 break 266 } 267 } 268 if i >= len(proofData.TxIndexs) { // can't find the txIndex 269 return 270 } 271 272 proofData.TxIndexs = append(proofData.TxIndexs[:i], proofData.TxIndexs[i+1:]...) 273 proofData.TxProofs = append(proofData.TxProofs[:i], proofData.TxProofs[i+1:]...) 274 if len(proofData.TxIndexs) == 0 { 275 // delete the whole proof data 276 db.Delete(key3) 277 } else { 278 // update the proof data 279 bs, _ := rlp.EncodeToBytes(proofData) 280 db.Put(key3, bs) 281 } 282 } 283 284 func decodeTx(txBytes []byte) (*types.Transaction, error) { 285 286 tx := new(types.Transaction) 287 rlpStream := rlp.NewStream(bytes.NewBuffer(txBytes), 0) 288 if err := tx.DecodeRLP(rlpStream); err != nil { 289 return nil, err 290 } 291 return tx, nil 292 }