You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
3.9 KiB

package atto
1 year ago
import (
"crypto/rand"
1 year ago
"fmt"
"io/ioutil"
"math/big"
"net/http"
"strings"
"golang.org/x/crypto/blake2b"
1 year ago
)
// GenerateSeed generates a new random seed.
func GenerateSeed() (string, error) {
b := make([]byte, 32)
_, err := rand.Read(b)
return fmt.Sprintf("%X", b), err
}
// NewPrivateKey creates a private key from the given seed and index.
func NewPrivateKey(seed string, index uint32) (*big.Int, error) {
seedInt, ok := big.NewInt(0).SetString(seed, 16)
if !ok {
return nil, fmt.Errorf("could not parse seed")
}
seedBytes := bigIntToBytes(seedInt, 32)
indexBytes := bigIntToBytes(big.NewInt(int64(index)), 4)
in := append(seedBytes, indexBytes...)
privateKeyBytes := blake2b.Sum256(in)
return big.NewInt(0).SetBytes(privateKeyBytes[:]), nil
}
1 year ago
func base32Encode(in *big.Int) string {
alphabet := []byte("13456789abcdefghijkmnopqrstuwxyz")
bigZero := big.NewInt(0)
bigRadix := big.NewInt(32)
num := big.NewInt(0).SetBytes(in.Bytes())
out := make([]byte, 0)
mod := new(big.Int)
for num.Cmp(bigZero) > 0 {
num.DivMod(num, bigRadix, mod)
out = append(out, alphabet[mod.Int64()])
}
for i := 0; i < len(out)/2; i++ {
out[i], out[len(out)-1-i] = out[len(out)-1-i], out[i]
}
return string(out)
}
func base32Decode(in string) (*big.Int, error) {
reverseAlphabet := map[rune]*big.Int{}
reverseAlphabet['1'] = big.NewInt(0)
reverseAlphabet['3'] = big.NewInt(1)
reverseAlphabet['4'] = big.NewInt(2)
reverseAlphabet['5'] = big.NewInt(3)
reverseAlphabet['6'] = big.NewInt(4)
reverseAlphabet['7'] = big.NewInt(5)
reverseAlphabet['8'] = big.NewInt(6)
reverseAlphabet['9'] = big.NewInt(7)
reverseAlphabet['a'] = big.NewInt(8)
reverseAlphabet['b'] = big.NewInt(9)
reverseAlphabet['c'] = big.NewInt(10)
reverseAlphabet['d'] = big.NewInt(11)
reverseAlphabet['e'] = big.NewInt(12)
reverseAlphabet['f'] = big.NewInt(13)
reverseAlphabet['g'] = big.NewInt(14)
reverseAlphabet['h'] = big.NewInt(15)
reverseAlphabet['i'] = big.NewInt(16)
reverseAlphabet['j'] = big.NewInt(17)
reverseAlphabet['k'] = big.NewInt(18)
reverseAlphabet['m'] = big.NewInt(19)
reverseAlphabet['n'] = big.NewInt(20)
reverseAlphabet['o'] = big.NewInt(21)
reverseAlphabet['p'] = big.NewInt(22)
reverseAlphabet['q'] = big.NewInt(23)
reverseAlphabet['r'] = big.NewInt(24)
reverseAlphabet['s'] = big.NewInt(25)
reverseAlphabet['t'] = big.NewInt(26)
reverseAlphabet['u'] = big.NewInt(27)
reverseAlphabet['w'] = big.NewInt(28)
reverseAlphabet['x'] = big.NewInt(29)
reverseAlphabet['y'] = big.NewInt(30)
reverseAlphabet['z'] = big.NewInt(31)
out := big.NewInt(0)
radix := big.NewInt(32)
for _, r := range in {
out.Mul(out, radix)
1 year ago
val, ok := reverseAlphabet[r]
if !ok {
return out, fmt.Errorf("'%c' is no legal base32 character", r)
}
out.Add(out, val)
1 year ago
}
return out, nil
}
func bigIntToBytes(x *big.Int, n int) []byte {
return x.FillBytes(make([]byte, n, n))
}
1 year ago
func revertBytes(in []byte) []byte {
for i := 0; i < len(in)/2; i++ {
in[i], in[len(in)-1-i] = in[len(in)-1-i], in[i]
}
return in
}
func doRPC(requestBody, node string) (responseBytes []byte, err error) {
req, err := http.NewRequest("POST", node, strings.NewReader(requestBody))
if err != nil {
return
}
req.Header.Add("Content-Type", "application/json")
if RequestInterceptor != nil {
if err = RequestInterceptor(req); err != nil {
err = fmt.Errorf("request interceptor failed: %v", err)
return
}
}
resp, err := http.DefaultClient.Do(req)
1 year ago
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
err = fmt.Errorf("received unexpected HTTP return code %d", resp.StatusCode)
return
}
return ioutil.ReadAll(resp.Body)
}
func getPublicKeyFromAddress(address string) (*big.Int, error) {
if len(address) == 64 {
return base32Decode(address[4:56])
} else if len(address) == 65 {
return base32Decode(address[5:57])
}
return nil, fmt.Errorf("could not parse address %s", address)
1 year ago
}