github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/internal/testchain/transaction.go (about)

     1  package testchain
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	gio "io"
     7  	"strings"
     8  
     9  	clisc "github.com/nspcc-dev/neo-go/cli/smartcontract"
    10  	"github.com/nspcc-dev/neo-go/internal/versionutil"
    11  	"github.com/nspcc-dev/neo-go/pkg/compiler"
    12  	"github.com/nspcc-dev/neo-go/pkg/config"
    13  	"github.com/nspcc-dev/neo-go/pkg/core/block"
    14  	"github.com/nspcc-dev/neo-go/pkg/core/fee"
    15  	"github.com/nspcc-dev/neo-go/pkg/core/native"
    16  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    17  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    18  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    19  	"github.com/nspcc-dev/neo-go/pkg/io"
    20  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    21  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
    22  	"github.com/nspcc-dev/neo-go/pkg/util"
    23  )
    24  
    25  // Ledger is an interface that abstracts the implementation of the blockchain.
    26  type Ledger interface {
    27  	BlockHeight() uint32
    28  	CalculateAttributesFee(tx *transaction.Transaction) int64
    29  	FeePerByte() int64
    30  	GetBaseExecFee() int64
    31  	GetHeader(hash util.Uint256) (*block.Header, error)
    32  	GetHeaderHash(uint32) util.Uint256
    33  	HeaderHeight() uint32
    34  	ManagementContractHash() util.Uint160
    35  }
    36  
    37  var (
    38  	ownerHash   = MultisigScriptHash()
    39  	ownerScript = MultisigVerificationScript()
    40  )
    41  
    42  // NewTransferFromOwner returns a transaction transferring funds from NEO and GAS owner.
    43  func NewTransferFromOwner(bc Ledger, contractHash, to util.Uint160, amount int64,
    44  	nonce, validUntil uint32) (*transaction.Transaction, error) {
    45  	script, err := smartcontract.CreateCallWithAssertScript(contractHash, "transfer", ownerHash, to, amount, nil)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	tx := transaction.New(script, 11000000)
    51  	tx.ValidUntilBlock = validUntil
    52  	tx.Nonce = nonce
    53  	tx.Signers = []transaction.Signer{{
    54  		Account:          ownerHash,
    55  		Scopes:           transaction.CalledByEntry,
    56  		AllowedContracts: nil,
    57  		AllowedGroups:    nil,
    58  	}}
    59  	return tx, SignTx(bc, tx)
    60  }
    61  
    62  // NewDeployTx returns a new deployment transaction for a contract with the source from r and a name equal to
    63  // the filename without '.go' suffix.
    64  func NewDeployTx(bc Ledger, name string, sender util.Uint160, r gio.Reader, confFile *string) (*transaction.Transaction, util.Uint160, []byte, error) {
    65  	// nef.NewFile() cares about version a lot.
    66  	config.Version = versionutil.TestVersion
    67  
    68  	o := &compiler.Options{
    69  		Name:            strings.TrimSuffix(name, ".go"),
    70  		NoStandardCheck: true,
    71  		NoEventsCheck:   true,
    72  	}
    73  	if confFile != nil {
    74  		conf, err := clisc.ParseContractConfig(*confFile)
    75  		if err != nil {
    76  			return nil, util.Uint160{}, nil, fmt.Errorf("failed to parse configuration: %w", err)
    77  		}
    78  		o.Name = conf.Name
    79  		o.SourceURL = conf.SourceURL
    80  		o.ContractEvents = conf.Events
    81  		o.DeclaredNamedTypes = conf.NamedTypes
    82  		o.ContractSupportedStandards = conf.SupportedStandards
    83  		o.Permissions = make([]manifest.Permission, len(conf.Permissions))
    84  		for i := range conf.Permissions {
    85  			o.Permissions[i] = manifest.Permission(conf.Permissions[i])
    86  		}
    87  		o.SafeMethods = conf.SafeMethods
    88  		o.Overloads = conf.Overloads
    89  	}
    90  
    91  	ne, di, err := compiler.CompileWithOptions(name, r, o)
    92  	if err != nil {
    93  		return nil, util.Uint160{}, nil, err
    94  	}
    95  
    96  	m, err := compiler.CreateManifest(di, o)
    97  	if err != nil {
    98  		return nil, util.Uint160{}, nil, fmt.Errorf("failed to create manifest: %w", err)
    99  	}
   100  
   101  	rawManifest, err := json.Marshal(m)
   102  	if err != nil {
   103  		return nil, util.Uint160{}, nil, err
   104  	}
   105  	neb, err := ne.Bytes()
   106  	if err != nil {
   107  		return nil, util.Uint160{}, nil, err
   108  	}
   109  	script, err := smartcontract.CreateCallScript(bc.ManagementContractHash(), "deploy", neb, rawManifest)
   110  	if err != nil {
   111  		return nil, util.Uint160{}, nil, err
   112  	}
   113  
   114  	tx := transaction.New(script, 100*native.GASFactor)
   115  	tx.Signers = []transaction.Signer{{Account: sender}}
   116  	h := state.CreateContractHash(tx.Sender(), ne.Checksum, m.Name)
   117  
   118  	return tx, h, ne.Script, nil
   119  }
   120  
   121  // SignTx signs the provided transactions with validator keys.
   122  func SignTx(bc Ledger, txs ...*transaction.Transaction) error {
   123  	signTxGeneric(bc, Sign, ownerScript, txs...)
   124  	return nil
   125  }
   126  
   127  // SignTxCommittee signs transactions by committee.
   128  func SignTxCommittee(bc Ledger, txs ...*transaction.Transaction) error {
   129  	signTxGeneric(bc, SignCommittee, CommitteeVerificationScript(), txs...)
   130  	return nil
   131  }
   132  
   133  func signTxGeneric(bc Ledger, sign func(hash.Hashable) []byte, verif []byte, txs ...*transaction.Transaction) {
   134  	for _, tx := range txs {
   135  		size := io.GetVarSize(tx)
   136  		netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), verif)
   137  		tx.NetworkFee += netFee
   138  		size += sizeDelta
   139  		tx.NetworkFee += int64(size)*bc.FeePerByte() + bc.CalculateAttributesFee(tx)
   140  		tx.Scripts = []transaction.Witness{{
   141  			InvocationScript:   sign(tx),
   142  			VerificationScript: verif,
   143  		}}
   144  	}
   145  }