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 }