status-go/account/generator/path_decoder.go

227 lines
3.7 KiB
Go
Raw Permalink Normal View History

package generator
import (
"fmt"
"io"
"strconv"
"strings"
)
type startingPoint int
const (
tokenMaster = 0x6D // char m
tokenSeparator = 0x2F // char /
tokenHardened = 0x27 // char '
tokenDot = 0x2E // char .
hardenedStart = 0x80000000 // 2^31
)
const (
startingPointMaster startingPoint = iota + 1
startingPointCurrent
startingPointParent
)
type parseFunc = func() error
type pathDecoder struct {
s string
r *strings.Reader
f parseFunc
pos int
path []uint32
start startingPoint
currentToken string
currentTokenHardened bool
}
func newPathDecoder(path string) (*pathDecoder, error) {
d := &pathDecoder{
s: path,
r: strings.NewReader(path),
}
if err := d.reset(); err != nil {
return nil, err
}
return d, nil
}
func (d *pathDecoder) reset() error {
_, err := d.r.Seek(0, io.SeekStart)
if err != nil {
return err
}
d.pos = 0
d.start = startingPointCurrent
d.f = d.parseStart
d.path = make([]uint32, 0)
d.resetCurrentToken()
return nil
}
func (d *pathDecoder) resetCurrentToken() {
d.currentToken = ""
d.currentTokenHardened = false
}
func (d *pathDecoder) parse() (startingPoint, []uint32, error) {
for {
err := d.f()
if err != nil {
if err == io.EOF {
err = nil
} else {
err = fmt.Errorf("error parsing derivation path %s; at position %d, %s", d.s, d.pos, err.Error())
}
return d.start, d.path, err
}
}
}
func (d *pathDecoder) readByte() (byte, error) {
b, err := d.r.ReadByte()
if err != nil {
return b, err
}
d.pos++
return b, nil
}
func (d *pathDecoder) unreadByte() error {
err := d.r.UnreadByte()
if err != nil {
return err
}
d.pos--
return nil
}
func (d *pathDecoder) parseStart() error {
b, err := d.readByte()
if err != nil {
return err
}
if b == tokenMaster {
d.start = startingPointMaster
d.f = d.parseSeparator
return nil
}
if b == tokenDot {
b2, err := d.readByte()
if err != nil {
return err
}
if b2 == tokenDot {
d.f = d.parseSeparator
d.start = startingPointParent
return nil
}
d.f = d.parseSeparator
d.start = startingPointCurrent
return d.unreadByte()
}
d.f = d.parseSegment
return d.unreadByte()
}
func (d *pathDecoder) saveSegment() error {
if len(d.currentToken) > 0 {
i, err := strconv.ParseUint(d.currentToken, 10, 32)
if err != nil {
return err
}
if i >= hardenedStart {
d.pos -= len(d.currentToken) - 1
return fmt.Errorf("index must be lower than 2^31, got %d", i)
}
if d.currentTokenHardened {
i += hardenedStart
}
d.path = append(d.path, uint32(i))
}
d.f = d.parseSegment
d.resetCurrentToken()
return nil
}
func (d *pathDecoder) parseSeparator() error {
b, err := d.readByte()
if err != nil {
return err
}
if b == tokenSeparator {
return d.saveSegment()
}
return fmt.Errorf("expected %s, got %s", string(rune(tokenSeparator)), string(rune(b)))
}
func (d *pathDecoder) parseSegment() error {
b, err := d.readByte()
if err == io.EOF {
if len(d.currentToken) == 0 {
return fmt.Errorf("expected number, got EOF")
}
if newErr := d.saveSegment(); newErr != nil {
return newErr
}
return err
}
if err != nil {
return err
}
if len(d.currentToken) > 0 && b == tokenSeparator {
return d.saveSegment()
}
if len(d.currentToken) > 0 && b == tokenHardened {
d.currentTokenHardened = true
d.f = d.parseSeparator
return nil
}
if b < 0x30 || b > 0x39 {
return fmt.Errorf("expected number, got %s", string(b))
}
d.currentToken = fmt.Sprintf("%s%s", d.currentToken, string(b))
return nil
}
func decodePath(str string) (startingPoint, []uint32, error) {
d, err := newPathDecoder(str)
if err != nil {
return 0, nil, err
}
return d.parse()
}