From a7601334f72b6e1575cccc791d141fd83dee4571 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Mon, 18 Oct 2021 14:12:26 +0300 Subject: [PATCH] [#150] container: allow only alphabet calls in `Put` For notary-enabled environment we expect `put` to be signed by alphabet. Also group notary-disabled logic inside a single if and add tests for balance checks. Signed-off-by: Evgenii Stratonikov --- container/container_contract.go | 39 +++++++++++++----------------- tests/balance_test.go | 6 +++++ tests/container_test.go | 43 +++++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/container/container_contract.go b/container/container_contract.go index 6dc07ca..27e7c17 100644 --- a/container/container_contract.go +++ b/container/container_contract.go @@ -173,37 +173,24 @@ func PutNamed(container []byte, signature interop.Signature, needRegister = checkNiceNameAvailable(nnsContractAddr, domain) } - var ( // for invocation collection without notary - alphabet = common.AlphabetNodes() - nodeKey []byte - alphabetCall bool - ) - - if notaryDisabled { - nodeKey = common.InnerRingInvoker(alphabet) - alphabetCall = len(nodeKey) != 0 - } else { - multiaddr := common.AlphabetAddress() - alphabetCall = runtime.CheckWitness(multiaddr) - } - + alphabet := common.AlphabetNodes() from := common.WalletToScriptHash(ownerID) netmapContractAddr := storage.Get(ctx, netmapContractKey).(interop.Hash160) balanceContractAddr := storage.Get(ctx, balanceContractKey).(interop.Hash160) containerFee := contract.Call(netmapContractAddr, "config", contract.ReadOnly, containerFeeKey).(int) balance := contract.Call(balanceContractAddr, "balanceOf", contract.ReadOnly, from).(int) - details := common.ContainerFeeTransferDetails(containerID) - if !alphabetCall { - if balance < containerFee*len(alphabet) { - panic("insufficient balance to create container") - } - runtime.Notify("containerPut", container, signature, publicKey, token) - return + if balance < containerFee*len(alphabet) { + panic("insufficient balance to create container") } - // todo: check if new container with unique container id if notaryDisabled { + nodeKey := common.InnerRingInvoker(alphabet) + if len(nodeKey) == 0 { + runtime.Notify("containerPut", container, signature, publicKey, token) + return + } + threshold := len(alphabet)*2/3 + 1 id := common.InvokeID([]interface{}{container, signature, publicKey}, []byte("put")) @@ -213,7 +200,15 @@ func PutNamed(container []byte, signature interop.Signature, } common.RemoveVotes(ctx, id) + } else { + multiaddr := common.AlphabetAddress() + if !runtime.CheckWitness(multiaddr) { + panic("put: alphabet witness check failed") + } } + // todo: check if new container with unique container id + + details := common.ContainerFeeTransferDetails(containerID) for i := 0; i < len(alphabet); i++ { node := alphabet[i] diff --git a/tests/balance_test.go b/tests/balance_test.go index 84eac3b..7bf564c 100644 --- a/tests/balance_test.go +++ b/tests/balance_test.go @@ -5,6 +5,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/wallet" ) const balancePath = "../balance" @@ -16,3 +17,8 @@ func deployBalanceContract(t *testing.T, bc *core.Blockchain, addrNetmap, addrCo args[2] = addrContainer return DeployContract(t, bc, balancePath, args) } + +func balanceMint(t *testing.T, bc *core.Blockchain, acc *wallet.Account, h util.Uint160, amount int64, details []byte) { + tx := PrepareInvoke(t, bc, CommitteeAcc, h, "mint", acc.Contract.ScriptHash(), amount, details) + AddBlockCheckHalt(t, bc, tx) +} diff --git a/tests/container_test.go b/tests/container_test.go index f9007d3..ab75606 100644 --- a/tests/container_test.go +++ b/tests/container_test.go @@ -15,6 +15,8 @@ import ( const containerPath = "../container" +const containerFee = 0_0100_0000 + func deployContainerContract(t *testing.T, bc *core.Blockchain, addrNetmap, addrBalance, addrNNS util.Uint160) util.Uint160 { args := make([]interface{}, 6) args[0] = int64(0) @@ -26,7 +28,7 @@ func deployContainerContract(t *testing.T, bc *core.Blockchain, addrNetmap, addr return DeployContract(t, bc, containerPath, args) } -func prepareContainerContract(t *testing.T, bc *core.Blockchain) util.Uint160 { +func prepareContainerContract(t *testing.T, bc *core.Blockchain) (util.Uint160, util.Uint160) { addrNNS := DeployContract(t, bc, nnsPath, nil) ctrNetmap, err := ContractInfo(CommitteeAcc.Contract.ScriptHash(), netmapPath) @@ -38,9 +40,9 @@ func prepareContainerContract(t *testing.T, bc *core.Blockchain) util.Uint160 { ctrContainer, err := ContractInfo(CommitteeAcc.Contract.ScriptHash(), containerPath) require.NoError(t, err) - deployNetmapContract(t, bc, ctrBalance.Hash, ctrContainer.Hash, "ContainerFee", []byte{}) - deployBalanceContract(t, bc, ctrNetmap.Hash, ctrContainer.Hash) - return deployContainerContract(t, bc, ctrNetmap.Hash, ctrBalance.Hash, addrNNS) + deployNetmapContract(t, bc, ctrBalance.Hash, ctrContainer.Hash, "ContainerFee", int64(containerFee)) + balHash := deployBalanceContract(t, bc, ctrNetmap.Hash, ctrContainer.Hash) + return deployContainerContract(t, bc, ctrNetmap.Hash, ctrBalance.Hash, addrNNS), balHash } func setContainerOwner(c []byte, owner *wallet.Account) { @@ -49,7 +51,7 @@ func setContainerOwner(c []byte, owner *wallet.Account) { func TestContainerPut(t *testing.T) { bc := NewChain(t) - h := prepareContainerContract(t, bc) + h, balanceHash := prepareContainerContract(t, bc) acc := NewAccount(t, bc) dummySig := make([]byte, 64) @@ -59,17 +61,30 @@ func TestContainerPut(t *testing.T) { setContainerOwner(container, acc) containerID := sha256.Sum256(container) - tx := PrepareInvoke(t, bc, CommitteeAcc, h, "put", container, dummySig, dummyPub, dummyToken) + putArgs := []interface{}{container, dummySig, dummyPub, dummyToken} + tx := PrepareInvoke(t, bc, CommitteeAcc, h, "put", putArgs...) + AddBlock(t, bc, tx) + CheckFault(t, bc, tx.Hash(), "insufficient balance to create container") + + balanceMint(t, bc, acc, balanceHash, containerFee*1, []byte{}) + + tx = PrepareInvoke(t, bc, acc, h, "put", putArgs...) + AddBlock(t, bc, tx) + CheckFault(t, bc, tx.Hash(), "alphabet witness check failed") + + tx = PrepareInvoke(t, bc, CommitteeAcc, h, "put", putArgs...) AddBlockCheckHalt(t, bc, tx) t.Run("with nice names", func(t *testing.T) { nnsHash := contracts[nnsPath].Hash - tx = PrepareInvoke(t, bc, CommitteeAcc, h, "putNamed", - container, dummySig, dummyPub, dummyToken, "mycnt", "") + balanceMint(t, bc, acc, balanceHash, containerFee*1, []byte{}) + + putArgs := []interface{}{container, dummySig, dummyPub, dummyToken, "mycnt", ""} + tx = PrepareInvoke(t, bc, CommitteeAcc, h, "putNamed", putArgs...) AddBlockCheckHalt(t, bc, tx) - tx = PrepareInvoke(t, bc, CommitteeAcc, nnsHash, "resolve", "mycnt.neofs", int64(nns.TXT)) + tx = PrepareInvoke(t, bc, acc, nnsHash, "resolve", "mycnt.neofs", int64(nns.TXT)) CheckTestInvoke(t, bc, tx, stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray([]byte(base58.Encode(containerID[:]))), })) @@ -84,13 +99,15 @@ func TestContainerPut(t *testing.T) { container[len(container)-1] = 10 containerID = sha256.Sum256(container) - tx = PrepareInvoke(t, bc, []*wallet.Account{CommitteeAcc, acc}, nnsHash, "register", - "second.neofs", acc.Contract.ScriptHash(), + tx = PrepareInvoke(t, bc, CommitteeAcc, nnsHash, "register", + "second.neofs", CommitteeAcc.Contract.ScriptHash(), "whateveriwant@world.com", int64(0), int64(0), int64(0), int64(0)) AddBlockCheckHalt(t, bc, tx) - tx = PrepareInvoke(t, bc, []*wallet.Account{CommitteeAcc, acc}, h, "putNamed", - container, dummySig, dummyPub, dummyToken, "second", "neofs") + balanceMint(t, bc, acc, balanceHash, containerFee*1, []byte{}) + + putArgs := []interface{}{container, dummySig, dummyPub, dummyToken, "second", "neofs"} + tx = PrepareInvoke(t, bc, CommitteeAcc, h, "putNamed", putArgs...) AddBlockCheckHalt(t, bc, tx) tx = PrepareInvoke(t, bc, CommitteeAcc, nnsHash, "resolve", "second.neofs", int64(nns.TXT))