// Package signers associates signers and their corresponding keys. package signers import ( "bytes" "encoding/binary" "github.com/vapor/crypto/ed25519/chainkd" "github.com/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] }