From 94cd3dca83a6f265bb6c8ad68ab7b3948a556b16 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 6 Dec 2023 19:15:33 +0300 Subject: [PATCH 1/4] [#53] go.mod: Resolve ambiguous import There was a problem with `go mod tidy`. Signed-off-by: Evgenii Stratonikov --- go.mod | 4 +++- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index fc4ad2e..b792938 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.frostfs.info/TrueCloudLab/frostfs-contract go 1.20 require ( - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.3.1 github.com/mr-tron/base58 v1.2.0 github.com/nspcc-dev/neo-go v0.103.0 github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5 @@ -54,6 +54,8 @@ require ( golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect + google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect diff --git a/go.sum b/go.sum index d5eccca..f05c034 100644 --- a/go.sum +++ b/go.sum @@ -145,8 +145,8 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= @@ -548,7 +548,8 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 h1:eSaPbMR4T7WfH9FvABk36NBMacoTUKdWCvV0dx+KfOg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -561,7 +562,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -- 2.45.3 From 91b36a7eb3e367f9b5c51174a35c25318026ebb3 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 6 Dec 2023 19:16:08 +0300 Subject: [PATCH 2/4] [#53] go.mod: Update neo-go to the laster master Pickup new neotest features. Signed-off-by: Evgenii Stratonikov --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index b792938..da74cd4 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.20 require ( github.com/google/uuid v1.3.1 github.com/mr-tron/base58 v1.2.0 - github.com/nspcc-dev/neo-go v0.103.0 - github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5 + github.com/nspcc-dev/neo-go v0.104.1-0.20231206061802-441eb8aa86be + github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0 github.com/stretchr/testify v1.8.4 go.uber.org/zap v1.26.0 ) @@ -29,7 +29,7 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c // indirect - github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect + github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c // indirect github.com/nspcc-dev/neofs-crypto v0.4.0 // indirect github.com/nspcc-dev/rfc6979 v0.2.0 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect @@ -45,7 +45,7 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 // indirect github.com/twmb/murmur3 v1.1.5 // indirect github.com/urfave/cli v1.22.5 // indirect - go.etcd.io/bbolt v1.3.7 // indirect + go.etcd.io/bbolt v1.3.8 // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/mod v0.12.0 // indirect diff --git a/go.sum b/go.sum index f05c034..4d74acf 100644 --- a/go.sum +++ b/go.sum @@ -195,13 +195,13 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c h1:uyK5aLbAhrnZtnvobJLN24gGUrlxIJAAFqiWl+liZuo= github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c/go.mod h1:kjBC9F8L25GR+kIHy/1KgG/KfcoGnVwIiyovgq1uszk= -github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg= -github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= +github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c h1:OOQeE613BH93ICPq3eke5N78gWNeMjcBWkmD2NKyXVg= +github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y= -github.com/nspcc-dev/neo-go v0.103.0 h1:UVyWPhzZdfYFG35ORP3FRDLh8J/raRQ6m8SptDdlgfM= -github.com/nspcc-dev/neo-go v0.103.0/go.mod h1:x+wmcYqpZYJwLp1l/pHZrqNp3RSWlkMymWGDij3/OPo= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5 h1:09CpI5uwsxb1EeFPIKQRwwWlfCmDD/Dwwh01lPiQScM= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag= +github.com/nspcc-dev/neo-go v0.104.1-0.20231206061802-441eb8aa86be h1:nZ2Hi5JSXdq3JXDi/8lms1UXQDAA5LVGpOpcrf2bRVA= +github.com/nspcc-dev/neo-go v0.104.1-0.20231206061802-441eb8aa86be/go.mod h1:dsu8+VDMgGF7QNtPFBU4seE3pxSq8fYCuk3A6he4+ZQ= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0 h1:N+dMIBmteXjJpkH6UZ7HmNftuFxkqszfGLbhsEctnv0= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag= github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk= github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4= github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs= @@ -281,8 +281,8 @@ github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74/go.mod h1 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -- 2.45.3 From d7cb550a5eced063e750689fe5de8f994e22eaab Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 6 Dec 2023 19:23:33 +0300 Subject: [PATCH 3/4] [#53] common: Use interop.Hash160 in address producing functions Signed-off-by: Evgenii Stratonikov --- common/ir.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/ir.go b/common/ir.go index fbc813a..a639bf1 100644 --- a/common/ir.go +++ b/common/ir.go @@ -25,20 +25,20 @@ func AlphabetNodes() []interop.PublicKey { } // AlphabetAddress returns multi address of alphabet public keys. -func AlphabetAddress() []byte { +func AlphabetAddress() interop.Hash160 { alphabet := neo.GetCommittee() return Multiaddress(alphabet, false) } // CommitteeAddress returns multi address of committee. -func CommitteeAddress() []byte { +func CommitteeAddress() interop.Hash160 { committee := neo.GetCommittee() return Multiaddress(committee, true) } // Multiaddress returns default multisignature account address for N keys. // If committee set to true, it is `M = N/2+1` committee account. -func Multiaddress(n []interop.PublicKey, committee bool) []byte { +func Multiaddress(n []interop.PublicKey, committee bool) interop.Hash160 { threshold := len(n)*2/3 + 1 if committee { threshold = len(n)/2 + 1 -- 2.45.3 From bc3186575ff47b75a811a78ddef3b10869f2d4e2 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 6 Dec 2023 19:45:17 +0300 Subject: [PATCH 4/4] [#53] proxy: Allow using proxy by trusted accounts Signed-off-by: Evgenii Stratonikov --- proxy/proxy_contract.go | 40 +++++++++++++++---- rpcclient/proxy/client.go | 48 ++++++++++++++++++++++- tests/proxy_test.go | 82 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 155 insertions(+), 15 deletions(-) diff --git a/proxy/proxy_contract.go b/proxy/proxy_contract.go index 7157062..be59473 100644 --- a/proxy/proxy_contract.go +++ b/proxy/proxy_contract.go @@ -5,10 +5,13 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/management" - "github.com/nspcc-dev/neo-go/pkg/interop/native/neo" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + "github.com/nspcc-dev/neo-go/pkg/interop/storage" + "github.com/nspcc-dev/neo-go/pkg/interop/util" ) +const accountKeyPrefix = 'a' + // OnNEP17Payment is a callback for NEP-17 compatible native GAS contract. func OnNEP17Payment(from interop.Hash160, amount int, data any) { caller := runtime.GetCallingScriptHash() @@ -40,19 +43,40 @@ func Update(script []byte, manifest []byte, data any) { // Verify method returns true if transaction contains valid multisignature of // Alphabet nodes of the Inner Ring. -func Verify() bool { - alphabet := neo.GetCommittee() - sig := common.Multiaddress(alphabet, false) +func Verify(addr interop.Hash160) bool { + common.CheckWitness(addr) - if !runtime.CheckWitness(sig) { - sig = common.Multiaddress(alphabet, true) - return runtime.CheckWitness(sig) + ctx := storage.GetReadOnlyContext() + if storage.Get(ctx, append([]byte{accountKeyPrefix}, addr...)) != nil { + return true } - return true + if util.Equals(addr, common.CommitteeAddress()) { + return true + } + + if util.Equals(addr, common.AlphabetAddress()) { + return true + } + + return false } // Version returns the version of the contract. func Version() int { return common.Version } + +func AddAccount(addr interop.Hash160) { + common.CheckWitness(common.CommitteeAddress()) + + ctx := storage.GetContext() + storage.Put(ctx, append([]byte{accountKeyPrefix}, addr...), []byte{1}) +} + +func RemoveAccount(addr interop.Hash160) { + common.CheckWitness(common.CommitteeAddress()) + + ctx := storage.GetContext() + storage.Delete(ctx, append([]byte{accountKeyPrefix}, addr...)) +} diff --git a/rpcclient/proxy/client.go b/rpcclient/proxy/client.go index 10acfda..7481ff9 100644 --- a/rpcclient/proxy/client.go +++ b/rpcclient/proxy/client.go @@ -52,8 +52,8 @@ func New(actor Actor, hash util.Uint160) *Contract { } // Verify invokes `verify` method of contract. -func (c *ContractReader) Verify() (bool, error) { - return unwrap.Bool(c.invoker.Call(c.hash, "verify")) +func (c *ContractReader) Verify(addr util.Uint160) (bool, error) { + return unwrap.Bool(c.invoker.Call(c.hash, "verify", addr)) } // Version invokes `version` method of contract. @@ -61,6 +61,50 @@ func (c *ContractReader) Version() (*big.Int, error) { return unwrap.BigInt(c.invoker.Call(c.hash, "version")) } +// AddAccount creates a transaction invoking `addAccount` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) AddAccount(addr util.Uint160) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "addAccount", addr) +} + +// AddAccountTransaction creates a transaction invoking `addAccount` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) AddAccountTransaction(addr util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "addAccount", addr) +} + +// AddAccountUnsigned creates a transaction invoking `addAccount` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) AddAccountUnsigned(addr util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "addAccount", nil, addr) +} + +// RemoveAccount creates a transaction invoking `removeAccount` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) RemoveAccount(addr util.Uint160) (util.Uint256, uint32, error) { + return c.actor.SendCall(c.hash, "removeAccount", addr) +} + +// RemoveAccountTransaction creates a transaction invoking `removeAccount` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) RemoveAccountTransaction(addr util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeCall(c.hash, "removeAccount", addr) +} + +// RemoveAccountUnsigned creates a transaction invoking `removeAccount` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) RemoveAccountUnsigned(addr util.Uint160) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(c.hash, "removeAccount", nil, addr) +} + // Update creates a transaction invoking `update` method of the contract. // This transaction is signed and immediately sent to the network. // The values returned are its hash, ValidUntilBlock value and error if any. diff --git a/tests/proxy_test.go b/tests/proxy_test.go index 845fb16..5b89443 100644 --- a/tests/proxy_test.go +++ b/tests/proxy_test.go @@ -1,12 +1,21 @@ package tests import ( + "errors" "path" "testing" + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" + "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/neotest" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" ) const proxyPath = "../proxy" @@ -36,13 +45,76 @@ func newProxyInvoker(t *testing.T) *neotest.ContractInvoker { func TestVerify(t *testing.T) { e := newProxyInvoker(t) + acc := e.NewAccount(t) - const method = "verify" + gas := e.NewInvoker(e.NativeHash(t, nativenames.Gas), e.Validator) + gas.Invoke(t, true, "transfer", e.Validator.ScriptHash(), e.Hash, 100_0000_0000, nil) - e.Invoke(t, stackitem.NewBool(true), method) + t.Run("proxy + committee", func(t *testing.T) { + s := &proxySigner{contract: e.Hash, account: e.CommitteeHash} + tx := e.PrepareInvocation(t, []byte{byte(opcode.RET)}, []neotest.Signer{s, e.Committee}) + require.NoError(t, e.Chain.VerifyTx(tx)) + }) + t.Run("proxy + custom account", func(t *testing.T) { + s := &proxySigner{contract: e.Hash, account: acc.ScriptHash()} + t.Run("bad, only proxy", func(t *testing.T) { + tx := e.PrepareInvocation(t, []byte{byte(opcode.RET)}, []neotest.Signer{s, acc}) + require.Error(t, e.Chain.VerifyTx(tx)) + }) - notAlphabet := e.NewAccount(t) - cNotAlphabet := e.WithSigners(notAlphabet) + e.Invoke(t, stackitem.Null{}, "addAccount", s.account) - cNotAlphabet.Invoke(t, stackitem.NewBool(false), method) + tx := e.PrepareInvocation(t, []byte{byte(opcode.RET)}, []neotest.Signer{s, acc}) + require.NoError(t, e.Chain.VerifyTx(tx)) + }) +} + +type proxySigner struct { + contract util.Uint160 + account util.Uint160 +} + +var _ neotest.ContractSigner = (*proxySigner)(nil) + +func (s *proxySigner) Script() []byte { + return nil +} +func (s *proxySigner) ScriptHash() util.Uint160 { + return s.contract +} +func (s *proxySigner) SignHashable(uint32, hash.Hashable) []byte { + panic("not implemented") +} +func (s *proxySigner) SignTx(_ netmode.Magic, tx *transaction.Transaction) error { + pos := -1 + for i := range tx.Signers { + if tx.Signers[i].Account.Equals(s.contract) { + pos = i + break + } + } + if pos < 0 { + return errors.New("transaction is not signed by this account") + } + if len(tx.Scripts) < pos { + return errors.New("transaction is not yet signed by the previous signer") + } + + invoc, err := s.InvocationScript(tx) + if err != nil { + return err + } + + w := transaction.Witness{InvocationScript: invoc} + if len(tx.Scripts) == pos { + tx.Scripts = append(tx.Scripts, w) + } else { + tx.Scripts[pos].InvocationScript = invoc + } + return nil +} +func (s *proxySigner) InvocationScript(tx *transaction.Transaction) ([]byte, error) { + w := io.NewBufBinWriter() + emit.Any(w.BinWriter, s.account) + return w.Bytes(), nil } -- 2.45.3