github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/blockchain/signers/signers.go (about)

     1  // Package signers associates signers and their corresponding keys.
     2  package signers
     3  
     4  import (
     5  	"bytes"
     6  	"encoding/binary"
     7  	"github.com/bytom/bytom/crypto/ed25519/chainkd"
     8  	"github.com/bytom/bytom/errors"
     9  )
    10  
    11  type keySpace byte
    12  
    13  const (
    14  	AssetKeySpace   keySpace = 0
    15  	AccountKeySpace keySpace = 1
    16  )
    17  
    18  const (
    19  	//BIP0032 compatible previous derivation rule m/account/address_index
    20  	BIP0032 uint8 = iota
    21  	//BIP0032 path derivation rule m/purpose'/coin_type'/account'/change/address_index
    22  	BIP0044
    23  )
    24  
    25  var (
    26  	// ErrBadQuorum is returned by Create when the quorum
    27  	// provided is less than 1 or greater than the number
    28  	// of xpubs provided.
    29  	ErrBadQuorum = errors.New("quorum must be greater than or equal to 1, and must be less than or equal to the length of xpubs")
    30  
    31  	// ErrBadXPub is returned by Create when the xpub
    32  	// provided isn't valid.
    33  	ErrBadXPub = errors.New("invalid xpub format")
    34  
    35  	// ErrNoXPubs is returned by create when the xpubs
    36  	// slice provided is empty.
    37  	ErrNoXPubs = errors.New("at least one xpub is required")
    38  
    39  	// ErrDupeXPub is returned by create when the same xpub
    40  	// appears twice in a single call.
    41  	ErrDupeXPub   = errors.New("xpubs cannot contain the same key more than once")
    42  	ErrDeriveRule = errors.New("invalid key derive rule")
    43  )
    44  
    45  var (
    46  	// BIP44Purpose purpose field 0x0000002c little-endian mode.
    47  	BIP44Purpose = []byte{0x2C, 0x00, 0x00, 0x00}
    48  	// BTMCoinType coin type field 0x00000099 little-endian mode.
    49  	BTMCoinType = []byte{0x99, 0x00, 0x00, 0x00}
    50  )
    51  
    52  // Signer is the abstract concept of a signer,
    53  // which is composed of a set of keys as well as
    54  // the amount of signatures needed for quorum.
    55  type Signer struct {
    56  	Type       string         `json:"type"`
    57  	XPubs      []chainkd.XPub `json:"xpubs"`
    58  	Quorum     int            `json:"quorum"`
    59  	KeyIndex   uint64         `json:"key_index"`
    60  	DeriveRule uint8          `json:"derive_rule"`
    61  }
    62  
    63  // GetBip0032Path returns the complete path for bip0032 derived keys
    64  func GetBip0032Path(s *Signer, ks keySpace, itemIndexes ...uint64) [][]byte {
    65  	var path [][]byte
    66  	signerPath := [9]byte{byte(ks)}
    67  	binary.LittleEndian.PutUint64(signerPath[1:], s.KeyIndex)
    68  	path = append(path, signerPath[:])
    69  	for _, idx := range itemIndexes {
    70  		var idxBytes [8]byte
    71  		binary.LittleEndian.PutUint64(idxBytes[:], idx)
    72  		path = append(path, idxBytes[:])
    73  	}
    74  	return path
    75  }
    76  
    77  // getBip0044Path returns the complete path for bip0044 derived keys
    78  func getBip0044Path(accountIndex uint64, change bool, addrIndex uint64) [][]byte {
    79  	var path [][]byte
    80  	path = append(path, BIP44Purpose[:]) //purpose
    81  	path = append(path, BTMCoinType[:])  //coin type
    82  	accIdxBytes := make([]byte, 4)
    83  	binary.LittleEndian.PutUint32(accIdxBytes, uint32(accountIndex))
    84  	path = append(path, accIdxBytes) //account index
    85  	branchBytes := make([]byte, 4)
    86  	if change {
    87  		binary.LittleEndian.PutUint32(branchBytes, uint32(1))
    88  	} else {
    89  		binary.LittleEndian.PutUint32(branchBytes, uint32(0))
    90  	}
    91  	path = append(path, branchBytes) //change
    92  	addrIdxBytes := make([]byte, 4)
    93  	binary.LittleEndian.PutUint32(addrIdxBytes[:], uint32(addrIndex))
    94  	path = append(path, addrIdxBytes[:]) //address index
    95  	return path
    96  }
    97  
    98  // Path returns the complete path for derived keys
    99  func Path(s *Signer, ks keySpace, change bool, addrIndex uint64) ([][]byte, error) {
   100  	switch s.DeriveRule {
   101  	case BIP0032:
   102  		return GetBip0032Path(s, ks, addrIndex), nil
   103  	case BIP0044:
   104  		return getBip0044Path(s.KeyIndex, change, addrIndex), nil
   105  	}
   106  	return nil, ErrDeriveRule
   107  }
   108  
   109  // Create creates and stores a Signer in the database
   110  func Create(signerType string, xpubs []chainkd.XPub, quorum int, keyIndex uint64, deriveRule uint8) (*Signer, error) {
   111  	if len(xpubs) == 0 {
   112  		return nil, errors.Wrap(ErrNoXPubs)
   113  	}
   114  
   115  	xpubsMap := map[chainkd.XPub]bool{}
   116  	for _, xpub := range xpubs {
   117  		if _, ok := xpubsMap[xpub]; ok {
   118  			return nil, errors.WithDetailf(ErrDupeXPub, "duplicated key=%x", xpub)
   119  		}
   120  		xpubsMap[xpub] = true
   121  	}
   122  
   123  	if quorum == 0 || quorum > len(xpubs) {
   124  		return nil, errors.Wrap(ErrBadQuorum)
   125  	}
   126  
   127  	return &Signer{
   128  		Type:       signerType,
   129  		XPubs:      xpubs,
   130  		Quorum:     quorum,
   131  		KeyIndex:   keyIndex,
   132  		DeriveRule: deriveRule,
   133  	}, nil
   134  }
   135  
   136  type SortKeys []chainkd.XPub
   137  
   138  func (s SortKeys) Len() int           { return len(s) }
   139  func (s SortKeys) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 }
   140  func (s SortKeys) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }