github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/chain/core/rawdb/tx3_local_database_util.go (about)

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