2024-07-04 22:34:40 +00:00
package smartcontract_test
2021-10-25 11:48:21 +00:00
import (
2022-10-26 20:27:24 +00:00
"bytes"
2021-10-25 11:48:21 +00:00
"encoding/json"
2023-05-11 10:25:45 +00:00
"fmt"
2021-10-25 11:48:21 +00:00
"os"
"path/filepath"
"testing"
2024-07-04 22:34:40 +00:00
"github.com/nspcc-dev/neo-go/internal/testcli"
2021-10-25 11:48:21 +00:00
"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"
"github.com/stretchr/testify/require"
)
func TestGenerate ( t * testing . T ) {
m := manifest . NewManifest ( "MyContract" )
m . ABI . Methods = append ( m . ABI . Methods ,
manifest . Method {
Name : manifest . MethodDeploy ,
ReturnType : smartcontract . VoidType ,
} ,
manifest . Method {
Name : "sum" ,
Parameters : [ ] manifest . Parameter {
manifest . NewParameter ( "first" , smartcontract . IntegerType ) ,
manifest . NewParameter ( "second" , smartcontract . IntegerType ) ,
} ,
ReturnType : smartcontract . IntegerType ,
} ,
manifest . Method {
Name : "sum" , // overloaded method
Parameters : [ ] manifest . Parameter {
manifest . NewParameter ( "first" , smartcontract . IntegerType ) ,
manifest . NewParameter ( "second" , smartcontract . IntegerType ) ,
manifest . NewParameter ( "third" , smartcontract . IntegerType ) ,
} ,
ReturnType : smartcontract . IntegerType ,
} ,
manifest . Method {
Name : "sum3" ,
Parameters : [ ] manifest . Parameter { } ,
ReturnType : smartcontract . IntegerType ,
Safe : true ,
} ,
2022-10-27 19:35:06 +00:00
manifest . Method {
Name : "zum" ,
Parameters : [ ] manifest . Parameter {
manifest . NewParameter ( "type" , smartcontract . IntegerType ) ,
manifest . NewParameter ( "typev" , smartcontract . IntegerType ) ,
manifest . NewParameter ( "func" , smartcontract . IntegerType ) ,
} ,
ReturnType : smartcontract . IntegerType ,
} ,
2021-10-25 11:48:21 +00:00
manifest . Method {
Name : "justExecute" ,
Parameters : [ ] manifest . Parameter {
manifest . NewParameter ( "arr" , smartcontract . ArrayType ) ,
} ,
ReturnType : smartcontract . VoidType ,
} ,
manifest . Method {
Name : "getPublicKey" ,
Parameters : nil ,
ReturnType : smartcontract . PublicKeyType ,
} ,
manifest . Method {
Name : "otherTypes" ,
Parameters : [ ] manifest . Parameter {
manifest . NewParameter ( "ctr" , smartcontract . Hash160Type ) ,
manifest . NewParameter ( "tx" , smartcontract . Hash256Type ) ,
manifest . NewParameter ( "sig" , smartcontract . SignatureType ) ,
manifest . NewParameter ( "data" , smartcontract . AnyType ) ,
} ,
ReturnType : smartcontract . BoolType ,
} ,
manifest . Method {
Name : "searchStorage" ,
Parameters : [ ] manifest . Parameter {
manifest . NewParameter ( "ctx" , smartcontract . InteropInterfaceType ) ,
} ,
ReturnType : smartcontract . InteropInterfaceType ,
} ,
manifest . Method {
Name : "getFromMap" ,
Parameters : [ ] manifest . Parameter {
manifest . NewParameter ( "intMap" , smartcontract . MapType ) ,
manifest . NewParameter ( "indices" , smartcontract . ArrayType ) ,
} ,
ReturnType : smartcontract . ArrayType ,
} ,
manifest . Method {
Name : "doSomething" ,
Parameters : [ ] manifest . Parameter {
manifest . NewParameter ( "bytes" , smartcontract . ByteArrayType ) ,
manifest . NewParameter ( "str" , smartcontract . StringType ) ,
} ,
ReturnType : smartcontract . InteropInterfaceType ,
} ,
manifest . Method {
Name : "getBlockWrapper" ,
Parameters : [ ] manifest . Parameter { } ,
ReturnType : smartcontract . InteropInterfaceType ,
} ,
manifest . Method {
Name : "myFunc" ,
Parameters : [ ] manifest . Parameter {
manifest . NewParameter ( "in" , smartcontract . MapType ) ,
} ,
ReturnType : smartcontract . ArrayType ,
} )
manifestFile := filepath . Join ( t . TempDir ( ) , "manifest.json" )
outFile := filepath . Join ( t . TempDir ( ) , "out.go" )
rawManifest , err := json . Marshal ( m )
require . NoError ( t , err )
2022-02-22 16:27:32 +00:00
require . NoError ( t , os . WriteFile ( manifestFile , rawManifest , os . ModePerm ) )
2021-10-25 11:48:21 +00:00
h := util . Uint160 {
0x04 , 0x08 , 0x15 , 0x16 , 0x23 , 0x42 , 0x43 , 0x44 , 0x00 , 0x01 ,
0xCA , 0xFE , 0xBA , 0xBE , 0xDE , 0xAD , 0xBE , 0xEF , 0x03 , 0x04 ,
}
2024-07-04 22:34:40 +00:00
e := testcli . NewExecutor ( t , false )
2021-10-25 11:48:21 +00:00
rawCfg := ` package : wrapper
hash : ` + h.StringLE() + `
overrides :
searchStorage . ctx : storage . Context
searchStorage : iterator . Iterator
getFromMap . intMap : "map[string]int"
getFromMap . indices : "[]string"
getFromMap : "[]int"
getBlockWrapper : ledger . Block
myFunc . in : "map[int]github.com/heyitsme/mycontract.Input"
myFunc : "[]github.com/heyitsme/mycontract.Output"
callflags :
doSomething : ReadStates
`
cfgPath := filepath . Join ( t . TempDir ( ) , "binding.yml" )
2022-02-22 16:27:32 +00:00
require . NoError ( t , os . WriteFile ( cfgPath , [ ] byte ( rawCfg ) , os . ModePerm ) )
2021-10-25 11:48:21 +00:00
2024-07-04 22:34:40 +00:00
e . Run ( t , [ ] string { "" , "contract" , "generate-wrapper" ,
2021-10-25 11:48:21 +00:00
"--manifest" , manifestFile ,
"--config" , cfgPath ,
"--out" , outFile ,
"--hash" , h . StringLE ( ) ,
2024-07-04 22:34:40 +00:00
} ... )
2021-10-25 11:48:21 +00:00
2024-04-08 22:02:05 +00:00
const expected = ` // Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
2023-12-25 10:01:36 +00:00
// Package wrapper contains wrappers for MyContract contract.
2021-10-25 11:48:21 +00:00
package wrapper
import (
"github.com/heyitsme/mycontract"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
// Hash contains contract hash in big-endian form.
const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\xbe\xef\x03\x04"
// Sum invokes ` + "`sum`" + ` method of contract.
func Sum ( first int , second int ) int {
return neogointernal . CallWithToken ( Hash , "sum" , int ( contract . All ) , first , second ) . ( int )
}
2024-01-24 08:38:06 +00:00
// Sum2 invokes ` + "`sum`" + ` method of contract.
func Sum2 ( first int , second int , third int ) int {
2021-10-25 11:48:21 +00:00
return neogointernal . CallWithToken ( Hash , "sum" , int ( contract . All ) , first , second , third ) . ( int )
}
// Sum3 invokes ` + "`sum3`" + ` method of contract.
func Sum3 ( ) int {
return neogointernal . CallWithToken ( Hash , "sum3" , int ( contract . ReadOnly ) ) . ( int )
}
2022-10-27 19:35:06 +00:00
// Zum invokes ` + "`zum`" + ` method of contract.
func Zum ( typev int , typev_ int , funcv int ) int {
return neogointernal . CallWithToken ( Hash , "zum" , int ( contract . All ) , typev , typev_ , funcv ) . ( int )
}
2021-10-25 11:48:21 +00:00
// JustExecute invokes ` + "`justExecute`" + ` method of contract.
2023-04-03 10:34:24 +00:00
func JustExecute ( arr [ ] any ) {
2021-10-25 11:48:21 +00:00
neogointernal . CallWithTokenNoRet ( Hash , "justExecute" , int ( contract . All ) , arr )
}
// GetPublicKey invokes ` + "`getPublicKey`" + ` method of contract.
func GetPublicKey ( ) interop . PublicKey {
return neogointernal . CallWithToken ( Hash , "getPublicKey" , int ( contract . All ) ) . ( interop . PublicKey )
}
// OtherTypes invokes ` + "`otherTypes`" + ` method of contract.
2023-04-03 10:34:24 +00:00
func OtherTypes ( ctr interop . Hash160 , tx interop . Hash256 , sig interop . Signature , data any ) bool {
2021-10-25 11:48:21 +00:00
return neogointernal . CallWithToken ( Hash , "otherTypes" , int ( contract . All ) , ctr , tx , sig , data ) . ( bool )
}
// SearchStorage invokes ` + "`searchStorage`" + ` method of contract.
func SearchStorage ( ctx storage . Context ) iterator . Iterator {
return neogointernal . CallWithToken ( Hash , "searchStorage" , int ( contract . All ) , ctx ) . ( iterator . Iterator )
}
// GetFromMap invokes ` + "`getFromMap`" + ` method of contract.
func GetFromMap ( intMap map [ string ] int , indices [ ] string ) [ ] int {
return neogointernal . CallWithToken ( Hash , "getFromMap" , int ( contract . All ) , intMap , indices ) . ( [ ] int )
}
// DoSomething invokes ` + "`doSomething`" + ` method of contract.
2023-04-03 10:34:24 +00:00
func DoSomething ( bytes [ ] byte , str string ) any {
return neogointernal . CallWithToken ( Hash , "doSomething" , int ( contract . ReadStates ) , bytes , str ) . ( any )
2021-10-25 11:48:21 +00:00
}
// GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract.
func GetBlockWrapper ( ) ledger . Block {
return neogointernal . CallWithToken ( Hash , "getBlockWrapper" , int ( contract . All ) ) . ( ledger . Block )
}
// MyFunc invokes ` + "`myFunc`" + ` method of contract.
func MyFunc ( in map [ int ] mycontract . Input ) [ ] mycontract . Output {
return neogointernal . CallWithToken ( Hash , "myFunc" , int ( contract . All ) , in ) . ( [ ] mycontract . Output )
}
`
2022-02-22 16:27:32 +00:00
data , err := os . ReadFile ( outFile )
2021-10-25 11:48:21 +00:00
require . NoError ( t , err )
require . Equal ( t , expected , string ( data ) )
2024-04-08 22:02:05 +00:00
2024-07-04 22:34:40 +00:00
e . Run ( t , [ ] string { "" , "contract" , "generate-wrapper" ,
2024-04-08 22:02:05 +00:00
"--manifest" , manifestFile ,
"--config" , cfgPath ,
"--out" , outFile ,
2024-07-04 22:34:40 +00:00
} ... )
2024-04-08 22:02:05 +00:00
expectedWithDynamicHash := ` // Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package wrapper contains wrappers for MyContract contract.
package wrapper
import (
"github.com/heyitsme/mycontract"
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
)
// Contract represents the MyContract smart contract.
type Contract struct {
Hash interop . Hash160
}
// NewContract returns a new Contract instance with the specified hash.
func NewContract ( hash interop . Hash160 ) Contract {
return Contract { Hash : hash }
}
// Sum invokes ` + "`sum`" + ` method of contract.
func ( c Contract ) Sum ( first int , second int ) int {
return contract . Call ( c . Hash , "sum" , contract . All , first , second ) . ( int )
}
// Sum2 invokes ` + "`sum`" + ` method of contract.
func ( c Contract ) Sum2 ( first int , second int , third int ) int {
return contract . Call ( c . Hash , "sum" , contract . All , first , second , third ) . ( int )
}
// Sum3 invokes ` + "`sum3`" + ` method of contract.
func ( c Contract ) Sum3 ( ) int {
return contract . Call ( c . Hash , "sum3" , contract . ReadOnly ) . ( int )
}
// Zum invokes ` + "`zum`" + ` method of contract.
func ( c Contract ) Zum ( typev int , typev_ int , funcv int ) int {
return contract . Call ( c . Hash , "zum" , contract . All , typev , typev_ , funcv ) . ( int )
}
// JustExecute invokes ` + "`justExecute`" + ` method of contract.
func ( c Contract ) JustExecute ( arr [ ] any ) {
contract . Call ( c . Hash , "justExecute" , contract . All , arr )
}
// GetPublicKey invokes ` + "`getPublicKey`" + ` method of contract.
func ( c Contract ) GetPublicKey ( ) interop . PublicKey {
return contract . Call ( c . Hash , "getPublicKey" , contract . All ) . ( interop . PublicKey )
}
// OtherTypes invokes ` + "`otherTypes`" + ` method of contract.
func ( c Contract ) OtherTypes ( ctr interop . Hash160 , tx interop . Hash256 , sig interop . Signature , data any ) bool {
return contract . Call ( c . Hash , "otherTypes" , contract . All , ctr , tx , sig , data ) . ( bool )
}
// SearchStorage invokes ` + "`searchStorage`" + ` method of contract.
func ( c Contract ) SearchStorage ( ctx storage . Context ) iterator . Iterator {
return contract . Call ( c . Hash , "searchStorage" , contract . All , ctx ) . ( iterator . Iterator )
}
// GetFromMap invokes ` + "`getFromMap`" + ` method of contract.
func ( c Contract ) GetFromMap ( intMap map [ string ] int , indices [ ] string ) [ ] int {
return contract . Call ( c . Hash , "getFromMap" , contract . All , intMap , indices ) . ( [ ] int )
}
// DoSomething invokes ` + "`doSomething`" + ` method of contract.
func ( c Contract ) DoSomething ( bytes [ ] byte , str string ) any {
return contract . Call ( c . Hash , "doSomething" , contract . ReadStates , bytes , str ) . ( any )
}
// GetBlockWrapper invokes ` + "`getBlockWrapper`" + ` method of contract.
func ( c Contract ) GetBlockWrapper ( ) ledger . Block {
return contract . Call ( c . Hash , "getBlockWrapper" , contract . All ) . ( ledger . Block )
}
// MyFunc invokes ` + "`myFunc`" + ` method of contract.
func ( c Contract ) MyFunc ( in map [ int ] mycontract . Input ) [ ] mycontract . Output {
return contract . Call ( c . Hash , "myFunc" , contract . All , in ) . ( [ ] mycontract . Output )
}
`
data , err = os . ReadFile ( outFile )
require . NoError ( t , err )
require . Equal ( t , expectedWithDynamicHash , string ( data ) )
2021-10-25 11:48:21 +00:00
}
func TestGenerateValidPackageName ( t * testing . T ) {
m := manifest . NewManifest ( "My space\tcontract" )
m . ABI . Methods = append ( m . ABI . Methods ,
manifest . Method {
Name : "get" ,
Parameters : [ ] manifest . Parameter { } ,
ReturnType : smartcontract . IntegerType ,
2022-10-26 20:27:24 +00:00
Safe : true ,
2021-10-25 11:48:21 +00:00
} ,
)
manifestFile := filepath . Join ( t . TempDir ( ) , "manifest.json" )
outFile := filepath . Join ( t . TempDir ( ) , "out.go" )
rawManifest , err := json . Marshal ( m )
require . NoError ( t , err )
2022-02-22 16:27:32 +00:00
require . NoError ( t , os . WriteFile ( manifestFile , rawManifest , os . ModePerm ) )
2021-10-25 11:48:21 +00:00
h := util . Uint160 {
0x04 , 0x08 , 0x15 , 0x16 , 0x23 , 0x42 , 0x43 , 0x44 , 0x00 , 0x01 ,
0xCA , 0xFE , 0xBA , 0xBE , 0xDE , 0xAD , 0xBE , 0xEF , 0x03 , 0x04 ,
}
2024-07-04 22:34:40 +00:00
e := testcli . NewExecutor ( t , false )
e . Run ( t , [ ] string { "" , "contract" , "generate-wrapper" ,
2021-10-25 11:48:21 +00:00
"--manifest" , manifestFile ,
"--out" , outFile ,
2022-02-25 07:27:51 +00:00
"--hash" , "0x" + h . StringLE ( ) ,
2024-07-04 22:34:40 +00:00
} ... )
2021-10-25 11:48:21 +00:00
2022-02-22 16:27:32 +00:00
data , err := os . ReadFile ( outFile )
2021-10-25 11:48:21 +00:00
require . NoError ( t , err )
2024-04-08 22:02:05 +00:00
require . Equal ( t , ` // Code generated by neo-go contract generate-wrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
2023-12-25 10:01:36 +00:00
// Package myspacecontract contains wrappers for My space contract contract.
2021-10-25 11:48:21 +00:00
package myspacecontract
import (
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/neogointernal"
)
// Hash contains contract hash in big-endian form.
const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\xbe\xef\x03\x04"
// Get invokes `+"`get`"+` method of contract.
func Get ( ) int {
2022-10-26 20:27:24 +00:00
return neogointernal . CallWithToken ( Hash , "get" , int ( contract . ReadOnly ) ) . ( int )
}
` , string ( data ) )
2024-07-04 22:34:40 +00:00
e . Run ( t , [ ] string { "" , "contract" , "generate-rpcwrapper" ,
2022-10-26 20:27:24 +00:00
"--manifest" , manifestFile ,
"--out" , outFile ,
"--hash" , "0x" + h . StringLE ( ) ,
2024-07-04 22:34:40 +00:00
} ... )
2022-10-26 20:27:24 +00:00
data , err = os . ReadFile ( outFile )
require . NoError ( t , err )
2023-12-25 10:01:36 +00:00
require . Equal ( t , ` // Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package myspacecontract contains RPC wrappers for My space contract contract.
2022-10-26 20:27:24 +00:00
package myspacecontract
import (
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
2022-11-08 12:43:32 +00:00
"github.com/nspcc-dev/neo-go/pkg/util"
2022-10-26 20:27:24 +00:00
"math/big"
)
// Hash contains contract hash.
var Hash = util . Uint160 { 0x4 , 0x8 , 0x15 , 0x16 , 0x23 , 0x42 , 0x43 , 0x44 , 0x0 , 0x1 , 0xca , 0xfe , 0xba , 0xbe , 0xde , 0xad , 0xbe , 0xef , 0x3 , 0x4 }
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
2023-04-03 10:34:24 +00:00
Call ( contract util . Uint160 , operation string , params ... any ) ( * result . Invoke , error )
2022-10-26 20:27:24 +00:00
}
// ContractReader implements safe contract methods.
type ContractReader struct {
invoker Invoker
2023-10-18 14:58:39 +00:00
hash util . Uint160
2022-10-26 20:27:24 +00:00
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader ( invoker Invoker ) * ContractReader {
2023-05-11 10:25:45 +00:00
var hash = Hash
return & ContractReader { invoker , hash }
2022-10-26 20:27:24 +00:00
}
// Get invokes `+"`get`"+` method of contract.
func ( c * ContractReader ) Get ( ) ( * big . Int , error ) {
2023-05-11 10:25:45 +00:00
return unwrap . BigInt ( c . invoker . Call ( c . hash , "get" ) )
2021-10-25 11:48:21 +00:00
}
` , string ( data ) )
}
2023-05-23 07:42:27 +00:00
// rewriteExpectedOutputs denotes whether expected output files should be rewritten
// for TestGenerateRPCBindings and TestAssistedRPCBindings.
const rewriteExpectedOutputs = false
2022-10-26 20:27:24 +00:00
func TestGenerateRPCBindings ( t * testing . T ) {
tmpDir := t . TempDir ( )
2024-07-04 22:34:40 +00:00
e := testcli . NewExecutor ( t , false )
2022-10-26 20:27:24 +00:00
2022-11-08 12:43:32 +00:00
var checkBinding = func ( manifest string , hash string , good string ) {
t . Run ( manifest , func ( t * testing . T ) {
outFile := filepath . Join ( tmpDir , "out.go" )
2024-07-04 22:34:40 +00:00
e . Run ( t , [ ] string { "" , "contract" , "generate-rpcwrapper" ,
2022-11-08 12:43:32 +00:00
"--manifest" , manifest ,
"--out" , outFile ,
"--hash" , hash ,
2024-07-04 22:34:40 +00:00
} ... )
2022-11-08 12:43:32 +00:00
data , err := os . ReadFile ( outFile )
require . NoError ( t , err )
data = bytes . ReplaceAll ( data , [ ] byte ( "\r" ) , [ ] byte { } ) // Windows.
2023-05-23 07:42:27 +00:00
if rewriteExpectedOutputs {
require . NoError ( t , os . WriteFile ( good , data , os . ModePerm ) )
} else {
expected , err := os . ReadFile ( good )
require . NoError ( t , err )
expected = bytes . ReplaceAll ( expected , [ ] byte ( "\r" ) , [ ] byte { } ) // Windows.
require . Equal ( t , string ( expected ) , string ( data ) )
}
2022-11-08 12:43:32 +00:00
} )
}
2022-10-26 20:27:24 +00:00
2022-11-08 12:43:32 +00:00
checkBinding ( filepath . Join ( "testdata" , "nex" , "nex.manifest.json" ) ,
"0xa2a67f09e8cf22c6bfd5cea24adc0f4bf0a11aa8" ,
filepath . Join ( "testdata" , "nex" , "nex.go" ) )
checkBinding ( filepath . Join ( "testdata" , "nameservice" , "nns.manifest.json" ) ,
"0x50ac1c37690cc2cfc594472833cf57505d5f46de" ,
filepath . Join ( "testdata" , "nameservice" , "nns.go" ) )
checkBinding ( filepath . Join ( "testdata" , "gas" , "gas.manifest.json" ) ,
"0xd2a4cff31913016155e38e474a2c06d08be276cf" ,
filepath . Join ( "testdata" , "gas" , "gas.go" ) )
2022-11-08 14:00:26 +00:00
checkBinding ( filepath . Join ( "testdata" , "verifyrpc" , "verify.manifest.json" ) ,
2022-11-08 13:36:38 +00:00
"0x00112233445566778899aabbccddeeff00112233" ,
filepath . Join ( "testdata" , "verifyrpc" , "verify.go" ) )
2022-11-09 08:58:01 +00:00
checkBinding ( filepath . Join ( "testdata" , "nonepiter" , "iter.manifest.json" ) ,
"0x00112233445566778899aabbccddeeff00112233" ,
filepath . Join ( "testdata" , "nonepiter" , "iter.go" ) )
2023-05-23 07:42:27 +00:00
require . False ( t , rewriteExpectedOutputs )
2022-10-26 20:27:24 +00:00
}
2022-11-11 11:59:01 +00:00
func TestAssistedRPCBindings ( t * testing . T ) {
tmpDir := t . TempDir ( )
2024-07-04 22:34:40 +00:00
e := testcli . NewExecutor ( t , false )
2022-11-11 11:59:01 +00:00
2024-11-08 07:21:28 +00:00
var checkBinding = func ( source , configFile , expectedFile string , hasDefinedHash , guessEventTypes bool , suffix ... string ) {
2023-05-25 15:03:05 +00:00
testName := source
if len ( suffix ) != 0 {
testName += suffix [ 0 ]
}
2023-05-11 10:25:45 +00:00
testName += fmt . Sprintf ( ", predefined hash: %t" , hasDefinedHash )
2023-05-25 15:03:05 +00:00
t . Run ( testName , func ( t * testing . T ) {
2023-11-21 14:41:34 +00:00
outFile := filepath . Join ( tmpDir , "out.go" )
2024-11-08 07:21:28 +00:00
if configFile == "" {
if len ( suffix ) != 0 {
configFile = filepath . Join ( source , "config" + suffix [ 0 ] + ".yml" )
} else {
configFile = filepath . Join ( source , "config.yml" )
}
}
if expectedFile == "" {
expectedFile = filepath . Join ( source , "rpcbindings.out" )
if len ( suffix ) != 0 {
expectedFile = filepath . Join ( source , "rpcbindings" + suffix [ 0 ] + ".out" )
} else if ! hasDefinedHash {
expectedFile = filepath . Join ( source , "rpcbindings_dynamic_hash.out" )
}
2023-05-25 15:03:05 +00:00
}
2022-11-11 11:59:01 +00:00
manifestF := filepath . Join ( tmpDir , "manifest.json" )
bindingF := filepath . Join ( tmpDir , "binding.yml" )
nefF := filepath . Join ( tmpDir , "out.nef" )
2023-05-25 15:39:27 +00:00
cmd := [ ] string { "" , "contract" , "compile" ,
2022-11-11 11:59:01 +00:00
"--in" , source ,
2023-05-25 15:03:05 +00:00
"--config" , configFile ,
2022-11-11 11:59:01 +00:00
"--manifest" , manifestF ,
"--bindings" , bindingF ,
"--out" , nefF ,
2023-05-25 15:39:27 +00:00
}
if guessEventTypes {
cmd = append ( cmd , "--guess-eventtypes" )
}
2024-07-04 22:34:40 +00:00
e . Run ( t , cmd ... )
2023-05-11 10:25:45 +00:00
cmds := [ ] string { "" , "contract" , "generate-rpcwrapper" ,
2022-11-11 11:59:01 +00:00
"--config" , bindingF ,
"--manifest" , manifestF ,
2023-11-21 14:41:34 +00:00
"--out" , outFile ,
2023-05-11 10:25:45 +00:00
}
if hasDefinedHash {
cmds = append ( cmds , "--hash" , "0x00112233445566778899aabbccddeeff00112233" )
}
2024-07-04 22:34:40 +00:00
e . Run ( t , cmds ... )
2022-11-11 11:59:01 +00:00
2023-11-21 14:41:34 +00:00
data , err := os . ReadFile ( outFile )
2022-11-11 11:59:01 +00:00
require . NoError ( t , err )
data = bytes . ReplaceAll ( data , [ ] byte ( "\r" ) , [ ] byte { } ) // Windows.
2023-05-23 07:42:27 +00:00
if rewriteExpectedOutputs {
require . NoError ( t , os . WriteFile ( expectedFile , data , os . ModePerm ) )
} else {
expected , err := os . ReadFile ( expectedFile )
require . NoError ( t , err )
expected = bytes . ReplaceAll ( expected , [ ] byte ( "\r" ) , [ ] byte { } ) // Windows.
require . Equal ( t , string ( expected ) , string ( data ) )
}
2022-11-11 11:59:01 +00:00
} )
}
2023-05-11 10:25:45 +00:00
for _ , hasDefinedHash := range [ ] bool { true , false } {
2024-11-08 07:21:28 +00:00
checkBinding ( filepath . Join ( "testdata" , "rpcbindings" , "types" ) , "" , "" , hasDefinedHash , false )
checkBinding ( filepath . Join ( "testdata" , "rpcbindings" , "structs" ) , "" , "" , hasDefinedHash , false )
checkBinding ( filepath . Join ( "testdata" , "rpcbindings" , "royalty" ) , "" , "" , hasDefinedHash , false )
2023-05-11 10:25:45 +00:00
}
2024-11-08 07:21:28 +00:00
checkBinding ( filepath . Join ( "testdata" , "rpcbindings" , "notifications" ) , "" , "" , true , false )
checkBinding ( filepath . Join ( "testdata" , "rpcbindings" , "notifications" ) , "" , "" , true , false , "_extended" )
checkBinding ( filepath . Join ( "testdata" , "rpcbindings" , "notifications" ) , "" , "" , true , true , "_guessed" )
checkBinding ( filepath . Join ( ".." , ".." , "examples" , "nft-d" ) , filepath . Join ( ".." , ".." , "examples" , "nft-d" , "nft.yml" ) , filepath . Join ( "testdata" , "rpcbindings" , "nft-d" , "rpcbindings_dynamic_hash.out" ) , false , false )
checkBinding ( filepath . Join ( ".." , ".." , "examples" , "nft-d" ) , filepath . Join ( ".." , ".." , "examples" , "nft-d" , "nft.yml" ) , filepath . Join ( "testdata" , "rpcbindings" , "nft-d" , "rpcbindings.out" ) , true , true )
checkBinding ( filepath . Join ( ".." , ".." , "examples" , "nft-nd" ) , filepath . Join ( ".." , ".." , "examples" , "nft-nd" , "nft.yml" ) , filepath . Join ( "testdata" , "rpcbindings" , "nft-nd" , "rpcbindings_dynamic_hash.out" ) , false , false )
checkBinding ( filepath . Join ( ".." , ".." , "examples" , "nft-nd" ) , filepath . Join ( ".." , ".." , "examples" , "nft-nd" , "nft.yml" ) , filepath . Join ( "testdata" , "rpcbindings" , "nft-nd" , "rpcbindings.out" ) , true , true )
2023-05-23 07:42:27 +00:00
require . False ( t , rewriteExpectedOutputs )
2022-11-11 11:59:01 +00:00
}
2021-10-25 11:48:21 +00:00
func TestGenerate_Errors ( t * testing . T ) {
2024-07-04 22:34:40 +00:00
e := testcli . NewExecutor ( t , false )
args := [ ] string { "neo-go" , "contract" , "generate-wrapper" }
2022-08-22 10:41:57 +00:00
t . Run ( "invalid hash" , func ( t * testing . T ) {
2024-07-04 22:34:40 +00:00
e . RunWithErrorCheckExit ( t , "invalid contract hash" , append ( args , "--hash" , "xxx" , "--manifest" , "yyy" , "--out" , "zzz" ) ... )
2022-08-22 10:41:57 +00:00
} )
2021-10-25 11:48:21 +00:00
t . Run ( "missing manifest argument" , func ( t * testing . T ) {
2024-07-04 22:34:40 +00:00
e . RunWithErrorCheck ( t , ` Required flag "manifest" not set ` , append ( args , "--hash" , util . Uint160 { } . StringLE ( ) , "--out" , "zzz" ) ... )
2021-10-25 11:48:21 +00:00
} )
t . Run ( "missing manifest file" , func ( t * testing . T ) {
2024-07-04 22:34:40 +00:00
e . RunWithErrorCheckExit ( t , "can't read contract manifest" , append ( args , "--manifest" , "notexists" , "--hash" , util . Uint160 { } . StringLE ( ) , "--out" , "zzz" ) ... )
2021-10-25 11:48:21 +00:00
} )
2022-08-22 10:41:57 +00:00
t . Run ( "empty manifest" , func ( t * testing . T ) {
2021-10-25 11:48:21 +00:00
manifestFile := filepath . Join ( t . TempDir ( ) , "invalid.json" )
2022-02-22 16:27:32 +00:00
require . NoError ( t , os . WriteFile ( manifestFile , [ ] byte ( "[]" ) , os . ModePerm ) )
2024-07-04 22:34:40 +00:00
e . RunWithErrorCheckExit ( t , "json: cannot unmarshal array into Go value of type manifest.Manifest" , append ( args , "--manifest" , manifestFile , "--hash" , util . Uint160 { } . StringLE ( ) , "--out" , "zzz" ) ... )
2022-08-22 10:41:57 +00:00
} )
t . Run ( "invalid manifest" , func ( t * testing . T ) {
manifestFile := filepath . Join ( t . TempDir ( ) , "invalid.json" )
m := manifest . NewManifest ( "MyContract" ) // no methods
rawManifest , err := json . Marshal ( m )
require . NoError ( t , err )
require . NoError ( t , os . WriteFile ( manifestFile , rawManifest , os . ModePerm ) )
2024-07-04 22:34:40 +00:00
e . RunWithErrorCheckExit ( t , "ABI: no methods" , append ( args , "--manifest" , manifestFile , "--hash" , util . Uint160 { } . StringLE ( ) , "--out" , "zzz" ) ... )
2021-10-25 11:48:21 +00:00
} )
manifestFile := filepath . Join ( t . TempDir ( ) , "manifest.json" )
m := manifest . NewManifest ( "MyContract" )
2022-08-22 10:41:57 +00:00
m . ABI . Methods = append ( m . ABI . Methods , manifest . Method {
Name : "method0" ,
Offset : 0 ,
ReturnType : smartcontract . AnyType ,
Safe : true ,
} )
2021-10-25 11:48:21 +00:00
rawManifest , err := json . Marshal ( m )
require . NoError ( t , err )
2022-02-22 16:27:32 +00:00
require . NoError ( t , os . WriteFile ( manifestFile , rawManifest , os . ModePerm ) )
2021-10-25 11:48:21 +00:00
t . Run ( "missing config" , func ( t * testing . T ) {
2024-07-04 22:34:40 +00:00
e . RunWithErrorCheckExit ( t , "can't read config file" , append ( args , "--manifest" , manifestFile , "--hash" , util . Uint160 { } . StringLE ( ) ,
"--config" , filepath . Join ( t . TempDir ( ) , "not.exists.yml" ) , "--out" , "zzz" ) ... )
2021-10-25 11:48:21 +00:00
} )
t . Run ( "invalid config" , func ( t * testing . T ) {
rawCfg := ` package : wrapper
callflags :
someFunc : ReadSometimes
`
cfgPath := filepath . Join ( t . TempDir ( ) , "binding.yml" )
2022-02-22 16:27:32 +00:00
require . NoError ( t , os . WriteFile ( cfgPath , [ ] byte ( rawCfg ) , os . ModePerm ) )
2021-10-25 11:48:21 +00:00
2024-07-04 22:34:40 +00:00
e . RunWithErrorCheckExit ( t , "can't parse config file" , append ( args , "--manifest" , manifestFile , "--hash" , util . Uint160 { } . StringLE ( ) ,
"--config" , cfgPath , "--out" , "zzz" ) ... )
2021-10-25 11:48:21 +00:00
} )
}
2023-05-25 16:17:49 +00:00
func TestCompile_GuessEventTypes ( t * testing . T ) {
2024-07-04 22:34:40 +00:00
e := testcli . NewExecutor ( t , false )
2023-05-25 16:17:49 +00:00
check := func ( t * testing . T , source string , expectedErrText string ) {
tmpDir := t . TempDir ( )
configFile := filepath . Join ( source , "invalid.yml" )
manifestF := filepath . Join ( tmpDir , "invalid.manifest.json" )
bindingF := filepath . Join ( tmpDir , "invalid.binding.yml" )
nefF := filepath . Join ( tmpDir , "invalid.out.nef" )
cmd := [ ] string { "" , "contract" , "compile" ,
"--in" , source ,
"--config" , configFile ,
"--manifest" , manifestF ,
"--bindings" , bindingF ,
"--out" , nefF ,
"--guess-eventtypes" ,
}
2024-07-04 22:34:40 +00:00
e . RunWithErrorCheckExit ( t , expectedErrText , cmd ... )
2023-05-25 16:17:49 +00:00
}
t . Run ( "not declared in manifest" , func ( t * testing . T ) {
2023-08-11 13:07:02 +00:00
check ( t , filepath . Join ( "testdata" , "rpcbindings" , "invalid1" ) , "inconsistent usages of event `Non declared event`: not declared in the contract config" )
2023-05-25 16:17:49 +00:00
} )
t . Run ( "invalid number of params" , func ( t * testing . T ) {
2023-08-11 13:07:02 +00:00
check ( t , filepath . Join ( "testdata" , "rpcbindings" , "invalid2" ) , "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1" )
2023-05-25 16:17:49 +00:00
} )
/ *
// TODO: this on is a controversial one. If event information is provided in the config file, then conversion code
// will be emitted by the compiler according to the parameter type provided via config. Thus, we can be sure that
// either event parameter has the type specified in the config file or the execution of the contract will fail.
// Thus, this testcase is always failing (no compilation error occures).
// Question: do we want to compare `RealType` of the emitted parameter with the one expected in the manifest?
t . Run ( "SC parameter type mismatch" , func ( t * testing . T ) {
2023-08-11 13:07:02 +00:00
check ( t , filepath . Join ( "testdata" , "rpcbindings" , "invalid3" ) , "inconsistent usages of event `SomeEvent` against config: number of params mismatch: 2 vs 1" )
2023-05-25 16:17:49 +00:00
} )
* /
t . Run ( "extended types mismatch" , func ( t * testing . T ) {
2023-08-11 13:07:02 +00:00
check ( t , filepath . Join ( "testdata" , "rpcbindings" , "invalid4" ) , "inconsistent usages of event `SomeEvent`: extended type of param #0 mismatch" )
2023-05-25 16:17:49 +00:00
} )
2023-05-29 15:50:36 +00:00
t . Run ( "named types redeclare" , func ( t * testing . T ) {
2023-08-11 13:07:02 +00:00
check ( t , filepath . Join ( "testdata" , "rpcbindings" , "invalid5" ) , "configured declared named type intersects with the contract's one: `invalid5.NamedStruct`" )
2023-05-29 15:50:36 +00:00
} )
2023-05-25 16:17:49 +00:00
}
2023-08-11 13:59:31 +00:00
func TestGenerateRPCBindings_Errors ( t * testing . T ) {
2024-07-04 22:34:40 +00:00
e := testcli . NewExecutor ( t , false )
2023-08-11 13:59:31 +00:00
t . Run ( "duplicating resulting fields" , func ( t * testing . T ) {
check := func ( t * testing . T , packageName string , autogen bool , expectedError string ) {
tmpDir := t . TempDir ( )
source := filepath . Join ( "testdata" , "rpcbindings" , packageName )
configFile := filepath . Join ( source , "invalid.yml" )
out := filepath . Join ( tmpDir , "rpcbindings.out" )
manifestF := filepath . Join ( tmpDir , "manifest.json" )
bindingF := filepath . Join ( tmpDir , "binding.yml" )
nefF := filepath . Join ( tmpDir , "out.nef" )
cmd := [ ] string { "" , "contract" , "compile" ,
"--in" , source ,
"--config" , configFile ,
"--manifest" , manifestF ,
"--bindings" , bindingF ,
"--out" , nefF ,
}
if autogen {
cmd = append ( cmd , "--guess-eventtypes" )
}
2024-07-04 22:34:40 +00:00
e . Run ( t , cmd ... )
2023-08-11 13:59:31 +00:00
cmds := [ ] string { "" , "contract" , "generate-rpcwrapper" ,
"--config" , bindingF ,
"--manifest" , manifestF ,
"--out" , out ,
}
2024-07-04 22:34:40 +00:00
e . RunWithErrorCheckExit ( t , expectedError , cmds ... )
2023-08-11 13:59:31 +00:00
}
t . Run ( "event" , func ( t * testing . T ) {
check ( t , "invalid6" , false , "error during generation: named type `SomeStruct` has two fields with identical resulting binding name `Field`" )
} )
t . Run ( "autogen event" , func ( t * testing . T ) {
check ( t , "invalid7" , true , "error during generation: named type `invalid7.SomeStruct` has two fields with identical resulting binding name `Field`" )
} )
t . Run ( "struct" , func ( t * testing . T ) {
check ( t , "invalid8" , false , "error during generation: named type `invalid8.SomeStruct` has two fields with identical resulting binding name `Field`" )
} )
} )
}