contract/name/name.go
package name
import (
"encoding/binary"
"fmt"
"strings"
"github.com/aergoio/aergo/v2/state"
"github.com/aergoio/aergo/v2/state/statedb"
"github.com/aergoio/aergo/v2/types"
"github.com/aergoio/aergo/v2/types/dbkey"
)
type NameMap struct {
Version byte
Owner []byte
Destination []byte
}
func CreateName(scs *statedb.ContractState, tx *types.TxBody, sender, receiver *state.AccountState, name string) error {
amount := tx.GetAmountBigInt()
if err := state.SendBalance(sender, receiver, amount); err != nil {
return err
}
return createName(scs, []byte(name), sender.ID())
}
func createName(scs *statedb.ContractState, name []byte, owner []byte) error {
// return setAddress(scs, name, owner)
return registerOwner(scs, name, owner, owner)
}
// UpdateName is avaliable after bid implement
func UpdateName(bs *state.BlockState, scs *statedb.ContractState, tx *types.TxBody,
sender, receiver *state.AccountState, name, to string) error {
if len(getAddress(scs, []byte(name))) <= types.NameLength {
return fmt.Errorf("%s is not created yet", string(name))
}
destination, _ := types.DecodeAddress(to)
destination = GetAddress(scs, destination)
amount := tx.GetAmountBigInt()
if err := state.SendBalance(sender, receiver, amount); err != nil {
return err
}
contract, err := statedb.OpenContractStateAccount(destination, bs.StateDB)
if err != nil {
return types.ErrTxInvalidRecipient
}
creator, err := contract.GetData(dbkey.CreatorMeta())
if err != nil {
return err
}
ownerAddr := destination
if creator != nil {
ownerAddr, err = types.DecodeAddress(string(creator))
if err != nil {
return types.ErrTxInvalidRecipient
}
}
return updateName(scs, []byte(name), ownerAddr, destination)
}
func updateName(scs *statedb.ContractState, name []byte, owner []byte, to []byte) error {
//return setAddress(scs, name, to)
return registerOwner(scs, name, owner, to)
}
func isPredefined(name []byte, legacy bool) bool {
if legacy {
return len(name) == types.AddressLength || strings.Contains(string(name), ".")
}
return len(name) == types.AddressLength || types.IsSpecialAccount(name)
}
// Resolve is resolve name for chain
func Resolve(bs *state.BlockState, name []byte, legacy bool) ([]byte, error) {
if isPredefined(name, legacy) {
return name, nil
}
scs, err := openContract(bs)
if err != nil {
return nil, err
}
return getAddress(scs, name), nil
}
func openContract(bs *state.BlockState) (*statedb.ContractState, error) {
v, err := state.GetAccountState([]byte(types.AergoName), bs.StateDB)
if err != nil {
return nil, err
}
scs, err := statedb.OpenContractState(v.ID(), v.State(), bs.StateDB)
if err != nil {
return nil, err
}
return scs, nil
}
// GetAddress is resolve name for mempool
func GetAddress(scs *statedb.ContractState, name []byte) []byte {
if len(name) == types.AddressLength || types.IsSpecialAccount(name) {
return name
}
return getAddress(scs, name)
}
// GetAddressLegacy is resolve name for mempool by buggy logic, leaved for backward compatibility
func GetAddressLegacy(scs *statedb.ContractState, name []byte) []byte {
if len(name) == types.AddressLength || strings.Contains(string(name), ".") {
return name
}
return getAddress(scs, name)
}
func getAddress(scs *statedb.ContractState, name []byte) []byte {
nameMap := getNameMap(scs, name, true)
if nameMap != nil {
return nameMap.Destination
}
return nil
}
func GetOwner(scs *statedb.ContractState, name []byte) []byte {
return getOwner(scs, name, true)
}
func getOwner(scs *statedb.ContractState, name []byte, useInitial bool) []byte {
nameMap := getNameMap(scs, name, useInitial)
if nameMap != nil {
return nameMap.Owner
}
return nil
}
func getNameMap(scs *statedb.ContractState, name []byte, useInitial bool) *NameMap {
var err error
var ownerdata []byte
if useInitial {
ownerdata, err = scs.GetInitialData(dbkey.Name(name))
} else {
ownerdata, err = scs.GetData(dbkey.Name(name))
}
if err != nil {
return nil
}
return deserializeNameMap(ownerdata)
}
func GetNameInfo(ncs *statedb.ContractState, name string) (*types.NameInfo, error) {
owner := getOwner(ncs, []byte(name), true)
return &types.NameInfo{Name: &types.Name{Name: string(name)}, Owner: owner, Destination: GetAddress(ncs, []byte(name))}, nil
}
func registerOwner(scs *statedb.ContractState, name, owner, destination []byte) error {
nameMap := &NameMap{Version: 1, Owner: owner, Destination: destination}
return setNameMap(scs, name, nameMap)
}
func setNameMap(scs *statedb.ContractState, name []byte, n *NameMap) error {
return scs.SetData(dbkey.Name(name), serializeNameMap(n))
}
func serializeNameMap(n *NameMap) []byte {
var ret []byte
if n != nil {
ret = append(ret, n.Version)
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, uint64(len(n.Owner)))
ret = append(ret, buf...)
ret = append(ret, n.Owner...)
binary.LittleEndian.PutUint64(buf, uint64(len(n.Destination)))
ret = append(ret, buf...)
ret = append(ret, n.Destination...)
}
return ret
}
func deserializeNameMap(data []byte) *NameMap {
if data != nil {
version := data[0]
if version != 1 {
panic("could not deserializeOwner, not supported version")
}
offset := 1
next := offset + 8
sizeOfAddr := binary.LittleEndian.Uint64(data[offset:next])
offset = next
next = offset + int(sizeOfAddr)
owner := data[offset:next]
offset = next
next = offset + 8
sizeOfDest := binary.LittleEndian.Uint64(data[offset:next])
offset = next
next = offset + int(sizeOfDest)
destination := data[offset:next]
return &NameMap{
Version: version,
Owner: owner,
Destination: destination,
}
}
return nil
}