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 }