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