neotest: Add contract signer support
Signed-off-by: Dmitrii Stepanov <dima-stepan@yandex.ru>
This commit is contained in:
parent
6c0c2a6a98
commit
7b53a0c239
3 changed files with 104 additions and 7 deletions
|
@ -225,7 +225,7 @@ func TestLedger_GetTransactionSignersInteropAPI(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}}
|
||||
neotest.AddNetworkFee(e.Chain, tx, c.Committee)
|
||||
neotest.AddNetworkFee(t, e.Chain, tx, c.Committee)
|
||||
neotest.AddSystemFee(e.Chain, tx, -1)
|
||||
require.NoError(t, c.Committee.SignTx(e.Chain.GetConfig().Magic, tx))
|
||||
c.AddNewBlock(t, tx)
|
||||
|
|
|
@ -106,7 +106,7 @@ func (e *Executor) SignTx(t testing.TB, tx *transaction.Transaction, sysFee int6
|
|||
Scopes: transaction.Global,
|
||||
})
|
||||
}
|
||||
AddNetworkFee(e.Chain, tx, signers...)
|
||||
AddNetworkFee(t, e.Chain, tx, signers...)
|
||||
AddSystemFee(e.Chain, tx, sysFee)
|
||||
|
||||
for _, acc := range signers {
|
||||
|
@ -280,7 +280,7 @@ func NewDeployTxBy(t testing.TB, bc *core.Blockchain, signer Signer, c *Contract
|
|||
Account: signer.ScriptHash(),
|
||||
Scopes: transaction.Global,
|
||||
}}
|
||||
AddNetworkFee(bc, tx, signer)
|
||||
AddNetworkFee(t, bc, tx, signer)
|
||||
require.NoError(t, signer.SignTx(netmode.UnitTestNet, tx))
|
||||
return tx
|
||||
}
|
||||
|
@ -297,13 +297,31 @@ func AddSystemFee(bc *core.Blockchain, tx *transaction.Transaction, sysFee int64
|
|||
}
|
||||
|
||||
// AddNetworkFee adds network fee to the transaction.
|
||||
func AddNetworkFee(bc *core.Blockchain, tx *transaction.Transaction, signers ...Signer) {
|
||||
func AddNetworkFee(t testing.TB, bc *core.Blockchain, tx *transaction.Transaction, signers ...Signer) {
|
||||
baseFee := bc.GetBaseExecFee()
|
||||
size := io.GetVarSize(tx)
|
||||
for _, sgr := range signers {
|
||||
netFee, sizeDelta := fee.Calculate(baseFee, sgr.Script())
|
||||
tx.NetworkFee += netFee
|
||||
size += sizeDelta
|
||||
if csgr, ok := sgr.(ContractSigner); ok {
|
||||
sc, err := csgr.InvocationScript(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
txCopy := *tx
|
||||
ic, err := bc.GetTestVM(trigger.Verification, &txCopy, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
ic.UseSigners(tx.Signers)
|
||||
ic.VM.GasLimit = bc.GetMaxVerificationGAS()
|
||||
|
||||
require.NoError(t, bc.InitVerificationContext(ic, csgr.ScriptHash(), &transaction.Witness{InvocationScript: sc, VerificationScript: csgr.Script()}))
|
||||
require.NoError(t, ic.VM.Run())
|
||||
|
||||
tx.NetworkFee += ic.VM.GasConsumed()
|
||||
size += io.GetVarSize(sc) + io.GetVarSize(csgr.Script())
|
||||
} else {
|
||||
netFee, sizeDelta := fee.Calculate(baseFee, sgr.Script())
|
||||
tx.NetworkFee += netFee
|
||||
size += sizeDelta
|
||||
}
|
||||
}
|
||||
tx.NetworkFee += int64(size)*bc.FeePerByte() + bc.CalculateAttributesFee(tx)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package neotest
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
|
@ -10,8 +11,10 @@ import (
|
|||
"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/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"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/wallet"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -44,6 +47,13 @@ type MultiSigner interface {
|
|||
Single(n int) SingleSigner
|
||||
}
|
||||
|
||||
// ContractSigner is an interface for contract signer.
|
||||
type ContractSigner interface {
|
||||
Signer
|
||||
// InvocationScript returns an invocation script to be used as invocation script for contract-based witness.
|
||||
InvocationScript(tx *transaction.Transaction) ([]byte, error)
|
||||
}
|
||||
|
||||
// signer represents a simple-signature signer.
|
||||
type signer wallet.Account
|
||||
|
||||
|
@ -179,3 +189,72 @@ func checkMultiSigner(t testing.TB, s Signer) {
|
|||
require.Equal(t, h, accs[i].Contract.ScriptHash(), "inconsistent multi-signer accounts")
|
||||
}
|
||||
}
|
||||
|
||||
type contractSigner struct {
|
||||
params func(tx *transaction.Transaction) []any
|
||||
scriptHash util.Uint160
|
||||
}
|
||||
|
||||
// NewContractSigner returns a contract signer for the provided contract hash.
|
||||
// getInvParams must return params to be used as invocation script for contract-based witness.
|
||||
func NewContractSigner(h util.Uint160, getInvParams func(tx *transaction.Transaction) []any) ContractSigner {
|
||||
return &contractSigner{
|
||||
scriptHash: h,
|
||||
params: getInvParams,
|
||||
}
|
||||
}
|
||||
|
||||
// InvocationScript implements ContractSigner.
|
||||
func (s *contractSigner) InvocationScript(tx *transaction.Transaction) ([]byte, error) {
|
||||
params := s.params(tx)
|
||||
script := io.NewBufBinWriter()
|
||||
for i := range params {
|
||||
emit.Any(script.BinWriter, params[i])
|
||||
}
|
||||
if script.Err != nil {
|
||||
return nil, script.Err
|
||||
}
|
||||
return script.Bytes(), nil
|
||||
}
|
||||
|
||||
// Script implements ContractSigner.
|
||||
func (s *contractSigner) Script() []byte {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
// ScriptHash implements ContractSigner.
|
||||
func (s *contractSigner) ScriptHash() util.Uint160 {
|
||||
return s.scriptHash
|
||||
}
|
||||
|
||||
// SignHashable implements ContractSigner.
|
||||
func (s *contractSigner) SignHashable(uint32, hash.Hashable) []byte {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
// SignTx implements ContractSigner.
|
||||
func (s *contractSigner) SignTx(magic netmode.Magic, tx *transaction.Transaction) error {
|
||||
pos := -1
|
||||
for idx := range tx.Signers {
|
||||
if tx.Signers[idx].Account.Equals(s.ScriptHash()) {
|
||||
pos = idx
|
||||
break
|
||||
}
|
||||
}
|
||||
if pos < 0 {
|
||||
return fmt.Errorf("signer %s not found", s.ScriptHash().String())
|
||||
}
|
||||
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
|
||||
}
|
||||
if len(tx.Scripts) == pos {
|
||||
tx.Scripts = append(tx.Scripts, transaction.Witness{})
|
||||
}
|
||||
tx.Scripts[pos].InvocationScript = invoc
|
||||
tx.Scripts[pos].VerificationScript = s.Script()
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue