github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/blockchain/txbuilder/signature_witness.go (about)

     1  package txbuilder
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  
     7  	log "github.com/sirupsen/logrus"
     8  
     9  	"github.com/bytom/bytom/crypto/ed25519/chainkd"
    10  	"github.com/bytom/bytom/crypto/sha3pool"
    11  	chainjson "github.com/bytom/bytom/encoding/json"
    12  	"github.com/bytom/bytom/errors"
    13  	"github.com/bytom/bytom/protocol/vm"
    14  )
    15  
    16  type (
    17  	// SignatureWitness is a sign struct
    18  	SignatureWitness struct {
    19  		// Quorum is the number of signatures required.
    20  		Quorum int `json:"quorum"`
    21  
    22  		// Keys are the identities of the keys to sign with.
    23  		Keys []keyID `json:"keys"`
    24  
    25  		// Program is the predicate part of the signature program, whose hash is what gets
    26  		// signed. If empty, it is computed during Sign from the outputs
    27  		// and the current input of the transaction.
    28  		Program chainjson.HexBytes `json:"program"`
    29  
    30  		// Sigs are signatures of Program made from each of the Keys
    31  		// during Sign.
    32  		Sigs []chainjson.HexBytes `json:"signatures"`
    33  	}
    34  
    35  	keyID struct {
    36  		XPub           chainkd.XPub         `json:"xpub"`
    37  		DerivationPath []chainjson.HexBytes `json:"derivation_path"`
    38  	}
    39  )
    40  
    41  // ErrEmptyProgram is a type of error
    42  var ErrEmptyProgram = errors.New("empty signature program")
    43  
    44  // Sign populates sw.Sigs with as many signatures of the predicate in
    45  // sw.Program as it can from the overlapping set of keys in sw.Keys.
    46  //
    47  // If sw.Program is empty, it is populated with an _inferred_ predicate:
    48  // a program committing to aspects of the current
    49  // transaction. Specifically, the program commits to:
    50  //  - the mintime and maxtime of the transaction (if non-zero)
    51  //  - the outputID of the current input
    52  //  - the assetID, amount, control program of each output.
    53  func (sw *SignatureWitness) sign(ctx context.Context, tpl *Template, index uint32, auth string, signFn SignFunc) error {
    54  	// Compute the predicate to sign. This is either a
    55  	// txsighash program if tpl.AllowAdditional is false (i.e., the tx is complete
    56  	// and no further changes are allowed) or a program enforcing
    57  	// constraints derived from the existing outputs and current input.
    58  	if len(sw.Program) == 0 {
    59  		var err error
    60  		sw.Program, err = buildSigProgram(tpl, tpl.SigningInstructions[index].Position)
    61  		if err != nil {
    62  			return err
    63  		}
    64  		if len(sw.Program) == 0 {
    65  			return ErrEmptyProgram
    66  		}
    67  	}
    68  	if len(sw.Sigs) < len(sw.Keys) {
    69  		// Each key in sw.Keys may produce a signature in sw.Sigs. Make
    70  		// sure there are enough slots in sw.Sigs and that we preserve any
    71  		// sigs already present.
    72  		newSigs := make([]chainjson.HexBytes, len(sw.Keys))
    73  		copy(newSigs, sw.Sigs)
    74  		sw.Sigs = newSigs
    75  	}
    76  	var h [32]byte
    77  	sha3pool.Sum256(h[:], sw.Program)
    78  	for i, keyID := range sw.Keys {
    79  		if len(sw.Sigs[i]) > 0 {
    80  			// Already have a signature for this key
    81  			continue
    82  		}
    83  		path := make([][]byte, len(keyID.DerivationPath))
    84  		for i, p := range keyID.DerivationPath {
    85  			path[i] = p
    86  		}
    87  		sigBytes, err := signFn(ctx, keyID.XPub, path, h, auth)
    88  		if err != nil {
    89  			log.WithFields(log.Fields{"module": logModule, "err": err}).Warningf("computing signature %d", i)
    90  			continue
    91  		}
    92  
    93  		// This break is ordered to avoid signing transaction successfully only once for a multiple-sign account
    94  		// that consist of different keys by the same password. Exit immediately when the signature is success,
    95  		// it means that only one signature will be successful in the loop for this multiple-sign account.
    96  		sw.Sigs[i] = sigBytes
    97  		break
    98  	}
    99  	return nil
   100  }
   101  
   102  func (sw SignatureWitness) materialize(args *[][]byte) error {
   103  	// This is the value of N for the CHECKPREDICATE call. The code
   104  	// assumes that everything already in the arg list before this call
   105  	// to Materialize is input to the signature program, so N is
   106  	// len(*args).
   107  	*args = append(*args, vm.Int64Bytes(int64(len(*args))))
   108  
   109  	var nsigs int
   110  	for i := 0; i < len(sw.Sigs) && nsigs < sw.Quorum; i++ {
   111  		if len(sw.Sigs[i]) > 0 {
   112  			*args = append(*args, sw.Sigs[i])
   113  			nsigs++
   114  		}
   115  	}
   116  	*args = append(*args, sw.Program)
   117  	return nil
   118  }
   119  
   120  // MarshalJSON convert struct to json
   121  func (sw SignatureWitness) MarshalJSON() ([]byte, error) {
   122  	obj := struct {
   123  		Type   string               `json:"type"`
   124  		Quorum int                  `json:"quorum"`
   125  		Keys   []keyID              `json:"keys"`
   126  		Sigs   []chainjson.HexBytes `json:"signatures"`
   127  	}{
   128  		Type:   "signature",
   129  		Quorum: sw.Quorum,
   130  		Keys:   sw.Keys,
   131  		Sigs:   sw.Sigs,
   132  	}
   133  	return json.Marshal(obj)
   134  }