[#49] Support contract migration

At initialization contract saves master script hash
that allows to re-initialize or migrate contract.

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
Alex Vanin 2021-02-11 18:55:32 +03:00 committed by Alex Vanin
parent a4a9a49a76
commit 88c738b736
11 changed files with 186 additions and 28 deletions

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
@ -34,15 +35,16 @@ func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
}
}
func Init(addrNetmap []byte, name string, index, total int) {
if storage.Get(ctx, netmapKey) != nil {
panic("contract already deployed")
func Init(owner interop.Hash160, addrNetmap []byte, name string, index, total int) {
if !common.HasUpdateAccess(ctx) {
panic("only owner can reinitialize contract")
}
if len(addrNetmap) != 20 {
panic("incorrect length of contract script hash")
}
storage.Put(ctx, common.OwnerKey, owner)
storage.Put(ctx, netmapKey, addrNetmap)
storage.Put(ctx, nameKey, name)
storage.Put(ctx, indexKey, index)
@ -53,6 +55,18 @@ func Init(addrNetmap []byte, name string, index, total int) {
runtime.Log(name + " contract initialized")
}
func Migrate(script []byte, manifest []byte) bool {
if !common.HasUpdateAccess(ctx) {
runtime.Log("only owner can update contract")
return false
}
management.Update(script, manifest)
runtime.Log("alphabet contract updated")
return true
}
func Gas() int {
return gas.BalanceOf(runtime.GetExecutingScriptHash())
}

View file

@ -4,6 +4,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
@ -36,8 +37,7 @@ func (a auditHeader) ID() []byte {
const (
version = 1
netmapContractKey = "netmapScriptHash"
netmapContractKeyLn = len(netmapContractKey)
netmapContractKey = "netmapScriptHash"
)
var ctx storage.Context
@ -46,20 +46,33 @@ func init() {
ctx = storage.GetContext()
}
func Init(addrNetmap interop.Hash160) {
if storage.Get(ctx, netmapContractKey) != nil {
panic("init: contract already deployed")
func Init(owner interop.Hash160, addrNetmap interop.Hash160) {
if !common.HasUpdateAccess(ctx) {
panic("only owner can reinitialize contract")
}
if len(addrNetmap) != 20 {
panic("init: incorrect length of contract script hash")
}
storage.Put(ctx, common.OwnerKey, owner)
storage.Put(ctx, netmapContractKey, addrNetmap)
runtime.Log("audit contract initialized")
}
func Migrate(script []byte, manifest []byte) bool {
if !common.HasUpdateAccess(ctx) {
runtime.Log("only owner can update contract")
return false
}
management.Update(script, manifest)
runtime.Log("audit contract updated")
return true
}
func Put(rawAuditResult []byte) bool {
innerRing := common.InnerRingListViaStorage(ctx, netmapContractKey)
@ -126,10 +139,18 @@ func ListByNode(epoch int, cid []byte, key interop.PublicKey) [][]byte {
func list(it iterator.Iterator) [][]byte {
var result [][]byte
ignore := [][]byte{
[]byte(netmapContractKey),
[]byte(common.OwnerKey),
}
loop:
for iterator.Next(it) {
key := iterator.Value(it).([]byte) // iterator MUST BE `storage.KeysOnly`
if len(key) == netmapContractKeyLn {
continue
for _, ignoreKey := range ignore {
if common.BytesEqual(key, ignoreKey) {
continue loop
}
}
result = append(result, key)

View file

@ -4,6 +4,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
@ -63,21 +64,34 @@ func init() {
token = CreateToken()
}
func Init(addrNetmap, addrContainer []byte) {
if storage.Get(ctx, netmapContractKey) != nil {
panic("init: contract already deployed")
func Init(owner interop.Hash160, addrNetmap, addrContainer []byte) {
if !common.HasUpdateAccess(ctx) {
panic("only owner can reinitialize contract")
}
if len(addrNetmap) != 20 || len(addrContainer) != 20 {
panic("init: incorrect length of contract script hash")
}
storage.Put(ctx, common.OwnerKey, owner)
storage.Put(ctx, netmapContractKey, addrNetmap)
storage.Put(ctx, containerContractKey, addrContainer)
runtime.Log("balance contract initialized")
}
func Migrate(script []byte, manifest []byte) bool {
if !common.HasUpdateAccess(ctx) {
runtime.Log("only owner can update contract")
return false
}
management.Update(script, manifest)
runtime.Log("balance contract updated")
return true
}
func Symbol() string {
return token.Symbol
}

22
common/update.go Normal file
View file

@ -0,0 +1,22 @@
package common
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"
)
const OwnerKey = "contractOwner"
// HasUpdateAccess returns true if contract can be initialized, re-initialized
// or migrated.
func HasUpdateAccess(ctx storage.Context) bool {
data := storage.Get(ctx, OwnerKey)
if data == nil { // contract has not been initialized yet, return true
return true
}
owner := data.(interop.Hash160)
return runtime.CheckWitness(owner)
}

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
@ -59,17 +60,16 @@ func init() {
ctx = storage.GetContext()
}
func Init(addrNetmap, addrBalance, addrID []byte) {
if storage.Get(ctx, netmapContractKey) != nil &&
storage.Get(ctx, balanceContractKey) != nil &&
storage.Get(ctx, neofsIDContractKey) != nil {
panic("init: contract already deployed")
func Init(owner interop.Hash160, addrNetmap, addrBalance, addrID []byte) {
if !common.HasUpdateAccess(ctx) {
panic("only owner can reinitialize contract")
}
if len(addrNetmap) != 20 || len(addrBalance) != 20 || len(addrID) != 20 {
panic("init: incorrect length of contract script hash")
}
storage.Put(ctx, common.OwnerKey, owner)
storage.Put(ctx, netmapContractKey, addrNetmap)
storage.Put(ctx, balanceContractKey, addrBalance)
storage.Put(ctx, neofsIDContractKey, addrID)
@ -77,6 +77,18 @@ func Init(addrNetmap, addrBalance, addrID []byte) {
runtime.Log("container contract initialized")
}
func Migrate(script []byte, manifest []byte) bool {
if !common.HasUpdateAccess(ctx) {
runtime.Log("only owner can update contract")
return false
}
management.Update(script, manifest)
runtime.Log("container contract updated")
return true
}
func Put(container, signature, publicKey []byte) bool {
netmapContractAddr := storage.Get(ctx, netmapContractKey).([]byte)
innerRing := common.InnerRingList(netmapContractAddr)

2
go.mod
View file

@ -2,4 +2,4 @@ module github.com/nspcc-dev/neofs-contract
go 1.13
require github.com/nspcc-dev/neo-go v0.93.0-pre.0.20210211114309-2ee755e09fcd
require github.com/nspcc-dev/neo-go v0.93.0-pre.0.20210212114643-94672d4d83b7

4
go.sum
View file

@ -139,6 +139,8 @@ github.com/nspcc-dev/neo-go v0.93.0-pre.0.20210208141009-71494e6ae449 h1:gfsr6+g
github.com/nspcc-dev/neo-go v0.93.0-pre.0.20210208141009-71494e6ae449/go.mod h1:6D4NY4Bs1OTan3VdLvAuy9kGt460/2dsfHcthQGS+QU=
github.com/nspcc-dev/neo-go v0.93.0-pre.0.20210211114309-2ee755e09fcd h1:4zMEQ+xVaa6oEnzzHOlRwwRQMdze/so7YpjjE8bfkPI=
github.com/nspcc-dev/neo-go v0.93.0-pre.0.20210211114309-2ee755e09fcd/go.mod h1:6D4NY4Bs1OTan3VdLvAuy9kGt460/2dsfHcthQGS+QU=
github.com/nspcc-dev/neo-go v0.93.0-pre.0.20210212114643-94672d4d83b7 h1:4DPdZM6M1idOhkpMlZsoNjNTuj0Y4I5EyVxe5FA5jmY=
github.com/nspcc-dev/neo-go v0.93.0-pre.0.20210212114643-94672d4d83b7/go.mod h1:6tixfAd+d8TIm05DA874j6t898G/fyqA2fHVJxkJCXQ=
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
@ -239,6 +241,8 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View file

@ -38,6 +38,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
@ -84,9 +85,9 @@ func init() {
}
// Init set up initial inner ring node keys.
func Init(args [][]byte) bool {
if storage.Get(ctx, innerRingKey) != nil {
panic("neofs: contract already deployed")
func Init(owner interop.PublicKey, args [][]byte) bool {
if !common.HasUpdateAccess(ctx) {
panic("only owner can reinitialize contract")
}
var irList []common.IRNode
@ -109,11 +110,26 @@ func Init(args [][]byte) bool {
common.SetSerialized(ctx, candidatesKey, []common.IRNode{})
common.SetSerialized(ctx, cashedChequesKey, []cheque{})
storage.Put(ctx, common.OwnerKey, owner)
runtime.Log("neofs: contract initialized")
return true
}
// Migrate updates smart contract execution script and manifest.
func Migrate(script []byte, manifest []byte) bool {
if !common.HasUpdateAccess(ctx) {
runtime.Log("only owner can update contract")
return false
}
management.Update(script, manifest)
runtime.Log("neofs contract updated")
return true
}
// InnerRingList returns array of inner ring node keys.
func InnerRingList() []common.IRNode {
return getInnerRingNodes(ctx, innerRingKey)

View file

@ -1,8 +1,10 @@
package neofsidcontract
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
@ -27,21 +29,34 @@ func init() {
ctx = storage.GetContext()
}
func Init(addrNetmap, addrContainer []byte) {
if storage.Get(ctx, netmapContractKey) != nil {
panic("init: contract already deployed")
func Init(owner interop.Hash160, addrNetmap, addrContainer []byte) {
if !common.HasUpdateAccess(ctx) {
panic("only owner can reinitialize contract")
}
if len(addrNetmap) != 20 || len(addrContainer) != 20 {
panic("init: incorrect length of contract script hash")
}
storage.Put(ctx, common.OwnerKey, owner)
storage.Put(ctx, netmapContractKey, addrNetmap)
storage.Put(ctx, containerContractKey, addrContainer)
runtime.Log("neofsid contract initialized")
}
func Migrate(script []byte, manifest []byte) bool {
if !common.HasUpdateAccess(ctx) {
runtime.Log("only owner can update contract")
return false
}
management.Update(script, manifest)
runtime.Log("neofsid contract updated")
return true
}
func AddKey(owner []byte, keys [][]byte) bool {
var (
n int // number of votes for inner ring invoke

View file

@ -1,9 +1,11 @@
package netmapcontract
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
"github.com/nspcc-dev/neo-go/pkg/interop/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
@ -57,9 +59,9 @@ func init() {
// Init function sets up initial list of inner ring public keys and should
// be invoked once at neofs infrastructure setup.
func Init(keys [][]byte) {
if storage.Get(ctx, innerRingKey) != nil {
panic("netmap: contract already initialized")
func Init(owner interop.Hash160, keys [][]byte) {
if !common.HasUpdateAccess(ctx) {
panic("only owner can reinitialize contract")
}
var irList []common.IRNode
@ -69,6 +71,8 @@ func Init(keys [][]byte) {
irList = append(irList, common.IRNode{PublicKey: key})
}
storage.Put(ctx, common.OwnerKey, owner)
common.SetSerialized(ctx, innerRingKey, irList)
// epoch number is a little endian int, it doesn't need to be serialized
@ -83,6 +87,18 @@ func Init(keys [][]byte) {
runtime.Log("netmap contract initialized")
}
func Migrate(script []byte, manifest []byte) bool {
if !common.HasUpdateAccess(ctx) {
runtime.Log("only owner can update contract")
return false
}
management.Update(script, manifest)
runtime.Log("netmap contract updated")
return true
}
func InnerRingList() []common.IRNode {
return getIRNodes(ctx)
}

View file

@ -1,6 +1,8 @@
package reputationcontract
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neofs-contract/common"
@ -28,6 +30,28 @@ func init() {
ctx = storage.GetContext()
}
func Init(owner interop.Hash160) {
if !common.HasUpdateAccess(ctx) {
panic("only owner can reinitialize contract")
}
storage.Put(ctx, common.OwnerKey, owner)
runtime.Log("reputation contract initialized")
}
func Migrate(script []byte, manifest []byte) bool {
if !common.HasUpdateAccess(ctx) {
runtime.Log("only owner can update contract")
return false
}
management.Update(script, manifest)
runtime.Log("reputation contract updated")
return true
}
func Put(manager, epoch, typ []byte, newTrustList [][]byte) bool {
if !runtime.CheckWitness(manager) {
panic("put: incorrect manager key")