neo-go/pkg/core/interop/contract/account_test.go
Anna Shaleva 8162e9033d *: replace slice.Copy with bytes.Clone
And refactor some code a bit, don't use bytes.Clone where type-specific
helpers may be used instead.

Close #2907.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2024-03-05 13:54:10 +03:00

175 lines
5.9 KiB
Go

package contract_test
import (
"bytes"
"math"
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/stretchr/testify/require"
)
func TestCreateStandardAccount(t *testing.T) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
w := io.NewBufBinWriter()
t.Run("Good", func(t *testing.T) {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
pub := priv.PublicKey()
emit.Bytes(w.BinWriter, pub.Bytes())
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateStandardAccount)
require.NoError(t, w.Err)
script := w.Bytes()
tx := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
res := e.GetTxExecResult(t, tx.Hash())
value := res.Stack[0].Value().([]byte)
u, err := util.Uint160DecodeBytesBE(value)
require.NoError(t, err)
require.Equal(t, pub.GetScriptHash(), u)
})
t.Run("InvalidKey", func(t *testing.T) {
w.Reset()
emit.Bytes(w.BinWriter, []byte{1, 2, 3})
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateStandardAccount)
require.NoError(t, w.Err)
script := w.Bytes()
tx := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
e.AddNewBlock(t, tx)
e.CheckFault(t, tx.Hash(), "invalid prefix 1")
})
}
func TestCreateMultisigAccount(t *testing.T) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
w := io.NewBufBinWriter()
createScript := func(t *testing.T, pubs []any, m int) []byte {
w.Reset()
emit.Array(w.BinWriter, pubs...)
emit.Int(w.BinWriter, int64(m))
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateMultisigAccount)
require.NoError(t, w.Err)
return w.Bytes()
}
t.Run("Good", func(t *testing.T) {
m, n := 3, 5
pubs := make(keys.PublicKeys, n)
arr := make([]any, n)
for i := range pubs {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
pubs[i] = pk.PublicKey()
arr[i] = pubs[i].Bytes()
}
script := createScript(t, arr, m)
txH := e.InvokeScript(t, script, []neotest.Signer{acc})
e.CheckHalt(t, txH)
res := e.GetTxExecResult(t, txH)
value := res.Stack[0].Value().([]byte)
u, err := util.Uint160DecodeBytesBE(value)
require.NoError(t, err)
expected, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
require.NoError(t, err)
require.Equal(t, hash.Hash160(expected), u)
})
t.Run("InvalidKey", func(t *testing.T) {
script := createScript(t, []any{[]byte{1, 2, 3}}, 1)
e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "invalid prefix 1")
})
t.Run("Invalid m", func(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
script := createScript(t, []any{pk.PublicKey().Bytes()}, 2)
e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "length of the signatures (2) is higher then the number of public keys")
})
t.Run("m overflows int32", func(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
m := big.NewInt(math.MaxInt32)
m.Add(m, big.NewInt(1))
w.Reset()
emit.Array(w.BinWriter, pk.Bytes())
emit.BigInt(w.BinWriter, m)
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateMultisigAccount)
require.NoError(t, w.Err)
e.InvokeScriptCheckFAULT(t, w.Bytes(), []neotest.Signer{acc}, "m must be positive and fit int32")
})
}
func TestCreateAccount_HFAspidochelone(t *testing.T) {
const enabledHeight = 3
bc, acc := chain.NewSingleWithCustomConfig(t, func(c *config.Blockchain) {
c.P2PSigExtensions = true // `basicchain.Init` requires Notary enabled
c.Hardforks = map[string]uint32{
config.HFAspidochelone.String(): enabledHeight,
}
})
e := neotest.NewExecutor(t, bc, acc, acc)
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
pub := priv.PublicKey()
w := io.NewBufBinWriter()
emit.Array(w.BinWriter, []any{pub.Bytes(), pub.Bytes(), pub.Bytes()}...)
emit.Int(w.BinWriter, int64(2))
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateMultisigAccount)
require.NoError(t, w.Err)
multisigScript := bytes.Clone(w.Bytes())
w.Reset()
emit.Bytes(w.BinWriter, pub.Bytes())
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateStandardAccount)
require.NoError(t, w.Err)
standardScript := bytes.Clone(w.Bytes())
createAccTx := func(t *testing.T, script []byte) *transaction.Transaction {
tx := e.PrepareInvocation(t, script, []neotest.Signer{e.Committee}, bc.BlockHeight()+1)
return tx
}
// blocks #1, #2: old prices
tx1Standard := createAccTx(t, standardScript)
tx1Multisig := createAccTx(t, multisigScript)
e.AddNewBlock(t, tx1Standard, tx1Multisig)
e.CheckHalt(t, tx1Standard.Hash())
e.CheckHalt(t, tx1Multisig.Hash())
tx2Standard := createAccTx(t, standardScript)
tx2Multisig := createAccTx(t, multisigScript)
e.AddNewBlock(t, tx2Standard, tx2Multisig)
e.CheckHalt(t, tx2Standard.Hash())
e.CheckHalt(t, tx2Multisig.Hash())
// block #3: updated prices (larger than the previous ones)
require.Equal(t, uint32(enabledHeight-1), e.Chain.BlockHeight())
tx3Standard := createAccTx(t, standardScript)
tx3Multisig := createAccTx(t, multisigScript)
e.AddNewBlock(t, tx3Standard, tx3Multisig)
e.CheckHalt(t, tx3Standard.Hash())
e.CheckHalt(t, tx3Multisig.Hash())
require.True(t, tx1Standard.SystemFee == tx2Standard.SystemFee)
require.True(t, tx1Multisig.SystemFee == tx2Multisig.SystemFee)
require.True(t, tx2Standard.SystemFee < tx3Standard.SystemFee)
require.True(t, tx2Multisig.SystemFee < tx3Multisig.SystemFee)
}