diff --git a/config/protocol.mainnet.yml b/config/protocol.mainnet.yml index d602bb43d..1eaef7375 100644 --- a/config/protocol.mainnet.yml +++ b/config/protocol.mainnet.yml @@ -31,7 +31,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] ApplicationConfiguration: # LogPath could be set up in case you need stdout logs to some proper file. diff --git a/config/protocol.privnet.docker.four.yml b/config/protocol.privnet.docker.four.yml index 3fb17befd..926a45eda 100644 --- a/config/protocol.privnet.docker.four.yml +++ b/config/protocol.privnet.docker.four.yml @@ -27,7 +27,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] ApplicationConfiguration: # LogPath could be set up in case you need stdout logs to some proper file. diff --git a/config/protocol.privnet.docker.one.yml b/config/protocol.privnet.docker.one.yml index f23b01f28..375fe2df4 100644 --- a/config/protocol.privnet.docker.one.yml +++ b/config/protocol.privnet.docker.one.yml @@ -27,7 +27,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] ApplicationConfiguration: # LogPath could be set up in case you need stdout logs to some proper file. diff --git a/config/protocol.privnet.docker.single.yml b/config/protocol.privnet.docker.single.yml index 7401609b4..474207422 100644 --- a/config/protocol.privnet.docker.single.yml +++ b/config/protocol.privnet.docker.single.yml @@ -21,7 +21,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] ApplicationConfiguration: # LogPath could be set up in case you need stdout logs to some proper file. diff --git a/config/protocol.privnet.docker.three.yml b/config/protocol.privnet.docker.three.yml index e4c9ebfd0..3c0fa1c8e 100644 --- a/config/protocol.privnet.docker.three.yml +++ b/config/protocol.privnet.docker.three.yml @@ -27,7 +27,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] ApplicationConfiguration: # LogPath could be set up in case you need stdout logs to some proper file. diff --git a/config/protocol.privnet.docker.two.yml b/config/protocol.privnet.docker.two.yml index d32e79a56..a0a1855d3 100644 --- a/config/protocol.privnet.docker.two.yml +++ b/config/protocol.privnet.docker.two.yml @@ -27,7 +27,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] ApplicationConfiguration: # LogPath could be set up in case you need stdout logs to some proper file. diff --git a/config/protocol.privnet.yml b/config/protocol.privnet.yml index 0ca78c3dc..6f54b307c 100644 --- a/config/protocol.privnet.yml +++ b/config/protocol.privnet.yml @@ -27,7 +27,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] ApplicationConfiguration: # LogPath could be set up in case you need stdout logs to some proper file. diff --git a/config/protocol.testnet.yml b/config/protocol.testnet.yml index ac9c45a3f..2f1411bef 100644 --- a/config/protocol.testnet.yml +++ b/config/protocol.testnet.yml @@ -31,7 +31,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] ApplicationConfiguration: # LogPath could be set up in case you need stdout logs to some proper file. diff --git a/config/protocol.unit_testnet.single.yml b/config/protocol.unit_testnet.single.yml index 076487504..1e565796a 100644 --- a/config/protocol.unit_testnet.single.yml +++ b/config/protocol.unit_testnet.single.yml @@ -19,7 +19,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] Notary: [0] ApplicationConfiguration: diff --git a/config/protocol.unit_testnet.yml b/config/protocol.unit_testnet.yml index c56b71090..7644ef3f1 100644 --- a/config/protocol.unit_testnet.yml +++ b/config/protocol.unit_testnet.yml @@ -28,7 +28,6 @@ ProtocolConfiguration: PolicyContract: [0] RoleManagement: [0] OracleContract: [0] - NameService: [0] Notary: [0] ApplicationConfiguration: diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index a81264523..74cacaf42 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -15,18 +15,17 @@ const reservedContractID = -100 // Contracts is a set of registered native contracts. type Contracts struct { - Management *Management - Ledger *Ledger - NEO *NEO - GAS *GAS - Policy *Policy - Oracle *Oracle - Designate *Designate - NameService *NameService - Notary *Notary - Crypto *Crypto - Std *Std - Contracts []interop.Contract + Management *Management + Ledger *Ledger + NEO *NEO + GAS *GAS + Policy *Policy + Oracle *Oracle + Designate *Designate + Notary *Notary + Crypto *Crypto + Std *Std + Contracts []interop.Contract // persistScript is vm script which executes "onPersist" method of every native contract. persistScript []byte // postPersistScript is vm script which executes "postPersist" method of every native contract. @@ -103,11 +102,6 @@ func NewContracts(p2pSigExtensionsEnabled bool, nativeUpdateHistories map[string cs.Oracle = oracle cs.Contracts = append(cs.Contracts, oracle) - ns := newNameService() - ns.NEO = neo - cs.NameService = ns - cs.Contracts = append(cs.Contracts, ns) - if p2pSigExtensionsEnabled { notary := newNotary() notary.GAS = gas diff --git a/pkg/core/native/name_service.go b/pkg/core/native/name_service.go deleted file mode 100644 index 4116d1fbc..000000000 --- a/pkg/core/native/name_service.go +++ /dev/null @@ -1,723 +0,0 @@ -package native - -import ( - "encoding/binary" - "errors" - "math" - "math/big" - "net" - "regexp" - "sort" - "strings" - "unicode/utf8" - - "github.com/nspcc-dev/neo-go/pkg/core/dao" - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" - "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" - "github.com/nspcc-dev/neo-go/pkg/core/native/nnsrecords" - "github.com/nspcc-dev/neo-go/pkg/core/state" - "github.com/nspcc-dev/neo-go/pkg/crypto/hash" - "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" - "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" - "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" -) - -// NameService represents native NameService contract. -type NameService struct { - nonfungible - NEO *NEO -} - -type nameState struct { - state.NFTTokenState - // Expiration is token expiration height. - Expiration uint32 - // HasAdmin is true if token has admin. - HasAdmin bool - // Admin is token admin. - Admin util.Uint160 -} - -const ( - nameServiceID = -10 - - prefixRoots = 10 - prefixDomainPrice = 22 - prefixExpiration = 20 - prefixRecord = 12 - - secondsInYear = 365 * 24 * 3600 - - // DefaultDomainPrice is the default price of register method. - DefaultDomainPrice = 10_00000000 - // MinDomainNameLength is minimum domain length. - MinDomainNameLength = 3 - // MaxDomainNameLength is maximum domain length. - MaxDomainNameLength = 255 -) - -var ( - // Lookahead is not supported by Go, but it is simple `(?=.{3,255}$)`, - // so we check name length explicitly. - nameRegex = regexp.MustCompile(`^([a-z0-9]{1,62}\.)+[a-z][a-z0-9]{0,15}$`) - ipv4Regex = regexp.MustCompile(`^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])$`) - ipv6Regex = regexp.MustCompile("(?:^)(([0-9a-f]{1,4}:){7,7}[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,7}:|([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}|([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:((:[0-9a-f]{1,4}){1,6})|:((:[0-9a-f]{1,4}){1,7}|:))$") - rootRegex = regexp.MustCompile("^[a-z][a-z0-9]{0,15}$") -) - -// matchName checks if provided name is valid. -func matchName(name string) bool { - ln := len(name) - return MinDomainNameLength <= ln && ln <= MaxDomainNameLength && - nameRegex.Match([]byte(name)) -} - -func newNameService() *NameService { - nf := newNonFungible(nativenames.NameService, nameServiceID, "NNS", 0) - nf.getTokenKey = func(tokenID []byte) []byte { - return append([]byte{prefixNFTToken}, hash.Hash160(tokenID).BytesBE()...) - } - nf.newTokenState = func() nftTokenState { - return new(nameState) - } - nf.onTransferred = func(tok nftTokenState) { - tok.(*nameState).HasAdmin = false - } - - n := &NameService{nonfungible: *nf} - defer n.UpdateHash() - - desc := newDescriptor("addRoot", smartcontract.VoidType, - manifest.NewParameter("root", smartcontract.StringType)) - md := newMethodAndPrice(n.addRoot, 1<<15, callflag.States) - n.AddMethod(md, desc) - - desc = newDescriptor("setPrice", smartcontract.VoidType, - manifest.NewParameter("price", smartcontract.IntegerType)) - md = newMethodAndPrice(n.setPrice, 1<<15, callflag.States) - n.AddMethod(md, desc) - - desc = newDescriptor("getPrice", smartcontract.IntegerType) - md = newMethodAndPrice(n.getPrice, 1<<15, callflag.ReadStates) - n.AddMethod(md, desc) - - desc = newDescriptor("isAvailable", smartcontract.BoolType, - manifest.NewParameter("name", smartcontract.StringType)) - md = newMethodAndPrice(n.isAvailable, 1<<15, callflag.ReadStates) - n.AddMethod(md, desc) - - desc = newDescriptor("register", smartcontract.BoolType, - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("owner", smartcontract.Hash160Type)) - md = newMethodAndPrice(n.register, 1<<15, callflag.States) - n.AddMethod(md, desc) - - desc = newDescriptor("renew", smartcontract.IntegerType, - manifest.NewParameter("name", smartcontract.StringType)) - md = newMethodAndPrice(n.renew, 0, callflag.States) - n.AddMethod(md, desc) - - desc = newDescriptor("setAdmin", smartcontract.VoidType, - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("admin", smartcontract.Hash160Type)) - md = newMethodAndPrice(n.setAdmin, 1<<15, callflag.States) - md.StorageFee = 20 - n.AddMethod(md, desc) - - desc = newDescriptor("setRecord", smartcontract.VoidType, - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("type", smartcontract.IntegerType), - manifest.NewParameter("data", smartcontract.StringType)) - md = newMethodAndPrice(n.setRecord, 1<<15, callflag.States) - md.StorageFee = 200 - n.AddMethod(md, desc) - - desc = newDescriptor("getRecord", smartcontract.StringType, - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("type", smartcontract.IntegerType)) - md = newMethodAndPrice(n.getRecord, 1<<15, callflag.ReadStates) - n.AddMethod(md, desc) - - desc = newDescriptor("deleteRecord", smartcontract.VoidType, - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("type", smartcontract.IntegerType)) - md = newMethodAndPrice(n.deleteRecord, 1<<15, callflag.States) - n.AddMethod(md, desc) - - desc = newDescriptor("resolve", smartcontract.StringType, - manifest.NewParameter("name", smartcontract.StringType), - manifest.NewParameter("type", smartcontract.IntegerType)) - md = newMethodAndPrice(n.resolve, 1<<17, callflag.ReadStates) - n.AddMethod(md, desc) - - return n -} - -// Metadata implements interop.Contract interface. -func (n *NameService) Metadata() *interop.ContractMD { - return &n.ContractMD -} - -// Initialize implements interop.Contract interface. -func (n *NameService) Initialize(ic *interop.Context) error { - if err := n.nonfungible.Initialize(ic); err != nil { - return err - } - if err := setIntWithKey(n.ID, ic.DAO, []byte{prefixDomainPrice}, DefaultDomainPrice); err != nil { - return err - } - roots := stringList{} - return putSerializableToDAO(n.ID, ic.DAO, []byte{prefixRoots}, &roots) -} - -// OnPersist implements interop.Contract interface. -func (n *NameService) OnPersist(ic *interop.Context) error { - now := uint32(ic.Block.Timestamp/1000 + 1) - keys := []string{} - ic.DAO.Seek(n.ID, []byte{prefixExpiration}, func(k, v []byte) { - if binary.BigEndian.Uint32(k) >= now { - return - } - // Removal is done separately because of `Seek` takes storage mutex. - keys = append(keys, string(k)) - }) - - var keysToRemove [][]byte - key := []byte{prefixExpiration} - keyRecord := []byte{prefixRecord} - for i := range keys { - key[0] = prefixExpiration - key = append(key[:1], []byte(keys[i])...) - if err := ic.DAO.DeleteStorageItem(n.ID, key); err != nil { - return err - } - - keysToRemove = keysToRemove[:0] - key[0] = prefixRecord - key = append(key[:1], keys[i][4:]...) - ic.DAO.Seek(n.ID, key, func(k, v []byte) { - keysToRemove = append(keysToRemove, k) - }) - for i := range keysToRemove { - keyRecord = append(keyRecord[:0], key...) - keyRecord = append(keyRecord, keysToRemove[i]...) - err := ic.DAO.DeleteStorageItem(n.ID, keyRecord) - if err != nil { - return err - } - } - - key[0] = prefixNFTToken - n.burnByKey(ic, key) - } - return nil -} - -// PostPersist implements interop.Contract interface. -func (n *NameService) PostPersist(ic *interop.Context) error { - return nil -} - -func (n *NameService) addRoot(ic *interop.Context, args []stackitem.Item) stackitem.Item { - root := toString(args[0]) - if !rootRegex.Match([]byte(root)) { - panic("invalid root") - } - - n.checkCommittee(ic) - roots, _ := n.getRootsInternal(ic.DAO) - if !roots.add(root) { - panic("name already exists") - } - - err := putSerializableToDAO(n.ID, ic.DAO, []byte{prefixRoots}, &roots) - if err != nil { - panic(err) - } - return stackitem.Null{} -} - -var maxPrice = big.NewInt(10000_00000000) - -func (n *NameService) setPrice(ic *interop.Context, args []stackitem.Item) stackitem.Item { - price := toBigInt(args[0]) - if price.Sign() <= 0 || price.Cmp(maxPrice) >= 0 { - panic("invalid price") - } - - n.checkCommittee(ic) - err := ic.DAO.PutStorageItem(n.ID, []byte{prefixDomainPrice}, bigint.ToBytes(price)) - if err != nil { - panic(err) - } - return stackitem.Null{} -} - -func (n *NameService) getPrice(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(n.getPriceInternal(ic.DAO)) -} - -func (n *NameService) getPriceInternal(d dao.DAO) *big.Int { - si := d.GetStorageItem(n.ID, []byte{prefixDomainPrice}) - return bigint.FromBytes(si) -} - -func (n *NameService) parseName(item stackitem.Item) (string, []string, []byte) { - name := toName(item) - names := strings.Split(name, ".") - if len(names) != 2 { - panic("invalid name") - } - return name, names, n.getTokenKey([]byte(name)) -} - -func (n *NameService) isAvailable(ic *interop.Context, args []stackitem.Item) stackitem.Item { - _, names, key := n.parseName(args[0]) - if ic.DAO.GetStorageItem(n.ID, key) != nil { - return stackitem.NewBool(false) - } - - roots, _ := n.getRootsInternal(ic.DAO) - _, ok := roots.index(names[1]) - if !ok { - panic("domain is not registered") - } - return stackitem.NewBool(true) -} - -func (n *NameService) getRootsInternal(d dao.DAO) (stringList, bool) { - var sl stringList - err := getSerializableFromDAO(n.ID, d, []byte{prefixRoots}, &sl) - if err != nil { - // Roots are being stored in `Initialize()` and thus must always be present. - panic(err) - } - return sl, true -} - -func (n *NameService) register(ic *interop.Context, args []stackitem.Item) stackitem.Item { - name, names, key := n.parseName(args[0]) - owner := toUint160(args[1]) - if !n.checkWitness(ic, owner) { - panic("owner is not witnessed") - } - - if ic.DAO.GetStorageItem(n.ID, key) != nil { - return stackitem.NewBool(false) - } - - roots, _ := n.getRootsInternal(ic.DAO) - if _, ok := roots.index(names[1]); !ok { - panic("missing root") - } - if !ic.VM.AddGas(n.getPriceInternal(ic.DAO).Int64()) { - panic("insufficient gas") - } - token := &nameState{ - NFTTokenState: state.NFTTokenState{ - Owner: owner, - Name: name, - }, - Expiration: uint32(ic.Block.Timestamp/1000 + secondsInYear), - } - n.mint(ic, token) - err := ic.DAO.PutStorageItem(n.ID, - makeExpirationKey(token.Expiration, token.ID()), - state.StorageItem{0}) - if err != nil { - panic(err) - } - return stackitem.NewBool(true) -} - -func (n *NameService) renew(ic *interop.Context, args []stackitem.Item) stackitem.Item { - _, _, key := n.parseName(args[0]) - if !ic.VM.AddGas(n.getPriceInternal(ic.DAO).Int64()) { - panic("insufficient gas") - } - token := new(nameState) - err := getSerializableFromDAO(n.ID, ic.DAO, key, token) - if err != nil { - panic(err) - } - - keyExpiration := makeExpirationKey(token.Expiration, token.ID()) - if err := ic.DAO.DeleteStorageItem(n.ID, keyExpiration); err != nil { - panic(err) - } - - token.Expiration += secondsInYear - err = putSerializableToDAO(n.ID, ic.DAO, key, token) - if err != nil { - panic(err) - } - - binary.BigEndian.PutUint32(key[1:], token.Expiration) - err = ic.DAO.PutStorageItem(n.ID, key, state.StorageItem{0}) - if err != nil { - panic(err) - } - bi := new(big.Int).SetUint64(uint64(token.Expiration)) - return stackitem.NewBigInteger(bi) -} - -func (n *NameService) setAdmin(ic *interop.Context, args []stackitem.Item) stackitem.Item { - _, _, key := n.parseName(args[0]) - - var admin util.Uint160 - _, isNull := args[1].(stackitem.Null) - if !isNull { - admin = toUint160(args[1]) - if !n.checkWitness(ic, admin) { - panic("not witnessed by admin") - } - } - - token := new(nameState) - err := getSerializableFromDAO(n.ID, ic.DAO, key, token) - if err != nil { - panic(err) - } - if !n.checkWitness(ic, token.Owner) { - panic("only owner can set admin") - } - token.HasAdmin = !isNull - token.Admin = admin - err = putSerializableToDAO(n.ID, ic.DAO, key, token) - if err != nil { - panic(err) - } - return stackitem.Null{} -} - -func (n *NameService) checkWitness(ic *interop.Context, owner util.Uint160) bool { - ok, err := runtime.CheckHashedWitness(ic, owner) - if err != nil { - panic(err) - } - return ok -} - -func (n *NameService) checkCommittee(ic *interop.Context) { - if !n.NEO.checkCommittee(ic) { - panic("not witnessed by committee") - } -} - -func (n *NameService) checkAdmin(ic *interop.Context, token *nameState) bool { - if n.checkWitness(ic, token.Owner) { - return true - } - return token.HasAdmin && n.checkWitness(ic, token.Admin) -} - -func (n *NameService) setRecord(ic *interop.Context, args []stackitem.Item) stackitem.Item { - name := toName(args[0]) - rt := toRecordType(args[1]) - data := toString(args[2]) - checkName(rt, data) - - domain := toDomain(name) - token, _, err := n.tokenState(ic.DAO, []byte(domain)) - if err != nil { - panic(err) - } - if !n.checkAdmin(ic, token.(*nameState)) { - panic("not witnessed by admin") - } - key := makeRecordKey(domain, name, rt) - si := state.StorageItem(data) - if err := ic.DAO.PutStorageItem(n.ID, key, si); err != nil { - panic(err) - } - return stackitem.Null{} -} - -func checkName(rt nnsrecords.Type, name string) { - var valid bool - switch rt { - case nnsrecords.A: - // We can't rely on `len(ip) == net.IPv4len` because - // IPv4 can be parsed to mapped representation. - valid = ipv4Regex.MatchString(name) && - net.ParseIP(name) != nil - case nnsrecords.CNAME: - valid = matchName(name) - case nnsrecords.TXT: - valid = utf8.RuneCountInString(name) <= 255 - case nnsrecords.AAAA: - valid = ipv6Regex.MatchString(name) && - net.ParseIP(name) != nil - } - if !valid { - panic("invalid name") - } -} - -func (n *NameService) getRecord(ic *interop.Context, args []stackitem.Item) stackitem.Item { - name := toName(args[0]) - domain := toDomain(name) - rt := toRecordType(args[1]) - key := makeRecordKey(domain, name, rt) - si := ic.DAO.GetStorageItem(n.ID, key) - if si == nil { - return stackitem.Null{} - } - return stackitem.NewByteArray(si) -} - -func (n *NameService) deleteRecord(ic *interop.Context, args []stackitem.Item) stackitem.Item { - name := toName(args[0]) - rt := toRecordType(args[1]) - domain := toDomain(name) - key := n.getTokenKey([]byte(domain)) - token := new(nameState) - err := getSerializableFromDAO(n.ID, ic.DAO, key, token) - if err != nil { - panic(err) - } - - if !n.checkAdmin(ic, token) { - panic("not witnessed by admin") - } - - key = makeRecordKey(domain, name, rt) - if err := ic.DAO.DeleteStorageItem(n.ID, key); err != nil { - panic(err) - } - return stackitem.Null{} -} - -func (n *NameService) resolve(ic *interop.Context, args []stackitem.Item) stackitem.Item { - name := toString(args[0]) - rt := toRecordType(args[1]) - result, ok := n.resolveInternal(ic, name, rt, 2) - if !ok { - return stackitem.Null{} - } - return stackitem.NewByteArray([]byte(result)) -} - -func (n *NameService) resolveInternal(ic *interop.Context, name string, t nnsrecords.Type, redirect int) (string, bool) { - if redirect < 0 { - panic("invalid redirect") - } - records := n.getRecordsInternal(ic.DAO, name) - if data, ok := records[t]; ok { - return data, true - } - data, ok := records[nnsrecords.CNAME] - if !ok { - return "", false - } - return n.resolveInternal(ic, data, t, redirect-1) -} - -func (n *NameService) getRecordsInternal(d dao.DAO, name string) map[nnsrecords.Type]string { - domain := toDomain(name) - key := makeRecordKey(domain, name, 0) - key = key[:len(key)-1] - res := make(map[nnsrecords.Type]string) - d.Seek(n.ID, key, func(k, v []byte) { - rt := nnsrecords.Type(k[len(k)-1]) - res[rt] = string(v) - }) - return res -} - -func makeRecordKey(domain, name string, rt nnsrecords.Type) []byte { - key := make([]byte, 1+util.Uint160Size+util.Uint160Size+1) - key[0] = prefixRecord - i := 1 - i += copy(key[i:], hash.Hash160([]byte(domain)).BytesBE()) - i += copy(key[i:], hash.Hash160([]byte(name)).BytesBE()) - key[i] = byte(rt) - return key -} - -func makeExpirationKey(expiration uint32, tokenID []byte) []byte { - key := make([]byte, 1+4+util.Uint160Size) - key[0] = prefixExpiration - binary.BigEndian.PutUint32(key[1:], expiration) - copy(key[5:], hash.Hash160(tokenID).BytesBE()) - return key -} - -// ToMap implements nftTokenState interface. -func (s *nameState) ToMap() *stackitem.Map { - m := s.NFTTokenState.ToMap() - m.Add(stackitem.NewByteArray([]byte("expiration")), - stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(s.Expiration)))) - return m -} - -// EncodeBinary implements io.Serializable. -func (s *nameState) EncodeBinary(w *io.BinWriter) { - stackitem.EncodeBinaryStackItem(s.ToStackItem(), w) -} - -// DecodeBinary implements io.Serializable. -func (s *nameState) DecodeBinary(r *io.BinReader) { - item := stackitem.DecodeBinaryStackItem(r) - if r.Err == nil { - r.Err = s.FromStackItem(item) - } -} - -// ToStackItem implements nftTokenState interface. -func (s *nameState) ToStackItem() stackitem.Item { - item := s.NFTTokenState.ToStackItem().(*stackitem.Struct) - exp := new(big.Int).SetUint64(uint64(s.Expiration)) - item.Append(stackitem.NewBigInteger(exp)) - if s.HasAdmin { - item.Append(stackitem.NewByteArray(s.Admin.BytesBE())) - } else { - item.Append(stackitem.Null{}) - } - return item -} - -// FromStackItem implements nftTokenState interface. -func (s *nameState) FromStackItem(item stackitem.Item) error { - err := s.NFTTokenState.FromStackItem(item) - if err != nil { - return err - } - elems := item.Value().([]stackitem.Item) - if len(elems) < 4 { - return errors.New("invalid stack item") - } - bi, err := elems[2].TryInteger() - if err != nil || !bi.IsUint64() { - return errors.New("invalid stack item") - } - - _, isNull := elems[3].(stackitem.Null) - if !isNull { - bs, err := elems[3].TryBytes() - if err != nil { - return err - } - u, err := util.Uint160DecodeBytesBE(bs) - if err != nil { - return err - } - s.Admin = u - } - s.Expiration = uint32(bi.Uint64()) - s.HasAdmin = !isNull - return nil -} - -// Helpers - -func domainFromString(name string) (string, bool) { - i := strings.LastIndexAny(name, ".") - if i < 0 { - return "", false - } - i = strings.LastIndexAny(name[:i], ".") - if i < 0 { - return name, true - } - return name[i+1:], true -} - -func toDomain(name string) string { - domain, ok := domainFromString(name) - if !ok { - panic("invalid record") - } - return domain -} - -func toRecordType(item stackitem.Item) nnsrecords.Type { - bi, err := item.TryInteger() - if err != nil || !bi.IsInt64() { - panic("invalid record type") - } - val := bi.Uint64() - if val > math.MaxUint8 { - panic("invalid record type") - } - switch rt := nnsrecords.Type(val); rt { - case nnsrecords.A, nnsrecords.CNAME, nnsrecords.TXT, nnsrecords.AAAA: - return rt - default: - panic("invalid record type") - } -} - -func toName(item stackitem.Item) string { - name := toString(item) - if !matchName(name) { - panic("invalid name") - } - return name -} - -type stringList []string - -// ToStackItem converts sl to stack item. -func (sl stringList) ToStackItem() stackitem.Item { - arr := make([]stackitem.Item, len(sl)) - for i := range sl { - arr[i] = stackitem.NewByteArray([]byte(sl[i])) - } - return stackitem.NewArray(arr) -} - -// FromStackItem converts stack item to string list. -func (sl *stringList) FromStackItem(item stackitem.Item) error { - arr, ok := item.Value().([]stackitem.Item) - if !ok { - return errors.New("invalid stack item") - } - res := make([]string, len(arr)) - for i := range res { - s, err := stackitem.ToString(arr[i]) - if err != nil { - return err - } - res[i] = s - } - *sl = res - return nil -} - -// EncodeBinary implements io.Serializable. -func (sl stringList) EncodeBinary(w *io.BinWriter) { - stackitem.EncodeBinaryStackItem(sl.ToStackItem(), w) -} - -// DecodeBinary implements io.Serializable. -func (sl *stringList) DecodeBinary(r *io.BinReader) { - item := stackitem.DecodeBinaryStackItem(r) - if r.Err == nil { - r.Err = sl.FromStackItem(item) - } -} - -func (sl stringList) index(s string) (int, bool) { - index := sort.Search(len(sl), func(i int) bool { - return sl[i] >= s - }) - return index, index < len(sl) && sl[index] == s -} - -func (sl *stringList) add(s string) bool { - index, has := sl.index(s) - if has { - return false - } - - *sl = append(*sl, "") - copy((*sl)[index+1:], (*sl)[index:]) - (*sl)[index] = s - return true -} diff --git a/pkg/core/native/nativenames/names.go b/pkg/core/native/nativenames/names.go index 74b4f9d8c..65c03c0d1 100644 --- a/pkg/core/native/nativenames/names.go +++ b/pkg/core/native/nativenames/names.go @@ -10,7 +10,6 @@ const ( Oracle = "OracleContract" Designation = "RoleManagement" Notary = "Notary" - NameService = "NameService" CryptoLib = "CryptoLib" StdLib = "StdLib" ) @@ -25,7 +24,6 @@ func IsValid(name string) bool { name == Oracle || name == Designation || name == Notary || - name == NameService || name == CryptoLib || name == StdLib } diff --git a/pkg/core/native/nonfungible.go b/pkg/core/native/nonfungible.go index 4288824f7..1f08b4ded 100644 --- a/pkg/core/native/nonfungible.go +++ b/pkg/core/native/nonfungible.go @@ -247,6 +247,8 @@ func (n *nonfungible) tokensOf(ic *interop.Context, args []stackitem.Item) stack return stackitem.NewInterop(iter) } +var _ = (*nonfungible).mint // fix unused warning + func (n *nonfungible) mint(ic *interop.Context, s nftTokenState) { key := n.getTokenKey(s.ID()) if ic.DAO.GetStorageItem(n.ID, key) != nil {