[#118] nns: Integrate 'frostfsid'
Signed-off-by: Alexander Chuprov <a.chuprov@yadro.com>
This commit is contained in:
parent
dda8ad5246
commit
11d5b17bac
6 changed files with 139 additions and 21 deletions
36
common/address.go
Normal file
36
common/address.go
Normal 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
|
||||
}
|
|
@ -43,4 +43,4 @@ events:
|
|||
permissions:
|
||||
- hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
|
||||
methods: ["update"]
|
||||
- methods: ["onNEP11Payment"]
|
||||
- methods: ["onNEP11Payment","getValueForSubjectKey"]
|
||||
|
|
33
nns/frostfsid.go
Normal file
33
nns/frostfsid.go
Normal 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
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -381,7 +381,11 @@ func register(ctx storage.Context, name string, owner interop.Hash160, email str
|
|||
if !isValid(owner) {
|
||||
panic("invalid owner")
|
||||
}
|
||||
common.CheckOwnerWitness(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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 })
|
||||
|
||||
|
|
Loading…
Reference in a new issue