github.com/Tri-stone/burrow@v0.25.0/txs/envelope.go (about)

     1  package txs
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hyperledger/burrow/acm"
     7  	"github.com/hyperledger/burrow/acm/acmstate"
     8  	"github.com/hyperledger/burrow/crypto"
     9  	"github.com/hyperledger/burrow/event/query"
    10  	"github.com/hyperledger/burrow/txs/payload"
    11  )
    12  
    13  type Codec interface {
    14  	Encoder
    15  	Decoder
    16  }
    17  
    18  type Encoder interface {
    19  	EncodeTx(envelope *Envelope) ([]byte, error)
    20  }
    21  
    22  type Decoder interface {
    23  	DecodeTx(txBytes []byte) (*Envelope, error)
    24  }
    25  
    26  // Enclose a Payload in an Envelope so it is ready to be signed by first wrapping the Payload
    27  // as a Tx (including ChainID) and writing it to the Tx field of the Envelope
    28  func Enclose(chainID string, payload payload.Payload) *Envelope {
    29  	body := NewTx(payload)
    30  	body.ChainID = chainID
    31  	return body.Enclose()
    32  }
    33  
    34  func (txEnv *Envelope) String() string {
    35  	return fmt.Sprintf("TxEnvelope{Signatures: %v, Tx: %s}", len(txEnv.Signatories), txEnv.Tx)
    36  }
    37  
    38  // Attempts to 'realise' the PublicKey and Address of a Signatory possibly referring to state
    39  // in the case where the Signatory contains an Address by no PublicKey. Checks consistency in other
    40  // cases, possibly generating the Address from the PublicKey
    41  func (s *Signatory) RealisePublicKey(getter acmstate.AccountGetter) error {
    42  	const errPrefix = "could not realise public key for signatory"
    43  	if s.PublicKey == nil {
    44  		if s.Address == nil {
    45  			return fmt.Errorf("%s: address not provided", errPrefix)
    46  		}
    47  		acc, err := getter.GetAccount(*s.Address)
    48  		if err != nil {
    49  			return fmt.Errorf("%s: could not get account %v: %v", errPrefix, *s.Address, err)
    50  		}
    51  		publicKey := acc.PublicKey
    52  		s.PublicKey = &publicKey
    53  	}
    54  	if !s.PublicKey.IsValid() {
    55  		return fmt.Errorf("%s: public key %v is invalid", errPrefix, *s.PublicKey)
    56  	}
    57  	address := s.PublicKey.GetAddress()
    58  	if s.Address == nil {
    59  		s.Address = &address
    60  	} else if address != *s.Address {
    61  		return fmt.Errorf("address %v provided with signatory does not match address generated from "+
    62  			"public key %v", *s.Address, address)
    63  	}
    64  	return nil
    65  }
    66  
    67  // Returns an error if Envelope has a nil transaction or zero signatures (and therefore could not possibly be valid)
    68  func (txEnv *Envelope) Validate() error {
    69  	if txEnv.Tx == nil {
    70  		return fmt.Errorf("transaction envelope contains no (successfully unmarshalled) transaction")
    71  	}
    72  	if len(txEnv.Signatories) == 0 {
    73  		return fmt.Errorf("transaction envelope contains no (successfully unmarshalled) signatories")
    74  	}
    75  	for i, sig := range txEnv.Signatories {
    76  		err := sig.Validate()
    77  		if err != nil {
    78  			return fmt.Errorf("Signatory %v is invalid: %v", i, err)
    79  		}
    80  	}
    81  	return nil
    82  }
    83  
    84  func (sig *Signatory) Validate() error {
    85  	if sig.Address == nil {
    86  		return fmt.Errorf("has nil Address: %v", sig)
    87  	}
    88  	if sig.PublicKey == nil {
    89  		return fmt.Errorf("has nil PublicKey: %v", sig)
    90  	}
    91  	return nil
    92  }
    93  
    94  // Verifies the validity of the Signatories' Signatures in the Envelope. The Signatories must
    95  // appear in the same order as the inputs as returned by Tx.GetInputs().
    96  func (txEnv *Envelope) Verify(getter acmstate.AccountGetter, chainID string) error {
    97  	err := txEnv.Validate()
    98  	if err != nil {
    99  		return err
   100  	}
   101  	errPrefix := fmt.Sprintf("could not verify transaction %X", txEnv.Tx.Hash())
   102  	if txEnv.Tx.ChainID != chainID {
   103  		return fmt.Errorf("%s: ChainID in envelope is %s but receiving chain has ID %s",
   104  			errPrefix, txEnv.Tx.ChainID, chainID)
   105  	}
   106  	inputs := txEnv.Tx.GetInputs()
   107  	if len(inputs) != len(txEnv.Signatories) {
   108  		return fmt.Errorf("%s: number of inputs (= %v) should equal number of signatories (= %v)",
   109  			errPrefix, len(inputs), len(txEnv.Signatories))
   110  	}
   111  	signBytes, err := txEnv.Tx.SignBytes()
   112  	if err != nil {
   113  		return fmt.Errorf("%s: could not generate SignBytes: %v", errPrefix, err)
   114  	}
   115  	// Expect order to match (we could build lookup but we want Verify to be quicker than Sign which does order sigs)
   116  	for i, s := range txEnv.Signatories {
   117  		if inputs[i].Address != *s.Address {
   118  			return fmt.Errorf("signatory %v has address %v but input %v has address %v",
   119  				i, *s.Address, i, inputs[i].Address)
   120  		}
   121  		err = s.PublicKey.Verify(signBytes, s.Signature)
   122  		if err != nil {
   123  			return fmt.Errorf("invalid signature in signatory %v: %v", *s.Address, err)
   124  		}
   125  	}
   126  	return nil
   127  }
   128  
   129  // Sign the Tx Envelope by adding Signatories containing the signatures for each TxInput.
   130  // signing accounts for each input must be provided (in any order).
   131  func (txEnv *Envelope) Sign(signingAccounts ...acm.AddressableSigner) error {
   132  	// Clear any existing
   133  	txEnv.Signatories = nil
   134  	signBytes, err := txEnv.Tx.SignBytes()
   135  	if err != nil {
   136  		return err
   137  	}
   138  	signingAccountMap := make(map[crypto.Address]acm.AddressableSigner)
   139  	for _, sa := range signingAccounts {
   140  		signingAccountMap[sa.GetAddress()] = sa
   141  	}
   142  	// Sign in order of inputs
   143  	for i, in := range txEnv.Tx.GetInputs() {
   144  		sa, ok := signingAccountMap[in.Address]
   145  		if !ok {
   146  			return fmt.Errorf("account to sign %v (position %v) not passed to Sign, passed: %v", in, i, signingAccounts)
   147  		}
   148  		sig, err := sa.Sign(signBytes)
   149  		if err != nil {
   150  			return err
   151  		}
   152  		address := sa.GetAddress()
   153  		publicKey := sa.GetPublicKey()
   154  		txEnv.Signatories = append(txEnv.Signatories, Signatory{
   155  			Address:   &address,
   156  			PublicKey: &publicKey,
   157  			Signature: sig,
   158  		})
   159  	}
   160  	return nil
   161  }
   162  
   163  func (txEnv *Envelope) Tagged() query.Tagged {
   164  	if txEnv != nil {
   165  		return query.MergeTags(query.MustReflectTags(txEnv, "Signatories"), txEnv.Tx.Tagged())
   166  	} else {
   167  		return query.TagMap{}
   168  	}
   169  }