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  }