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)
|
neotest.AddSystemFee(e.Chain, tx, -1)
|
||||||
require.NoError(t, c.Committee.SignTx(e.Chain.GetConfig().Magic, tx))
|
require.NoError(t, c.Committee.SignTx(e.Chain.GetConfig().Magic, tx))
|
||||||
c.AddNewBlock(t, tx)
|
c.AddNewBlock(t, tx)
|
||||||
|
|
|
@ -106,7 +106,7 @@ func (e *Executor) SignTx(t testing.TB, tx *transaction.Transaction, sysFee int6
|
||||||
Scopes: transaction.Global,
|
Scopes: transaction.Global,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
AddNetworkFee(e.Chain, tx, signers...)
|
AddNetworkFee(t, e.Chain, tx, signers...)
|
||||||
AddSystemFee(e.Chain, tx, sysFee)
|
AddSystemFee(e.Chain, tx, sysFee)
|
||||||
|
|
||||||
for _, acc := range signers {
|
for _, acc := range signers {
|
||||||
|
@ -280,7 +280,7 @@ func NewDeployTxBy(t testing.TB, bc *core.Blockchain, signer Signer, c *Contract
|
||||||
Account: signer.ScriptHash(),
|
Account: signer.ScriptHash(),
|
||||||
Scopes: transaction.Global,
|
Scopes: transaction.Global,
|
||||||
}}
|
}}
|
||||||
AddNetworkFee(bc, tx, signer)
|
AddNetworkFee(t, bc, tx, signer)
|
||||||
require.NoError(t, signer.SignTx(netmode.UnitTestNet, tx))
|
require.NoError(t, signer.SignTx(netmode.UnitTestNet, tx))
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
@ -297,13 +297,31 @@ func AddSystemFee(bc *core.Blockchain, tx *transaction.Transaction, sysFee int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddNetworkFee adds network fee to the transaction.
|
// 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()
|
baseFee := bc.GetBaseExecFee()
|
||||||
size := io.GetVarSize(tx)
|
size := io.GetVarSize(tx)
|
||||||
for _, sgr := range signers {
|
for _, sgr := range signers {
|
||||||
netFee, sizeDelta := fee.Calculate(baseFee, sgr.Script())
|
if csgr, ok := sgr.(ContractSigner); ok {
|
||||||
tx.NetworkFee += netFee
|
sc, err := csgr.InvocationScript(tx)
|
||||||
size += sizeDelta
|
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)
|
tx.NetworkFee += int64(size)*bc.FeePerByte() + bc.CalculateAttributesFee(tx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package neotest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -10,8 +11,10 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"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/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"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/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"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/vm/opcode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -44,6 +47,13 @@ type MultiSigner interface {
|
||||||
Single(n int) SingleSigner
|
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.
|
// signer represents a simple-signature signer.
|
||||||
type signer wallet.Account
|
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")
|
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