github.com/iotexproject/iotex-core@v1.14.1-rc1/action/rlp_tx.go (about)

     1  package action
     2  
     3  import (
     4  	"encoding/hex"
     5  	"math/big"
     6  	"strings"
     7  
     8  	"github.com/ethereum/go-ethereum/core/types"
     9  	"github.com/ethereum/go-ethereum/rlp"
    10  	"github.com/iotexproject/go-pkgs/crypto"
    11  	"github.com/iotexproject/go-pkgs/hash"
    12  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  func rlpRawHash(rawTx *types.Transaction, signer types.Signer) (hash.Hash256, error) {
    17  	h := signer.Hash(rawTx)
    18  	return hash.BytesToHash256(h[:]), nil
    19  }
    20  
    21  func rlpSignedHash(tx *types.Transaction, signer types.Signer, sig []byte) (hash.Hash256, error) {
    22  	signedTx, err := RawTxToSignedTx(tx, signer, sig)
    23  	if err != nil {
    24  		return hash.ZeroHash256, err
    25  	}
    26  	h := signedTx.Hash()
    27  	return hash.BytesToHash256(h[:]), nil
    28  }
    29  
    30  // RawTxToSignedTx converts the raw tx to corresponding signed tx
    31  func RawTxToSignedTx(rawTx *types.Transaction, signer types.Signer, sig []byte) (*types.Transaction, error) {
    32  	if len(sig) != 65 {
    33  		return nil, errors.Errorf("invalid signature length = %d, expecting 65", len(sig))
    34  	}
    35  	sc := make([]byte, 65)
    36  	copy(sc, sig)
    37  	if sc[64] >= 27 {
    38  		sc[64] -= 27
    39  	}
    40  
    41  	signedTx, err := rawTx.WithSignature(signer, sc)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	return signedTx, nil
    46  }
    47  
    48  // DecodeRawTx decodes raw data string into eth tx
    49  func DecodeRawTx(rawData string, chainID uint32) (tx *types.Transaction, sig []byte, pubkey crypto.PublicKey, err error) {
    50  	//remove Hex prefix and decode string to byte
    51  	rawData = strings.Replace(rawData, "0x", "", -1)
    52  	rawData = strings.Replace(rawData, "0X", "", -1)
    53  	var dataInString []byte
    54  	dataInString, err = hex.DecodeString(rawData)
    55  	if err != nil {
    56  		return
    57  	}
    58  
    59  	// decode raw data into rlp tx
    60  	tx = &types.Transaction{}
    61  	err = rlp.DecodeBytes(dataInString, tx)
    62  	if err != nil {
    63  		return
    64  	}
    65  
    66  	// extract signature and recover pubkey
    67  	v, r, s := tx.RawSignatureValues()
    68  	recID := uint32(v.Int64()) - 2*chainID - 8
    69  	sig = make([]byte, 65)
    70  	rSize := len(r.Bytes())
    71  	copy(sig[32-rSize:32], r.Bytes())
    72  	sSize := len(s.Bytes())
    73  	copy(sig[64-sSize:], s.Bytes())
    74  	sig[64] = byte(recID)
    75  
    76  	// recover public key
    77  	rawHash := types.NewEIP155Signer(big.NewInt(int64(chainID))).Hash(tx)
    78  	pubkey, err = crypto.RecoverPubkey(rawHash[:], sig)
    79  	return
    80  }
    81  
    82  // NewEthSigner returns the proper signer for Eth-compatible tx
    83  func NewEthSigner(txType iotextypes.Encoding, chainID uint32) (types.Signer, error) {
    84  	switch txType {
    85  	case iotextypes.Encoding_IOTEX_PROTOBUF, iotextypes.Encoding_ETHEREUM_UNPROTECTED:
    86  		// native tx use same signature format as that of Homestead (for pre-EIP155 unprotected tx)
    87  		return types.HomesteadSigner{}, nil
    88  	case iotextypes.Encoding_ETHEREUM_EIP155:
    89  		return types.NewEIP2930Signer(big.NewInt(int64(chainID))), nil
    90  	default:
    91  		return nil, ErrInvalidAct
    92  	}
    93  }
    94  
    95  // DecodeEtherTx decodes raw data string into eth tx
    96  func DecodeEtherTx(rawData string) (*types.Transaction, error) {
    97  	//remove Hex prefix and decode string to byte
    98  	if strings.HasPrefix(rawData, "0x") || strings.HasPrefix(rawData, "0X") {
    99  		rawData = rawData[2:]
   100  	}
   101  	rawTxBytes, err := hex.DecodeString(rawData)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	// decode raw data into eth tx
   107  	tx := types.Transaction{}
   108  	if err = tx.UnmarshalBinary(rawTxBytes); err != nil {
   109  		return nil, err
   110  	}
   111  	return &tx, nil
   112  }
   113  
   114  // ExtractTypeSigPubkey extracts tx type, signature, and pubkey
   115  func ExtractTypeSigPubkey(tx *types.Transaction) (iotextypes.Encoding, []byte, crypto.PublicKey, error) {
   116  	var (
   117  		encoding iotextypes.Encoding
   118  		signer   = types.NewEIP2930Signer(tx.ChainId()) // by default assume latest signer
   119  		V, R, S  = tx.RawSignatureValues()
   120  	)
   121  	// extract correct V value
   122  	switch tx.Type() {
   123  	case types.LegacyTxType:
   124  		if tx.Protected() {
   125  			chainIDMul := tx.ChainId()
   126  			V = new(big.Int).Sub(V, new(big.Int).Lsh(chainIDMul, 1))
   127  			V.Sub(V, big.NewInt(8))
   128  			encoding = iotextypes.Encoding_ETHEREUM_EIP155
   129  		} else {
   130  			// tx has pre-EIP155 signature
   131  			encoding = iotextypes.Encoding_ETHEREUM_UNPROTECTED
   132  			signer = types.HomesteadSigner{}
   133  		}
   134  	default:
   135  		return encoding, nil, nil, ErrNotSupported
   136  	}
   137  
   138  	// construct signature
   139  	if V.BitLen() > 8 {
   140  		return encoding, nil, nil, ErrNotSupported
   141  	}
   142  
   143  	var (
   144  		r, s   = R.Bytes(), S.Bytes()
   145  		sig    = make([]byte, 65)
   146  		pubkey crypto.PublicKey
   147  		err    error
   148  	)
   149  	copy(sig[32-len(r):32], r)
   150  	copy(sig[64-len(s):64], s)
   151  	sig[64] = byte(V.Uint64())
   152  
   153  	// recover public key
   154  	rawHash := signer.Hash(tx)
   155  	pubkey, err = crypto.RecoverPubkey(rawHash[:], sig)
   156  	return encoding, sig, pubkey, err
   157  }