diff --git a/contracts/nns/config.yml b/contracts/nns/config.yml index d336597d..87353939 100644 --- a/contracts/nns/config.yml +++ b/contracts/nns/config.yml @@ -18,3 +18,5 @@ permissions: - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd methods: ["update"] - methods: ["onNEP11Payment"] +overloads: + renewDefault: renew diff --git a/contracts/nns/contract.go b/contracts/nns/contract.go index 7042f8fd..ce3e1595 100644 --- a/contracts/nns/contract.go +++ b/contracts/nns/contract.go @@ -72,6 +72,8 @@ const ( millisecondsInSecond = 1000 // millisecondsInYear is amount of milliseconds per year. millisecondsInYear = int64(365 * 24 * 3600 * millisecondsInSecond) + // millisecondsInTenYears is the amount of milliseconds per ten years. + millisecondsInTenYears = 10 * millisecondsInYear ) // RecordState is a type that registered entities are saved to. @@ -461,16 +463,30 @@ func saveDomain(ctx storage.Context, name, email string, refresh, retry, expire, putSoaRecord(ctx, name, email, refresh, retry, expire, ttl) } -// Renew increases domain expiration date. -func Renew(name string) int64 { +// RenewDefault increases domain expiration date for 1 year. +func RenewDefault(name string) int64 { + return Renew(name, 1) +} + +// Renew increases domain expiration date up to the specified amount of years. +func Renew(name string, years int) int64 { + if years < 1 || years > 10 { + panic("invalid renewal period value") + } if len(name) > maxDomainNameLength { - panic("too long name") + panic("invalid domain name format") } - runtime.BurnGas(GetPrice()) + runtime.BurnGas(int(GetPrice()) * years) ctx := storage.GetContext() ns := getNameState(ctx, []byte(name)) ns.checkAdmin() - ns.Expiration += millisecondsInYear + ns.Expiration += millisecondsInYear * int64(years) + + fragments := splitAndCheck(name) + // TLDs are not subject to this check. + if len(fragments) > 1 && ns.Expiration > int64(runtime.GetTime())+millisecondsInTenYears { + panic("10 years of expiration period at max is allowed") + } putNameState(ctx, ns) return ns.Expiration } diff --git a/tests/nns_test.go b/tests/nns_test.go index 99f71eff..e4f68527 100644 --- a/tests/nns_test.go +++ b/tests/nns_test.go @@ -401,16 +401,25 @@ func TestNNSRenew(t *testing.T) { const msPerYear = 365 * 24 * time.Hour / time.Millisecond b := c.TopBlock(t) - ts := b.Timestamp + uint64(expire*1000) + uint64(msPerYear) + renewalPeriod := int64(2) + ts := b.Timestamp + uint64(expire*1000) + uint64(msPerYear)*uint64(renewalPeriod) cAcc := c.WithSigners(acc) - cAcc.InvokeFail(t, "not witnessed by admin", "renew", "testdomain.com") - c1.Invoke(t, ts, "renew", "testdomain.com") + cAcc.InvokeFail(t, "not witnessed by admin", "renew", "testdomain.com", renewalPeriod) + c1.Invoke(t, ts, "renew", "testdomain.com", renewalPeriod) expected := stackitem.NewMapWithValue([]stackitem.MapElement{ {Key: stackitem.Make("name"), Value: stackitem.Make("testdomain.com")}, {Key: stackitem.Make("expiration"), Value: stackitem.Make(ts)}, {Key: stackitem.Make("admin"), Value: stackitem.Null{}}}) cAcc.Invoke(t, expected, "properties", "testdomain.com") + + // Invalid renewal period. + c1.InvokeFail(t, "invalid renewal period value", "renew", "testdomain.com", 11) + // Too large expiration period. + c1.InvokeFail(t, "10 years of expiration period at max is allowed", "renew", "testdomain.com", 10) + + // Default renewal period. + c1.Invoke(t, ts+uint64(msPerYear), "renew", "testdomain.com") } func TestNNSResolve(t *testing.T) {