1 // Package signers associates signers and their corresponding keys.
7 "github.com/vapor/crypto/ed25519/chainkd"
8 "github.com/vapor/errors"
14 AssetKeySpace keySpace = 0
15 AccountKeySpace keySpace = 1
19 //BIP0032 compatible previous derivation rule m/account/address_index
21 //BIP0032 path derivation rule m/purpose'/coin_type'/account'/change/address_index
26 // ErrBadQuorum is returned by Create when the quorum
27 // provided is less than 1 or greater than the number
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")
31 // ErrBadXPub is returned by Create when the xpub
32 // provided isn't valid.
33 ErrBadXPub = errors.New("invalid xpub format")
35 // ErrNoXPubs is returned by create when the xpubs
36 // slice provided is empty.
37 ErrNoXPubs = errors.New("at least one xpub is required")
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")
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}
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.
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"`
63 // GetBip0032Path returns the complete path for bip0032 derived keys
64 func GetBip0032Path(s *Signer, ks keySpace, itemIndexes ...uint64) [][]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 {
71 binary.LittleEndian.PutUint64(idxBytes[:], idx)
72 path = append(path, idxBytes[:])
77 // getBip0044Path returns the complete path for bip0044 derived keys
78 func getBip0044Path(accountIndex uint64, change bool, addrIndex uint64) [][]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)
87 binary.LittleEndian.PutUint32(branchBytes, uint32(1))
89 binary.LittleEndian.PutUint32(branchBytes, uint32(0))
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
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 {
102 return GetBip0032Path(s, ks, addrIndex), nil
104 return getBip0044Path(s.KeyIndex, change, addrIndex), nil
106 return nil, ErrDeriveRule
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) {
112 return nil, errors.Wrap(ErrNoXPubs)
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)
120 xpubsMap[xpub] = true
123 if quorum == 0 || quorum > len(xpubs) {
124 return nil, errors.Wrap(ErrBadQuorum)
132 DeriveRule: deriveRule,
136 type SortKeys []chainkd.XPub
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] }