From 9f33939dee592c89acd64c0db6224556301b368f Mon Sep 17 00:00:00 2001 From: alexvanin Date: Fri, 22 May 2020 11:06:12 +0300 Subject: [PATCH 01/25] Add "IsInnerRing" method Inner ring nodes check their presence in inner ring list during startup. "IsInnerRing" method allows to check presence efficiently by calling this method. --- neofs_contract.go | 58 +++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index bab6b25..f37a1df 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -33,27 +33,27 @@ func Main(op string, args []interface{}) interface{} { } /* - Utility operations - they will be changed in production: - - Deploy(params: address, pubKey, ... ) - setup initial inner ring state + Utility operations - they will be changed in production: + - Deploy(params: address, pubKey, ... ) - setup initial inner ring state - User operations: - - InnerRingList() - get list of inner ring nodes addresses and public keys - - InnerRingAddress(params: address, pubKey) - update address of the inner ring node with given public key - - InnerRingCandidateRemove(params: pubKey) - remove node with given public key from the inner ring candidate queue - - InnerRingCandidateAdd(params: address, pubKey) - add node to the inner ring candidate queue - - Deposit(params: pubKey, amount) - deposit GAS to the NeoFS account - - Withdraw(params: withdrawCheque) - withdraw GAS from the NeoFS account - - InnerRingUpdate(params: irCheque) - change list of inner ring nodes - - Version() - get version of the NeoFS smart-contract - - Params: - - address - string of the valid multiaddress (github.com/multiformats/multiaddr) - - pubKey - 33 byte public key - - withdrawCheque - serialized structure, that confirms GAS transfer; - contains inner ring signatures - - irCheque - serialized structure, that confirms new inner ring node list; - contains inner ring signatures + User operations: + - InnerRingList() - get list of inner ring nodes addresses and public keys + - InnerRingAddress(params: address, pubKey) - update address of the inner ring node with given public key + - InnerRingCandidateRemove(params: pubKey) - remove node with given public key from the inner ring candidate queue + - InnerRingCandidateAdd(params: address, pubKey) - add node to the inner ring candidate queue + - Deposit(params: pubKey, amount) - deposit GAS to the NeoFS account + - Withdraw(params: withdrawCheque) - withdraw GAS from the NeoFS account + - InnerRingUpdate(params: irCheque) - change list of inner ring nodes + - IsInnerRing(params: pubKey) - returns true if pubKey presented in inner ring list + - Version() - get version of the NeoFS smart-contract + Params: + - address - string of the valid multiaddress (github.com/multiformats/multiaddr) + - pubKey - 33 byte public key + - withdrawCheque - serialized structure, that confirms GAS transfer; + contains inner ring signatures + - irCheque - serialized structure, that confirms new inner ring node list; + contains inner ring signatures */ ctx := storage.GetContext() @@ -265,6 +265,26 @@ func Main(op string, args []interface{}) interface{} { putSerialized(ctx, "UsedVerifCheckList", c) return true + case "IsInnerRing": + if len(args) != 1 { + panic("isInnerRing: wrong arguments") + } + + key := args[0].([]byte) + if len(key) != 33 { + panic("isInnerRing: incorrect public key") + } + + irList := getSerialized(ctx, "InnerRingList").([]node) + for i := range irList { + node := irList[i] + + if util.Equals(node.pub, key) { + return true + } + } + + return false case "Version": return version } From 78b8af8f8389a67868dcdcb5a5bb01e8a5335aa6 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Tue, 26 May 2020 17:48:56 +0300 Subject: [PATCH 02/25] Do not store multiaddress of inner ring nodes With neo:morph environment, there will be no direct communication between inner ring nodes and storage nodes. neo:morph smart-contracts will identify inner ring nodes by their signatures. --- neofs_contract.go | 48 +++++++++--------------------------------- neofs_contract_test.go | 38 ++++++--------------------------- 2 files changed, 17 insertions(+), 69 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index f37a1df..d7a5998 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -9,8 +9,7 @@ import ( ) type node struct { - addr []byte - pub []byte + pub []byte } type check struct { @@ -38,9 +37,8 @@ func Main(op string, args []interface{}) interface{} { User operations: - InnerRingList() - get list of inner ring nodes addresses and public keys - - InnerRingAddress(params: address, pubKey) - update address of the inner ring node with given public key - InnerRingCandidateRemove(params: pubKey) - remove node with given public key from the inner ring candidate queue - - InnerRingCandidateAdd(params: address, pubKey) - add node to the inner ring candidate queue + - InnerRingCandidateAdd(params: pubKey) - add node to the inner ring candidate queue - Deposit(params: pubKey, amount) - deposit GAS to the NeoFS account - Withdraw(params: withdrawCheque) - withdraw GAS from the NeoFS account - InnerRingUpdate(params: irCheque) - change list of inner ring nodes @@ -64,16 +62,10 @@ func Main(op string, args []interface{}) interface{} { panic("contract already deployed") } - ln := len(args) - if ln%2 != 0 { - panic("provide pairs of inner ring address and public key") - } - irList = []node{} - for i := 0; i < ln/2; i++ { - addr := args[i*2].([]byte) - pub := args[i*2+1].([]byte) - irList = append(irList, node{addr: addr, pub: pub}) + for i := 0; i < len(args); i++ { + pub := args[i].([]byte) + irList = append(irList, node{pub: pub}) } data := runtime.Serialize(irList) @@ -88,22 +80,6 @@ func Main(op string, args []interface{}) interface{} { irList := getSerialized(ctx, "InnerRingList").([]node) return irList - case "InnerRingAddress": - addr := args[0].([]byte) - pub := args[1].([]byte) - irList := getSerialized(ctx, "InnerRingList").([]node) - if !containsPub(irList, pub) { - panic("inner ring node does not exist") - } - - if runtime.CheckWitness(pub) { - n := node{addr: addr, pub: pub} - - delSerializedIR(ctx, "InnerRingList", pub) - putSerialized(ctx, "InnerRingList", n) - } - - return true case "InnerRingCandidateRemove": data := args[0].([]byte) // public key if !runtime.CheckWitness(data) { @@ -114,19 +90,18 @@ func Main(op string, args []interface{}) interface{} { return true case "InnerRingCandidateAdd": - addr := args[0].([]byte) // valid multiaddr string - data := args[1].([]byte) // public key + key := args[0].([]byte) // public key - if !runtime.CheckWitness(data) { + if !runtime.CheckWitness(key) { panic("you should be the owner of the public key") } candidates := getSerialized(ctx, "InnerRingCandidates").([]node) - if containsPub(candidates, data) { + if containsPub(candidates, key) { panic("is already in list") } - from := pubToScriptHash(data) + from := pubToScriptHash(key) to := engine.GetExecutingScriptHash() params := []interface{}{from, to, innerRingCandidateFee} @@ -135,10 +110,7 @@ func Main(op string, args []interface{}) interface{} { panic("failed to transfer funds, aborting") } - candidate := node{ - addr: addr, - pub: data, - } + candidate := node{pub: key} if !putSerialized(ctx, "InnerRingCandidates", candidate) { panic("failed to put candidate into the queue") } diff --git a/neofs_contract_test.go b/neofs_contract_test.go index 1dc1474..d1cd41b 100644 --- a/neofs_contract_test.go +++ b/neofs_contract_test.go @@ -37,7 +37,6 @@ var ( type contract struct { script []byte - addrs []multiaddr.Multiaddr privs []*ecdsa.PrivateKey cgasHash string } @@ -49,43 +48,20 @@ func TestContract(t *testing.T) { plug.cgas[contractStr] = util.Fixed8FromInt64(1000) - // fail if odd number of arguments provided - v := initVM(contract, plug) - loadArg(t, v, "Deploy", []interface{}{contract.addrs[0].String()}) - require.Error(t, v.Run()) - - // correct arguments var args []interface{} - for i := range contract.addrs { - args = append(args, contract.addrs[i].String()) + for i := range contract.privs { args = append(args, crypto.MarshalPublicKey(&contract.privs[i].PublicKey)) } - v = initVM(contract, plug) + v := initVM(contract, plug) loadArg(t, v, "Deploy", args) require.NoError(t, v.Run()) - // double withdraw + // double deploy v = initVM(contract, plug) loadArg(t, v, "Deploy", args) require.Error(t, v.Run()) - t.Run("InnerRingAddress", func(t *testing.T) { - newAddr, err := multiaddr.NewMultiaddr("/dns6/fdb5:7305:0adf:0b0b::/tcp/8080") - require.NoError(t, err) - - previousAddr := contract.addrs[0] - key := crypto.MarshalPublicKey(&contract.privs[0].PublicKey) - - v := initVM(contract, plug) - loadArg(t, v, "InnerRingAddress", []interface{}{newAddr.String(), key}) - require.NoError(t, v.Run()) - require.False(t, bytes.Contains(plug.mem["InnerRingList"], []byte(previousAddr.String()))) - require.True(t, bytes.Contains(plug.mem["InnerRingList"], []byte(newAddr.String()))) - - contract.addrs[0] = newAddr - }) - t.Run("Deposit", func(t *testing.T) { v := initVM(contract, plug) before := plug.cgas[contractStr] @@ -128,7 +104,7 @@ func TestContract(t *testing.T) { key := crypto.MarshalPublicKey(&test.DecodeKey(1).PublicKey) plug.setCGASBalance(key, 4000) - loadArg(t, v, "InnerRingCandidateAdd", []interface{}{"/ip6/2001:4860:4860::8888/tcp/80", key}) + loadArg(t, v, "InnerRingCandidateAdd", []interface{}{key}) require.NoError(t, v.Run()) fee := util.Fixed8FromInt64(1) @@ -140,7 +116,7 @@ func TestContract(t *testing.T) { t.Run("Double InnerRingCandidateAdd", func(t *testing.T) { v := initVM(contract, plug) - loadArg(t, v, "InnerRingCandidateAdd", []interface{}{"/ip4/8.8.8.8/tcp/80", key}) + loadArg(t, v, "InnerRingCandidateAdd", []interface{}{key}) require.Error(t, v.Run()) }) }) @@ -150,7 +126,7 @@ func TestContract(t *testing.T) { plug.setCGASBalance(key, 4000) v := initVM(contract, plug) - loadArg(t, v, "InnerRingCandidateAdd", []interface{}{"/ip6/2001:4860:4860::8888/tcp/80", key}) + loadArg(t, v, "InnerRingCandidateAdd", []interface{}{key}) require.NoError(t, v.Run()) require.True(t, bytes.Contains(plug.mem["InnerRingCandidates"], key)) @@ -253,7 +229,7 @@ func initGoContract(t *testing.T, path string, n int) *contract { buf, err := compiler.Compile(f) require.NoError(t, err) - return &contract{script: buf, privs: getKeys(t, n), addrs: getAddrs(t, n)} + return &contract{script: buf, privs: getKeys(t, n)} } func getKeys(t *testing.T, n int) []*ecdsa.PrivateKey { From 4fbfa1bc98e9a882b8f3e91d319856ac9e795e20 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 27 May 2020 15:23:44 +0300 Subject: [PATCH 03/25] Collect "InnerRingUpdate" calls from inner ring nodes Inner ring nodes do not collect signatures for the cheque now. Instead they invoke "InnerRingUpdate" method and smart-contract checks if method was called from inner ring node. Then it accepts cheque if there were 2/3n+1 invokes. --- neofs_contract.go | 175 +++++++++++++++++++++++++++++++++------------- 1 file changed, 125 insertions(+), 50 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index d7a5998..01984f0 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -8,21 +8,29 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/util" ) -type node struct { - pub []byte -} +type ( + ballot struct { + id []byte + n int + } -type check struct { - id []byte - height []byte -} + node struct { + pub []byte + } -// GAS NEP-5 HASH -const tokenHash = "\x77\xea\x59\x6b\x7a\xdf\x7e\x4d\xd1\x40\x76\x97\x31\xb7\xd2\xf0\xe0\x6b\xcd\x9b" + check struct { + id []byte + height []byte + } +) -const innerRingCandidateFee = 100 * 1000 * 1000 // 10^8 - -const version = 1 +const ( + // GAS NEP-5 HASH + tokenHash = "\x77\xea\x59\x6b\x7a\xdf\x7e\x4d\xd1\x40\x76\x97\x31\xb7\xd2\xf0\xe0\x6b\xcd\x9b" + innerRingCandidateFee = 100 * 1000 * 1000 // 10^8 + version = 1 + voteKey = "ballots" +) func Main(op string, args []interface{}) interface{} { // The trigger determines whether this smart-contract is being @@ -75,6 +83,9 @@ func Main(op string, args []interface{}) interface{} { storage.Put(ctx, "UsedVerifCheckList", data) storage.Put(ctx, "InnerRingCandidates", data) + data = runtime.Serialize([]ballot{}) + storage.Put(ctx, voteKey, data) + return true case "InnerRingList": irList := getSerialized(ctx, "InnerRingList").([]node) @@ -181,60 +192,66 @@ func Main(op string, args []interface{}) interface{} { listSize := listItemCount * 33 offset := 8 + 2 + listSize - msg := data[:offset] - message := crypto.SHA256(msg) irList := getSerialized(ctx, "InnerRingList").([]node) - if !verifySignatures(irList, data, message, offset) { - panic("can't verify signatures") - } - usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) - c := check{ - id: id, - height: []byte{1}, // ir update cheques use height as id + threshold := len(irList)/3*2 + 1 + + if !isInnerRingRequest(irList) { + panic("innerRingUpdate: invoked by non inner ring node") } + + c := check{id: id} if containsCheck(usedList, c) { - panic("check has already been used") + panic("innerRingUpdate: cheque has non unique id") } - candidates := getSerialized(ctx, "InnerRingCandidates").([]node) - offset = 10 - newIR := []node{} + chequeHash := crypto.Hash256(data) - loop: - for i := 0; i < listItemCount; i, offset = i+1, offset+33 { - pub := data[offset : offset+33] + n := vote(ctx, chequeHash) + if n >= threshold { + removeVotes(ctx, chequeHash) - for j := 0; j < len(irList); j++ { - n := irList[j] - if util.Equals(n.pub, pub) { - newIR = append(newIR, n) - continue loop + candidates := getSerialized(ctx, "InnerRingCandidates").([]node) + offset = 10 + newIR := []node{} + + loop: + for i := 0; i < listItemCount; i, offset = i+1, offset+33 { + pub := data[offset : offset+33] + + for j := 0; j < len(irList); j++ { + n := irList[j] + if util.Equals(n.pub, pub) { + newIR = append(newIR, n) + continue loop + } + } + + for j := 0; j < len(candidates); j++ { + n := candidates[j] + if util.Equals(n.pub, pub) { + newIR = append(newIR, n) + continue loop + } } } - for j := 0; j < len(candidates); j++ { - n := candidates[j] - if util.Equals(n.pub, pub) { - newIR = append(newIR, n) - continue loop - } + if len(newIR) != listItemCount { + panic("new inner ring wasn't processed correctly") } - } - if len(newIR) != listItemCount { - panic("new inner ring wasn't processed correctly") - } + for i := 0; i < len(newIR); i++ { + n := newIR[i] + delSerializedIR(ctx, "InnerRingCandidates", n.pub) + } - for i := 0; i < len(newIR); i++ { - n := newIR[i] - delSerializedIR(ctx, "InnerRingCandidates", n.pub) - } + newIRData := runtime.Serialize(newIR) + storage.Put(ctx, "InnerRingList", newIRData) + putSerialized(ctx, "UsedVerifCheckList", c) - newIRData := runtime.Serialize(newIR) - storage.Put(ctx, "InnerRingList", newIRData) - putSerialized(ctx, "UsedVerifCheckList", c) + runtime.Notify("InnerRingUpdate", c.id, newIRData) + } return true case "IsInnerRing": @@ -405,3 +422,61 @@ func verifySignatures(irList []node, data []byte, message []byte, offset int) bo runtime.Log("not enough verified signatures") return false } + +// isInnerRingRequest returns true if contract was invoked by inner ring node. +func isInnerRingRequest(irList []node) bool { + for i := 0; i < len(irList); i++ { + irNode := irList[i] + + if runtime.CheckWitness(irNode.pub) { + return true + } + } + + return false +} + +// todo: votes must be from unique inner ring nods +func vote(ctx storage.Context, id []byte) int { + var ( + newCandidates = []ballot{} + candidates = getSerialized(ctx, voteKey).([]ballot) + found = -1 + ) + + for i := 0; i < len(candidates); i++ { + cnd := candidates[i] + if util.Equals(cnd.id, id) { + cnd = ballot{id: id, n: cnd.n + 1} + found = cnd.n + } + newCandidates = append(newCandidates, cnd) + } + + if found < 0 { + newCandidates = append(newCandidates, ballot{id: id, n: 1}) + found = 1 + } + + data := runtime.Serialize(newCandidates) + storage.Put(ctx, voteKey, data) + + return found +} + +func removeVotes(ctx storage.Context, id []byte) { + var ( + newCandidates = []ballot{} + candidates = getSerialized(ctx, voteKey).([]ballot) + ) + + for i := 0; i < len(candidates); i++ { + cnd := candidates[i] + if !util.Equals(cnd.id, id) { + newCandidates = append(newCandidates, cnd) + } + } + + data := runtime.Serialize(newCandidates) + storage.Put(ctx, voteKey, data) +} From 88e73a04d779f00de7f20c5601058e3578edb7fa Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 27 May 2020 15:26:57 +0300 Subject: [PATCH 04/25] Remove inner ring update test This test runs in neofs-node repository because it uses internal neofs-node structures. It will be reworked later. --- README.md | 6 ----- neofs_contract_test.go | 57 ------------------------------------------ 2 files changed, 63 deletions(-) diff --git a/README.md b/README.md index 859698e..9e579b4 100644 --- a/README.md +++ b/README.md @@ -91,12 +91,6 @@ PASS ok github.com/nspcc-dev/neofs-contract 0.453s ``` -### Skipped tests - -Some tests might be skipped for now. These tests used `neofs-node` -structures, that are not publicly available yet, e.g. cheque for -`InnerRingUpdate` call. - ## License This project is licensed under the GPLv3 License - see the diff --git a/neofs_contract_test.go b/neofs_contract_test.go index d1cd41b..34bc316 100644 --- a/neofs_contract_test.go +++ b/neofs_contract_test.go @@ -11,7 +11,6 @@ import ( "os" "testing" - "github.com/multiformats/go-multiaddr" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" @@ -145,48 +144,6 @@ func TestContract(t *testing.T) { require.NoError(t, v.Run()) require.False(t, bytes.Contains(plug.mem["InnerRingCandidates"], key)) }) - - t.Run("InnerRingUpdate", func(t *testing.T) { - t.Skip("implement getIRExcludeCheque without neofs-node dependency") - - pubKey := &test.DecodeKey(4).PublicKey - key := crypto.MarshalPublicKey(pubKey) - plug.setCGASBalance(key, 4000) - - var pubs []*ecdsa.PublicKey - for i := 0; i < len(contract.privs)-1; i++ { - pubs = append(pubs, &contract.privs[i].PublicKey) - } - pubs = append(pubs, pubKey) - cheque := getIRExcludeCheque(t, contract, pubs, 777) - - t.Run("Try without candidate", func(t *testing.T) { - v := initVM(contract, plug) - loadArg(t, v, "InnerRingUpdate", []interface{}{cheque}) - require.Error(t, v.Run()) - }) - - v := initVM(contract, plug) - loadArg(t, v, "InnerRingCandidateAdd", []interface{}{"addrX", key}) - require.NoError(t, v.Run()) - - v = initVM(contract, plug) - loadArg(t, v, "InnerRingUpdate", []interface{}{cheque}) - require.NoError(t, v.Run()) - - for i := 0; i < len(contract.privs)-1; i++ { - require.True(t, bytes.Contains(plug.mem["InnerRingList"], - crypto.MarshalPublicKey(&contract.privs[i].PublicKey))) - } - require.True(t, bytes.Contains(plug.mem["InnerRingList"], key)) - - t.Run("Double InnerRingUpdate", func(t *testing.T) { - newCheque := getIRExcludeCheque(t, contract, pubs, 777) - v = initVM(contract, plug) - loadArg(t, v, "InnerRingUpdate", []interface{}{newCheque}) - require.Error(t, v.Run()) - }) - }) } func getCheque(t *testing.T, c *contract, amount int64) (refs.OwnerID, []byte) { @@ -244,20 +201,6 @@ func getKeys(t *testing.T, n int) []*ecdsa.PrivateKey { return privs } -func getAddrs(t *testing.T, n int) []multiaddr.Multiaddr { - const template = "/dns4/10.120.14.%d/tcp/8080" - - addrs := make([]multiaddr.Multiaddr, n) - for i := range addrs { - var err error - - addrs[i], err = multiaddr.NewMultiaddr(fmt.Sprintf(template, i)) - require.NoError(t, err) - } - - return addrs -} - func mustHex(s string) []byte { result, err := hex.DecodeString(s) if err != nil { From cb00b6133eb690501044a561985a74d91006d403 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 28 May 2020 17:04:32 +0300 Subject: [PATCH 05/25] Update withdraw mechanism Withdraw operation works a bit different with neo:morph. User calls "Deposit" and "Withdraw" methods that produce notifications for the inner ring nodes. "Deposit" method does not produce feedback, but "Withdraw" does. Inner ring nodes check if user eligible to withdraw assets. If so, nodes invoke "Cheque" method. This method collects invocations from inner ring nods. When there are 2/3n + 1 invocations, smart-contract transfers assets back to the user and produces notifications. --- neofs_contract.go | 121 +++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 01984f0..d033a36 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -19,8 +19,7 @@ type ( } check struct { - id []byte - height []byte + id []byte } ) @@ -148,39 +147,74 @@ func Main(op string, args []interface{}) interface{} { runtime.Log("funds have been transferred") + var rcv = []byte{} + if len(args) == 3 { + rcv = args[2].([]byte) // todo: check if rcv value is valid + } + + runtime.Notify("Deposit", pk, amount, rcv) + return true case "Withdraw": - from := engine.GetExecutingScriptHash() - data := args[0].([]byte) - message := data[0:66] - uuid := data[0:25] - owner := data[25:50] - value := data[50:58] - height := data[58:66] - offset := 68 - usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) - - c := check{ - id: uuid, - height: height, - } - if containsCheck(usedList, c) { - panic("verification check has already been used") + if len(args) != 2 { + panic("withdraw: bad arguments") } + user := args[0].([]byte) + if !runtime.CheckWitness(user) { + // todo: consider something different with neoID + panic("withdraw: you should be the owner of the account") + } + + amount := args[1].(int) + if amount > 0 { + amount = amount * 100000000 + } + + runtime.Notify("Withdraw", user, amount) + + return true + case "Cheque": + if len(args) != 4 { + panic("cheque: bad arguments") + } + + id := args[0].([]byte) // unique cheque id + user := args[1].([]byte) // GAS receiver + amount := args[2].(int) // amount of GAS + lockAcc := args[3].([]byte) // lock account from internal banking that must be cashed out + + ctx := storage.GetContext() + + hashID := crypto.Hash256(id) irList := getSerialized(ctx, "InnerRingList").([]node) - if !verifySignatures(irList, data, message, offset) { - panic("can't verify signatures") + usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) + threshold := len(irList)/3*2 + 1 + + if !isInnerRingRequest(irList) { + panic("cheque: invoked by non inner ring node") } - h := owner[1:21] - params := []interface{}{from, h, value} - transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) - if !transferred { - panic("failed to transfer funds, aborting") + c := check{id: id} // todo: use different cheque id for inner ring update and withdraw + if containsCheck(usedList, c) { + panic("cheque: non unique id") } - putSerialized(ctx, "UsedVerifCheckList", c) + n := vote(ctx, hashID) + if n >= threshold { + removeVotes(ctx, hashID) + + from := engine.GetExecutingScriptHash() + params := []interface{}{from, user, amount} + + transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) + if !transferred { + panic("cheque: failed to transfer funds, aborting") + } + + putSerialized(ctx, "UsedVerifCheckList", c) + runtime.Notify("Cheque", id, user, amount, lockAcc) + } return true case "InnerRingUpdate": @@ -387,41 +421,6 @@ func delSerializedIR(ctx storage.Context, key string, value []byte) bool { runtime.Log("target element has not been removed") return false } -func verifySignatures(irList []node, data []byte, message []byte, offset int) bool { - n := len(irList) - f := (n - 1) / 3 - s := n - f - if s < 3 { - runtime.Log("not enough inner ring nodes for consensus") - return false - } - - used := [][]byte{} - count := 0 - for count < s && offset < len(data) { - pubkey := data[offset : offset+33] - signature := data[offset+33 : offset+97] - if containsPub(irList, pubkey) { - if crypto.VerifySignature(message, signature, pubkey) { - count++ - for i := 0; i < len(used); i++ { - if util.Equals(used[i], pubkey) { - panic("duplicate public keys") - } - } - used = append(used, pubkey) - } - } - offset += 97 - } - - if count >= s { - return true - } - - runtime.Log("not enough verified signatures") - return false -} // isInnerRingRequest returns true if contract was invoked by inner ring node. func isInnerRingRequest(irList []node) bool { From 893de17372d88e6428d9d9fe48446458c0d1e584 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 28 May 2020 17:05:39 +0300 Subject: [PATCH 06/25] Update tests for new withdraw mechanism --- neofs_contract_test.go | 83 ++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/neofs_contract_test.go b/neofs_contract_test.go index 34bc316..fbe2700 100644 --- a/neofs_contract_test.go +++ b/neofs_contract_test.go @@ -18,9 +18,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" - "github.com/nspcc-dev/neofs-api-go/accounting" - "github.com/nspcc-dev/neofs-api-go/decimal" - "github.com/nspcc-dev/neofs-api-go/refs" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" @@ -73,25 +70,50 @@ func TestContract(t *testing.T) { require.Equal(t, before+util.Fixed8FromInt64(1000), plug.cgas[contractStr]) require.Equal(t, util.Fixed8FromInt64(3000), plug.cgas[string(mustPKey(key).GetScriptHash().BytesBE())]) + require.True(t, len(plug.notify) > 0) + require.True(t, bytes.Equal([]byte("Deposit"), plug.notify[len(plug.notify)-1].([]byte))) }) t.Run("Withdraw", func(t *testing.T) { const amount = 21 + key := mustHex("031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a") v := initVM(contract, plug) - owner, cheque := getCheque(t, contract, amount) - ownerStr := string(owner[1:21]) + loadArg(t, v, "Withdraw", []interface{}{key, amount}) + require.NoError(t, v.Run()) + require.True(t, bytes.Equal([]byte("Withdraw"), plug.notify[len(plug.notify)-1].([]byte))) + }) + + t.Run("Cheque", func(t *testing.T) { + const amount = 21 + + user := randScriptHash() + lockAcc := randScriptHash() contractGas := plug.cgas[contractStr] - loadArg(t, v, "Withdraw", []interface{}{cheque}) - require.NoError(t, v.Run()) - require.Equal(t, contractGas-util.Fixed8FromInt64(amount), plug.cgas[contractStr]) - require.Equal(t, util.Fixed8FromInt64(amount), plug.cgas[ownerStr]) - - t.Run("Double Withdraw", func(t *testing.T) { + // call it threshold amount of times + for i := 0; i < 2*nodeCount/3+1; i++ { v := initVM(contract, plug) - loadArg(t, v, "Withdraw", []interface{}{cheque}) + loadArg(t, v, "Cheque", []interface{}{ + []byte("id"), + user, + int(util.Fixed8FromInt64(amount)), + lockAcc}) + + require.NoError(t, v.Run()) + } + + require.Equal(t, contractGas-util.Fixed8FromInt64(amount), plug.cgas[contractStr]) + require.Equal(t, util.Fixed8FromInt64(amount), plug.cgas[string(user)]) + require.True(t, len(plug.notify) > 0) + require.True(t, bytes.Equal([]byte("Cheque"), plug.notify[len(plug.notify)-1].([]byte))) + + t.Run("Double cheque", func(t *testing.T) { + v := initVM(contract, plug) + + loadArg(t, v, "Cheque", []interface{}{ + []byte("id"), user, amount, lockAcc}) require.Error(t, v.Run()) }) }) @@ -146,37 +168,6 @@ func TestContract(t *testing.T) { }) } -func getCheque(t *testing.T, c *contract, amount int64) (refs.OwnerID, []byte) { - id, err := accounting.NewChequeID() - require.NoError(t, err) - - priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - owner, err := refs.NewOwnerID(&priv.PublicKey) - require.NoError(t, err) - - cheque := accounting.Cheque{ - ID: id, - Owner: owner, - Amount: decimal.NewGAS(amount), - Height: 0, - } - - for _, key := range c.privs { - require.NoError(t, cheque.Sign(key)) - } - - data, err := cheque.MarshalBinary() - require.NoError(t, err) - - return owner, data -} - -func getIRExcludeCheque(t *testing.T, c *contract, pub []*ecdsa.PublicKey, id uint64) []byte { - panic("implement without neofs-node dependency") -} - func initGoContract(t *testing.T, path string, n int) *contract { f, err := os.Open(path) require.NoError(t, err) @@ -201,6 +192,12 @@ func getKeys(t *testing.T, n int) []*ecdsa.PrivateKey { return privs } +func randScriptHash() []byte { + var scriptHash = make([]byte, 20) + rand.Read(scriptHash) + return scriptHash +} + func mustHex(s string) []byte { result, err := hex.DecodeString(s) if err != nil { From 2f3cfee6ae308aa707c6cbaa1bd93a14b8b661da Mon Sep 17 00:00:00 2001 From: alexvanin Date: Fri, 29 May 2020 11:41:01 +0300 Subject: [PATCH 07/25] Update neo-go library to v0.75.0 This commit updates neo-go library and adds notification check for "Deposit", "Withdraw" and "Cheque" methods. --- go.mod | 9 ++- go.sum | 149 +++++++---------------------------------- neofs_contract_test.go | 66 +++++++++++------- 3 files changed, 72 insertions(+), 152 deletions(-) diff --git a/go.mod b/go.mod index d0e2ed3..5af5e22 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,12 @@ module github.com/nspcc-dev/neofs-contract go 1.12 require ( - github.com/multiformats/go-multiaddr v0.2.1 - github.com/nspcc-dev/neo-go v0.74.0 - github.com/nspcc-dev/neofs-api-go v0.5.0 + github.com/mr-tron/base58 v1.1.3 // indirect + github.com/nspcc-dev/neo-go v0.75.0 github.com/nspcc-dev/neofs-crypto v0.3.0 + github.com/pkg/errors v0.9.1 // indirect github.com/stretchr/testify v1.5.1 + golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect + golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 // indirect + gopkg.in/yaml.v2 v2.2.5 // indirect ) diff --git a/go.sum b/go.sum index 40ff7c6..2fafa27 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0= github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig= github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I= github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA= +github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= @@ -15,35 +15,29 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/awalterschulze/gographviz v0.0.0-20181013152038-b2885df04310/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/dgraph-io/badger/v2 v2.0.3/go.mod h1:3KY8+bsP8wI0OEnQJAKpd4wIJW/Mm32yw2j/9FUVnIM= +github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -53,47 +47,21 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -101,19 +69,14 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 h1:MHkK1uRtFbVqvAgvWxafZe54+5uBxLluGylDiKgdhwo= -github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -122,35 +85,22 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/multiformats/go-multiaddr v0.2.1 h1:SgG/cw5vqyB5QQe5FPe2TqggU9WtrA9X4nZw7LlVqOI= -github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= -github.com/multiformats/go-multihash v0.0.13 h1:06x+mk/zj1FoMsgNejLpy6QTvJqlSt/BhLEy87zidlc= -github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg= -github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= github.com/nspcc-dev/dbft v0.0.0-20200303183127-36d3da79c682/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= -github.com/nspcc-dev/hrw v1.0.8/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= -github.com/nspcc-dev/neo-go v0.74.0 h1:Y/XwUuA2aJNl1BG7KpyDO7mTrhMAlJzMUocBpIRQ2qQ= -github.com/nspcc-dev/neo-go v0.74.0/go.mod h1:XK86lR5XNQYlkiR9g6R6zsjrMRsGy9yduYq7ft3uECc= -github.com/nspcc-dev/neofs-api-go v0.5.0 h1:YGX2lEjFmiFYmk8gCkLINJjSh6hToKp2oo0sG5u8G+4= -github.com/nspcc-dev/neofs-api-go v0.5.0/go.mod h1:RlP++uLPHmXN81KWmdgBujo6FEeTn/b3aoQjHgKuNd4= +github.com/nspcc-dev/neo-go v0.75.0 h1:JX87gmRa0qJ7bMIDwqroQPEz7Y81feinvXq2pbKfpNE= +github.com/nspcc-dev/neo-go v0.75.0/go.mod h1:wtCZzd6wm1z+FKrZZD4q2wyUvWmwRx9y9v2N9i0NVM4= 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/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM= github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= -github.com/nspcc-dev/netmap v1.6.1/go.mod h1:mhV3UOg9ljQmu0teQShD6+JYX09XY5gu2I4hIByCH9M= github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= -github.com/nspcc-dev/tzhash v1.4.0/go.mod h1:Z8gp/VZbyWgPhaMp/KTmeoW5UTynp/N60g0jTtSzBws= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -163,39 +113,28 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -203,103 +142,63 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/neofs_contract_test.go b/neofs_contract_test.go index fbe2700..1f49852 100644 --- a/neofs_contract_test.go +++ b/neofs_contract_test.go @@ -59,34 +59,47 @@ func TestContract(t *testing.T) { require.Error(t, v.Run()) t.Run("Deposit", func(t *testing.T) { - v := initVM(contract, plug) + const ( + amount = 1000 + balance = 4000 + ) + before := plug.cgas[contractStr] + gas := util.Fixed8FromInt64(amount) - key := mustHex("031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a") - plug.setCGASBalance(key, 4000) + key, err := keys.NewPublicKeyFromString("031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a") + require.NoError(t, err) - loadArg(t, v, "Deposit", []interface{}{key, 1000}) + plug.setCGASBalance(key.Bytes(), balance) + + v := initVM(contract, plug) + loadArg(t, v, "Deposit", []interface{}{key.Bytes(), int(gas.IntegralValue())}) require.NoError(t, v.Run()) - require.Equal(t, before+util.Fixed8FromInt64(1000), plug.cgas[contractStr]) - require.Equal(t, util.Fixed8FromInt64(3000), plug.cgas[string(mustPKey(key).GetScriptHash().BytesBE())]) - require.True(t, len(plug.notify) > 0) - require.True(t, bytes.Equal([]byte("Deposit"), plug.notify[len(plug.notify)-1].([]byte))) + require.Equal(t, before+gas, plug.cgas[contractStr]) + require.Equal(t, util.Fixed8FromInt64(balance-amount), plug.cgas[string(key.GetScriptHash().BytesBE())]) + checkNotification(t, plug.notify, []byte("Deposit"), key.Bytes(), big.NewInt(int64(gas)), []byte{}) }) t.Run("Withdraw", func(t *testing.T) { const amount = 21 - key := mustHex("031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a") + + gas := util.Fixed8FromInt64(amount) + + key, err := keys.NewPublicKeyFromString("031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a") + require.NoError(t, err) v := initVM(contract, plug) - loadArg(t, v, "Withdraw", []interface{}{key, amount}) + loadArg(t, v, "Withdraw", []interface{}{key.Bytes(), amount}) require.NoError(t, v.Run()) - require.True(t, bytes.Equal([]byte("Withdraw"), plug.notify[len(plug.notify)-1].([]byte))) + checkNotification(t, plug.notify, []byte("Withdraw"), key.Bytes(), big.NewInt(int64(gas))) }) t.Run("Cheque", func(t *testing.T) { const amount = 21 + id := []byte("id") + gas := util.Fixed8FromInt64(amount) user := randScriptHash() lockAcc := randScriptHash() contractGas := plug.cgas[contractStr] @@ -95,25 +108,18 @@ func TestContract(t *testing.T) { for i := 0; i < 2*nodeCount/3+1; i++ { v := initVM(contract, plug) - loadArg(t, v, "Cheque", []interface{}{ - []byte("id"), - user, - int(util.Fixed8FromInt64(amount)), - lockAcc}) - + loadArg(t, v, "Cheque", []interface{}{id, user, int(gas), lockAcc}) require.NoError(t, v.Run()) } - require.Equal(t, contractGas-util.Fixed8FromInt64(amount), plug.cgas[contractStr]) - require.Equal(t, util.Fixed8FromInt64(amount), plug.cgas[string(user)]) - require.True(t, len(plug.notify) > 0) - require.True(t, bytes.Equal([]byte("Cheque"), plug.notify[len(plug.notify)-1].([]byte))) + require.Equal(t, contractGas-gas, plug.cgas[contractStr]) + require.Equal(t, gas, plug.cgas[string(user)]) + checkNotification(t, plug.notify, []byte("Cheque"), id, user, big.NewInt(int64(gas)), lockAcc) t.Run("Double cheque", func(t *testing.T) { v := initVM(contract, plug) - loadArg(t, v, "Cheque", []interface{}{ - []byte("id"), user, amount, lockAcc}) + loadArg(t, v, "Cheque", []interface{}{id, user, int(gas), lockAcc}) require.Error(t, v.Run()) }) }) @@ -229,7 +235,7 @@ func loadArg(t *testing.T, v *vm.VM, operation string, params []interface{}) { func toStackItem(v interface{}) vm.StackItem { switch val := v.(type) { case int: - return vm.NewBigIntegerItem(val) + return vm.NewBigIntegerItem(int64(val)) case string: return vm.NewByteArrayItem([]byte(val)) case []byte: @@ -448,3 +454,15 @@ func (s *storagePlugin) GetContext(v *vm.VM) error { v.Estack().PushVal(10) return nil } + +func checkNotification(t *testing.T, store []interface{}, args ...interface{}) { + ln := len(store) + require.True(t, ln > 0) + + notification := store[ln-1].([]interface{}) + require.Equal(t, len(args), len(notification)) + + for i := range args { + require.Equal(t, args[i], notification[i]) + } +} From 19db32b6549face968969f7047a16abf32a13141 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Fri, 29 May 2020 11:44:15 +0300 Subject: [PATCH 08/25] Add tx hash for "Deposit" and "Withdraw" notifications As the user can invoke several deposit or withdraw calls inner ring nodes should be able to differ them even if they have the same arguments. To do that neofs-contract notifies about tx hash, which can be used as unique identifier of withdraw or deposit operation. --- neofs_contract.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index d033a36..6d70638 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -5,6 +5,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/engine" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/storage" + "github.com/nspcc-dev/neo-go/pkg/interop/transaction" "github.com/nspcc-dev/neo-go/pkg/interop/util" ) @@ -152,7 +153,10 @@ func Main(op string, args []interface{}) interface{} { rcv = args[2].([]byte) // todo: check if rcv value is valid } - runtime.Notify("Deposit", pk, amount, rcv) + tx := engine.GetScriptContainer() + txHash := transaction.GetHash(tx) + + runtime.Notify("Deposit", pk, amount, rcv, txHash) return true case "Withdraw": @@ -171,7 +175,10 @@ func Main(op string, args []interface{}) interface{} { amount = amount * 100000000 } - runtime.Notify("Withdraw", user, amount) + tx := engine.GetScriptContainer() + txHash := transaction.GetHash(tx) + + runtime.Notify("Withdraw", user, amount, txHash) return true case "Cheque": From 2efabc95c2e67a54350a5f44195a880166e07898 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Fri, 29 May 2020 11:51:28 +0300 Subject: [PATCH 09/25] Check tx hash in notification assert To compile contract in test environment there are two more mocks for GetScriptContainer and GetHash interops. --- neofs_contract_test.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/neofs_contract_test.go b/neofs_contract_test.go index 1f49852..75719f5 100644 --- a/neofs_contract_test.go +++ b/neofs_contract_test.go @@ -29,6 +29,7 @@ var ( contractHash = util.Uint160{0x1, 0x2, 0x3, 0x4} // token hash is not random to run tests of .avm or .go files contractStr = string(contractHash[:]) + txHash = mustHex("3ca2575bd90129e3730c46ba3f163fcfd5fff11eaedb2b6aa3d76bd03ab8a890") ) type contract struct { @@ -78,7 +79,7 @@ func TestContract(t *testing.T) { require.Equal(t, before+gas, plug.cgas[contractStr]) require.Equal(t, util.Fixed8FromInt64(balance-amount), plug.cgas[string(key.GetScriptHash().BytesBE())]) - checkNotification(t, plug.notify, []byte("Deposit"), key.Bytes(), big.NewInt(int64(gas)), []byte{}) + checkNotification(t, plug.notify, []byte("Deposit"), key.Bytes(), big.NewInt(int64(gas)), []byte{}, txHash) }) t.Run("Withdraw", func(t *testing.T) { @@ -92,7 +93,7 @@ func TestContract(t *testing.T) { v := initVM(contract, plug) loadArg(t, v, "Withdraw", []interface{}{key.Bytes(), amount}) require.NoError(t, v.Run()) - checkNotification(t, plug.notify, []byte("Withdraw"), key.Bytes(), big.NewInt(int64(gas))) + checkNotification(t, plug.notify, []byte("Withdraw"), key.Bytes(), big.NewInt(int64(gas)), txHash) }) t.Run("Cheque", func(t *testing.T) { @@ -287,6 +288,8 @@ func newStoragePlugin(t *testing.T) *storagePlugin { s.notify = append(s.notify, toInterface(val)) return nil } + s.interops[getID("System.ExecutionEngine.GetScriptContainer")] = s.GetScriptContainer + s.interops[getID("Neo.Transaction.GetHash")] = s.GetHash return s } @@ -410,6 +413,16 @@ func (s *storagePlugin) GetExecutingScriptHash(v *vm.VM) error { return nil } +func (s *storagePlugin) GetScriptContainer(v *vm.VM) error { + v.Estack().PushVal(true) + return nil +} + +func (s *storagePlugin) GetHash(v *vm.VM) error { + v.Estack().PushVal(txHash) + return nil +} + func (s *storagePlugin) logStorage(op string, key, value []byte) { s.storageOps = append(s.storageOps, kv{ Operation: op, From dc84dde87b74339885604eb5c7226f7e4194a1c3 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Mon, 15 Jun 2020 18:54:24 +0300 Subject: [PATCH 10/25] Limit ballot lifetime and store unique inner ring invocations --- neofs_contract.go | 63 +++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 6d70638..f3cd9cd 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -1,6 +1,7 @@ package smart_contract import ( + "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" "github.com/nspcc-dev/neo-go/pkg/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/interop/engine" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" @@ -11,8 +12,9 @@ import ( type ( ballot struct { - id []byte - n int + id []byte // id of the voting decision + n [][]byte // already voted inner ring nodes + block int // block with the last vote } node struct { @@ -30,6 +32,7 @@ const ( innerRingCandidateFee = 100 * 1000 * 1000 // 10^8 version = 1 voteKey = "ballots" + blockDiff = 20 // change base on performance evaluation ) func Main(op string, args []interface{}) interface{} { @@ -198,7 +201,8 @@ func Main(op string, args []interface{}) interface{} { usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) threshold := len(irList)/3*2 + 1 - if !isInnerRingRequest(irList) { + irKey := innerRingInvoker(irList) + if len(irKey) == 0 { panic("cheque: invoked by non inner ring node") } @@ -207,7 +211,7 @@ func Main(op string, args []interface{}) interface{} { panic("cheque: non unique id") } - n := vote(ctx, hashID) + n := vote(ctx, hashID, irKey) if n >= threshold { removeVotes(ctx, hashID) @@ -238,7 +242,8 @@ func Main(op string, args []interface{}) interface{} { usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) threshold := len(irList)/3*2 + 1 - if !isInnerRingRequest(irList) { + irKey := innerRingInvoker(irList) + if len(irKey) == 0 { panic("innerRingUpdate: invoked by non inner ring node") } @@ -249,7 +254,7 @@ func Main(op string, args []interface{}) interface{} { chequeHash := crypto.Hash256(data) - n := vote(ctx, chequeHash) + n := vote(ctx, chequeHash, irKey) if n >= threshold { removeVotes(ctx, chequeHash) @@ -429,38 +434,54 @@ func delSerializedIR(ctx storage.Context, key string, value []byte) bool { return false } -// isInnerRingRequest returns true if contract was invoked by inner ring node. -func isInnerRingRequest(irList []node) bool { - for i := 0; i < len(irList); i++ { - irNode := irList[i] - - if runtime.CheckWitness(irNode.pub) { - return true +// isInnerRingInvoker returns public key of inner ring node that invoked contract. +func innerRingInvoker(ir []node) []byte { + for i := 0; i < len(ir); i++ { + node := ir[i] + if runtime.CheckWitness(node.pub) { + return node.pub } } - return false + return nil } -// todo: votes must be from unique inner ring nods -func vote(ctx storage.Context, id []byte) int { +func vote(ctx storage.Context, id, from []byte) int { var ( - newCandidates = []ballot{} + newCandidates []ballot candidates = getSerialized(ctx, voteKey).([]ballot) found = -1 + blockHeight = blockchain.GetHeight() ) for i := 0; i < len(candidates); i++ { cnd := candidates[i] if util.Equals(cnd.id, id) { - cnd = ballot{id: id, n: cnd.n + 1} - found = cnd.n + voters := cnd.n + + for j := range voters { + if util.Equals(voters[j], from) { + return len(voters) + } + } + + voters = append(voters, from) + cnd = ballot{id: id, n: voters, block: blockHeight} + found = len(voters) + } + + // do not add old ballots, they are invalid + if blockHeight-cnd.block <= blockDiff { + newCandidates = append(newCandidates, cnd) } - newCandidates = append(newCandidates, cnd) } if found < 0 { - newCandidates = append(newCandidates, ballot{id: id, n: 1}) + voters := [][]byte{from} + newCandidates = append(newCandidates, ballot{ + id: id, + n: voters, + block: blockHeight}) found = 1 } From f10d346925de9aa40e614ab3e2743cd4df923d1d Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 17 Jun 2020 12:54:41 +0300 Subject: [PATCH 11/25] Fix tests for unique inner ring invoke checker --- neofs_contract.go | 2 +- neofs_contract_test.go | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index f3cd9cd..56d426a 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -434,7 +434,7 @@ func delSerializedIR(ctx storage.Context, key string, value []byte) bool { return false } -// isInnerRingInvoker returns public key of inner ring node that invoked contract. +// innerRingInvoker returns public key of inner ring node that invoked contract. func innerRingInvoker(ir []node) []byte { for i := 0; i < len(ir); i++ { node := ir[i] diff --git a/neofs_contract_test.go b/neofs_contract_test.go index 75719f5..e9a2736 100644 --- a/neofs_contract_test.go +++ b/neofs_contract_test.go @@ -44,6 +44,7 @@ func TestContract(t *testing.T) { contract := initGoContract(t, contractTemplate, nodeCount) plug.cgas[contractStr] = util.Fixed8FromInt64(1000) + plug.invokeKey = crypto.MarshalPublicKey(&contract.privs[0].PublicKey) var args []interface{} for i := range contract.privs { @@ -107,6 +108,7 @@ func TestContract(t *testing.T) { // call it threshold amount of times for i := 0; i < 2*nodeCount/3+1; i++ { + plug.invokeKey = crypto.MarshalPublicKey(&contract.privs[i].PublicKey) v := initVM(contract, plug) loadArg(t, v, "Cheque", []interface{}{id, user, int(gas), lockAcc}) @@ -260,6 +262,7 @@ type storagePlugin struct { interops map[uint32]vm.InteropFunc storageOps []kv notify []interface{} + invokeKey []byte } func newStoragePlugin(t *testing.T) *storagePlugin { @@ -290,6 +293,7 @@ func newStoragePlugin(t *testing.T) *storagePlugin { } s.interops[getID("System.ExecutionEngine.GetScriptContainer")] = s.GetScriptContainer s.interops[getID("Neo.Transaction.GetHash")] = s.GetHash + s.interops[getID("Neo.Blockchain.GetHeight")] = s.GetHeight return s } @@ -402,7 +406,13 @@ func (s *storagePlugin) GetTrigger(v *vm.VM) error { } func (s *storagePlugin) CheckWitness(v *vm.VM) error { - v.Estack().PushVal(true) + key := v.Estack().Pop().Value().([]byte) + if bytes.Equal(key, s.invokeKey) { + v.Estack().PushVal(true) + } else { + v.Estack().PushVal(false) + } + return nil } @@ -423,6 +433,11 @@ func (s *storagePlugin) GetHash(v *vm.VM) error { return nil } +func (s *storagePlugin) GetHeight(v *vm.VM) error { + v.Estack().PushVal(42) + return nil +} + func (s *storagePlugin) logStorage(op string, key, value []byte) { s.storageOps = append(s.storageOps, kv{ Operation: op, From b9ca56d8914770c6f86678b7d7f974c8e7ee8fd6 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 9 Jul 2020 16:38:53 +0300 Subject: [PATCH 12/25] Update neo-go to v0.90.0-pre and fix smart-contract Neo-go v0.90.0-pre implements neo3 features. --- go.mod | 2 +- go.sum | 9 +++++++ neofs_config.yml | 14 ++--------- neofs_contract.go | 60 +++++++++++++++++++++++++---------------------- 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 5af5e22..a3c06a6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.12 require ( github.com/mr-tron/base58 v1.1.3 // indirect - github.com/nspcc-dev/neo-go v0.75.0 + github.com/nspcc-dev/neo-go v0.90.0-pre.0.20200709120952-d234db9864e2 github.com/nspcc-dev/neofs-crypto v0.3.0 github.com/pkg/errors v0.9.1 // indirect github.com/stretchr/testify v1.5.1 diff --git a/go.sum b/go.sum index 2fafa27..cb18a8f 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0= github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig= github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I= +github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84 h1:gcTXk9aO+PhHudJNPFJ9H4RmKjdzz40Tvv2NE1BwRZ0= github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -37,6 +38,7 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= +github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -54,6 +56,7 @@ github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8l github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -66,6 +69,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -91,9 +95,12 @@ github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= github.com/nspcc-dev/dbft v0.0.0-20200303183127-36d3da79c682/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= +github.com/nspcc-dev/dbft v0.0.0-20200623100921-5a182c20965e/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= github.com/nspcc-dev/neo-go v0.75.0 h1:JX87gmRa0qJ7bMIDwqroQPEz7Y81feinvXq2pbKfpNE= github.com/nspcc-dev/neo-go v0.75.0/go.mod h1:wtCZzd6wm1z+FKrZZD4q2wyUvWmwRx9y9v2N9i0NVM4= +github.com/nspcc-dev/neo-go v0.90.0-pre.0.20200709120952-d234db9864e2 h1:+5yZQtwlTY2klOejyEmPHIkimZBZ3qKaOQ0+CU/2G/I= +github.com/nspcc-dev/neo-go v0.90.0-pre.0.20200709120952-d234db9864e2/go.mod h1:Y27fkOIYUVt2yAoYkb833F45/q6pdLRdeAZKawHcpfE= 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/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM= @@ -106,6 +113,7 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -190,6 +198,7 @@ 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= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/neofs_config.yml b/neofs_config.yml index 84f5d00..7c0479d 100644 --- a/neofs_config.yml +++ b/neofs_config.yml @@ -1,12 +1,2 @@ ---- - -project: - author: Neo SPCC - email: ops@nspcc.ru - version: 0.99 - name: Neofs Smart Contract - hasstorage: true - hasdynamicinvocation: true - ispayable: true - returntype: ByteArray - parameters: ['String', 'Array'] +hasstorage: true +ispayable: false diff --git a/neofs_contract.go b/neofs_contract.go index 56d426a..fe39a33 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -1,12 +1,12 @@ package smart_contract import ( + "github.com/nspcc-dev/neo-go/pkg/interop/binary" "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" "github.com/nspcc-dev/neo-go/pkg/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/interop/engine" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/storage" - "github.com/nspcc-dev/neo-go/pkg/interop/transaction" "github.com/nspcc-dev/neo-go/pkg/interop/util" ) @@ -79,14 +79,14 @@ func Main(op string, args []interface{}) interface{} { irList = append(irList, node{pub: pub}) } - data := runtime.Serialize(irList) + data := binary.Serialize(irList) storage.Put(ctx, "InnerRingList", data) - data = runtime.Serialize([]interface{}{}) + data = binary.Serialize([]interface{}{}) storage.Put(ctx, "UsedVerifCheckList", data) storage.Put(ctx, "InnerRingCandidates", data) - data = runtime.Serialize([]ballot{}) + data = binary.Serialize([]ballot{}) storage.Put(ctx, voteKey, data) return true @@ -116,7 +116,7 @@ func Main(op string, args []interface{}) interface{} { } from := pubToScriptHash(key) - to := engine.GetExecutingScriptHash() + to := runtime.GetExecutingScriptHash() params := []interface{}{from, to, innerRingCandidateFee} transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) @@ -142,7 +142,7 @@ func Main(op string, args []interface{}) interface{} { } from := pubToScriptHash(pk) - to := engine.GetExecutingScriptHash() + to := runtime.GetExecutingScriptHash() params := []interface{}{from, to, amount} transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) if !transferred { @@ -156,8 +156,7 @@ func Main(op string, args []interface{}) interface{} { rcv = args[2].([]byte) // todo: check if rcv value is valid } - tx := engine.GetScriptContainer() - txHash := transaction.GetHash(tx) + txHash := runtime.GetScriptContainer().Hash runtime.Notify("Deposit", pk, amount, rcv, txHash) @@ -178,8 +177,7 @@ func Main(op string, args []interface{}) interface{} { amount = amount * 100000000 } - tx := engine.GetScriptContainer() - txHash := transaction.GetHash(tx) + txHash := runtime.GetScriptContainer().Hash runtime.Notify("Withdraw", user, amount, txHash) @@ -196,7 +194,7 @@ func Main(op string, args []interface{}) interface{} { ctx := storage.GetContext() - hashID := crypto.Hash256(id) + hashID := crypto.SHA256(id) irList := getSerialized(ctx, "InnerRingList").([]node) usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) threshold := len(irList)/3*2 + 1 @@ -215,7 +213,7 @@ func Main(op string, args []interface{}) interface{} { if n >= threshold { removeVotes(ctx, hashID) - from := engine.GetExecutingScriptHash() + from := runtime.GetExecutingScriptHash() params := []interface{}{from, user, amount} transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) @@ -252,7 +250,7 @@ func Main(op string, args []interface{}) interface{} { panic("innerRingUpdate: cheque has non unique id") } - chequeHash := crypto.Hash256(data) + chequeHash := crypto.SHA256(data) n := vote(ctx, chequeHash, irKey) if n >= threshold { @@ -292,7 +290,7 @@ func Main(op string, args []interface{}) interface{} { delSerializedIR(ctx, "InnerRingCandidates", n.pub) } - newIRData := runtime.Serialize(newIR) + newIRData := binary.Serialize(newIR) storage.Put(ctx, "InnerRingList", newIRData) putSerialized(ctx, "UsedVerifCheckList", c) @@ -327,10 +325,11 @@ func Main(op string, args []interface{}) interface{} { panic("unknown operation") } +// fixme: use strict type deserialization wrappers func getSerialized(ctx storage.Context, key string) interface{} { data := storage.Get(ctx, key).([]byte) if len(data) != 0 { - return runtime.Deserialize(data) + return binary.Deserialize(data) } return nil } @@ -341,7 +340,7 @@ func delSerialized(ctx storage.Context, key string, value []byte) bool { var newList [][]byte if len(data) != 0 { - lst := runtime.Deserialize(data).([][]byte) + lst := binary.Deserialize(data).([][]byte) for i := 0; i < len(lst); i++ { if util.Equals(value, lst[i]) { deleted = true @@ -351,7 +350,7 @@ func delSerialized(ctx storage.Context, key string, value []byte) bool { } if deleted { if len(newList) != 0 { - data := runtime.Serialize(newList) + data := binary.Serialize(newList) storage.Put(ctx, key, data) } else { storage.Delete(ctx, key) @@ -371,22 +370,27 @@ func putSerialized(ctx storage.Context, key string, value interface{}) bool { var lst []interface{} if len(data) != 0 { - lst = runtime.Deserialize(data).([]interface{}) + lst = binary.Deserialize(data).([]interface{}) } lst = append(lst, value) - data = runtime.Serialize(lst) + data = binary.Serialize(lst) storage.Put(ctx, key, data) return true } func pubToScriptHash(pkey []byte) []byte { - pre := []byte{0x21} - buf := append(pre, pkey...) - buf = append(buf, 0xac) - h := crypto.Hash160(buf) - return h + // pre := []byte{0x21} + // buf := append(pre, pkey...) + // buf = append(buf, 0xac) + // h := crypto.Hash160(buf) + // + // return h + + // fixme: someday ripemd syscall will appear + // or simply store script-hashes along with public key + return []byte{0x0F, 0xED} } func containsCheck(lst []check, c check) bool { @@ -413,7 +417,7 @@ func delSerializedIR(ctx storage.Context, key string, value []byte) bool { newList := []node{} if len(data) != 0 { - lst := runtime.Deserialize(data).([]node) + lst := binary.Deserialize(data).([]node) for i := 0; i < len(lst); i++ { n := lst[i] if util.Equals(value, n.pub) { @@ -423,7 +427,7 @@ func delSerializedIR(ctx storage.Context, key string, value []byte) bool { } } if deleted { - data := runtime.Serialize(newList) + data := binary.Serialize(newList) storage.Put(ctx, key, data) runtime.Log("target element has been removed") return true @@ -485,7 +489,7 @@ func vote(ctx storage.Context, id, from []byte) int { found = 1 } - data := runtime.Serialize(newCandidates) + data := binary.Serialize(newCandidates) storage.Put(ctx, voteKey, data) return found @@ -504,6 +508,6 @@ func removeVotes(ctx storage.Context, id []byte) { } } - data := runtime.Serialize(newCandidates) + data := binary.Serialize(newCandidates) storage.Put(ctx, voteKey, data) } From 62ba54e88eec87bb2aeed65d2592beedebfb9199 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 15 Jul 2020 11:47:29 +0300 Subject: [PATCH 13/25] Update neo-go to v0.90.0 and Makefile --- .gitignore | 2 ++ Makefile | 2 +- go.mod | 2 +- go.sum | 48 ++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 4e905e7..b1fc9d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.avm +*.nef +config.json /vendor/ diff --git a/Makefile b/Makefile index 17946ba..d60ae64 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ NEOGO?=neo-go .PHONY: build tests build: - $(NEOGO) contract compile -i neofs_contract.go + $(NEOGO) contract compile -i neofs_contract.go -c neofs_config.yml -m config.json tests: go mod vendor diff --git a/go.mod b/go.mod index a3c06a6..f19f3f6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.12 require ( github.com/mr-tron/base58 v1.1.3 // indirect - github.com/nspcc-dev/neo-go v0.90.0-pre.0.20200709120952-d234db9864e2 + github.com/nspcc-dev/neo-go v0.90.0 github.com/nspcc-dev/neofs-crypto v0.3.0 github.com/pkg/errors v0.9.1 // indirect github.com/stretchr/testify v1.5.1 diff --git a/go.sum b/go.sum index cb18a8f..7a325db 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,11 @@ github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1 github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I= github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84 h1:gcTXk9aO+PhHudJNPFJ9H4RmKjdzz40Tvv2NE1BwRZ0= github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA= +github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo= github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= @@ -13,13 +16,18 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U= github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI= github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA= github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -31,19 +39,25 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v2 v2.0.3 h1:inzdf6VF/NZ+tJ8RwwYMjJMvsOALTHYdozn0qSl6XJI= github.com/dgraph-io/badger/v2 v2.0.3/go.mod h1:3KY8+bsP8wI0OEnQJAKpd4wIJW/Mm32yw2j/9FUVnIM= +github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3 h1:MQLRM35Pp0yAyBYksjbj1nZI/w6eyRY/mWoM1sFf4kU= github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-redis/redis v6.10.2+incompatible h1:SLbqrO/Ik1nhXA5/cbEs1P5MUBo1Qq4ihlNfGnnipPw= github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= @@ -51,15 +65,20 @@ github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaL github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -69,6 +88,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= @@ -79,6 +99,7 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -94,13 +115,11 @@ github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+d github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= -github.com/nspcc-dev/dbft v0.0.0-20200303183127-36d3da79c682/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= -github.com/nspcc-dev/dbft v0.0.0-20200623100921-5a182c20965e/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= +github.com/nspcc-dev/dbft v0.0.0-20200711144034-c526ccc6f570 h1:EHBwlOyd2m06C3dnxhpPokpYqlNg7u5ZX/uPBhjYuZ4= +github.com/nspcc-dev/dbft v0.0.0-20200711144034-c526ccc6f570/go.mod h1:1FYQXSbb6/9HQIkoF8XO7W/S8N7AZRkBsgwbcXRvk0E= github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= -github.com/nspcc-dev/neo-go v0.75.0 h1:JX87gmRa0qJ7bMIDwqroQPEz7Y81feinvXq2pbKfpNE= -github.com/nspcc-dev/neo-go v0.75.0/go.mod h1:wtCZzd6wm1z+FKrZZD4q2wyUvWmwRx9y9v2N9i0NVM4= -github.com/nspcc-dev/neo-go v0.90.0-pre.0.20200709120952-d234db9864e2 h1:+5yZQtwlTY2klOejyEmPHIkimZBZ3qKaOQ0+CU/2G/I= -github.com/nspcc-dev/neo-go v0.90.0-pre.0.20200709120952-d234db9864e2/go.mod h1:Y27fkOIYUVt2yAoYkb833F45/q6pdLRdeAZKawHcpfE= +github.com/nspcc-dev/neo-go v0.90.0 h1:ABNDrJuF9C1XuLQu0q9DKSVMlg9eQn/g6rX8Jbr31bo= +github.com/nspcc-dev/neo-go v0.90.0/go.mod h1:pPFdnApJwUSRAnpdiPBZl7I7jv0doDg5naecpSPK4+Q= 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/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM= @@ -109,8 +128,10 @@ github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452 github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -122,14 +143,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -150,16 +175,22 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73 h1:I2drr5K0tykBofr74ZEGliE/Hf6fNkEbcPyFvsy7wZk= github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= +github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7 h1:Y17pEjKgx2X0A69WQPGa8hx/Myzu+4NdUxlkZpbAYio= github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -173,6 +204,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -192,19 +224,23 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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/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= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From ca54f71d300aa60a3909f794090aab9f11f93dbc Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 15 Jul 2020 11:51:49 +0300 Subject: [PATCH 14/25] Update "Deposit" method Deposit method now takes script hashes instead of public keys and uses native gas token. --- neofs_contract.go | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index fe39a33..6f103b2 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -27,10 +27,9 @@ type ( ) const ( - // GAS NEP-5 HASH - tokenHash = "\x77\xea\x59\x6b\x7a\xdf\x7e\x4d\xd1\x40\x76\x97\x31\xb7\xd2\xf0\xe0\x6b\xcd\x9b" + tokenHash = "\x3b\x7d\x37\x11\xc6\xf0\xcc\xf9\xb1\xdc\xa9\x03\xd1\xbf\xa1\xd8\x96\xf1\x23\x8c" innerRingCandidateFee = 100 * 1000 * 1000 // 10^8 - version = 1 + version = 2 voteKey = "ballots" blockDiff = 20 // change base on performance evaluation ) @@ -131,9 +130,13 @@ func Main(op string, args []interface{}) interface{} { return true case "Deposit": - pk := args[0].([]byte) - if !runtime.CheckWitness(pk) { - panic("you should be the owner of the public key") + if len(args) < 2 || len(args) > 3 { + panic("deposit: bad arguments") + } + + from := args[0].([]byte) + if !runtime.CheckWitness(from) { + panic("deposit: you should be the owner of the wallet") } amount := args[1].(int) @@ -141,24 +144,23 @@ func Main(op string, args []interface{}) interface{} { amount = amount * 100000000 } - from := pubToScriptHash(pk) to := runtime.GetExecutingScriptHash() params := []interface{}{from, to, amount} + transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) if !transferred { - panic("failed to transfer funds, aborting") + panic("deposit: failed to transfer funds, aborting") } - runtime.Log("funds have been transferred") + runtime.Log("deposit: funds have been transferred") - var rcv = []byte{} + var rcv = from if len(args) == 3 { rcv = args[2].([]byte) // todo: check if rcv value is valid } - txHash := runtime.GetScriptContainer().Hash - - runtime.Notify("Deposit", pk, amount, rcv, txHash) + tx := runtime.GetScriptContainer() + runtime.Notify("Deposit", from, amount, rcv, tx.Hash) return true case "Withdraw": From bf8b6f91d751423a21bee132700ac6ef972127fd Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 15 Jul 2020 12:05:03 +0300 Subject: [PATCH 15/25] Fix withdraw log message --- neofs_contract.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 6f103b2..9f39b66 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -170,8 +170,7 @@ func Main(op string, args []interface{}) interface{} { user := args[0].([]byte) if !runtime.CheckWitness(user) { - // todo: consider something different with neoID - panic("withdraw: you should be the owner of the account") + panic("withdraw: you should be the owner of the wallet") } amount := args[1].(int) @@ -179,9 +178,8 @@ func Main(op string, args []interface{}) interface{} { amount = amount * 100000000 } - txHash := runtime.GetScriptContainer().Hash - - runtime.Notify("Withdraw", user, amount, txHash) + tx := runtime.GetScriptContainer() + runtime.Notify("Withdraw", user, amount, tx.Hash) return true case "Cheque": From 2f86e8f5d0fcad5dcba0439a50a417ad11e47e6e Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 15 Jul 2020 19:17:34 +0300 Subject: [PATCH 16/25] Rename check structure to cheque Cheque is widely used in neofs as a definition of structure that connected with configuration changing or asset withdraw. This name is used to avoid confusion with 'check' with verification meaning. --- neofs_contract.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 9f39b66..c582d7d 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -21,7 +21,7 @@ type ( pub []byte } - check struct { + cheque struct { id []byte } ) @@ -196,7 +196,7 @@ func Main(op string, args []interface{}) interface{} { hashID := crypto.SHA256(id) irList := getSerialized(ctx, "InnerRingList").([]node) - usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) + usedList := getSerialized(ctx, "UsedVerifCheckList").([]cheque) threshold := len(irList)/3*2 + 1 irKey := innerRingInvoker(irList) @@ -204,7 +204,7 @@ func Main(op string, args []interface{}) interface{} { panic("cheque: invoked by non inner ring node") } - c := check{id: id} // todo: use different cheque id for inner ring update and withdraw + c := cheque{id: id} // todo: use different cheque id for inner ring update and withdraw if containsCheck(usedList, c) { panic("cheque: non unique id") } @@ -237,7 +237,7 @@ func Main(op string, args []interface{}) interface{} { offset := 8 + 2 + listSize irList := getSerialized(ctx, "InnerRingList").([]node) - usedList := getSerialized(ctx, "UsedVerifCheckList").([]check) + usedList := getSerialized(ctx, "UsedVerifCheckList").([]cheque) threshold := len(irList)/3*2 + 1 irKey := innerRingInvoker(irList) @@ -245,7 +245,7 @@ func Main(op string, args []interface{}) interface{} { panic("innerRingUpdate: invoked by non inner ring node") } - c := check{id: id} + c := cheque{id: id} if containsCheck(usedList, c) { panic("innerRingUpdate: cheque has non unique id") } @@ -393,7 +393,7 @@ func pubToScriptHash(pkey []byte) []byte { return []byte{0x0F, 0xED} } -func containsCheck(lst []check, c check) bool { +func containsCheck(lst []cheque, c cheque) bool { for i := 0; i < len(lst); i++ { if util.Equals(c, lst[i]) { return true From 209740f060994559b2bdba2d757c925350296574 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 15 Jul 2020 19:21:51 +0300 Subject: [PATCH 17/25] Use generic setter and type defined getters With NEO3.0 there is a null pointers in VM, so we can use it to check if value was stored in cotnract storage or not. --- neofs_contract.go | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index c582d7d..9e893e9 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -453,18 +453,18 @@ func innerRingInvoker(ir []node) []byte { func vote(ctx storage.Context, id, from []byte) int { var ( newCandidates []ballot - candidates = getSerialized(ctx, voteKey).([]ballot) + candidates = getBallots(ctx) found = -1 blockHeight = blockchain.GetHeight() ) for i := 0; i < len(candidates); i++ { cnd := candidates[i] - if util.Equals(cnd.id, id) { + if bytesEqual(cnd.id, id) { voters := cnd.n for j := range voters { - if util.Equals(voters[j], from) { + if bytesEqual(voters[j], from) { return len(voters) } } @@ -481,33 +481,54 @@ func vote(ctx storage.Context, id, from []byte) int { } if found < 0 { + found = 1 voters := [][]byte{from} + newCandidates = append(newCandidates, ballot{ id: id, n: voters, block: blockHeight}) - found = 1 } - data := binary.Serialize(newCandidates) - storage.Put(ctx, voteKey, data) + setSerialized(ctx, voteKey, newCandidates) return found } func removeVotes(ctx storage.Context, id []byte) { var ( - newCandidates = []ballot{} - candidates = getSerialized(ctx, voteKey).([]ballot) + newCandidates []ballot + candidates = getBallots(ctx) ) for i := 0; i < len(candidates); i++ { cnd := candidates[i] - if !util.Equals(cnd.id, id) { + if !bytesEqual(cnd.id, id) { newCandidates = append(newCandidates, cnd) } } - data := binary.Serialize(newCandidates) - storage.Put(ctx, voteKey, data) + setSerialized(ctx, voteKey, newCandidates) +} + +// setSerialized serializes data and puts it into contract storage. +func setSerialized(ctx storage.Context, key interface{}, value interface{}) { + data := binary.Serialize(value) + storage.Put(ctx, key, data) +} + +// getInnerRingNodes returns deserialized slice of vote ballots. +func getBallots(ctx storage.Context) []ballot { + data := storage.Get(ctx, voteKey) + if data != nil { + return binary.Deserialize(data.([]byte)).([]ballot) + } + + return []ballot{} +} + +// bytesEqual compares two slice of bytes by wrapping them into strings, +// which is necessary with new util.Equal interop behaviour, see neo-go#1176. +func bytesEqual(a []byte, b []byte) bool { + return util.Equals(string(a), string(b)) } From 62d5d863f49ca3dc0eeae9f35ad5200b76f4d894 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 15 Jul 2020 19:26:24 +0300 Subject: [PATCH 18/25] Rename "Deploy" method to "Init" There was a bit of confusion between contract deploying and calling "Deploy" method. Also new "Init" method uses generic setter to initialize contract storage. --- neofs_contract.go | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 9e893e9..6d8c2f2 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -30,7 +30,10 @@ const ( tokenHash = "\x3b\x7d\x37\x11\xc6\xf0\xcc\xf9\xb1\xdc\xa9\x03\xd1\xbf\xa1\xd8\x96\xf1\x23\x8c" innerRingCandidateFee = 100 * 1000 * 1000 // 10^8 version = 2 + innerRingKey = "innerring" voteKey = "ballots" + candidatesKey = "candidates" + cashedChequesKey = "cheques" blockDiff = 20 // change base on performance evaluation ) @@ -65,34 +68,31 @@ func Main(op string, args []interface{}) interface{} { */ ctx := storage.GetContext() + switch op { - case "Deploy": - irList := getSerialized(ctx, "InnerRingList").([]node) - if len(irList) >= 3 { - panic("contract already deployed") + case "Init": + if storage.Get(ctx, innerRingKey) != nil { + panic("neofs: contract already deployed") } - irList = []node{} + var irList []node + for i := 0; i < len(args); i++ { pub := args[i].([]byte) irList = append(irList, node{pub: pub}) } - data := binary.Serialize(irList) - storage.Put(ctx, "InnerRingList", data) + // initialize all storage slices + setSerialized(ctx, innerRingKey, irList) + setSerialized(ctx, voteKey, []ballot{}) + setSerialized(ctx, candidatesKey, []node{}) + setSerialized(ctx, cashedChequesKey, []cheque{}) - data = binary.Serialize([]interface{}{}) - storage.Put(ctx, "UsedVerifCheckList", data) - storage.Put(ctx, "InnerRingCandidates", data) - - data = binary.Serialize([]ballot{}) - storage.Put(ctx, voteKey, data) + runtime.Log("neofs: contract initialized") return true case "InnerRingList": - irList := getSerialized(ctx, "InnerRingList").([]node) - - return irList + return getInnerRingNodes(ctx) case "InnerRingCandidateRemove": data := args[0].([]byte) // public key if !runtime.CheckWitness(data) { @@ -308,11 +308,11 @@ func Main(op string, args []interface{}) interface{} { panic("isInnerRing: incorrect public key") } - irList := getSerialized(ctx, "InnerRingList").([]node) + irList := getInnerRingNodes(ctx) for i := range irList { node := irList[i] - if util.Equals(node.pub, key) { + if bytesEqual(node.pub, key) { return true } } @@ -517,6 +517,16 @@ func setSerialized(ctx storage.Context, key interface{}, value interface{}) { storage.Put(ctx, key, data) } +// getInnerRingNodes returns deserialized slice of inner ring nodes from storage. +func getInnerRingNodes(ctx storage.Context) []node { + data := storage.Get(ctx, innerRingKey) + if data != nil { + return binary.Deserialize(data.([]byte)).([]node) + } + + return []node{} +} + // getInnerRingNodes returns deserialized slice of vote ballots. func getBallots(ctx storage.Context) []ballot { data := storage.Get(ctx, voteKey) From 6797c7c1a72bbdae36c375705dd816bed405b837 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Wed, 15 Jul 2020 19:31:34 +0300 Subject: [PATCH 19/25] Update "Cheque" method --- neofs_contract.go | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 6d8c2f2..3cabb60 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -190,22 +190,23 @@ func Main(op string, args []interface{}) interface{} { id := args[0].([]byte) // unique cheque id user := args[1].([]byte) // GAS receiver amount := args[2].(int) // amount of GAS - lockAcc := args[3].([]byte) // lock account from internal banking that must be cashed out + lockAcc := args[3].([]byte) // lock account from internal balance contract - ctx := storage.GetContext() - - hashID := crypto.SHA256(id) - irList := getSerialized(ctx, "InnerRingList").([]node) - usedList := getSerialized(ctx, "UsedVerifCheckList").([]cheque) + irList := getInnerRingNodes(ctx) threshold := len(irList)/3*2 + 1 + cashedCheques := getCashedCheques(ctx) + hashID := crypto.SHA256(id) + irKey := innerRingInvoker(irList) if len(irKey) == 0 { panic("cheque: invoked by non inner ring node") } - c := cheque{id: id} // todo: use different cheque id for inner ring update and withdraw - if containsCheck(usedList, c) { + c := cheque{id: id} + + list, ok := addCheque(cashedCheques, c) + if !ok { panic("cheque: non unique id") } @@ -221,7 +222,9 @@ func Main(op string, args []interface{}) interface{} { panic("cheque: failed to transfer funds, aborting") } - putSerialized(ctx, "UsedVerifCheckList", c) + runtime.Log("cheque: funds have been transferred") + + setSerialized(ctx, cashedChequesKey, list) runtime.Notify("Cheque", id, user, amount, lockAcc) } @@ -527,6 +530,16 @@ func getInnerRingNodes(ctx storage.Context) []node { return []node{} } +// getInnerRingNodes returns deserialized slice of used cheques. +func getCashedCheques(ctx storage.Context) []cheque { + data := storage.Get(ctx, cashedChequesKey) + if data != nil { + return binary.Deserialize(data.([]byte)).([]cheque) + } + + return []cheque{} +} + // getInnerRingNodes returns deserialized slice of vote ballots. func getBallots(ctx storage.Context) []ballot { data := storage.Get(ctx, voteKey) @@ -537,6 +550,19 @@ func getBallots(ctx storage.Context) []ballot { return []ballot{} } +// addCheque returns slice of cheques with appended cheque 'c' and bool flag +// that set to false if cheque 'c' is already presented in the slice 'lst'. +func addCheque(lst []cheque, c cheque) ([]cheque, bool) { + for i := 0; i < len(lst); i++ { + if bytesEqual(c.id, lst[i].id) { + return nil, false + } + } + + lst = append(lst, c) + return lst, true +} + // bytesEqual compares two slice of bytes by wrapping them into strings, // which is necessary with new util.Equal interop behaviour, see neo-go#1176. func bytesEqual(a []byte, b []byte) bool { From 8ef70e70d752e7fffd9f95574c2cc656e4f93f41 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 16 Jul 2020 11:02:19 +0300 Subject: [PATCH 20/25] Add "Bind" and "Unbind" methods New methods allow to bind and unbind public keys with the owner, therefore the user can store and access data with the key, that does not related to the wallet. --- neofs_contract.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/neofs_contract.go b/neofs_contract.go index 3cabb60..58f6c10 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -35,6 +35,7 @@ const ( candidatesKey = "candidates" cashedChequesKey = "cheques" blockDiff = 20 // change base on performance evaluation + publicKeySize = 33 ) func Main(op string, args []interface{}) interface{} { @@ -228,6 +229,29 @@ func Main(op string, args []interface{}) interface{} { runtime.Notify("Cheque", id, user, amount, lockAcc) } + return true + case "Bind", "Unbind": + if len(args) < 2 { + panic("binding: bad arguments") + } + + user := args[0].([]byte) + if !runtime.CheckWitness(user) { + panic("binding: you should be the owner of the wallet") + } + + var keys [][]byte + for i := 1; i < len(args); i++ { + pub := args[i].([]byte) + if len(pub) != publicKeySize { + panic("binding: incorrect public key size") + } + + keys = append(keys, pub) + } + + runtime.Notify(op, user, keys) + return true case "InnerRingUpdate": data := args[0].([]byte) From abdbe6b9dc4f60b92758d9a5d5828367482c1553 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 16 Jul 2020 15:06:49 +0300 Subject: [PATCH 21/25] Update methods for inner ring candidate managing This commit: - increases default candidate fee, - adds function to list inner ring candidates, - updates add and remove candidate methods. --- neofs_contract.go | 78 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 58f6c10..2c2a953 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -3,6 +3,7 @@ package smart_contract import ( "github.com/nspcc-dev/neo-go/pkg/interop/binary" "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" + "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/engine" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" @@ -28,7 +29,7 @@ type ( const ( tokenHash = "\x3b\x7d\x37\x11\xc6\xf0\xcc\xf9\xb1\xdc\xa9\x03\xd1\xbf\xa1\xd8\x96\xf1\x23\x8c" - innerRingCandidateFee = 100 * 1000 * 1000 // 10^8 + innerRingCandidateFee = 100 * 1_0000_0000 // 100 Fixed8 Gas version = 2 innerRingKey = "innerring" voteKey = "ballots" @@ -93,41 +94,63 @@ func Main(op string, args []interface{}) interface{} { return true case "InnerRingList": - return getInnerRingNodes(ctx) + return getInnerRingNodes(ctx, innerRingKey) + case "InnerRingCandidates": + return getInnerRingNodes(ctx, candidatesKey) case "InnerRingCandidateRemove": - data := args[0].([]byte) // public key - if !runtime.CheckWitness(data) { - panic("you should be the owner of the public key") + if len(args) != 1 { + panic("irCandidateRemove: bad arguments") } - delSerializedIR(ctx, "InnerRingCandidates", data) + key := args[0].([]byte) // inner ring candidate public key + if !runtime.CheckWitness(key) { + panic("irCandidateRemove: you should be the owner of the public key") + } + + var nodes = []node{} // it is explicit declaration of empty slice, not nil + candidates := getInnerRingNodes(ctx, candidatesKey) + + for i := range candidates { + c := candidates[i] + if !bytesEqual(c.pub, key) { + nodes = append(nodes, c) + } else { + runtime.Log("irCandidateRemove: candidate has been removed") + } + } + + setSerialized(ctx, candidatesKey, nodes) return true case "InnerRingCandidateAdd": - key := args[0].([]byte) // public key + if len(args) != 1 { + panic("irCandidateAdd: bad arguments") + } + key := args[0].([]byte) // inner ring candidate public key if !runtime.CheckWitness(key) { - panic("you should be the owner of the public key") + panic("irCandidateAdd: you should be the owner of the public key") } - candidates := getSerialized(ctx, "InnerRingCandidates").([]node) - if containsPub(candidates, key) { - panic("is already in list") + c := node{pub: key} + candidates := getInnerRingNodes(ctx, candidatesKey) + + list, ok := addNode(candidates, c) + if !ok { + panic("irCandidateAdd: candidate already in the list") } - from := pubToScriptHash(key) + from := contract.CreateStandardAccount(key) to := runtime.GetExecutingScriptHash() params := []interface{}{from, to, innerRingCandidateFee} transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) if !transferred { - panic("failed to transfer funds, aborting") + panic("irCandidateAdd: failed to transfer funds, aborting") } - candidate := node{pub: key} - if !putSerialized(ctx, "InnerRingCandidates", candidate) { - panic("failed to put candidate into the queue") - } + runtime.Log("irCandidateAdd: candidate has been added") + setSerialized(ctx, candidatesKey, list) return true case "Deposit": @@ -193,7 +216,7 @@ func Main(op string, args []interface{}) interface{} { amount := args[2].(int) // amount of GAS lockAcc := args[3].([]byte) // lock account from internal balance contract - irList := getInnerRingNodes(ctx) + irList := getInnerRingNodes(ctx, innerRingKey) threshold := len(irList)/3*2 + 1 cashedCheques := getCashedCheques(ctx) @@ -335,7 +358,7 @@ func Main(op string, args []interface{}) interface{} { panic("isInnerRing: incorrect public key") } - irList := getInnerRingNodes(ctx) + irList := getInnerRingNodes(ctx, innerRingKey) for i := range irList { node := irList[i] @@ -545,8 +568,8 @@ func setSerialized(ctx storage.Context, key interface{}, value interface{}) { } // getInnerRingNodes returns deserialized slice of inner ring nodes from storage. -func getInnerRingNodes(ctx storage.Context) []node { - data := storage.Get(ctx, innerRingKey) +func getInnerRingNodes(ctx storage.Context, key string) []node { + data := storage.Get(ctx, key) if data != nil { return binary.Deserialize(data.([]byte)).([]node) } @@ -587,6 +610,19 @@ func addCheque(lst []cheque, c cheque) ([]cheque, bool) { return lst, true } +// addNode returns slice of nodes with appended node 'n' and bool flag +// that set to false if node 'n' is already presented in the slice 'lst'. +func addNode(lst []node, n node) ([]node, bool) { + for i := 0; i < len(lst); i++ { + if bytesEqual(n.pub, lst[i].pub) { + return nil, false + } + } + + lst = append(lst, n) + return lst, true +} + // bytesEqual compares two slice of bytes by wrapping them into strings, // which is necessary with new util.Equal interop behaviour, see neo-go#1176. func bytesEqual(a []byte, b []byte) bool { From 7a806bd550a9858f0f6faded40edd25a0e30698c Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 16 Jul 2020 17:23:52 +0300 Subject: [PATCH 22/25] Update "InnerRingUpdate" method --- neofs_contract.go | 130 +++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 2c2a953..7d36587 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -37,6 +37,7 @@ const ( cashedChequesKey = "cheques" blockDiff = 20 // change base on performance evaluation publicKeySize = 33 + minInnerRingSize = 3 ) func Main(op string, args []interface{}) interface{} { @@ -107,7 +108,7 @@ func Main(op string, args []interface{}) interface{} { panic("irCandidateRemove: you should be the owner of the public key") } - var nodes = []node{} // it is explicit declaration of empty slice, not nil + nodes := []node{} // it is explicit declaration of empty slice, not nil candidates := getInnerRingNodes(ctx, candidatesKey) for i := range candidates { @@ -277,17 +278,12 @@ func Main(op string, args []interface{}) interface{} { return true case "InnerRingUpdate": - data := args[0].([]byte) + if len(args) < 1+minInnerRingSize { + // cheque id + inner ring public keys + panic("irUpdate: bad arguments") + } - id := data[:8] - var ln interface{} = data[8:10] - listItemCount := ln.(int) - listSize := listItemCount * 33 - - offset := 8 + 2 + listSize - - irList := getSerialized(ctx, "InnerRingList").([]node) - usedList := getSerialized(ctx, "UsedVerifCheckList").([]cheque) + irList := getInnerRingNodes(ctx, innerRingKey) threshold := len(irList)/3*2 + 1 irKey := innerRingInvoker(irList) @@ -295,56 +291,61 @@ func Main(op string, args []interface{}) interface{} { panic("innerRingUpdate: invoked by non inner ring node") } + id := args[0].([]byte) c := cheque{id: id} - if containsCheck(usedList, c) { - panic("innerRingUpdate: cheque has non unique id") + + cashedCheques := getCashedCheques(ctx) + + chequesList, ok := addCheque(cashedCheques, c) + if !ok { + panic("irUpdate: non unique id") } - chequeHash := crypto.SHA256(data) + oldNodes := 0 + candidates := getInnerRingNodes(ctx, candidatesKey) + newIR := []node{} - n := vote(ctx, chequeHash, irKey) + loop: + for i := 1; i < len(args); i++ { + key := args[i].([]byte) + if len(key) != publicKeySize { + panic("irUpdate: invalid public key in inner ring list") + } + + // find key in actual inner ring list + for j := 0; j < len(irList); j++ { + n := irList[j] + if bytesEqual(n.pub, key) { + newIR = append(newIR, n) + oldNodes++ + + continue loop + } + } + + // find key in candidates list + candidates, newIR, ok = rmNodeByKey(candidates, newIR, key) + if !ok { + panic("irUpdate: unknown public key in inner ring list") + } + } + + if oldNodes < len(newIR)*2/3+1 { + panic("irUpdate: inner ring change rate must not be more than 1/3 ") + } + + hashID := crypto.SHA256(id) + + n := vote(ctx, hashID, irKey) if n >= threshold { - removeVotes(ctx, chequeHash) + removeVotes(ctx, hashID) - candidates := getSerialized(ctx, "InnerRingCandidates").([]node) - offset = 10 - newIR := []node{} + setSerialized(ctx, candidatesKey, candidates) + setSerialized(ctx, innerRingKey, newIR) + setSerialized(ctx, cashedChequesKey, chequesList) - loop: - for i := 0; i < listItemCount; i, offset = i+1, offset+33 { - pub := data[offset : offset+33] - - for j := 0; j < len(irList); j++ { - n := irList[j] - if util.Equals(n.pub, pub) { - newIR = append(newIR, n) - continue loop - } - } - - for j := 0; j < len(candidates); j++ { - n := candidates[j] - if util.Equals(n.pub, pub) { - newIR = append(newIR, n) - continue loop - } - } - } - - if len(newIR) != listItemCount { - panic("new inner ring wasn't processed correctly") - } - - for i := 0; i < len(newIR); i++ { - n := newIR[i] - delSerializedIR(ctx, "InnerRingCandidates", n.pub) - } - - newIRData := binary.Serialize(newIR) - storage.Put(ctx, "InnerRingList", newIRData) - putSerialized(ctx, "UsedVerifCheckList", c) - - runtime.Notify("InnerRingUpdate", c.id, newIRData) + runtime.Notify("InnerRingUpdate", c.id, newIR) + runtime.Log("irUpdate: inner ring list has been updated") } return true @@ -623,6 +624,27 @@ func addNode(lst []node, n node) ([]node, bool) { return lst, true } +// rmNodeByKey returns slice of nodes without node with key 'k', +// slices of nodes 'add' with node with key 'k' and bool flag, +// that set to false if node with a key 'k' does not exists in the slice 'lst'. +func rmNodeByKey(lst, add []node, k []byte) ([]node, []node, bool) { + var ( + flag bool + newLst = []node{} // it is explicit declaration of empty slice, not nil + ) + + for i := 0; i < len(lst); i++ { + if bytesEqual(k, lst[i].pub) { + add = append(add, lst[i]) + flag = true + } else { + newLst = append(newLst, lst[i]) + } + } + + return newLst, add, flag +} + // bytesEqual compares two slice of bytes by wrapping them into strings, // which is necessary with new util.Equal interop behaviour, see neo-go#1176. func bytesEqual(a []byte, b []byte) bool { From 8c7bcea436818025107c9bb5d304da289b68cdba Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 16 Jul 2020 18:59:49 +0300 Subject: [PATCH 23/25] Add system config methods NeoFS contract will contain installation parameters for storage network such as max object size, epoch duration, etc. --- neofs_contract.go | 117 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 2 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index 7d36587..b9a5fe8 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -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/engine" + "github.com/nspcc-dev/neo-go/pkg/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/util" @@ -25,11 +26,17 @@ type ( cheque struct { id []byte } + + record struct { + key []byte + val []byte + } ) const ( tokenHash = "\x3b\x7d\x37\x11\xc6\xf0\xcc\xf9\xb1\xdc\xa9\x03\xd1\xbf\xa1\xd8\x96\xf1\x23\x8c" - innerRingCandidateFee = 100 * 1_0000_0000 // 100 Fixed8 Gas + defaultCandidateFee = 100 * 1_0000_0000 // 100 Fixed8 Gas + candidateFeeConfigKey = "InnerRingCandidateFee" version = 2 innerRingKey = "innerring" voteKey = "ballots" @@ -40,6 +47,10 @@ const ( minInnerRingSize = 3 ) +var ( + configPrefix = []byte("config") +) + func Main(op string, args []interface{}) interface{} { // The trigger determines whether this smart-contract is being // run in 'verification' or 'application' mode. @@ -143,7 +154,8 @@ func Main(op string, args []interface{}) interface{} { from := contract.CreateStandardAccount(key) to := runtime.GetExecutingScriptHash() - params := []interface{}{from, to, innerRingCandidateFee} + fee := getConfig(ctx, candidateFeeConfigKey).(int) + params := []interface{}{from, to, fee} transferred := engine.AppCall([]byte(tokenHash), "transfer", params).(bool) if !transferred { @@ -369,6 +381,91 @@ func Main(op string, args []interface{}) interface{} { } return false + case "Config": + if len(args) != 1 { + panic("config: bad arguments") + } + + key := args[0].([]byte) + + return getConfig(ctx, key) + case "SetConfig": + if len(args) != 3 { + panic("setConfig: bad arguments") + } + + // check if it is inner ring invocation + irList := getInnerRingNodes(ctx, innerRingKey) + threshold := len(irList)/3*2 + 1 + + irKey := innerRingInvoker(irList) + if len(irKey) == 0 { + panic("setConfig: invoked by non inner ring node") + } + + // check unique id of the operation + id := args[0].([]byte) + c := cheque{id: id} + cashedCheques := getCashedCheques(ctx) + + chequesList, ok := addCheque(cashedCheques, c) + if !ok { + panic("setConfig: non unique id") + } + + // vote for new configuration value + hashID := crypto.SHA256(id) + + n := vote(ctx, hashID, irKey) + if n >= threshold { + removeVotes(ctx, hashID) + + key := args[1] + val := args[2] + + setConfig(ctx, key, val) + setSerialized(ctx, cashedChequesKey, chequesList) + + runtime.Notify("SetConfig", id, key, val) + runtime.Log("setConfig: configuration has been updated") + } + + return true + case "ListConfig": + var config []record + + it := storage.Find(ctx, configPrefix) + for iterator.Next(it) { + key := iterator.Key(it).([]byte) + val := iterator.Value(it).([]byte) + r := record{key: key[len(configPrefix):], val: val} + + config = append(config, r) + } + + return config + case "InitConfig": + if getConfig(ctx, candidateFeeConfigKey) != nil { + panic("neofs: configuration already installed") + } + + ln := len(args) + if ln%2 != 0 { + panic("initConfig: bad arguments") + } + + setConfig(ctx, candidateFeeConfigKey, defaultCandidateFee) + + for i := 0; i < ln/2; i++ { + key := args[i*2] + val := args[i*2+1] + + setConfig(ctx, key, val) + } + + runtime.Log("neofs: config has been installed") + + return true case "Version": return version } @@ -598,6 +695,22 @@ func getBallots(ctx storage.Context) []ballot { return []ballot{} } +// getConfig returns installed neofs configuration value or nil if it is not set. +func getConfig(ctx storage.Context, key interface{}) interface{} { + postfix := key.([]byte) + storageKey := append(configPrefix, postfix...) + + return storage.Get(ctx, storageKey) +} + +// setConfig sets neofs configuration value in the contract storage. +func setConfig(ctx storage.Context, key, val interface{}) { + postfix := key.([]byte) + storageKey := append(configPrefix, postfix...) + + storage.Put(ctx, storageKey, val) +} + // addCheque returns slice of cheques with appended cheque 'c' and bool flag // that set to false if cheque 'c' is already presented in the slice 'lst'. func addCheque(lst []cheque, c cheque) ([]cheque, bool) { From 013a70f3bfd595e648cf3449443909172941cd15 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Mon, 20 Jul 2020 16:09:40 +0300 Subject: [PATCH 24/25] Remove unused functions and fix lint errors --- neofs_contract.go | 209 ++++++++++++++-------------------------------- 1 file changed, 61 insertions(+), 148 deletions(-) diff --git a/neofs_contract.go b/neofs_contract.go index b9a5fe8..d252121 100644 --- a/neofs_contract.go +++ b/neofs_contract.go @@ -1,5 +1,42 @@ package smart_contract +/* + NeoFS Smart Contract for NEO3.0. + + Utility operations, executed once in deploy stage: + - Init(pubKey, ... ) - setup initial inner ring nodes + - InitConfig(key, value, key, value...) - setup initial NeoFS configuration + + User operations: + - Deposit(script-hash, amount, script-hash(?)) - deposit gas assets to this script-hash address to NeoFS balance + - Withdraw(script-hash, amount) - initialize gas asset withdraw from NeoFS balance + - Bind(script-hash, pubKeys...) - bind public key with user's account to use it in NeoFS requests + - Unbind(script-hash, pubKeys...) - unbind public key from user's account + + Inner ring list operations: + - InnerRingList() - returns array of inner ring node keys + - InnerRingCandidates() - returns array of inner ring candidate node keys + - IsInnerRing(pubKey) - returns 'true' if key is inside of inner ring list + - InnerRingCandidateAdd(pubKey) - adds key to the list of inner ring candidates + - InnerRingCandidateRemove(pubKey) - removes key from the list of inner ring candidates + - InnerRingUpdate(id, pubKeys...) - updates list of inner ring nodes with provided list of public keys + + Config operations: + - Config(key) - returns value of NeoFS configuration with key 'key' + - ListConfig() - returns array of all key-value pairs of NeoFS configuration + - SetConfig(id, key, value) - set key-value pair as a NeoFS runtime configuration value + + Other utility operations: + - Version - returns contract version + - Cheque(id, script- hash, amount, script-hash) - sends gas assets back to the user if they were successfully + locked in NeoFS balance contract + + Parameters: + - (?) - parameter can be omitted + - pubKey - 33 bytes of public key + - id - unique byte sequence +*/ + import ( "github.com/nspcc-dev/neo-go/pkg/interop/binary" "github.com/nspcc-dev/neo-go/pkg/interop/blockchain" @@ -34,17 +71,22 @@ type ( ) const ( - tokenHash = "\x3b\x7d\x37\x11\xc6\xf0\xcc\xf9\xb1\xdc\xa9\x03\xd1\xbf\xa1\xd8\x96\xf1\x23\x8c" + // native gas token script hash + tokenHash = "\x3b\x7d\x37\x11\xc6\xf0\xcc\xf9\xb1\xdc\xa9\x03\xd1\xbf\xa1\xd8\x96\xf1\x23\x8c" + defaultCandidateFee = 100 * 1_0000_0000 // 100 Fixed8 Gas candidateFeeConfigKey = "InnerRingCandidateFee" - version = 2 - innerRingKey = "innerring" - voteKey = "ballots" - candidatesKey = "candidates" - cashedChequesKey = "cheques" - blockDiff = 20 // change base on performance evaluation - publicKeySize = 33 - minInnerRingSize = 3 + + version = 2 + + innerRingKey = "innerring" + voteKey = "ballots" + candidatesKey = "candidates" + cashedChequesKey = "cheques" + + blockDiff = 20 // change base on performance evaluation + publicKeySize = 33 + minInnerRingSize = 3 ) var ( @@ -58,29 +100,6 @@ func Main(op string, args []interface{}) interface{} { return false } - /* - Utility operations - they will be changed in production: - - Deploy(params: address, pubKey, ... ) - setup initial inner ring state - - User operations: - - InnerRingList() - get list of inner ring nodes addresses and public keys - - InnerRingCandidateRemove(params: pubKey) - remove node with given public key from the inner ring candidate queue - - InnerRingCandidateAdd(params: pubKey) - add node to the inner ring candidate queue - - Deposit(params: pubKey, amount) - deposit GAS to the NeoFS account - - Withdraw(params: withdrawCheque) - withdraw GAS from the NeoFS account - - InnerRingUpdate(params: irCheque) - change list of inner ring nodes - - IsInnerRing(params: pubKey) - returns true if pubKey presented in inner ring list - - Version() - get version of the NeoFS smart-contract - - Params: - - address - string of the valid multiaddress (github.com/multiformats/multiaddr) - - pubKey - 33 byte public key - - withdrawCheque - serialized structure, that confirms GAS transfer; - contains inner ring signatures - - irCheque - serialized structure, that confirms new inner ring node list; - contains inner ring signatures - */ - ctx := storage.GetContext() switch op { @@ -277,6 +296,7 @@ func Main(op string, args []interface{}) interface{} { } var keys [][]byte + for i := 1; i < len(args); i++ { pub := args[i].([]byte) if len(pub) != publicKeySize { @@ -367,7 +387,7 @@ func Main(op string, args []interface{}) interface{} { } key := args[0].([]byte) - if len(key) != 33 { + if len(key) != publicKeySize { panic("isInnerRing: incorrect public key") } @@ -473,119 +493,6 @@ func Main(op string, args []interface{}) interface{} { panic("unknown operation") } -// fixme: use strict type deserialization wrappers -func getSerialized(ctx storage.Context, key string) interface{} { - data := storage.Get(ctx, key).([]byte) - if len(data) != 0 { - return binary.Deserialize(data) - } - return nil -} - -func delSerialized(ctx storage.Context, key string, value []byte) bool { - data := storage.Get(ctx, key).([]byte) - deleted := false - - var newList [][]byte - if len(data) != 0 { - lst := binary.Deserialize(data).([][]byte) - for i := 0; i < len(lst); i++ { - if util.Equals(value, lst[i]) { - deleted = true - } else { - newList = append(newList, lst[i]) - } - } - if deleted { - if len(newList) != 0 { - data := binary.Serialize(newList) - storage.Put(ctx, key, data) - } else { - storage.Delete(ctx, key) - } - runtime.Log("target element has been removed") - return true - } - - } - - runtime.Log("target element has not been removed") - return false -} - -func putSerialized(ctx storage.Context, key string, value interface{}) bool { - data := storage.Get(ctx, key).([]byte) - - var lst []interface{} - if len(data) != 0 { - lst = binary.Deserialize(data).([]interface{}) - } - - lst = append(lst, value) - data = binary.Serialize(lst) - storage.Put(ctx, key, data) - - return true -} - -func pubToScriptHash(pkey []byte) []byte { - // pre := []byte{0x21} - // buf := append(pre, pkey...) - // buf = append(buf, 0xac) - // h := crypto.Hash160(buf) - // - // return h - - // fixme: someday ripemd syscall will appear - // or simply store script-hashes along with public key - return []byte{0x0F, 0xED} -} - -func containsCheck(lst []cheque, c cheque) bool { - for i := 0; i < len(lst); i++ { - if util.Equals(c, lst[i]) { - return true - } - } - return false -} -func containsPub(lst []node, elem []byte) bool { - for i := 0; i < len(lst); i++ { - e := lst[i] - if util.Equals(elem, e.pub) { - return true - } - } - return false -} - -func delSerializedIR(ctx storage.Context, key string, value []byte) bool { - data := storage.Get(ctx, key).([]byte) - deleted := false - - newList := []node{} - if len(data) != 0 { - lst := binary.Deserialize(data).([]node) - for i := 0; i < len(lst); i++ { - n := lst[i] - if util.Equals(value, n.pub) { - deleted = true - } else { - newList = append(newList, n) - } - } - if deleted { - data := binary.Serialize(newList) - storage.Put(ctx, key, data) - runtime.Log("target element has been removed") - return true - } - } - - runtime.Log("target element has not been removed") - return false -} - // innerRingInvoker returns public key of inner ring node that invoked contract. func innerRingInvoker(ir []node) []byte { for i := 0; i < len(ir); i++ { @@ -598,9 +505,11 @@ func innerRingInvoker(ir []node) []byte { return nil } +// vote adds ballot for the decision with specific 'id' and returns amount +// on unique voters for that decision. func vote(ctx storage.Context, id, from []byte) int { var ( - newCandidates []ballot + newCandidates = []ballot{} // it is explicit declaration of empty slice, not nil candidates = getBallots(ctx) found = -1 blockHeight = blockchain.GetHeight() @@ -643,9 +552,11 @@ func vote(ctx storage.Context, id, from []byte) int { return found } +// removeVotes clears ballots of the decision that has benn aceepted by +// inner ring nodes. func removeVotes(ctx storage.Context, id []byte) { var ( - newCandidates []ballot + newCandidates = []ballot{} // it is explicit declaration of empty slice, not nil candidates = getBallots(ctx) ) @@ -721,6 +632,7 @@ func addCheque(lst []cheque, c cheque) ([]cheque, bool) { } lst = append(lst, c) + return lst, true } @@ -734,6 +646,7 @@ func addNode(lst []node, n node) ([]node, bool) { } lst = append(lst, n) + return lst, true } From 8460ab01e470439062d5bc56005a4285764dd697 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Mon, 20 Jul 2020 16:14:40 +0300 Subject: [PATCH 25/25] Remove tests and update readme file --- README.md | 77 +------ neofs_contract_test.go | 496 ----------------------------------------- 2 files changed, 10 insertions(+), 563 deletions(-) delete mode 100644 neofs_contract_test.go diff --git a/README.md b/README.md index 9e579b4..c3f7ac5 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,31 @@ # NeoFS smart-contract -This smart-contract controls list of NeoFS Inner Ring nodes and provides -methods to deposit and withdraw assets. These assets are used as a payment and -a reward for data storage. - +This smart-contract controls list of NeoFS Inner Ring nodes, user assets in +NeoFS balance contract and stores NeoFS runtime configuration. ## Getting Started This repository contains: -- NeoFS smart-contract written in Go -- Unit tests for the smart-contract +- NeoFS smart-contract in Go ### Prerequisites To compile smart-contract you need: -- [neo-go](https://github.com/nspcc-dev/neo-go) >= 0.74.0 - - -To run tests you need: - -- [go](https://golang.org/dl/) >= 1.12 +- [neo-go](https://github.com/nspcc-dev/neo-go) >= 0.90.0 ## Compiling To compile smart contract run `make build` command. Compiled contract -`neofs_contract.avm` will be placed in the same directory. +`neofs_contract.nef` and manifest `config.json` will be placed in the same +directory. ``` -$ make build -neo-go contract compile -i neofs_contract.go -02a600c56b6a007bc46a517bc468164e656f2e52756e74696d652e476574547269676765726165880d9e640700006c756668164e656f2e53746f726167652e476574436f6e74657874616a527bc46a00c376064465706c6f798764c7016a52c30d496e6e657252696e674c6973747c657c0d6a537bc46a5 -3c3c000a0642f0019636f6e747261637420616c7265616479206465706c6f796564680f4e656f2e52756e74696d652e4c6f67f06a51c3c06a547bc46a54c35297009e6448003270726f76696465207061697273206f6620696e6e65722072696e67206164647265737320616e64207075626c6963206b65 -... -c46a00c36a59c37c6592fd6476006a52c36a5ac36a59c3ad6469006a58c38b6a587bc4006a5b7bc46a5bc36a57c3c09f6444006a57c36a5bc3c36a59c387642b00156475706c6963617465207075626c6963206b657973680f4e656f2e52756e74696d652e4c6f67f06a5bc38b6a5b7bc462b7ff6a57c36 -a59c3787cc86a577bc46a53c30161936a537bc46234ff6a58c36a56c3a2640700516c75661e6e6f7420656e6f756768207665726966696564207369676e617475726573680f4e656f2e52756e74696d652e4c6f6761006c7566 -$ ls neofs_contract.avm -neofs_contract.avm +$ make build +neo-go contract compile -i neofs_contract.go -c neofs_config.yml -m config.json +$ ls neofs_contract.nef config.json +config.json neofs_contract.nef ``` You can specify path to the `neo-go` binary with `NEOGO` environment variable: @@ -46,51 +34,6 @@ You can specify path to the `neo-go` binary with `NEOGO` environment variable: $ NEOGO=/home/user/neo-go/bin/neo-go make build ``` -## Running the tests - -`neofs_contract_test.go` file contains tests for most of the provided methods. -It compiles smart-contract and uses instance of the NeoVM to run -code. - -To test smart contract run `make tests` command. - -``` -$ make tests -go mod vendor -go test -mod=vendor -v -race github.com/nspcc-dev/neofs-contract -=== RUN TestContract - TestContract: neofs_contract_test.go:360: provide pairs of inner ring address and public key - TestContract: neofs_contract_test.go:360: contract already deployed -=== RUN TestContract/InnerRingAddress - TestContract: neofs_contract_test.go:360: target element has been removed -=== RUN TestContract/Deposit - TestContract: neofs_contract_test.go:360: funds have been transfered -=== RUN TestContract/Withdraw -=== RUN TestContract/Withdraw/Double_Withdraw - TestContract: neofs_contract_test.go:360: verification check has already been used -=== RUN TestContract/InnerRingCandidateAdd -=== RUN TestContract/InnerRingCandidateAdd/Double_InnerRingCandidateAdd - TestContract: neofs_contract_test.go:360: is already in list -=== RUN TestContract/InnerRingCandidateRemove -=== RUN TestContract/InnerRingCandidateRemove/Remove_unknown_candidate - TestContract: neofs_contract_test.go:360: target element has not been removed - TestContract: neofs_contract_test.go:360: target element has not been removed -=== RUN TestContract/InnerRingUpdate - TestContract/InnerRingUpdate: neofs_contract_test.go:174: implement getIRExcludeCheque without neofs-node dependency ---- PASS: TestContract (0.43s) - --- PASS: TestContract/InnerRingAddress (0.00s) - --- PASS: TestContract/Deposit (0.00s) - --- PASS: TestContract/Withdraw (0.01s) - --- PASS: TestContract/Withdraw/Double_Withdraw (0.00s) - --- PASS: TestContract/InnerRingCandidateAdd (0.00s) - --- PASS: TestContract/InnerRingCandidateAdd/Double_InnerRingCandidateAdd (0.00s) - --- PASS: TestContract/InnerRingCandidateRemove (0.00s) - --- PASS: TestContract/InnerRingCandidateRemove/Remove_unknown_candidate (0.00s) - --- SKIP: TestContract/InnerRingUpdate (0.00s) -PASS -ok github.com/nspcc-dev/neofs-contract 0.453s -``` - ## License This project is licensed under the GPLv3 License - see the diff --git a/neofs_contract_test.go b/neofs_contract_test.go deleted file mode 100644 index e9a2736..0000000 --- a/neofs_contract_test.go +++ /dev/null @@ -1,496 +0,0 @@ -package smart_contract - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "encoding/hex" - "fmt" - "math/big" - "os" - "testing" - - "github.com/nspcc-dev/neo-go/pkg/compiler" - "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neo-go/pkg/io" - "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" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-crypto/test" - "github.com/stretchr/testify/require" -) - -const contractTemplate = "./neofs_contract.go" - -var ( - contractHash = util.Uint160{0x1, 0x2, 0x3, 0x4} - // token hash is not random to run tests of .avm or .go files - contractStr = string(contractHash[:]) - txHash = mustHex("3ca2575bd90129e3730c46ba3f163fcfd5fff11eaedb2b6aa3d76bd03ab8a890") -) - -type contract struct { - script []byte - privs []*ecdsa.PrivateKey - cgasHash string -} - -func TestContract(t *testing.T) { - const nodeCount = 6 - plug := newStoragePlugin(t) - contract := initGoContract(t, contractTemplate, nodeCount) - - plug.cgas[contractStr] = util.Fixed8FromInt64(1000) - plug.invokeKey = crypto.MarshalPublicKey(&contract.privs[0].PublicKey) - - var args []interface{} - for i := range contract.privs { - args = append(args, crypto.MarshalPublicKey(&contract.privs[i].PublicKey)) - } - - v := initVM(contract, plug) - loadArg(t, v, "Deploy", args) - require.NoError(t, v.Run()) - - // double deploy - v = initVM(contract, plug) - loadArg(t, v, "Deploy", args) - require.Error(t, v.Run()) - - t.Run("Deposit", func(t *testing.T) { - const ( - amount = 1000 - balance = 4000 - ) - - before := plug.cgas[contractStr] - gas := util.Fixed8FromInt64(amount) - - key, err := keys.NewPublicKeyFromString("031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a") - require.NoError(t, err) - - plug.setCGASBalance(key.Bytes(), balance) - - v := initVM(contract, plug) - loadArg(t, v, "Deposit", []interface{}{key.Bytes(), int(gas.IntegralValue())}) - require.NoError(t, v.Run()) - - require.Equal(t, before+gas, plug.cgas[contractStr]) - require.Equal(t, util.Fixed8FromInt64(balance-amount), plug.cgas[string(key.GetScriptHash().BytesBE())]) - checkNotification(t, plug.notify, []byte("Deposit"), key.Bytes(), big.NewInt(int64(gas)), []byte{}, txHash) - }) - - t.Run("Withdraw", func(t *testing.T) { - const amount = 21 - - gas := util.Fixed8FromInt64(amount) - - key, err := keys.NewPublicKeyFromString("031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a") - require.NoError(t, err) - - v := initVM(contract, plug) - loadArg(t, v, "Withdraw", []interface{}{key.Bytes(), amount}) - require.NoError(t, v.Run()) - checkNotification(t, plug.notify, []byte("Withdraw"), key.Bytes(), big.NewInt(int64(gas)), txHash) - }) - - t.Run("Cheque", func(t *testing.T) { - const amount = 21 - - id := []byte("id") - gas := util.Fixed8FromInt64(amount) - user := randScriptHash() - lockAcc := randScriptHash() - contractGas := plug.cgas[contractStr] - - // call it threshold amount of times - for i := 0; i < 2*nodeCount/3+1; i++ { - plug.invokeKey = crypto.MarshalPublicKey(&contract.privs[i].PublicKey) - v := initVM(contract, plug) - - loadArg(t, v, "Cheque", []interface{}{id, user, int(gas), lockAcc}) - require.NoError(t, v.Run()) - } - - require.Equal(t, contractGas-gas, plug.cgas[contractStr]) - require.Equal(t, gas, plug.cgas[string(user)]) - checkNotification(t, plug.notify, []byte("Cheque"), id, user, big.NewInt(int64(gas)), lockAcc) - - t.Run("Double cheque", func(t *testing.T) { - v := initVM(contract, plug) - - loadArg(t, v, "Cheque", []interface{}{id, user, int(gas), lockAcc}) - require.Error(t, v.Run()) - }) - }) - - t.Run("InnerRingCandidateAdd", func(t *testing.T) { - v := initVM(contract, plug) - before := plug.cgas[contractStr] - - key := crypto.MarshalPublicKey(&test.DecodeKey(1).PublicKey) - plug.setCGASBalance(key, 4000) - - loadArg(t, v, "InnerRingCandidateAdd", []interface{}{key}) - require.NoError(t, v.Run()) - - fee := util.Fixed8FromInt64(1) - - require.Equal(t, before+fee, plug.cgas[contractStr]) - require.Equal(t, util.Fixed8FromInt64(4000)-fee, - plug.cgas[string(mustPKey(key).GetScriptHash().BytesBE())]) - require.True(t, bytes.Contains(plug.mem["InnerRingCandidates"], key)) - - t.Run("Double InnerRingCandidateAdd", func(t *testing.T) { - v := initVM(contract, plug) - loadArg(t, v, "InnerRingCandidateAdd", []interface{}{key}) - require.Error(t, v.Run()) - }) - }) - - t.Run("InnerRingCandidateRemove", func(t *testing.T) { - key := crypto.MarshalPublicKey(&test.DecodeKey(2).PublicKey) - plug.setCGASBalance(key, 4000) - - v := initVM(contract, plug) - loadArg(t, v, "InnerRingCandidateAdd", []interface{}{key}) - require.NoError(t, v.Run()) - require.True(t, bytes.Contains(plug.mem["InnerRingCandidates"], key)) - - t.Run("Remove unknown candidate", func(t *testing.T) { - v := initVM(contract, plug) - // unknown candidate - badKey := crypto.MarshalPublicKey(&test.DecodeKey(3).PublicKey) - loadArg(t, v, "InnerRingCandidateRemove", []interface{}{badKey}) - require.NoError(t, v.Run()) - require.True(t, bytes.Contains(plug.mem["InnerRingCandidates"], key)) - }) - - v = initVM(contract, plug) - key = mustHex("031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a") - loadArg(t, v, "InnerRingCandidateRemove", []interface{}{key}) - require.NoError(t, v.Run()) - require.False(t, bytes.Contains(plug.mem["InnerRingCandidates"], key)) - }) -} - -func initGoContract(t *testing.T, path string, n int) *contract { - f, err := os.Open(path) - require.NoError(t, err) - - defer f.Close() - - buf, err := compiler.Compile(f) - require.NoError(t, err) - - return &contract{script: buf, privs: getKeys(t, n)} -} - -func getKeys(t *testing.T, n int) []*ecdsa.PrivateKey { - privs := make([]*ecdsa.PrivateKey, n) - for i := range privs { - var err error - - privs[i], err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - } - - return privs -} - -func randScriptHash() []byte { - var scriptHash = make([]byte, 20) - rand.Read(scriptHash) - return scriptHash -} - -func mustHex(s string) []byte { - result, err := hex.DecodeString(s) - if err != nil { - panic(fmt.Errorf("invalid hex: %v", err)) - } - - return result -} - -func initVM(c *contract, plug *storagePlugin) *vm.VM { - v := vm.New() - v.Load(c.script) - v.SetScriptGetter(plug.getScript) - v.RegisterInteropGetter(plug.getInterop) - - return v -} - -func loadArg(t *testing.T, v *vm.VM, operation string, params []interface{}) { - arr := make([]vm.StackItem, len(params)) - for i := range arr { - arr[i] = toStackItem(params[i]) - require.NotNil(t, arr[i], "invalid stack item") - } - v.Estack().PushVal(vm.NewArrayItem(arr)) - v.Estack().PushVal(operation) -} - -func toStackItem(v interface{}) vm.StackItem { - switch val := v.(type) { - case int: - return vm.NewBigIntegerItem(int64(val)) - case string: - return vm.NewByteArrayItem([]byte(val)) - case []byte: - return vm.NewByteArrayItem(val) - default: - return nil - } -} - -const cgasSyscall = "MockCGAS" - -type kv struct { - Operation string - Key []byte - Value []byte -} - -type storagePlugin struct { - mem map[string][]byte - cgas map[string]util.Fixed8 - interops map[uint32]vm.InteropFunc - storageOps []kv - notify []interface{} - invokeKey []byte -} - -func newStoragePlugin(t *testing.T) *storagePlugin { - s := &storagePlugin{ - mem: make(map[string][]byte), - cgas: make(map[string]util.Fixed8), - interops: make(map[uint32]vm.InteropFunc), - } - - s.interops[getID("Neo.Storage.Delete")] = s.Delete - s.interops[getID("Neo.Storage.Get")] = s.Get - s.interops[getID("Neo.Storage.GetContext")] = s.GetContext - s.interops[getID("Neo.Storage.Put")] = s.Put - s.interops[getID("Neo.Runtime.GetExecutingScriptHash")] = s.GetExecutingScriptHash - s.interops[getID("Neo.Runtime.GetTrigger")] = s.GetTrigger - s.interops[getID("Neo.Runtime.CheckWitness")] = s.CheckWitness - s.interops[getID("System.ExecutionEngine.GetExecutingScriptHash")] = s.GetExecutingScriptHash - s.interops[getID(cgasSyscall)] = s.CGASInvoke - s.interops[getID("Neo.Runtime.Log")] = func(v *vm.VM) error { - msg := string(v.Estack().Pop().Bytes()) - t.Log(msg) - return nil - } - s.interops[getID("Neo.Runtime.Notify")] = func(v *vm.VM) error { - val := v.Estack().Pop().Value() - s.notify = append(s.notify, toInterface(val)) - return nil - } - s.interops[getID("System.ExecutionEngine.GetScriptContainer")] = s.GetScriptContainer - s.interops[getID("Neo.Transaction.GetHash")] = s.GetHash - s.interops[getID("Neo.Blockchain.GetHeight")] = s.GetHeight - - return s -} - -func toInterface(val interface{}) interface{} { - switch v := val.(type) { - case []vm.StackItem: - arr := make([]interface{}, len(v)) - for i, item := range v { - arr[i] = toInterface(item) - } - return arr - case vm.StackItem: - return toInterface(v.Value()) - default: - return v - } -} - -func getID(name string) uint32 { - return vm.InteropNameToID([]byte(name)) -} - -func (s *storagePlugin) getInterop(id uint32) *vm.InteropFuncPrice { - f := s.interops[id] - if f != nil { - return &vm.InteropFuncPrice{Func: f, Price: 1} - } - - switch id { - case getID("Neo.Runtime.Serialize"): - case getID("Neo.Runtime.Deserialize"): - default: - panic("unexpected interop") - } - - return nil -} - -func mustPKey(pub []byte) *keys.PublicKey { - var pk keys.PublicKey - if err := pk.DecodeBytes(pub); err != nil { - panic(err) - } - return &pk -} - -func (s *storagePlugin) setCGASBalance(pub []byte, amount int64) { - pk := mustPKey(pub) - from := string(pk.GetScriptHash().BytesBE()) - s.cgas[from] = util.Fixed8FromInt64(amount) -} - -func (s *storagePlugin) CGASInvoke(v *vm.VM) error { - op := string(v.Estack().Pop().Bytes()) - args := v.Estack().Pop().Array() - - var result bool - - switch op { - case "transfer": - from := args[0].Value().([]byte) - to := args[1].Value().([]byte) - if len(from) != 20 || len(to) != 20 { - panic("invalid arguments") - } - - var amount util.Fixed8 - val := args[2].Value() - switch v := val.(type) { - case *big.Int: - amount = util.Fixed8(v.Int64()) - case []byte: - amount = util.Fixed8(emit.BytesToInt(v).Int64()) - default: - panic("invalid amount") - } - - if s.cgas[string(from)] >= amount { - s.cgas[string(from)] -= amount - s.cgas[string(to)] += amount - result = true - } - - default: - panic("invalid operation") - } - - v.Estack().PushVal(result) - - return nil -} - -func (s *storagePlugin) getScript(u util.Uint160) ([]byte, bool) { - var realHash util.Uint160 - copy(realHash[:], tokenHash[:]) - if u.Equals(realHash) { - buf := io.NewBufBinWriter() - emit.Syscall(buf.BinWriter, cgasSyscall) - return buf.Bytes(), false - } - panic("wrong script hash") -} - -func (s *storagePlugin) GetTrigger(v *vm.VM) error { - // todo: remove byte casting when neo-go issue will be resolved - // https: //github.com/nspcc-dev/neo-go/issues/776 - v.Estack().PushVal(byte(trigger.Application)) - return nil -} - -func (s *storagePlugin) CheckWitness(v *vm.VM) error { - key := v.Estack().Pop().Value().([]byte) - if bytes.Equal(key, s.invokeKey) { - v.Estack().PushVal(true) - } else { - v.Estack().PushVal(false) - } - - return nil -} - -func (s *storagePlugin) GetExecutingScriptHash(v *vm.VM) error { - var h util.Uint160 - copy(h[:], contractHash[:]) - v.Estack().PushVal(h.BytesBE()) - return nil -} - -func (s *storagePlugin) GetScriptContainer(v *vm.VM) error { - v.Estack().PushVal(true) - return nil -} - -func (s *storagePlugin) GetHash(v *vm.VM) error { - v.Estack().PushVal(txHash) - return nil -} - -func (s *storagePlugin) GetHeight(v *vm.VM) error { - v.Estack().PushVal(42) - return nil -} - -func (s *storagePlugin) logStorage(op string, key, value []byte) { - s.storageOps = append(s.storageOps, kv{ - Operation: op, - Key: key, - Value: value, - }) -} - -func (s *storagePlugin) Delete(v *vm.VM) error { - v.Estack().Pop() - key := v.Estack().Pop().Bytes() - s.logStorage("Delete", key, s.mem[string(key)]) - delete(s.mem, string(key)) - return nil -} - -func (s *storagePlugin) Put(v *vm.VM) error { - v.Estack().Pop() - key := v.Estack().Pop().Bytes() - value := v.Estack().Pop().Bytes() - s.logStorage("Put", key, value) - s.mem[string(key)] = value - return nil -} - -func (s *storagePlugin) Get(v *vm.VM) error { - v.Estack().Pop() - item := v.Estack().Pop().Bytes() - if val, ok := s.mem[string(item)]; ok { - v.Estack().PushVal(val) - s.logStorage("Get", item, val) - return nil - } - v.Estack().PushVal([]byte{}) - s.logStorage("Get", item, nil) - return nil -} - -func (s *storagePlugin) GetContext(v *vm.VM) error { - // Pushing anything on the stack here will work. This is just to satisfy - // the compiler, thinking it has pushed the context ^^. - v.Estack().PushVal(10) - return nil -} - -func checkNotification(t *testing.T, store []interface{}, args ...interface{}) { - ln := len(store) - require.True(t, ln > 0) - - notification := store[ln-1].([]interface{}) - require.Equal(t, len(args), len(notification)) - - for i := range args { - require.Equal(t, args[i], notification[i]) - } -}