188 lines
5.2 KiB
Go

// Copyright 2017-2021 DERO Project. All rights reserved.
// Use of this source code in any form is governed by RESEARCH license.
// license can be found in the LICENSE file.
// GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package address
import "fmt"
//import "bytes"
//import "encoding/binary"
//import "github.com/deroproject/derohe/config"
import "github.com/deroproject/derohe/crypto"
// older dero address https://cryptonote.org/cns/cns007.txt to understand address more
// current dero versions use https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
// there are 3 hrps for mainnet DERO, DEROI,DEROPROOF
// these are 3 hrps for testnet DETO, DETOI,DEROPROOF
// so a total of 5 hrps
// 1 byte version is support capable to represent 255 versions, currently we will be using only version 1
// point is 33 bytes
// payment id if present is 8 bytes
type Address struct {
Network uint64
Amount uint64 // integrated address can also contain amount to send
Mainnet bool
Proof bool
PublicKey *crypto.Point //33 byte compressed point
PaymentID []byte //integrated payment id is 8 bytes, It is encrypted on the blockchain
}
// SegwitAddrEncode encodes hrp(human-readable part) , version(int) and data(bytes array), returns Segwit Address / or error
func (a *Address) DeroAddrEncode() (string, error) {
hrp := "dero"
if !a.Mainnet {
hrp = "deto"
}
if len(a.PaymentID) == 8 {
hrp += "i"
}
if a.Proof {
hrp = "deroproof"
}
// first we are appending version byte
data_bytes := append([]byte{1}, a.PublicKey.EncodeCompressed()...)
data_bytes = append(data_bytes, a.PaymentID...)
var data_ints []int
for i := range data_bytes {
data_ints = append(data_ints, int(data_bytes[i]))
}
data, err := convertbits(data_ints, 8, 5, true)
if err != nil {
return "", err
}
ret, err := Encode(hrp, data)
if err != nil {
return "", err
}
return ret, nil
}
// stringifier
func (a Address) String() string {
result, _ := a.DeroAddrEncode()
return result
}
// split address into parts
func (a Address) Split() (z Address, payment_id []byte) {
z = a
z.PaymentID = []byte{}
if len(z.PaymentID) == 8 {
payment_id = append(payment_id, a.PaymentID...)
}
return z, payment_id
}
// if address has amount
func (a Address) IntegratedAmount() uint64 {
return a.Amount
}
func (a Address) Compressed() []byte {
return a.PublicKey.EncodeCompressed()
}
// tells whether address is mainnet address
func (a *Address) IsMainnet() bool {
return a.Mainnet
}
// tells whether address is mainnet address
func (a *Address) IsIntegratedAddress() bool {
return len(a.PaymentID) > 0
}
// tells whether address belongs to DERO Network
func (a *Address) IsDERONetwork() bool {
return true
}
// NewAddress decodes hrp(human-readable part) Address(string), returns address or error
func NewAddress(addr string) (result *Address, err error) {
dechrp, data, err := Decode(addr)
if err != nil {
return nil, err
}
result = &Address{PublicKey: new(crypto.Point)}
switch dechrp {
case "dero", "deroi", "deto", "detoi", "deroproof":
default:
return nil, fmt.Errorf("invalid human-readable part : %s != %s", "", dechrp)
}
if len(data) < 1 {
return nil, fmt.Errorf("invalid decode version: %d", len(data))
}
res, err := convertbits(data, 5, 8, false)
if err != nil {
return nil, err
}
if res[0] != 1 {
return nil, fmt.Errorf("invalid address version : %d", res[0])
}
res = res[1:]
var resbytes []byte
for _, b := range res {
resbytes = append(resbytes, byte(b))
}
if len(resbytes) < 33 {
return nil, fmt.Errorf("invalid address length as per spec : %d", len(res))
}
if err = result.PublicKey.DecodeCompressed(resbytes[0:33]); err != nil {
return
}
result.Mainnet = true
if dechrp == "deto" || dechrp == "detoi" {
result.Mainnet = false
}
if dechrp == "deroproof" {
result.Proof = true
}
switch {
case len(res) == 33 && (dechrp == "dero" || dechrp == "deto"):
case len(res) == 33+8 && (dechrp == "deroi" || dechrp == "detoi" || dechrp == "deroproof"): // address only has payment id
result.PaymentID = append(result.PaymentID, resbytes[33:33+8]...)
case len(res) == 33+8+8 && (dechrp == "deroi" || dechrp == "detoi"): // address has payment id and amount support
default:
return nil, fmt.Errorf("invalid address length as per spec : %d", len(res))
}
return result, nil
}
// create a new address from key
func NewAddressFromKeys(key *crypto.Point) (result *Address) {
result = &Address{
Mainnet: true,
PublicKey: new(crypto.Point).Set(key),
}
return
}