neo-go/internal/testchain/transaction.go
Anna Shaleva eeb439f548 internal: avoid race access to config.Version by tests
Network server constructor reads config.Version variable, and
testcli.DeployContract writes dummy config.Version which causes
race in tests. Avoid this race by moving config.Version initialisation
to a separate package and perform it inside test packages init().

Close #3011, close #3017.

Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
2023-10-10 11:59:53 +03:00

144 lines
4.5 KiB
Go

package testchain
import (
"encoding/json"
"fmt"
gio "io"
"strings"
clisc "github.com/nspcc-dev/neo-go/cli/smartcontract"
"github.com/nspcc-dev/neo-go/internal/versionutil"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"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/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Ledger is an interface that abstracts the implementation of the blockchain.
type Ledger interface {
BlockHeight() uint32
FeePerByte() int64
GetBaseExecFee() int64
GetHeader(hash util.Uint256) (*block.Header, error)
GetHeaderHash(uint32) util.Uint256
HeaderHeight() uint32
ManagementContractHash() util.Uint160
}
var (
ownerHash = MultisigScriptHash()
ownerScript = MultisigVerificationScript()
)
// NewTransferFromOwner returns a transaction transferring funds from NEO and GAS owner.
func NewTransferFromOwner(bc Ledger, contractHash, to util.Uint160, amount int64,
nonce, validUntil uint32) (*transaction.Transaction, error) {
script, err := smartcontract.CreateCallWithAssertScript(contractHash, "transfer", ownerHash, to, amount, nil)
if err != nil {
return nil, err
}
tx := transaction.New(script, 11000000)
tx.ValidUntilBlock = validUntil
tx.Nonce = nonce
tx.Signers = []transaction.Signer{{
Account: ownerHash,
Scopes: transaction.CalledByEntry,
AllowedContracts: nil,
AllowedGroups: nil,
}}
return tx, SignTx(bc, tx)
}
// NewDeployTx returns a new deployment transaction for a contract with the source from r and a name equal to
// the filename without '.go' suffix.
func NewDeployTx(bc Ledger, name string, sender util.Uint160, r gio.Reader, confFile *string) (*transaction.Transaction, util.Uint160, []byte, error) {
// nef.NewFile() cares about version a lot.
config.Version = versionutil.TestVersion
o := &compiler.Options{
Name: strings.TrimSuffix(name, ".go"),
NoStandardCheck: true,
NoEventsCheck: true,
}
if confFile != nil {
conf, err := clisc.ParseContractConfig(*confFile)
if err != nil {
return nil, util.Uint160{}, nil, fmt.Errorf("failed to parse configuration: %w", err)
}
o.Name = conf.Name
o.SourceURL = conf.SourceURL
o.ContractEvents = conf.Events
o.DeclaredNamedTypes = conf.NamedTypes
o.ContractSupportedStandards = conf.SupportedStandards
o.Permissions = make([]manifest.Permission, len(conf.Permissions))
for i := range conf.Permissions {
o.Permissions[i] = manifest.Permission(conf.Permissions[i])
}
o.SafeMethods = conf.SafeMethods
o.Overloads = conf.Overloads
}
ne, di, err := compiler.CompileWithOptions(name, r, o)
if err != nil {
return nil, util.Uint160{}, nil, err
}
m, err := compiler.CreateManifest(di, o)
if err != nil {
return nil, util.Uint160{}, nil, fmt.Errorf("failed to create manifest: %w", err)
}
rawManifest, err := json.Marshal(m)
if err != nil {
return nil, util.Uint160{}, nil, err
}
neb, err := ne.Bytes()
if err != nil {
return nil, util.Uint160{}, nil, err
}
script, err := smartcontract.CreateCallScript(bc.ManagementContractHash(), "deploy", neb, rawManifest)
if err != nil {
return nil, util.Uint160{}, nil, err
}
tx := transaction.New(script, 100*native.GASFactor)
tx.Signers = []transaction.Signer{{Account: sender}}
h := state.CreateContractHash(tx.Sender(), ne.Checksum, m.Name)
return tx, h, ne.Script, nil
}
// SignTx signs the provided transactions with validator keys.
func SignTx(bc Ledger, txs ...*transaction.Transaction) error {
signTxGeneric(bc, Sign, ownerScript, txs...)
return nil
}
// SignTxCommittee signs transactions by committee.
func SignTxCommittee(bc Ledger, txs ...*transaction.Transaction) error {
signTxGeneric(bc, SignCommittee, CommitteeVerificationScript(), txs...)
return nil
}
func signTxGeneric(bc Ledger, sign func(hash.Hashable) []byte, verif []byte, txs ...*transaction.Transaction) {
for _, tx := range txs {
size := io.GetVarSize(tx)
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), verif)
tx.NetworkFee += netFee
size += sizeDelta
tx.NetworkFee += int64(size) * bc.FeePerByte()
tx.Scripts = []transaction.Witness{{
InvocationScript: sign(tx),
VerificationScript: verif,
}}
}
}