[#118] nns: Integrate 'frostfsid'
All checks were successful
DCO action / DCO (pull_request) Successful in 1m2s
Code generation / Generate wrappers (pull_request) Successful in 1m27s
Tests / Tests (pull_request) Successful in 1m35s

Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
This commit is contained in:
Alexander Chuprov 2024-10-21 14:28:08 +03:00
parent dda8ad5246
commit 11d5b17bac
Signed by: achuprov
GPG key ID: 2D916FFD803B0EDD
6 changed files with 139 additions and 21 deletions

36
common/address.go Normal file
View file

@ -0,0 +1,36 @@
package common
func HexToBytes(s []byte) []byte {
n := len(s)
if n%2 != 0 {
panic("invalid size")
}
b := make([]byte, n/2)
for i := 0; i < n; i += 2 {
c1 := charToNibble(s[i])
c2 := charToNibble(s[i+1])
b[i/2] = byte((c1 << 4) | c2)
}
return b
}
func charToNibble(c byte) byte {
if c >= '0' && c <= '9' {
return c - '0'
} else if c >= 'a' && c <= 'f' {
return c - 'a' + 10
} else if c >= 'A' && c <= 'F' {
return c - 'A' + 10
} else {
panic("fail to convert char to nibble")
}
}
func ReverseBytes(b []byte) []byte {
n := len(b)
reversed := make([]byte, n)
for i := 0; i < n; i++ {
reversed[i] = b[n-1-i]
}
return reversed
}

View file

@ -43,4 +43,4 @@ events:
permissions:
- hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
methods: ["update"]
- methods: ["onNEP11Payment"]
- methods: ["onNEP11Payment","getValueForSubjectKey"]

33
nns/frostfsid.go Normal file
View file

@ -0,0 +1,33 @@
package nns
import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/common"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
const FrostfsIDNNSName = "frostfsid.frostfs"
const (
FrostfsIDNNSTLDPermissionKey = "nns-allow-register-tld"
FrostfsIDTLDRegistrationAllowed = "allow"
FrostfsIDTLDRegistrationDisallowed = "disallow"
)
func checkFrostfsID(ctx storage.Context) bool {
frostfsIDAddress := getRecordsByType(ctx, []byte(tokenIDFromName(FrostfsIDNNSName)), FrostfsIDNNSName, TXT)
if len(frostfsIDAddress) == 0 {
return false
}
frostfsidAddr := common.ReverseBytes(common.HexToBytes([]byte(frostfsIDAddress[0])))
signers := runtime.CurrentSigners()
for _, signer := range signers {
if res := contract.Call(frostfsidAddr, "getValueForSubjectKey", contract.All, signer.Account, FrostfsIDNNSTLDPermissionKey).(string); res == FrostfsIDTLDRegistrationAllowed {
return true
}
}
return false
}

View file

@ -3,6 +3,7 @@ package nns
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
// NameState represents domain name state.
@ -22,7 +23,8 @@ func (n NameState) ensureNotExpired() {
// checkAdmin panics if script container is not signed by the domain name admin.
func (n NameState) checkAdmin() {
if runtime.CheckWitness(n.Owner) {
ctx := storage.GetContext()
if runtime.CheckWitness(n.Owner) || checkFrostfsID(ctx) {
return
}
if n.Admin == nil || !runtime.CheckWitness(n.Admin) {

View file

@ -381,7 +381,11 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
if !isValid(owner) {
panic("invalid owner")
}
if !checkFrostfsID(ctx) {
common.CheckOwnerWitness(owner)
}
runtime.BurnGas(GetPrice())
var (
tokenKey = getTokenKey([]byte(name))
@ -932,7 +936,8 @@ func checkCommittee() {
}
l := len(committee)
committeeMultisig := contract.CreateMultisigAccount(l-(l-1)/2, committee)
if committeeMultisig == nil || !runtime.CheckWitness(committeeMultisig) {
ctx := storage.GetReadOnlyContext()
if (committeeMultisig == nil || !runtime.CheckWitness(committeeMultisig)) && !checkFrostfsID(ctx) {
panic("not witnessed by committee")
}
}

View file

@ -16,6 +16,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/rpcclient/gas"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
@ -23,7 +24,7 @@ const nnsPath = "../nns"
const msPerYear = 365 * 24 * time.Hour / time.Millisecond
func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
func newNNSInvoker(t *testing.T, addRoot, needFrostfsID bool) (*neotest.ContractInvoker, *neotest.ContractInvoker) {
e := newExecutor(t)
ctr := neotest.CompileFile(t, e.CommitteeHash, nnsPath, path.Join(nnsPath, "config.yml"))
e.DeployContract(t, ctr, nil)
@ -36,11 +37,24 @@ func newNNSInvoker(t *testing.T, addRoot bool) *neotest.ContractInvoker {
"com", c.CommitteeHash,
"myemail@frostfs.info", refresh, retry, expire, ttl)
}
return c
if !needFrostfsID {
return c, nil
}
frostfdID := deployFrostFSIDContract(t, e, e.CommitteeHash)
c.Invoke(t, true, "register",
nns.FrostfsIDNNSName, c.CommitteeHash,
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
c.Invoke(t, stackitem.Null{}, "addRecord",
nns.FrostfsIDNNSName, int64(nns.TXT), frostfdID.StringLE())
return c, e.CommitteeInvoker(frostfdID)
}
func TestNNSGeneric(t *testing.T) {
c := newNNSInvoker(t, false)
c, _ := newNNSInvoker(t, false, false)
c.Invoke(t, "NNS", "symbol")
c.Invoke(t, 0, "decimals")
@ -48,7 +62,7 @@ func TestNNSGeneric(t *testing.T) {
}
func TestNNSRegisterTLD(t *testing.T) {
c := newNNSInvoker(t, false)
c, _ := newNNSInvoker(t, false, false)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
@ -91,7 +105,7 @@ func TestNNSRegisterTLD(t *testing.T) {
}
func TestNNSRegister(t *testing.T) {
c := newNNSInvoker(t, false)
c, _ := newNNSInvoker(t, false, false)
accTop := c.NewAccount(t)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
@ -243,7 +257,7 @@ func TestNNSRegister(t *testing.T) {
}
func TestDeleteDomain(t *testing.T) {
c := newNNSInvoker(t, false)
c, _ := newNNSInvoker(t, false, false)
acc1 := c.NewAccount(t)
c1 := c.WithSigners(c.Committee, acc1)
@ -306,7 +320,7 @@ func TestDeleteDomain(t *testing.T) {
}
func TestGlobalDomain(t *testing.T) {
c := newNNSInvoker(t, false)
c, _ := newNNSInvoker(t, false, false)
accTop := c.NewAccount(t)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
@ -349,7 +363,7 @@ func TestGlobalDomain(t *testing.T) {
c.InvokeFail(t, "global domain is already taken", "isAvailable", "dom.testdomain.com")
}
func TestTLDRecord(t *testing.T) {
c := newNNSInvoker(t, true)
c, _ := newNNSInvoker(t, true, false)
c.Invoke(t, stackitem.Null{}, "addRecord",
"com", int64(nns.A), "1.2.3.4")
@ -358,7 +372,7 @@ func TestTLDRecord(t *testing.T) {
}
func TestNNSRegisterMulti(t *testing.T) {
c := newNNSInvoker(t, true)
c, _ := newNNSInvoker(t, true, false)
newArgs := func(domain string, account neotest.Signer) []any {
return []any{
@ -407,7 +421,7 @@ func TestNNSRegisterMulti(t *testing.T) {
}
func TestNNSUpdateSOA(t *testing.T) {
c := newNNSInvoker(t, true)
c, _ := newNNSInvoker(t, true, false)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
c.Invoke(t, true, "register",
@ -429,7 +443,7 @@ func TestNNSUpdateSOA(t *testing.T) {
}
func TestNNSGetAllRecords(t *testing.T) {
c := newNNSInvoker(t, true)
c, _ := newNNSInvoker(t, true, false)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
c.Invoke(t, true, "register",
@ -469,7 +483,7 @@ func TestNNSGetAllRecords(t *testing.T) {
}
func TestExpiration(t *testing.T) {
c := newNNSInvoker(t, true)
c, _ := newNNSInvoker(t, true, false)
refresh, retry, expire, ttl := int64(101), int64(102), int64(msPerYear/1000*10), int64(104)
c.Invoke(t, true, "register",
@ -508,7 +522,7 @@ func TestExpiration(t *testing.T) {
}
func TestNNSSetAdmin(t *testing.T) {
c := newNNSInvoker(t, true)
c, _ := newNNSInvoker(t, true, false)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
c.Invoke(t, true, "register",
@ -527,8 +541,36 @@ func TestNNSSetAdmin(t *testing.T) {
"testdomain.com", int64(nns.TXT), "will be added")
}
func TestNNS_Frostfsid(t *testing.T) {
nnsInv, f := newNNSInvoker(t, false, true)
acc, err := wallet.NewAccount()
require.NoError(t, err)
acc.PrivateKey().PublicKey().Bytes()
nnsUserInv := nnsInv.NewInvoker(nnsInv.Hash, newSigner(t, nnsInv, acc))
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
"testdomain.com", nnsInv.CommitteeHash,
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
f.Invoke(t, stackitem.Null{}, createSubjectMethod, "", acc.PrivateKey().PublicKey().Bytes())
f.Invoke(t, stackitem.Null{}, setSubjectKVMethod, acc.PublicKey().GetScriptHash(), nns.FrostfsIDNNSTLDPermissionKey, nns.FrostfsIDTLDRegistrationAllowed)
nnsUserInv.Invoke(t, true, "register",
"testdomain.com", nnsInv.CommitteeHash,
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
f.Invoke(t, stackitem.Null{}, setSubjectKVMethod, acc.PublicKey().GetScriptHash(), nns.FrostfsIDNNSTLDPermissionKey, nns.FrostfsIDTLDRegistrationDisallowed)
nnsUserInv.InvokeFail(t, "not witnessed by committee", "register",
"testdomain.kz", nnsInv.CommitteeHash,
"myemail@frostfs.info", defaultRefresh, defaultRetry, defaultExpire, defaultTTL)
}
func TestNNSIsAvailable(t *testing.T) {
c := newNNSInvoker(t, false)
c, _ := newNNSInvoker(t, false, false)
c.Invoke(t, true, "isAvailable", "com")
@ -575,7 +617,7 @@ func TestNNSIsAvailable(t *testing.T) {
}
func TestNNSRenew(t *testing.T) {
c := newNNSInvoker(t, true)
c, _ := newNNSInvoker(t, true, false)
acc := c.NewAccount(t)
c1 := c.WithSigners(c.Committee, acc)
@ -600,7 +642,7 @@ func TestNNSRenew(t *testing.T) {
}
func TestNNSResolve(t *testing.T) {
c := newNNSInvoker(t, true)
c, _ := newNNSInvoker(t, true, false)
refresh, retry, expire, ttl := int64(101), int64(102), int64(103), int64(104)
c.Invoke(t, true, "register",
@ -617,7 +659,7 @@ func TestNNSResolve(t *testing.T) {
}
func TestNNSAndProxy(t *testing.T) {
c := newNNSInvoker(t, false)
c, _ := newNNSInvoker(t, false, false)
proxyHash := deployProxyContract(t, c.Executor)
proxySigner := neotest.NewContractSigner(proxyHash, func(*transaction.Transaction) []any { return nil })