From a8a33a5e1d427f0bd80ec9b419a0bd1f3f5dd450 Mon Sep 17 00:00:00 2001 From: Airat Arifullin Date: Wed, 24 Apr 2024 19:46:59 +0300 Subject: [PATCH] [#1105] ape: Introduce contract storage with proxy contract verification * `ProxyVerificationContractStorage` uses Proxy contract as a cosigner. * `ProxyVerificationContractStorage` recreates a contract storage for each handler invocation because of an issue: rpc-actor from morph client may be expired. This way won't create a bottlenecks because it is expected that this contract storage implementation will be used not so often. * Make morph client return `RPCActor` (that is websocket client in fact). * Make `SwitchRPCGuardedActor` return `RPCActor` as it will be used for `ProxyVerificationContractStorage`. Signed-off-by: Airat Arifullin --- pkg/ape/contract_storage/proxy.go | 128 ++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 pkg/ape/contract_storage/proxy.go diff --git a/pkg/ape/contract_storage/proxy.go b/pkg/ape/contract_storage/proxy.go new file mode 100644 index 000000000..953b91a79 --- /dev/null +++ b/pkg/ape/contract_storage/proxy.go @@ -0,0 +1,128 @@ +package contractstorage + +import ( + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain" + "git.frostfs.info/TrueCloudLab/policy-engine/pkg/engine" + policy_morph "git.frostfs.info/TrueCloudLab/policy-engine/pkg/morph/policy" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/notary" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/wallet" +) + +type ProxyAdaptedContractStorage interface { + AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (util.Uint256, uint32, error) + + RemoveMorphRuleChain(name chain.Name, target engine.Target, chainID chain.ID) (util.Uint256, uint32, error) + + ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) +} + +var _ ProxyAdaptedContractStorage = (engine.MorphRuleChainStorage)(nil) + +type RPCActorProvider interface { + GetRPCActor() actor.RPCActor +} + +// ProxyVerificationContractStorage uses decorated MorphRuleChainStorage with actor where cosigner is a proxy contract. +type ProxyVerificationContractStorage struct { + rpcActorProvider RPCActorProvider + + acc *wallet.Account + + proxyScriptHash util.Uint160 + + policyScriptHash util.Uint160 +} + +var _ ProxyAdaptedContractStorage = (*ProxyVerificationContractStorage)(nil) + +func NewProxyVerificationContractStorage(rpcActorProvider RPCActorProvider, key *keys.PrivateKey, proxyScriptHash, policyScriptHash util.Uint160) *ProxyVerificationContractStorage { + return &ProxyVerificationContractStorage{ + rpcActorProvider: rpcActorProvider, + + acc: wallet.NewAccountFromPrivateKey(key), + + proxyScriptHash: proxyScriptHash, + + policyScriptHash: policyScriptHash, + } +} + +// contractStorageActorAdapter adapats *actor.Actor to policy_morph.ContractStorageActor interface. +type contractStorageActorAdapter struct { + *actor.Actor + rpcActor invoker.RPCInvoke +} + +func (n *contractStorageActorAdapter) GetRPCInvoker() invoker.RPCInvoke { + return n.rpcActor +} + +func (contractStorage *ProxyVerificationContractStorage) newContractStorageActor() (policy_morph.ContractStorageActor, error) { + rpcActor := contractStorage.rpcActorProvider.GetRPCActor() + act, err := actor.New(rpcActor, cosigners(contractStorage.acc, contractStorage.proxyScriptHash, contractStorage.policyScriptHash)) + if err != nil { + return nil, err + } + return &contractStorageActorAdapter{ + Actor: act, + rpcActor: rpcActor, + }, nil +} + +// AddMorphRuleChain add morph rule chain to Policy contract using both Proxy contract and storage account as consigners. +func (contractStorage *ProxyVerificationContractStorage) AddMorphRuleChain(name chain.Name, target engine.Target, c *chain.Chain) (util.Uint256, uint32, error) { + // contractStorageActor is reconstructed per each method invocation because RPCActor's (that is, basically, WSClient) connection may get invalidated, but + // ProxyVerificationContractStorage does not manage reconnections. + contractStorageActor, err := contractStorage.newContractStorageActor() + if err != nil { + return util.Uint256{}, 0, err + } + return policy_morph.NewContractStorage(contractStorageActor, contractStorage.policyScriptHash).AddMorphRuleChain(name, target, c) +} + +// RemoveMorphRuleChain removes morph rule chain from Policy contract using both Proxy contract and storage account as consigners. +func (contractStorage *ProxyVerificationContractStorage) RemoveMorphRuleChain(name chain.Name, target engine.Target, chainID chain.ID) (util.Uint256, uint32, error) { + // contractStorageActor is reconstructed per each method invocation because RPCActor's (that is, basically, WSClient) connection may get invalidated, but + // ProxyVerificationContractStorage does not manage reconnections. + contractStorageActor, err := contractStorage.newContractStorageActor() + if err != nil { + return util.Uint256{}, 0, err + } + return policy_morph.NewContractStorage(contractStorageActor, contractStorage.policyScriptHash).RemoveMorphRuleChain(name, target, chainID) +} + +// ListMorphRuleChains lists morph rule chains from Policy contract using both Proxy contract and storage account as consigners. +func (contractStorage *ProxyVerificationContractStorage) ListMorphRuleChains(name chain.Name, target engine.Target) ([]*chain.Chain, error) { + // contractStorageActor is reconstructed per each method invocation because RPCActor's (that is, basically, WSClient) connection may get invalidated, but + // ProxyVerificationContractStorage does not manage reconnections. + contractStorageActor, err := contractStorage.newContractStorageActor() + if err != nil { + return nil, err + } + return policy_morph.NewContractStorage(contractStorageActor, contractStorage.policyScriptHash).ListMorphRuleChains(name, target) +} + +func cosigners(acc *wallet.Account, proxyScriptHash, policyScriptHash util.Uint160) []actor.SignerAccount { + return []actor.SignerAccount{ + { + Signer: transaction.Signer{ + Account: proxyScriptHash, + Scopes: transaction.CustomContracts, + AllowedContracts: []util.Uint160{policyScriptHash}, + }, + Account: notary.FakeContractAccount(proxyScriptHash), + }, + { + Signer: transaction.Signer{ + Account: acc.Contract.ScriptHash(), + Scopes: transaction.CalledByEntry, + }, + Account: acc, + }, + } +}