From e3625152c60ce0c6435e70d131311283b119cb9b Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Sat, 23 Oct 2021 14:28:25 +0300 Subject: [PATCH] core: move NNS test out of core Signed-off-by: Evgeniy Stratonikov --- .../tests/nonnative_name_service_test.go | 463 +++++++++++++++++ pkg/core/helper_test.go | 25 +- pkg/core/native_neo_test.go | 7 + pkg/core/nonnative_name_service_test.go | 470 ------------------ 4 files changed, 474 insertions(+), 491 deletions(-) create mode 100644 examples/nft-nd-nns/tests/nonnative_name_service_test.go delete mode 100644 pkg/core/nonnative_name_service_test.go diff --git a/examples/nft-nd-nns/tests/nonnative_name_service_test.go b/examples/nft-nd-nns/tests/nonnative_name_service_test.go new file mode 100644 index 000000000..1b23bb862 --- /dev/null +++ b/examples/nft-nd-nns/tests/nonnative_name_service_test.go @@ -0,0 +1,463 @@ +package tests + +import ( + "strings" + "testing" + + nns "github.com/nspcc-dev/neo-go/examples/nft-nd-nns" + "github.com/nspcc-dev/neo-go/pkg/compiler" + "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/neotest" + "github.com/nspcc-dev/neo-go/pkg/neotest/chain" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/nspcc-dev/neo-go/pkg/wallet" + "github.com/stretchr/testify/require" +) + +func newExecutorWithNS(t *testing.T) (*neotest.Executor, util.Uint160) { + bc, acc := chain.NewSingle(t) + e := neotest.NewExecutor(t, bc, acc) + c := neotest.CompileFile(t, e.CommitteeHash, "..", "../nns.yml") + e.DeployContract(t, c, nil) + + h, err := e.Chain.GetContractScriptHash(1) + require.NoError(t, err) + require.Equal(t, c.Hash, h) + return e, c.Hash +} + +// +//func TestNameService_Price(t *testing.T) { +// e, nsHash := newExecutorWithNS(t) +// +// testGetSet(t, e.Chain, nsHash, "Price", +// defaultNameServiceDomainPrice, 0, 10000_00000000) +//} + +func TestNonfungible(t *testing.T) { + e, nsHash := newExecutorWithNS(t) + + acc := e.NewAccount(t) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, acc, "symbol", "NNS") + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, acc, "decimals", 0) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, acc, "totalSupply", 0) +} + +func TestAddRoot(t *testing.T) { + e, nsHash := newExecutorWithNS(t) + + t.Run("invalid format", func(t *testing.T) { + testNameServiceInvoke(t, e, nsHash, "addRoot", nil, "") + }) + t.Run("not signed by committee", func(t *testing.T) { + acc := e.NewAccount(t) + tx := e.PrepareInvoke(t, acc, nsHash, "addRoot", "some") + e.AddBlock(t, tx) + e.CheckFault(t, tx.Hash(), "not witnessed by committee") + }) + + testNameServiceInvoke(t, e, nsHash, "addRoot", stackitem.Null{}, "some") + t.Run("already exists", func(t *testing.T) { + testNameServiceInvoke(t, e, nsHash, "addRoot", nil, "some") + }) +} + +func TestExpiration(t *testing.T) { + e, nsHash := newExecutorWithNS(t) + bc := e.Chain + + acc := e.NewAccount(t) + + testNameServiceInvoke(t, e, nsHash, "addRoot", stackitem.Null{}, "com") + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, acc, "register", + true, "first.com", acc.Contract.ScriptHash()) + + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, acc, + "setRecord", stackitem.Null{}, "first.com", int64(nns.TXT), "sometext") + b1 := e.TopBlock(t) + + tx := e.PrepareInvoke(t, acc, nsHash, "register", "second.com", acc.Contract.ScriptHash()) + b2 := e.NewBlock(t, tx) + b2.Index = b1.Index + 1 + b2.PrevHash = b1.Hash() + b2.Timestamp = b1.Timestamp + 10000 + require.NoError(t, bc.AddBlock(e.SignBlock(b2))) + e.CheckHalt(t, tx.Hash()) + + tx = e.PrepareInvoke(t, acc, nsHash, "isAvailable", "first.com") + b3 := e.NewBlock(t, tx) + b3.Index = b2.Index + 1 + b3.PrevHash = b2.Hash() + b3.Timestamp = b1.Timestamp + (millisecondsInYear + 1) + require.NoError(t, bc.AddBlock(e.SignBlock(b3))) + e.CheckHalt(t, tx.Hash(), stackitem.NewBool(true)) + + tx = e.PrepareInvoke(t, acc, nsHash, "isAvailable", "second.com") + b4 := e.NewBlock(t, tx) + b4.Index = b3.Index + 1 + b4.PrevHash = b3.Hash() + b4.Timestamp = b3.Timestamp + 1000 + require.NoError(t, bc.AddBlock(e.SignBlock(b4))) + e.CheckHalt(t, tx.Hash(), stackitem.NewBool(false)) + + tx = e.PrepareInvoke(t, acc, nsHash, "getRecord", "first.com", int64(nns.TXT)) + b5 := e.NewBlock(t, tx) + b5.Index = b4.Index + 1 + b5.PrevHash = b4.Hash() + b5.Timestamp = b4.Timestamp + 1000 + require.NoError(t, bc.AddBlock(e.SignBlock(b5))) + e.CheckFault(t, tx.Hash(), "name has expired") +} + +const millisecondsInYear = 365 * 24 * 3600 * 1000 + +func TestRegisterAndRenew(t *testing.T) { + e, nsHash := newExecutorWithNS(t) + + testNameServiceInvoke(t, e, nsHash, "isAvailable", nil, "neo.com") + testNameServiceInvoke(t, e, nsHash, "addRoot", stackitem.Null{}, "org") + testNameServiceInvoke(t, e, nsHash, "isAvailable", nil, "neo.com") + testNameServiceInvoke(t, e, nsHash, "addRoot", stackitem.Null{}, "com") + testNameServiceInvoke(t, e, nsHash, "isAvailable", true, "neo.com") + testNameServiceInvoke(t, e, nsHash, "register", nil, "neo.org", e.CommitteeHash) + testNameServiceInvoke(t, e, nsHash, "register", nil, "docs.neo.org", e.CommitteeHash) + testNameServiceInvoke(t, e, nsHash, "register", nil, "\nneo.com'", e.CommitteeHash) + testNameServiceInvoke(t, e, nsHash, "register", nil, "neo.com\n", e.CommitteeHash) + testNameServiceInvoke(t, e, nsHash, "register", nil, "neo.com", e.CommitteeHash) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceDomainPrice, e.Committee, "register", + nil, "neo.com", e.CommitteeHash) + + testNameServiceInvoke(t, e, nsHash, "isAvailable", true, "neo.com") + testNameServiceInvoke(t, e, nsHash, "balanceOf", 0, e.CommitteeHash) + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, e.Committee, "register", + true, "neo.com", e.CommitteeHash) + topBlock := e.TopBlock(t) + expectedExpiration := topBlock.Timestamp + millisecondsInYear + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, e.Committee, "register", + false, "neo.com", e.CommitteeHash) + testNameServiceInvoke(t, e, nsHash, "isAvailable", false, "neo.com") + + props := stackitem.NewMap() + props.Add(stackitem.Make("name"), stackitem.Make("neo.com")) + props.Add(stackitem.Make("expiration"), stackitem.Make(expectedExpiration)) + testNameServiceInvoke(t, e, nsHash, "properties", props, "neo.com") + testNameServiceInvoke(t, e, nsHash, "balanceOf", 1, e.CommitteeHash) + testNameServiceInvoke(t, e, nsHash, "ownerOf", e.CommitteeHash.BytesBE(), []byte("neo.com")) + + t.Run("invalid token ID", func(t *testing.T) { + testNameServiceInvoke(t, e, nsHash, "properties", nil, "not.exists") + testNameServiceInvoke(t, e, nsHash, "ownerOf", nil, "not.exists") + testNameServiceInvoke(t, e, nsHash, "properties", nil, []interface{}{}) + testNameServiceInvoke(t, e, nsHash, "ownerOf", nil, []interface{}{}) + }) + + // Renew + expectedExpiration += millisecondsInYear + testNameServiceInvokeAux(t, e, nsHash, 100_0000_0000, e.Committee, "renew", expectedExpiration, "neo.com") + + props.Add(stackitem.Make("expiration"), stackitem.Make(expectedExpiration)) + testNameServiceInvoke(t, e, nsHash, "properties", props, "neo.com") +} + +func TestSetGetRecord(t *testing.T) { + e, nsHash := newExecutorWithNS(t) + + acc := e.NewAccount(t) + testNameServiceInvoke(t, e, nsHash, "addRoot", stackitem.Null{}, "com") + + t.Run("set before register", func(t *testing.T) { + testNameServiceInvoke(t, e, nsHash, "setRecord", nil, "neo.com", int64(nns.TXT), "sometext") + }) + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, e.Committee, "register", + true, "neo.com", e.CommitteeHash) + t.Run("invalid parameters", func(t *testing.T) { + testNameServiceInvoke(t, e, nsHash, "setRecord", nil, "neo.com", int64(0xFF), "1.2.3.4") + testNameServiceInvoke(t, e, nsHash, "setRecord", nil, "neo.com", int64(nns.A), "not.an.ip.address") + }) + t.Run("invalid witness", func(t *testing.T) { + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, acc, "setRecord", nil, + "neo.com", int64(nns.A), "1.2.3.4") + }) + testNameServiceInvoke(t, e, nsHash, "getRecord", stackitem.Null{}, "neo.com", int64(nns.A)) + testNameServiceInvoke(t, e, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.A), "1.2.3.4") + testNameServiceInvoke(t, e, nsHash, "getRecord", "1.2.3.4", "neo.com", int64(nns.A)) + testNameServiceInvoke(t, e, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.A), "1.2.3.4") + testNameServiceInvoke(t, e, nsHash, "getRecord", "1.2.3.4", "neo.com", int64(nns.A)) + testNameServiceInvoke(t, e, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.AAAA), "2001:0201:1f1f:0000:0000:0100:11a0:11df") + testNameServiceInvoke(t, e, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.CNAME), "nspcc.ru") + testNameServiceInvoke(t, e, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.TXT), "sometext") + + // Delete record. + t.Run("invalid witness", func(t *testing.T) { + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, acc, "setRecord", nil, + "neo.com", int64(nns.CNAME)) + }) + testNameServiceInvoke(t, e, nsHash, "getRecord", "nspcc.ru", "neo.com", int64(nns.CNAME)) + testNameServiceInvoke(t, e, nsHash, "deleteRecord", stackitem.Null{}, "neo.com", int64(nns.CNAME)) + testNameServiceInvoke(t, e, nsHash, "getRecord", stackitem.Null{}, "neo.com", int64(nns.CNAME)) + testNameServiceInvoke(t, e, nsHash, "getRecord", "1.2.3.4", "neo.com", int64(nns.A)) + + t.Run("SetRecord_compatibility", func(t *testing.T) { + // tests are got from the NNS C# implementation and changed accordingly to non-native implementation behaviour + testCases := []struct { + Type nns.RecordType + Name string + ShouldFail bool + }{ + {Type: nns.A, Name: "0.0.0.0", ShouldFail: true}, + {Type: nns.A, Name: "1.1.0.1"}, + {Type: nns.A, Name: "10.10.10.10", ShouldFail: true}, + {Type: nns.A, Name: "255.255.255.255", ShouldFail: true}, + {Type: nns.A, Name: "192.168.1.1", ShouldFail: true}, + {Type: nns.A, Name: "1a", ShouldFail: true}, + {Type: nns.A, Name: "256.0.0.0", ShouldFail: true}, + {Type: nns.A, Name: "01.01.01.01", ShouldFail: true}, + {Type: nns.A, Name: "00.0.0.0", ShouldFail: true}, + {Type: nns.A, Name: "0.0.0.-1", ShouldFail: true}, + {Type: nns.A, Name: "0.0.0.0.1", ShouldFail: true}, + {Type: nns.A, Name: "11111111.11111111.11111111.11111111", ShouldFail: true}, + {Type: nns.A, Name: "11111111.11111111.11111111.11111111", ShouldFail: true}, + {Type: nns.A, Name: "ff.ff.ff.ff", ShouldFail: true}, + {Type: nns.A, Name: "0.0.256", ShouldFail: true}, + {Type: nns.A, Name: "0.0.0", ShouldFail: true}, + {Type: nns.A, Name: "0.257", ShouldFail: true}, + {Type: nns.A, Name: "1.1", ShouldFail: true}, + {Type: nns.A, Name: "257", ShouldFail: true}, + {Type: nns.A, Name: "1", ShouldFail: true}, + // {2000} & {2001} & ]2002, 3ffe[ & {3fff} are valid values for IPv6 fragment0 + {Type: nns.AAAA, Name: "2002:db8::8:800:200c:417a", ShouldFail: true}, + {Type: nns.AAAA, Name: "3ffd:1b8::8:800:200c:417a"}, + {Type: nns.AAAA, Name: "3ffd::101"}, + {Type: nns.AAAA, Name: "2003::1"}, + {Type: nns.AAAA, Name: "2003::"}, + {Type: nns.AAAA, Name: "2002:db8:0:0:8:800:200c:417a", ShouldFail: true}, + {Type: nns.AAAA, Name: "3ffd:db8:0:0:8:800:200c:417a"}, + {Type: nns.AAAA, Name: "3ffd:0:0:0:0:0:0:101"}, + {Type: nns.AAAA, Name: "2002:0:0:0:0:0:0:101", ShouldFail: true}, + {Type: nns.AAAA, Name: "3ffd:0:0:0:0:0:0:101"}, + {Type: nns.AAAA, Name: "2001:200:0:0:0:0:0:1"}, + {Type: nns.AAAA, Name: "0:0:0:0:0:0:0:1", ShouldFail: true}, + {Type: nns.AAAA, Name: "2002:0:0:0:0:0:0:1", ShouldFail: true}, + {Type: nns.AAAA, Name: "2001:200:0:0:0:0:0:0"}, + {Type: nns.AAAA, Name: "2002:0:0:0:0:0:0:0", ShouldFail: true}, + {Type: nns.AAAA, Name: "2002:DB8::8:800:200C:417A", ShouldFail: true}, + {Type: nns.AAAA, Name: "3FFD:1B8::8:800:200C:417A"}, + {Type: nns.AAAA, Name: "3FFD::101"}, + {Type: nns.AAAA, Name: "3fFD::101"}, + {Type: nns.AAAA, Name: "2002:DB8:0:0:8:800:200C:417A", ShouldFail: true}, + {Type: nns.AAAA, Name: "3FFD:DB8:0:0:8:800:200C:417A"}, + {Type: nns.AAAA, Name: "3FFD:0:0:0:0:0:0:101"}, + {Type: nns.AAAA, Name: "3FFD::ffff:1.01.1.01", ShouldFail: true}, + {Type: nns.AAAA, Name: "2001:DB8:0:0:8:800:200C:4Z", ShouldFail: true}, + {Type: nns.AAAA, Name: "2001::13.1.68.3", ShouldFail: true}, + } + for _, testCase := range testCases { + var expected interface{} + if testCase.ShouldFail { + expected = nil + } else { + expected = stackitem.Null{} + } + t.Run(testCase.Name, func(t *testing.T) { + testNameServiceInvoke(t, e, nsHash, "setRecord", expected, "neo.com", int64(testCase.Type), testCase.Name) + }) + } + }) +} + +func TestSetAdmin(t *testing.T) { + e, nsHash := newExecutorWithNS(t) + + owner := e.NewAccount(t) + admin := e.NewAccount(t) + guest := e.NewAccount(t) + testNameServiceInvoke(t, e, nsHash, "addRoot", stackitem.Null{}, "com") + + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, owner, "register", true, + "neo.com", owner.PrivateKey().GetScriptHash()) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, guest, "setAdmin", nil, + "neo.com", admin.PrivateKey().GetScriptHash()) + + // Must be witnessed by both owner and admin. + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, owner, "setAdmin", nil, + "neo.com", admin.PrivateKey().GetScriptHash()) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, admin, "setAdmin", nil, + "neo.com", admin.PrivateKey().GetScriptHash()) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, []*wallet.Account{owner, admin}, + "setAdmin", stackitem.Null{}, + "neo.com", admin.PrivateKey().GetScriptHash()) + + t.Run("set and delete by admin", func(t *testing.T) { + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, admin, "setRecord", stackitem.Null{}, + "neo.com", int64(nns.TXT), "sometext") + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, guest, "deleteRecord", nil, + "neo.com", int64(nns.TXT)) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, admin, "deleteRecord", stackitem.Null{}, + "neo.com", int64(nns.TXT)) + }) + + t.Run("set admin to null", func(t *testing.T) { + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, admin, "setRecord", stackitem.Null{}, + "neo.com", int64(nns.TXT), "sometext") + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, owner, "setAdmin", stackitem.Null{}, + "neo.com", nil) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, admin, "deleteRecord", nil, + "neo.com", int64(nns.TXT)) + }) +} + +func TestTransfer(t *testing.T) { + e, nsHash := newExecutorWithNS(t) + + from := e.NewAccount(t) + to := e.NewAccount(t) + + testNameServiceInvoke(t, e, nsHash, "addRoot", stackitem.Null{}, "com") + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, from, "register", + true, "neo.com", from.PrivateKey().GetScriptHash()) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, from, "setRecord", stackitem.Null{}, + "neo.com", int64(nns.A), "1.2.3.4") + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, from, "transfer", + nil, to.Contract.ScriptHash().BytesBE(), []byte("not.exists"), nil) + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, e.Committee, "transfer", + false, to.Contract.ScriptHash().BytesBE(), []byte("neo.com"), nil) + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, from, "transfer", + true, to.Contract.ScriptHash().BytesBE(), []byte("neo.com"), nil) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, from, "totalSupply", 1) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, from, "ownerOf", + to.Contract.ScriptHash().BytesBE(), []byte("neo.com")) + + // without onNEP11Transfer + c := neotest.CompileSource(t, e.CommitteeHash, + strings.NewReader(`package foo + func Main() int { return 0 }`), + &compiler.Options{Name: "foo"}) + e.DeployContract(t, c, nil) + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, to, "transfer", + nil, c.Hash.BytesBE(), []byte("neo.com"), nil) + + // with onNEP11Transfer + c = neotest.CompileSource(t, e.CommitteeHash, + strings.NewReader(`package foo + import "github.com/nspcc-dev/neo-go/pkg/interop" + func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data interface{}) {}`), + &compiler.Options{Name: "foo"}) + e.DeployContract(t, c, nil) + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, to, "transfer", + true, c.Hash.BytesBE(), []byte("neo.com"), nil) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, from, "totalSupply", 1) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, from, "ownerOf", + c.Hash.BytesBE(), []byte("neo.com")) +} + +func TestTokensOf(t *testing.T) { + e, nsHash := newExecutorWithNS(t) + + acc1 := e.NewAccount(t) + acc2 := e.NewAccount(t) + + testNameServiceInvoke(t, e, nsHash, "addRoot", stackitem.Null{}, "com") + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, acc1, "register", + true, "neo.com", acc1.PrivateKey().GetScriptHash()) + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, acc2, "register", + true, "nspcc.com", acc2.PrivateKey().GetScriptHash()) + + testTokensOf(t, e, nsHash, [][]byte{[]byte("neo.com")}, acc1.Contract.ScriptHash().BytesBE()) + testTokensOf(t, e, nsHash, [][]byte{[]byte("nspcc.com")}, acc2.Contract.ScriptHash().BytesBE()) + testTokensOf(t, e, nsHash, [][]byte{[]byte("neo.com"), []byte("nspcc.com")}) + testTokensOf(t, e, nsHash, [][]byte{}, util.Uint160{}.BytesBE()) // empty hash is a valid hash still +} + +func testTokensOf(t *testing.T, e *neotest.Executor, nsHash util.Uint160, result [][]byte, args ...interface{}) { + method := "tokensOf" + if len(args) == 0 { + method = "tokens" + } + w := io.NewBufBinWriter() + emit.AppCall(w.BinWriter, nsHash, method, callflag.All, args...) + for range result { + emit.Opcodes(w.BinWriter, opcode.DUP) + emit.Syscall(w.BinWriter, interopnames.SystemIteratorNext) + emit.Opcodes(w.BinWriter, opcode.ASSERT) + + emit.Opcodes(w.BinWriter, opcode.DUP) + emit.Syscall(w.BinWriter, interopnames.SystemIteratorValue) + emit.Opcodes(w.BinWriter, opcode.SWAP) + } + emit.Opcodes(w.BinWriter, opcode.DROP) + emit.Int(w.BinWriter, int64(len(result))) + emit.Opcodes(w.BinWriter, opcode.PACK) + require.NoError(t, w.Err) + script := w.Bytes() + tx := transaction.New(script, defaultNameServiceSysfee) + tx.ValidUntilBlock = e.Chain.BlockHeight() + 1 + v, err := neotest.TestInvoke(e.Chain, tx) + if result == nil { + require.Error(t, err) + return + } + require.NoError(t, err) + arr := make([]stackitem.Item, 0, len(result)) + for i := len(result) - 1; i >= 0; i-- { + arr = append(arr, stackitem.Make(result[i])) + } + require.Equal(t, stackitem.NewArray(arr), v.Estack().Pop().Item()) +} + +func TestResolve(t *testing.T) { + e, nsHash := newExecutorWithNS(t) + + acc := e.NewAccount(t) + + testNameServiceInvoke(t, e, nsHash, "addRoot", stackitem.Null{}, "com") + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, acc, "register", + true, "neo.com", acc.PrivateKey().GetScriptHash()) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, acc, "setRecord", stackitem.Null{}, + "neo.com", int64(nns.A), "1.2.3.4") + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, acc, "setRecord", stackitem.Null{}, + "neo.com", int64(nns.CNAME), "alias.com") + + testNameServiceInvokeAux(t, e, nsHash, defaultRegisterSysfee, acc, "register", + true, "alias.com", acc.PrivateKey().GetScriptHash()) + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, acc, "setRecord", stackitem.Null{}, + "alias.com", int64(nns.TXT), "sometxt") + + testNameServiceInvoke(t, e, nsHash, "resolve", "1.2.3.4", + "neo.com", int64(nns.A)) + testNameServiceInvoke(t, e, nsHash, "resolve", "alias.com", + "neo.com", int64(nns.CNAME)) + testNameServiceInvoke(t, e, nsHash, "resolve", "sometxt", + "neo.com", int64(nns.TXT)) + testNameServiceInvoke(t, e, nsHash, "resolve", stackitem.Null{}, + "neo.com", int64(nns.AAAA)) +} + +const ( + defaultNameServiceDomainPrice = 10_0000_0000 + defaultNameServiceSysfee = 6000_0000 + defaultRegisterSysfee = 10_0000_0000 + defaultNameServiceDomainPrice +) + +func testNameServiceInvoke(t *testing.T, e *neotest.Executor, nsHash util.Uint160, method string, result interface{}, args ...interface{}) { + testNameServiceInvokeAux(t, e, nsHash, defaultNameServiceSysfee, e.Committee, method, result, args...) +} + +func testNameServiceInvokeAux(t *testing.T, e *neotest.Executor, nsHash util.Uint160, sysfee int64, signer interface{}, method string, result interface{}, args ...interface{}) { + if sysfee < 0 { + sysfee = defaultNameServiceSysfee + } + tx := e.PrepareInvokeNoSign(t, nsHash, method, args...) + e.SignTx(t, tx, sysfee, signer) + e.AddBlock(t, tx) + if result == nil { + e.CheckFault(t, tx.Hash(), "") + } else { + e.CheckHalt(t, tx.Hash(), stackitem.Make(result)) + } +} diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 182f88544..51b03a157 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -52,27 +52,10 @@ var neoOwner = testchain.MultisigScriptHash() // examplesPrefix is a prefix of the example smart-contracts. const examplesPrefix = "../../examples/" -// newTestChainWithNS should be called before newBlock invocation to properly setup -// global state. -func newTestChainWithNS(t *testing.T) (*Blockchain, util.Uint160) { - bc := newTestChainWithCustomCfg(t, nil) - acc := newAccountWithGAS(t, bc) - // Push NameService contract into the chain. - nsPath := examplesPrefix + "nft-nd-nns/" - nsConfigPath := nsPath + "nns.yml" - txDeploy4, _ := newDeployTx(t, bc, acc.PrivateKey().GetScriptHash(), nsPath, nsPath, &nsConfigPath) - txDeploy4.Nonce = 123 - txDeploy4.ValidUntilBlock = bc.BlockHeight() + 1 - require.NoError(t, addNetworkFee(bc, txDeploy4, acc)) - require.NoError(t, acc.SignTx(testchain.Network(), txDeploy4)) - b := bc.newBlock(txDeploy4) - require.NoError(t, bc.AddBlock(b)) - checkTxHalt(t, bc, txDeploy4.Hash()) - - h, err := bc.GetContractScriptHash(1) - require.NoError(t, err) - return bc, h -} +const ( + defaultNameServiceDomainPrice = 10_0000_0000 + defaultNameServiceSysfee = 6000_0000 +) // newTestChain should be called before newBlock invocation to properly setup // global state. diff --git a/pkg/core/native_neo_test.go b/pkg/core/native_neo_test.go index 4d2fbf2f1..a31cf80a5 100644 --- a/pkg/core/native_neo_test.go +++ b/pkg/core/native_neo_test.go @@ -469,3 +469,10 @@ func TestNEO_TransferZeroWithNonZeroBalance(t *testing.T) { check(t, false) }) } + +func newAccountWithGAS(t *testing.T, bc *Blockchain) *wallet.Account { + acc, err := wallet.NewAccount() + require.NoError(t, err) + transferTokenFromMultisigAccount(t, bc, acc.PrivateKey().GetScriptHash(), bc.contracts.GAS.Hash, 1000_00000000) + return acc +} diff --git a/pkg/core/nonnative_name_service_test.go b/pkg/core/nonnative_name_service_test.go deleted file mode 100644 index 0a071001c..000000000 --- a/pkg/core/nonnative_name_service_test.go +++ /dev/null @@ -1,470 +0,0 @@ -package core - -import ( - "testing" - - nns "github.com/nspcc-dev/neo-go/examples/nft-nd-nns" - "github.com/nspcc-dev/neo-go/internal/testchain" - "github.com/nspcc-dev/neo-go/pkg/core/block" - "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" - "github.com/nspcc-dev/neo-go/pkg/core/transaction" - "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" - "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" - "github.com/nspcc-dev/neo-go/pkg/vm/opcode" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "github.com/nspcc-dev/neo-go/pkg/wallet" - "github.com/stretchr/testify/require" -) - -func TestNameService_Price(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - testGetSet(t, bc, nsHash, "Price", - defaultNameServiceDomainPrice, 0, 10000_00000000) -} - -func TestNonfungible(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - acc := newAccountWithGAS(t, bc) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, acc, "symbol", "NNS") - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, acc, "decimals", 0) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, acc, "totalSupply", 0) -} - -func TestAddRoot(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - transferFundsToCommittee(t, bc) - - t.Run("invalid format", func(t *testing.T) { - testNameServiceInvoke(t, bc, nsHash, "addRoot", nil, "") - }) - t.Run("not signed by committee", func(t *testing.T) { - aer, err := invokeContractMethod(bc, 1000_0000, nsHash, "addRoot", "some") - require.NoError(t, err) - checkFAULTState(t, aer) - }) - - testNameServiceInvoke(t, bc, nsHash, "addRoot", stackitem.Null{}, "some") - t.Run("already exists", func(t *testing.T) { - testNameServiceInvoke(t, bc, nsHash, "addRoot", nil, "some") - }) -} - -func TestExpiration(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - transferFundsToCommittee(t, bc) - acc := newAccountWithGAS(t, bc) - - testNameServiceInvoke(t, bc, nsHash, "addRoot", stackitem.Null{}, "com") - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, acc, "register", - true, "first.com", acc.Contract.ScriptHash()) - - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, acc, - "setRecord", stackitem.Null{}, "first.com", int64(nns.TXT), "sometext") - b1 := bc.topBlock.Load().(*block.Block) - - tx, err := prepareContractMethodInvokeGeneric(bc, defaultRegisterSysfee, nsHash, - "register", acc, "second.com", acc.Contract.ScriptHash()) - require.NoError(t, err) - b2 := newBlockCustom(bc.GetConfig(), func(b *block.Block) { - b.Index = b1.Index + 1 - b.PrevHash = b1.Hash() - b.Timestamp = b1.Timestamp + 10000 - }, tx) - require.NoError(t, bc.AddBlock(b2)) - checkTxHalt(t, bc, tx.Hash()) - - tx, err = prepareContractMethodInvokeGeneric(bc, defaultNameServiceSysfee, nsHash, - "isAvailable", acc, "first.com") - require.NoError(t, err) - b3 := newBlockCustom(bc.GetConfig(), func(b *block.Block) { - b.Index = b2.Index + 1 - b.PrevHash = b2.Hash() - b.Timestamp = b1.Timestamp + (millisecondsInYear + 1) - }, tx) - require.NoError(t, bc.AddBlock(b3)) - aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application) - require.NoError(t, err) - checkResult(t, &aer[0], stackitem.NewBool(true)) - - tx, err = prepareContractMethodInvokeGeneric(bc, defaultNameServiceSysfee, nsHash, - "isAvailable", acc, "second.com") - require.NoError(t, err) - b4 := newBlockCustom(bc.GetConfig(), func(b *block.Block) { - b.Index = b3.Index + 1 - b.PrevHash = b3.Hash() - b.Timestamp = b3.Timestamp + 1000 - }, tx) - require.NoError(t, bc.AddBlock(b4)) - aer, err = bc.GetAppExecResults(tx.Hash(), trigger.Application) - require.NoError(t, err) - checkResult(t, &aer[0], stackitem.NewBool(false)) - - tx, err = prepareContractMethodInvokeGeneric(bc, defaultNameServiceSysfee, nsHash, - "getRecord", acc, "first.com", int64(nns.TXT)) - require.NoError(t, err) - b5 := newBlockCustom(bc.GetConfig(), func(b *block.Block) { - b.Index = b4.Index + 1 - b.PrevHash = b4.Hash() - b.Timestamp = b4.Timestamp + 1000 - }, tx) - require.NoError(t, bc.AddBlock(b5)) - aer, err = bc.GetAppExecResults(tx.Hash(), trigger.Application) - require.NoError(t, err) - checkFAULTState(t, &aer[0]) // name has expired (panic) -} - -const millisecondsInYear = 365 * 24 * 3600 * 1000 - -func TestRegisterAndRenew(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - transferFundsToCommittee(t, bc) - - testNameServiceInvoke(t, bc, nsHash, "isAvailable", nil, "neo.com") - testNameServiceInvoke(t, bc, nsHash, "addRoot", stackitem.Null{}, "org") - testNameServiceInvoke(t, bc, nsHash, "isAvailable", nil, "neo.com") - testNameServiceInvoke(t, bc, nsHash, "addRoot", stackitem.Null{}, "com") - testNameServiceInvoke(t, bc, nsHash, "isAvailable", true, "neo.com") - testNameServiceInvoke(t, bc, nsHash, "register", nil, "neo.org", testchain.CommitteeScriptHash()) - testNameServiceInvoke(t, bc, nsHash, "register", nil, "docs.neo.org", testchain.CommitteeScriptHash()) - testNameServiceInvoke(t, bc, nsHash, "register", nil, "\nneo.com'", testchain.CommitteeScriptHash()) - testNameServiceInvoke(t, bc, nsHash, "register", nil, "neo.com\n", testchain.CommitteeScriptHash()) - testNameServiceInvoke(t, bc, nsHash, "register", nil, "neo.com", testchain.CommitteeScriptHash()) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceDomainPrice, true, "register", - nil, "neo.com", testchain.CommitteeScriptHash()) - - testNameServiceInvoke(t, bc, nsHash, "isAvailable", true, "neo.com") - testNameServiceInvoke(t, bc, nsHash, "balanceOf", 0, testchain.CommitteeScriptHash()) - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, true, "register", - true, "neo.com", testchain.CommitteeScriptHash()) - topBlock := bc.topBlock.Load().(*block.Block) - expectedExpiration := topBlock.Timestamp + millisecondsInYear - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, true, "register", - false, "neo.com", testchain.CommitteeScriptHash()) - testNameServiceInvoke(t, bc, nsHash, "isAvailable", false, "neo.com") - - props := stackitem.NewMap() - props.Add(stackitem.Make("name"), stackitem.Make("neo.com")) - props.Add(stackitem.Make("expiration"), stackitem.Make(expectedExpiration)) - testNameServiceInvoke(t, bc, nsHash, "properties", props, "neo.com") - testNameServiceInvoke(t, bc, nsHash, "balanceOf", 1, testchain.CommitteeScriptHash()) - testNameServiceInvoke(t, bc, nsHash, "ownerOf", testchain.CommitteeScriptHash().BytesBE(), []byte("neo.com")) - - t.Run("invalid token ID", func(t *testing.T) { - testNameServiceInvoke(t, bc, nsHash, "properties", nil, "not.exists") - testNameServiceInvoke(t, bc, nsHash, "ownerOf", nil, "not.exists") - testNameServiceInvoke(t, bc, nsHash, "properties", nil, []interface{}{}) - testNameServiceInvoke(t, bc, nsHash, "ownerOf", nil, []interface{}{}) - }) - - // Renew - expectedExpiration += millisecondsInYear - testNameServiceInvokeAux(t, bc, nsHash, 100_0000_0000, true, "renew", expectedExpiration, "neo.com") - - props.Add(stackitem.Make("expiration"), stackitem.Make(expectedExpiration)) - testNameServiceInvoke(t, bc, nsHash, "properties", props, "neo.com") -} - -func TestSetGetRecord(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - transferFundsToCommittee(t, bc) - acc := newAccountWithGAS(t, bc) - testNameServiceInvoke(t, bc, nsHash, "addRoot", stackitem.Null{}, "com") - - t.Run("set before register", func(t *testing.T) { - testNameServiceInvoke(t, bc, nsHash, "setRecord", nil, "neo.com", int64(nns.TXT), "sometext") - }) - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, true, "register", - true, "neo.com", testchain.CommitteeScriptHash()) - t.Run("invalid parameters", func(t *testing.T) { - testNameServiceInvoke(t, bc, nsHash, "setRecord", nil, "neo.com", int64(0xFF), "1.2.3.4") - testNameServiceInvoke(t, bc, nsHash, "setRecord", nil, "neo.com", int64(nns.A), "not.an.ip.address") - }) - t.Run("invalid witness", func(t *testing.T) { - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, acc, "setRecord", nil, - "neo.com", int64(nns.A), "1.2.3.4") - }) - testNameServiceInvoke(t, bc, nsHash, "getRecord", stackitem.Null{}, "neo.com", int64(nns.A)) - testNameServiceInvoke(t, bc, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.A), "1.2.3.4") - testNameServiceInvoke(t, bc, nsHash, "getRecord", "1.2.3.4", "neo.com", int64(nns.A)) - testNameServiceInvoke(t, bc, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.A), "1.2.3.4") - testNameServiceInvoke(t, bc, nsHash, "getRecord", "1.2.3.4", "neo.com", int64(nns.A)) - testNameServiceInvoke(t, bc, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.AAAA), "2001:0201:1f1f:0000:0000:0100:11a0:11df") - testNameServiceInvoke(t, bc, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.CNAME), "nspcc.ru") - testNameServiceInvoke(t, bc, nsHash, "setRecord", stackitem.Null{}, "neo.com", int64(nns.TXT), "sometext") - - // Delete record. - t.Run("invalid witness", func(t *testing.T) { - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, acc, "setRecord", nil, - "neo.com", int64(nns.CNAME)) - }) - testNameServiceInvoke(t, bc, nsHash, "getRecord", "nspcc.ru", "neo.com", int64(nns.CNAME)) - testNameServiceInvoke(t, bc, nsHash, "deleteRecord", stackitem.Null{}, "neo.com", int64(nns.CNAME)) - testNameServiceInvoke(t, bc, nsHash, "getRecord", stackitem.Null{}, "neo.com", int64(nns.CNAME)) - testNameServiceInvoke(t, bc, nsHash, "getRecord", "1.2.3.4", "neo.com", int64(nns.A)) - - t.Run("SetRecord_compatibility", func(t *testing.T) { - // tests are got from the NNS C# implementation and changed accordingly to non-native implementation behaviour - testCases := []struct { - Type nns.RecordType - Name string - ShouldFail bool - }{ - {Type: nns.A, Name: "0.0.0.0", ShouldFail: true}, - {Type: nns.A, Name: "1.1.0.1"}, - {Type: nns.A, Name: "10.10.10.10", ShouldFail: true}, - {Type: nns.A, Name: "255.255.255.255", ShouldFail: true}, - {Type: nns.A, Name: "192.168.1.1", ShouldFail: true}, - {Type: nns.A, Name: "1a", ShouldFail: true}, - {Type: nns.A, Name: "256.0.0.0", ShouldFail: true}, - {Type: nns.A, Name: "01.01.01.01", ShouldFail: true}, - {Type: nns.A, Name: "00.0.0.0", ShouldFail: true}, - {Type: nns.A, Name: "0.0.0.-1", ShouldFail: true}, - {Type: nns.A, Name: "0.0.0.0.1", ShouldFail: true}, - {Type: nns.A, Name: "11111111.11111111.11111111.11111111", ShouldFail: true}, - {Type: nns.A, Name: "11111111.11111111.11111111.11111111", ShouldFail: true}, - {Type: nns.A, Name: "ff.ff.ff.ff", ShouldFail: true}, - {Type: nns.A, Name: "0.0.256", ShouldFail: true}, - {Type: nns.A, Name: "0.0.0", ShouldFail: true}, - {Type: nns.A, Name: "0.257", ShouldFail: true}, - {Type: nns.A, Name: "1.1", ShouldFail: true}, - {Type: nns.A, Name: "257", ShouldFail: true}, - {Type: nns.A, Name: "1", ShouldFail: true}, - // {2000} & {2001} & ]2002, 3ffe[ & {3fff} are valid values for IPv6 fragment0 - {Type: nns.AAAA, Name: "2002:db8::8:800:200c:417a", ShouldFail: true}, - {Type: nns.AAAA, Name: "3ffd:1b8::8:800:200c:417a"}, - {Type: nns.AAAA, Name: "3ffd::101"}, - {Type: nns.AAAA, Name: "2003::1"}, - {Type: nns.AAAA, Name: "2003::"}, - {Type: nns.AAAA, Name: "2002:db8:0:0:8:800:200c:417a", ShouldFail: true}, - {Type: nns.AAAA, Name: "3ffd:db8:0:0:8:800:200c:417a"}, - {Type: nns.AAAA, Name: "3ffd:0:0:0:0:0:0:101"}, - {Type: nns.AAAA, Name: "2002:0:0:0:0:0:0:101", ShouldFail: true}, - {Type: nns.AAAA, Name: "3ffd:0:0:0:0:0:0:101"}, - {Type: nns.AAAA, Name: "2001:200:0:0:0:0:0:1"}, - {Type: nns.AAAA, Name: "0:0:0:0:0:0:0:1", ShouldFail: true}, - {Type: nns.AAAA, Name: "2002:0:0:0:0:0:0:1", ShouldFail: true}, - {Type: nns.AAAA, Name: "2001:200:0:0:0:0:0:0"}, - {Type: nns.AAAA, Name: "2002:0:0:0:0:0:0:0", ShouldFail: true}, - {Type: nns.AAAA, Name: "2002:DB8::8:800:200C:417A", ShouldFail: true}, - {Type: nns.AAAA, Name: "3FFD:1B8::8:800:200C:417A"}, - {Type: nns.AAAA, Name: "3FFD::101"}, - {Type: nns.AAAA, Name: "3fFD::101"}, - {Type: nns.AAAA, Name: "2002:DB8:0:0:8:800:200C:417A", ShouldFail: true}, - {Type: nns.AAAA, Name: "3FFD:DB8:0:0:8:800:200C:417A"}, - {Type: nns.AAAA, Name: "3FFD:0:0:0:0:0:0:101"}, - {Type: nns.AAAA, Name: "3FFD::ffff:1.01.1.01", ShouldFail: true}, - {Type: nns.AAAA, Name: "2001:DB8:0:0:8:800:200C:4Z", ShouldFail: true}, - {Type: nns.AAAA, Name: "2001::13.1.68.3", ShouldFail: true}, - } - for _, testCase := range testCases { - var expected interface{} - if testCase.ShouldFail { - expected = nil - } else { - expected = stackitem.Null{} - } - t.Run(testCase.Name, func(t *testing.T) { - testNameServiceInvoke(t, bc, nsHash, "setRecord", expected, "neo.com", int64(testCase.Type), testCase.Name) - }) - } - }) -} - -func TestSetAdmin(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - transferFundsToCommittee(t, bc) - owner := newAccountWithGAS(t, bc) - admin := newAccountWithGAS(t, bc) - guest := newAccountWithGAS(t, bc) - testNameServiceInvoke(t, bc, nsHash, "addRoot", stackitem.Null{}, "com") - - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, owner, "register", true, - "neo.com", owner.PrivateKey().GetScriptHash()) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, guest, "setAdmin", nil, - "neo.com", admin.PrivateKey().GetScriptHash()) - - // Must be witnessed by both owner and admin. - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, owner, "setAdmin", nil, - "neo.com", admin.PrivateKey().GetScriptHash()) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, admin, "setAdmin", nil, - "neo.com", admin.PrivateKey().GetScriptHash()) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, []*wallet.Account{owner, admin}, - "setAdmin", stackitem.Null{}, - "neo.com", admin.PrivateKey().GetScriptHash()) - - t.Run("set and delete by admin", func(t *testing.T) { - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, admin, "setRecord", stackitem.Null{}, - "neo.com", int64(nns.TXT), "sometext") - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, guest, "deleteRecord", nil, - "neo.com", int64(nns.TXT)) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, admin, "deleteRecord", stackitem.Null{}, - "neo.com", int64(nns.TXT)) - }) - - t.Run("set admin to null", func(t *testing.T) { - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, admin, "setRecord", stackitem.Null{}, - "neo.com", int64(nns.TXT), "sometext") - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, owner, "setAdmin", stackitem.Null{}, - "neo.com", nil) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, admin, "deleteRecord", nil, - "neo.com", int64(nns.TXT)) - }) -} - -func TestTransfer(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - transferFundsToCommittee(t, bc) - from := newAccountWithGAS(t, bc) - to := newAccountWithGAS(t, bc) - - testNameServiceInvoke(t, bc, nsHash, "addRoot", stackitem.Null{}, "com") - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, from, "register", - true, "neo.com", from.PrivateKey().GetScriptHash()) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, from, "setRecord", stackitem.Null{}, - "neo.com", int64(nns.A), "1.2.3.4") - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, from, "transfer", - nil, to.Contract.ScriptHash().BytesBE(), []byte("not.exists"), nil) - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, true, "transfer", - false, to.Contract.ScriptHash().BytesBE(), []byte("neo.com"), nil) - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, from, "transfer", - true, to.Contract.ScriptHash().BytesBE(), []byte("neo.com"), nil) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, from, "totalSupply", 1) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, from, "ownerOf", - to.Contract.ScriptHash().BytesBE(), []byte("neo.com")) - cs, cs2 := getTestContractState(bc) // cs2 doesn't have OnNEP11Transfer - require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) - require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs2)) - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, to, "transfer", - nil, cs2.Hash.BytesBE(), []byte("neo.com"), nil) - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, to, "transfer", - true, cs.Hash.BytesBE(), []byte("neo.com"), nil) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, from, "totalSupply", 1) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, from, "ownerOf", - cs.Hash.BytesBE(), []byte("neo.com")) -} - -func TestTokensOf(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - transferFundsToCommittee(t, bc) - acc1 := newAccountWithGAS(t, bc) - acc2 := newAccountWithGAS(t, bc) - - testNameServiceInvoke(t, bc, nsHash, "addRoot", stackitem.Null{}, "com") - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, acc1, "register", - true, "neo.com", acc1.PrivateKey().GetScriptHash()) - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, acc2, "register", - true, "nspcc.com", acc2.PrivateKey().GetScriptHash()) - - testTokensOf(t, bc, nsHash, acc1, [][]byte{[]byte("neo.com")}, acc1.Contract.ScriptHash().BytesBE()) - testTokensOf(t, bc, nsHash, acc1, [][]byte{[]byte("nspcc.com")}, acc2.Contract.ScriptHash().BytesBE()) - testTokensOf(t, bc, nsHash, acc1, [][]byte{[]byte("neo.com"), []byte("nspcc.com")}) - testTokensOf(t, bc, nsHash, acc1, [][]byte{}, util.Uint160{}.BytesBE()) // empty hash is a valid hash still -} - -func testTokensOf(t *testing.T, bc *Blockchain, nsHash util.Uint160, signer *wallet.Account, result [][]byte, args ...interface{}) { - method := "tokensOf" - if len(args) == 0 { - method = "tokens" - } - w := io.NewBufBinWriter() - emit.AppCall(w.BinWriter, nsHash, method, callflag.All, args...) - for range result { - emit.Opcodes(w.BinWriter, opcode.DUP) - emit.Syscall(w.BinWriter, interopnames.SystemIteratorNext) - emit.Opcodes(w.BinWriter, opcode.ASSERT) - - emit.Opcodes(w.BinWriter, opcode.DUP) - emit.Syscall(w.BinWriter, interopnames.SystemIteratorValue) - emit.Opcodes(w.BinWriter, opcode.SWAP) - } - emit.Opcodes(w.BinWriter, opcode.DROP) - emit.Int(w.BinWriter, int64(len(result))) - emit.Opcodes(w.BinWriter, opcode.PACK) - require.NoError(t, w.Err) - script := w.Bytes() - tx := transaction.New(script, defaultNameServiceSysfee) - tx.ValidUntilBlock = bc.BlockHeight() + 1 - signTxWithAccounts(bc, tx, signer) - aers, err := persistBlock(bc, tx) - require.NoError(t, err) - if result == nil { - checkFAULTState(t, aers[0]) - return - } - arr := make([]stackitem.Item, 0, len(result)) - for i := len(result) - 1; i >= 0; i-- { - arr = append(arr, stackitem.Make(result[i])) - } - checkResult(t, aers[0], stackitem.NewArray(arr)) -} - -func TestResolve(t *testing.T) { - bc, nsHash := newTestChainWithNS(t) - - transferFundsToCommittee(t, bc) - acc := newAccountWithGAS(t, bc) - - testNameServiceInvoke(t, bc, nsHash, "addRoot", stackitem.Null{}, "com") - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, acc, "register", - true, "neo.com", acc.PrivateKey().GetScriptHash()) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, acc, "setRecord", stackitem.Null{}, - "neo.com", int64(nns.A), "1.2.3.4") - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, acc, "setRecord", stackitem.Null{}, - "neo.com", int64(nns.CNAME), "alias.com") - - testNameServiceInvokeAux(t, bc, nsHash, defaultRegisterSysfee, acc, "register", - true, "alias.com", acc.PrivateKey().GetScriptHash()) - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, acc, "setRecord", stackitem.Null{}, - "alias.com", int64(nns.TXT), "sometxt") - - testNameServiceInvoke(t, bc, nsHash, "resolve", "1.2.3.4", - "neo.com", int64(nns.A)) - testNameServiceInvoke(t, bc, nsHash, "resolve", "alias.com", - "neo.com", int64(nns.CNAME)) - testNameServiceInvoke(t, bc, nsHash, "resolve", "sometxt", - "neo.com", int64(nns.TXT)) - testNameServiceInvoke(t, bc, nsHash, "resolve", stackitem.Null{}, - "neo.com", int64(nns.AAAA)) -} - -const ( - defaultNameServiceDomainPrice = 10_0000_0000 - defaultNameServiceSysfee = 6000_0000 - defaultRegisterSysfee = 10_0000_0000 + defaultNameServiceDomainPrice -) - -func testNameServiceInvoke(t *testing.T, bc *Blockchain, nsHash util.Uint160, method string, result interface{}, args ...interface{}) { - testNameServiceInvokeAux(t, bc, nsHash, defaultNameServiceSysfee, true, method, result, args...) -} - -func testNameServiceInvokeAux(t *testing.T, bc *Blockchain, nsHash util.Uint160, sysfee int64, signer interface{}, method string, result interface{}, args ...interface{}) { - if sysfee < 0 { - sysfee = defaultNameServiceSysfee - } - aer, err := invokeContractMethodGeneric(bc, sysfee, nsHash, method, signer, args...) - require.NoError(t, err) - if result == nil { - checkFAULTState(t, aer) - return - } - checkResult(t, aer, stackitem.Make(result)) -} - -func newAccountWithGAS(t *testing.T, bc *Blockchain) *wallet.Account { - acc, err := wallet.NewAccount() - require.NoError(t, err) - transferTokenFromMultisigAccount(t, bc, acc.PrivateKey().GetScriptHash(), bc.contracts.GAS.Hash, 1000_00000000) - return acc -}