consensus/monero/address/address.go
2022-10-08 20:55:01 +02:00

128 lines
3.2 KiB
Go

package address
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"filippo.io/edwards25519"
"git.gammaspectra.live/P2Pool/moneroutil"
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
"golang.org/x/crypto/sha3"
)
type Address struct {
Network uint8
SpendPub edwards25519.Point
ViewPub edwards25519.Point
Checksum []byte
}
var scalar8, _ = edwards25519.NewScalar().SetCanonicalBytes([]byte{8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
func FromBase58(address string) *Address {
raw := moneroutil.DecodeMoneroBase58(address)
if len(raw) != 69 {
return nil
}
checksum := moneroutil.GetChecksum(raw[:65])
if bytes.Compare(checksum[:], raw[65:]) != 0 {
return nil
}
a := &Address{
Network: raw[0],
Checksum: checksum[:],
}
if _, err := a.SpendPub.SetBytes(raw[1:33]); err != nil {
return nil
}
if _, err := a.ViewPub.SetBytes(raw[33:65]); err != nil {
return nil
}
return a
}
func FromRawAddress(network uint8, spend, view types.Hash) *Address {
nice := make([]byte, 69)
nice[0] = network
copy(nice[1:], spend[:])
copy(nice[33:], view[:])
checksum := moneroutil.GetChecksum(nice[:65])
a := &Address{
Network: nice[0],
Checksum: checksum[:],
}
if _, err := a.SpendPub.SetBytes(nice[1:33]); err != nil {
return nil
}
if _, err := a.ViewPub.SetBytes(nice[33:65]); err != nil {
return nil
}
return a
}
func (a *Address) ToBase58() string {
return moneroutil.EncodeMoneroBase58([]byte{a.Network}, a.SpendPub.Bytes(), a.ViewPub.Bytes(), a.Checksum[:])
}
func (a *Address) GetDerivationForPrivateKey(privateKey *edwards25519.Scalar) *edwards25519.Point {
point := (&edwards25519.Point{}).ScalarMult(privateKey, &a.ViewPub)
return (&edwards25519.Point{}).ScalarMult(scalar8, point)
}
func GetDerivationSharedDataForOutputIndex(derivation *edwards25519.Point, outputIndex uint64) *edwards25519.Scalar {
varIntBuf := make([]byte, binary.MaxVarintLen64)
data := append(derivation.Bytes(), varIntBuf[:binary.PutUvarint(varIntBuf, outputIndex)]...)
hasher := sha3.NewLegacyKeccak256()
if _, err := hasher.Write(data); err != nil {
return nil
}
var wideBytes [64]byte
copy(wideBytes[:], hasher.Sum([]byte{}))
scalar, _ := edwards25519.NewScalar().SetUniformBytes(wideBytes[:])
return scalar
}
func (a *Address) GetPublicKeyForSharedData(sharedData *edwards25519.Scalar) *edwards25519.Point {
sG := (&edwards25519.Point{}).ScalarBaseMult(sharedData)
return (&edwards25519.Point{}).Add(sG, &a.SpendPub)
}
func (a *Address) GetEphemeralPublicKey(privateKey types.Hash, outputIndex uint64) (result types.Hash) {
pK, _ := edwards25519.NewScalar().SetCanonicalBytes(privateKey[:])
copy(result[:], a.GetPublicKeyForSharedData(GetDerivationSharedDataForOutputIndex(a.GetDerivationForPrivateKey(pK), outputIndex)).Bytes())
return
}
func (a *Address) MarshalJSON() ([]byte, error) {
return json.Marshal(a.ToBase58())
}
func (a *Address) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
if addr := FromBase58(s); addr != nil {
a.Network = addr.Network
a.SpendPub = addr.SpendPub
a.ViewPub = addr.ViewPub
a.Checksum = addr.Checksum
return nil
} else {
return errors.New("invalid address")
}
}