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

     1  package action
     2  
     3  import (
     4  	"encoding/hex"
     5  
     6  	"github.com/iotexproject/go-pkgs/crypto"
     7  	"github.com/iotexproject/go-pkgs/hash"
     8  	"github.com/iotexproject/iotex-address/address"
     9  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    10  	"github.com/pkg/errors"
    11  	"go.uber.org/zap"
    12  	"google.golang.org/protobuf/proto"
    13  
    14  	"github.com/iotexproject/iotex-core/pkg/log"
    15  	"github.com/iotexproject/iotex-core/pkg/util/byteutil"
    16  )
    17  
    18  // SealedEnvelope is a signed action envelope.
    19  type SealedEnvelope struct {
    20  	Envelope
    21  	encoding     iotextypes.Encoding
    22  	evmNetworkID uint32
    23  	srcPubkey    crypto.PublicKey
    24  	signature    []byte
    25  	srcAddress   address.Address
    26  	hash         hash.Hash256
    27  }
    28  
    29  // envelopeHash returns the raw hash of embedded Envelope (this is the hash to be signed)
    30  // an all-0 return value means the transaction is invalid
    31  func (sealed *SealedEnvelope) envelopeHash() (hash.Hash256, error) {
    32  	switch sealed.encoding {
    33  	case iotextypes.Encoding_ETHEREUM_EIP155, iotextypes.Encoding_ETHEREUM_UNPROTECTED:
    34  		act, ok := sealed.Action().(EthCompatibleAction)
    35  		if !ok {
    36  			return hash.ZeroHash256, ErrInvalidAct
    37  		}
    38  		tx, err := act.ToEthTx(sealed.evmNetworkID)
    39  		if err != nil {
    40  			return hash.ZeroHash256, err
    41  		}
    42  		signer, err := NewEthSigner(sealed.encoding, sealed.evmNetworkID)
    43  		if err != nil {
    44  			return hash.ZeroHash256, err
    45  		}
    46  		return rlpRawHash(tx, signer)
    47  	case iotextypes.Encoding_IOTEX_PROTOBUF:
    48  		return hash.Hash256b(byteutil.Must(proto.Marshal(sealed.Envelope.Proto()))), nil
    49  	default:
    50  		return hash.ZeroHash256, errors.Errorf("unknown encoding type %v", sealed.encoding)
    51  	}
    52  }
    53  
    54  // Hash returns the hash value of SealedEnvelope.
    55  // an all-0 return value means the transaction is invalid
    56  func (sealed *SealedEnvelope) Hash() (hash.Hash256, error) {
    57  	if sealed.hash == hash.ZeroHash256 {
    58  		hashVal, hashErr := sealed.calcHash()
    59  		if hashErr == nil {
    60  			sealed.hash = hashVal
    61  		}
    62  		return sealed.hash, hashErr
    63  	}
    64  	return sealed.hash, nil
    65  }
    66  
    67  func (sealed *SealedEnvelope) calcHash() (hash.Hash256, error) {
    68  	switch sealed.encoding {
    69  	case iotextypes.Encoding_ETHEREUM_EIP155, iotextypes.Encoding_ETHEREUM_UNPROTECTED:
    70  		act, ok := sealed.Action().(EthCompatibleAction)
    71  		if !ok {
    72  			return hash.ZeroHash256, ErrInvalidAct
    73  		}
    74  		tx, err := act.ToEthTx(sealed.evmNetworkID)
    75  		if err != nil {
    76  			return hash.ZeroHash256, err
    77  		}
    78  		signer, err := NewEthSigner(sealed.encoding, sealed.evmNetworkID)
    79  		if err != nil {
    80  			return hash.ZeroHash256, err
    81  		}
    82  		return rlpSignedHash(tx, signer, sealed.Signature())
    83  	case iotextypes.Encoding_IOTEX_PROTOBUF:
    84  		return hash.Hash256b(byteutil.Must(proto.Marshal(sealed.Proto()))), nil
    85  	default:
    86  		return hash.ZeroHash256, errors.Errorf("unknown encoding type %v", sealed.encoding)
    87  	}
    88  }
    89  
    90  // SrcPubkey returns the source public key
    91  func (sealed *SealedEnvelope) SrcPubkey() crypto.PublicKey { return sealed.srcPubkey }
    92  
    93  // SenderAddress returns address of the source public key
    94  func (sealed *SealedEnvelope) SenderAddress() address.Address {
    95  	if sealed.srcAddress == nil {
    96  		sealed.srcAddress = sealed.srcPubkey.Address()
    97  	}
    98  	return sealed.srcAddress
    99  }
   100  
   101  // Signature returns signature bytes
   102  func (sealed *SealedEnvelope) Signature() []byte {
   103  	sig := make([]byte, len(sealed.signature))
   104  	copy(sig, sealed.signature)
   105  	return sig
   106  }
   107  
   108  // Encoding returns the encoding
   109  func (sealed *SealedEnvelope) Encoding() uint32 {
   110  	return uint32(sealed.encoding)
   111  }
   112  
   113  // Proto converts it to it's proto scheme.
   114  func (sealed *SealedEnvelope) Proto() *iotextypes.Action {
   115  	return &iotextypes.Action{
   116  		Core:         sealed.Envelope.Proto(),
   117  		SenderPubKey: sealed.srcPubkey.Bytes(),
   118  		Signature:    sealed.signature,
   119  		Encoding:     sealed.encoding,
   120  	}
   121  }
   122  
   123  // loadProto loads from proto scheme.
   124  func (sealed *SealedEnvelope) loadProto(pbAct *iotextypes.Action, evmID uint32) error {
   125  	if pbAct == nil {
   126  		return ErrNilProto
   127  	}
   128  	if sealed == nil {
   129  		return ErrNilAction
   130  	}
   131  	sigSize := len(pbAct.GetSignature())
   132  	if sigSize != 65 {
   133  		return errors.Errorf("invalid signature length = %d, expecting 65", sigSize)
   134  	}
   135  
   136  	var elp Envelope = &envelope{}
   137  	if err := elp.LoadProto(pbAct.GetCore()); err != nil {
   138  		return err
   139  	}
   140  	// populate pubkey and signature
   141  	srcPub, err := crypto.BytesToPublicKey(pbAct.GetSenderPubKey())
   142  	if err != nil {
   143  		return err
   144  	}
   145  	encoding := pbAct.GetEncoding()
   146  	switch encoding {
   147  	case iotextypes.Encoding_ETHEREUM_EIP155, iotextypes.Encoding_ETHEREUM_UNPROTECTED:
   148  		// verify action type can support RLP-encoding
   149  		act, ok := elp.Action().(EthCompatibleAction)
   150  		if !ok {
   151  			return ErrInvalidAct
   152  		}
   153  		tx, err := act.ToEthTx(evmID)
   154  		if err != nil {
   155  			return err
   156  		}
   157  		signer, err := NewEthSigner(encoding, evmID)
   158  		if err != nil {
   159  			return err
   160  		}
   161  		if _, err = rlpSignedHash(tx, signer, pbAct.GetSignature()); err != nil {
   162  			return err
   163  		}
   164  		sealed.evmNetworkID = evmID
   165  	case iotextypes.Encoding_IOTEX_PROTOBUF:
   166  		break
   167  	default:
   168  		return errors.Errorf("unknown encoding type %v", encoding)
   169  	}
   170  
   171  	// clear 'sealed' and populate new value
   172  	sealed.Envelope = elp
   173  	sealed.srcPubkey = srcPub
   174  	sealed.signature = make([]byte, sigSize)
   175  	copy(sealed.signature, pbAct.GetSignature())
   176  	sealed.encoding = encoding
   177  	sealed.hash = hash.ZeroHash256
   178  	sealed.srcAddress = nil
   179  	return nil
   180  }
   181  
   182  // VerifySignature verifies the action using sender's public key
   183  func (sealed *SealedEnvelope) VerifySignature() error {
   184  	if sealed.SrcPubkey() == nil {
   185  		return errors.New("empty public key")
   186  	}
   187  	h, err := sealed.envelopeHash()
   188  	if err != nil {
   189  		return errors.Wrap(err, "failed to generate envelope hash")
   190  	}
   191  	if !sealed.SrcPubkey().Verify(h[:], sealed.Signature()) {
   192  		log.L().Info("failed to verify action hash",
   193  			zap.String("hash", hex.EncodeToString(h[:])),
   194  			zap.String("signature", hex.EncodeToString(sealed.Signature())))
   195  		return ErrInvalidSender
   196  	}
   197  	return nil
   198  }