OSDN Git Service

rename (#465)
[bytom/vapor.git] / blockchain / signers / signers.go
1 // Package signers associates signers and their corresponding keys.
2 package signers
3
4 import (
5         "bytes"
6         "encoding/binary"
7         "github.com/bytom/vapor/crypto/ed25519/chainkd"
8         "github.com/bytom/vapor/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] }