github.com/uuosio/chaintester@v0.0.0-20230731100329-1f6fad7372e5/client.go (about)

     1  package chaintester
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"fmt"
     9  	"os"
    10  	"reflect"
    11  
    12  	"github.com/uuosio/chaintester/interfaces"
    13  
    14  	"github.com/apache/thrift/lib/go/thrift"
    15  )
    16  
    17  type ActionArguments interfaces.ActionArguments
    18  
    19  type IPCClient struct {
    20  	seqId        int32
    21  	iprot, oprot thrift.TProtocol
    22  }
    23  
    24  // IPCClient implements TClient, and uses the standard message format for Thrift.
    25  // It is not safe for concurrent use.
    26  func NewIPCClient(inputProtocol, outputProtocol thrift.TProtocol) *IPCClient {
    27  	return &IPCClient{
    28  		iprot: inputProtocol,
    29  		oprot: outputProtocol,
    30  	}
    31  }
    32  
    33  func (p *IPCClient) Send(ctx context.Context, oprot thrift.TProtocol, seqId int32, method string, args thrift.TStruct) error {
    34  	// Set headers from context object on THeaderProtocol
    35  	if headerProt, ok := oprot.(*thrift.THeaderProtocol); ok {
    36  		headerProt.ClearWriteHeaders()
    37  		for _, key := range thrift.GetWriteHeaderList(ctx) {
    38  			if value, ok := thrift.GetHeader(ctx, key); ok {
    39  				headerProt.SetWriteHeader(key, value)
    40  			}
    41  		}
    42  	}
    43  
    44  	if err := oprot.WriteMessageBegin(ctx, method, thrift.CALL, seqId); err != nil {
    45  		return err
    46  	}
    47  	if err := args.Write(ctx, oprot); err != nil {
    48  		return err
    49  	}
    50  	if err := oprot.WriteMessageEnd(ctx); err != nil {
    51  		return err
    52  	}
    53  	return oprot.Flush(ctx)
    54  }
    55  
    56  func (p *IPCClient) Recv(ctx context.Context, iprot thrift.TProtocol, seqId int32, method string, result thrift.TStruct) error {
    57  	rMethod, rTypeId, rSeqId, err := iprot.ReadMessageBegin(ctx)
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	if method != rMethod {
    63  		return thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, fmt.Sprintf("%s: wrong method name", method))
    64  	} else if seqId != rSeqId {
    65  		return thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, fmt.Sprintf("%s: out of order sequence response", method))
    66  	} else if rTypeId == thrift.EXCEPTION {
    67  		exception := thrift.NewTApplicationException(0, "")
    68  		if err := exception.Read(ctx, iprot); err != nil {
    69  			return err
    70  		}
    71  
    72  		if err := iprot.ReadMessageEnd(ctx); err != nil {
    73  			return err
    74  		}
    75  
    76  		return exception
    77  	} else if rTypeId != thrift.REPLY {
    78  		return thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, fmt.Sprintf("%s: invalid message type", method))
    79  	}
    80  
    81  	if err := result.Read(ctx, iprot); err != nil {
    82  		return err
    83  	}
    84  
    85  	return iprot.ReadMessageEnd(ctx)
    86  }
    87  
    88  func (p *IPCClient) Call(ctx context.Context, method string, args, result thrift.TStruct) (thrift.ResponseMeta, error) {
    89  	p.seqId++
    90  	seqId := p.seqId
    91  
    92  	if err := p.Send(ctx, p.oprot, seqId, method, args); err != nil {
    93  		return thrift.ResponseMeta{}, err
    94  	}
    95  
    96  	// method is oneway
    97  	if result == nil {
    98  		return thrift.ResponseMeta{}, nil
    99  	}
   100  
   101  	err := p.Recv(ctx, p.iprot, seqId, method, result)
   102  	var headers thrift.THeaderMap
   103  	if hp, ok := p.iprot.(*thrift.THeaderProtocol); ok {
   104  		transport := reflect.ValueOf(hp).Elem().FieldByName("transport")
   105  		readHeaders := reflect.ValueOf(transport).Elem().FieldByName("readHeaders")
   106  		headers = readHeaders.Interface().(thrift.THeaderMap)
   107  	}
   108  	return thrift.ResponseMeta{
   109  		Headers: headers,
   110  	}, err
   111  }
   112  
   113  var defaultCtx = context.Background()
   114  
   115  type ChainTester struct {
   116  	interfaces.IPCChainTesterClient
   117  	client *IPCClient
   118  	id     int32
   119  }
   120  
   121  var g_ApplyRequestServer *ApplyRequestServer
   122  
   123  func GetApplyRequestServer() *ApplyRequestServer {
   124  	if g_ApplyRequestServer == nil {
   125  		g_ApplyRequestServer = NewApplyRequestServer()
   126  		g_ApplyRequestServer.AcceptOnce()
   127  	}
   128  	return g_ApplyRequestServer
   129  }
   130  
   131  var g_IPCClient *IPCClient = nil
   132  
   133  type DebuggerConfig struct {
   134  	DebuggerServerAddress     string
   135  	DebuggerServerPort        string
   136  	VMAPIServerAddress        string
   137  	VMAPIServerPort           string
   138  	ApplyRequestServerAddress string
   139  	ApplyRequestServerPort    string
   140  }
   141  
   142  var g_DebuggerConfig = DebuggerConfig{
   143  	DebuggerServerAddress:     "127.0.0.1",
   144  	DebuggerServerPort:        "9090",
   145  	VMAPIServerAddress:        "127.0.0.1",
   146  	VMAPIServerPort:           "9092",
   147  	ApplyRequestServerAddress: "127.0.0.1",
   148  	ApplyRequestServerPort:    "9091",
   149  }
   150  
   151  func GetDebuggerConfig() *DebuggerConfig {
   152  	return &g_DebuggerConfig
   153  }
   154  
   155  func SetDebuggerServerAddress(addr string) {
   156  	g_DebuggerConfig.DebuggerServerAddress = addr
   157  }
   158  
   159  func SetDebuggerServerPort(port string) {
   160  	g_DebuggerConfig.DebuggerServerPort = port
   161  }
   162  
   163  func SetApplyRequestServerAddress(addr string) {
   164  	g_DebuggerConfig.ApplyRequestServerAddress = addr
   165  }
   166  
   167  func SetApplyRequestServerPort(port string) {
   168  	g_DebuggerConfig.ApplyRequestServerPort = port
   169  }
   170  
   171  func SetVMAPIServerAddress(addr string) {
   172  	g_DebuggerConfig.VMAPIServerAddress = addr
   173  }
   174  
   175  func SetVMAPIServerPort(port string) {
   176  	g_DebuggerConfig.VMAPIServerPort = port
   177  }
   178  
   179  func GetIPCClient() *IPCClient {
   180  	if g_IPCClient == nil {
   181  		addr := fmt.Sprintf("%s:%s", g_DebuggerConfig.DebuggerServerAddress, g_DebuggerConfig.DebuggerServerPort)
   182  		iprot, oprot, err := NewProtocol(addr)
   183  		if err != nil {
   184  			panic(err)
   185  		}
   186  		g_IPCClient = NewIPCClient(iprot, oprot)
   187  		tester := interfaces.NewIPCChainTesterClient(g_IPCClient)
   188  
   189  		tester.InitVMAPI(defaultCtx)
   190  		InitVMAPI() //init vm api client
   191  
   192  		tester.InitApplyRequest(defaultCtx)
   193  		GetApplyRequestServer() // init apply request server
   194  
   195  	}
   196  	return g_IPCClient
   197  }
   198  
   199  // cannot use c (variable of type *IPCClient) as thrift.TClient value in argument to interfaces.NewIPCChainTesterClient: wrong type for method Call (have
   200  // 	func(ctx context.Context, method string, args github.com/apache/thrift/lib/go/thrift.TStruct, result github.com/apache/thrift/lib/go/thrift.TStruct) (chaintester.ResponseMeta, error), want
   201  // 	func(ctx context.Context, method string, args github.com/apache/thrift/lib/go/thrift.TStruct, result github.com/apache/thrift/lib/go/thrift.TStruct) (github.com/apache/thrift/lib/go/thrift.ResponseMeta, error))compilerInvalidIfaceAssign
   202  
   203  func NewChainTester() *ChainTester {
   204  	c := GetIPCClient()
   205  
   206  	tester := &ChainTester{
   207  		IPCChainTesterClient: *interfaces.NewIPCChainTesterClient(c),
   208  		client:               c,
   209  	}
   210  
   211  	var err error
   212  	tester.id, err = tester.NewChain_(defaultCtx, true)
   213  	if err != nil {
   214  		panic(err)
   215  	}
   216  	return tester
   217  }
   218  
   219  func (p *ChainTester) SetNativeApply(contract string, apply func(uint64, uint64, uint64)) {
   220  	if apply == nil {
   221  		p.EnableDebugContract(contract, false)
   222  		if applyMap, ok := g_ChainTesterApplyMap[p.id]; ok {
   223  			if _, ok := applyMap[contract]; ok {
   224  				delete(applyMap, contract)
   225  			}
   226  		}
   227  	} else {
   228  		if _, ok := g_ChainTesterApplyMap[p.id]; !ok {
   229  			g_ChainTesterApplyMap[p.id] = make(map[string]func(uint64, uint64, uint64))
   230  		}
   231  		p.EnableDebugContract(contract, true)
   232  		g_ChainTesterApplyMap[p.id][contract] = apply
   233  	}
   234  }
   235  
   236  func (p *ChainTester) Call(ctx context.Context, method string, args, result thrift.TStruct) (thrift.ResponseMeta, error) {
   237  	p.client.seqId++
   238  	seqId := p.client.seqId
   239  
   240  	if err := p.client.Send(ctx, p.client.oprot, seqId, method, args); err != nil {
   241  		return thrift.ResponseMeta{}, err
   242  	}
   243  
   244  	// method is oneway
   245  	if result == nil {
   246  		return thrift.ResponseMeta{}, nil
   247  	}
   248  
   249  	//runApplyRequestServer
   250  
   251  	//start apply request server
   252  	if "push_action" == method || "push_actions" == method || "produce_block" == method {
   253  		GetApplyRequestServer().Serve()
   254  	}
   255  
   256  	err := p.client.Recv(ctx, p.client.iprot, seqId, method, result)
   257  	var headers thrift.THeaderMap
   258  	if hp, ok := p.client.iprot.(*thrift.THeaderProtocol); ok {
   259  		transport := reflect.ValueOf(hp).Elem().FieldByName("transport")
   260  		readHeaders := reflect.ValueOf(transport).Elem().FieldByName("readHeaders")
   261  		headers = readHeaders.Interface().(thrift.THeaderMap)
   262  	}
   263  	return thrift.ResponseMeta{
   264  		Headers: headers,
   265  	}, err
   266  }
   267  
   268  func (p *ChainTester) pushAction(account string, action string, arguments *interfaces.ActionArguments, permissions string) (*JsonValue, error) {
   269  	var _args20 interfaces.IPCChainTesterPushActionArgs
   270  	_args20.ID = p.id
   271  	_args20.Account = account
   272  	_args20.Action = action
   273  	_args20.Arguments = arguments
   274  	_args20.Permissions = permissions
   275  	var _result22 interfaces.IPCChainTesterPushActionResult
   276  	var _meta21 thrift.ResponseMeta
   277  	var _err error
   278  	_meta21, _err = p.Call(defaultCtx, "push_action", &_args20, &_result22)
   279  	if _err != nil {
   280  		panic(_err)
   281  	}
   282  	p.IPCChainTesterClient.SetLastResponseMeta_(_meta21)
   283  	ret := _result22.GetSuccess()
   284  
   285  	value := &JsonValue{}
   286  	// fmt.Printf("++++++push_action return: %v", string(ret))
   287  	err := json.Unmarshal(ret, value)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	_, err = value.Get("except")
   293  	if err == nil {
   294  		return nil, NewTransactionError(ret)
   295  	} else {
   296  		return value, nil
   297  	}
   298  }
   299  
   300  func (p *ChainTester) PushAction(account string, action string, arguments interface{}, permissions string) (*JsonValue, error) {
   301  	_arguments := interfaces.NewActionArguments()
   302  	switch args := arguments.(type) {
   303  	case string:
   304  		_arguments.JSONArgs_ = &args
   305  	case []byte:
   306  		_arguments.RawArgs_ = args
   307  	default:
   308  		panic("Invalid arguments type")
   309  	}
   310  
   311  	return p.pushAction(account, action, _arguments, permissions)
   312  }
   313  
   314  func (p *ChainTester) PushActions(actions []*interfaces.Action) (*JsonValue, error) {
   315  	var _args29 interfaces.IPCChainTesterPushActionsArgs
   316  	_args29.ID = p.id
   317  	_args29.Actions = actions
   318  	var _result31 interfaces.IPCChainTesterPushActionsResult
   319  	var _meta30 thrift.ResponseMeta
   320  
   321  	var _err error
   322  	_meta30, _err = p.Call(defaultCtx, "push_actions", &_args29, &_result31)
   323  	p.SetLastResponseMeta_(_meta30)
   324  	if _err != nil {
   325  		return nil, _err
   326  	}
   327  	ret := _result31.GetSuccess()
   328  	value := &JsonValue{}
   329  	// fmt.Printf("++++++push_action return: %v", string(ret))
   330  	err := json.Unmarshal(ret, value)
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	_, err = value.Get("except")
   336  	if err == nil {
   337  		return nil, NewTransactionError(ret)
   338  	} else {
   339  		return value, nil
   340  	}
   341  }
   342  
   343  func (p *ChainTester) DeployContract(account string, wasmFile string, abiFile string) (err error) {
   344  	wasm, err := os.ReadFile(wasmFile)
   345  	if err != nil {
   346  		return err
   347  	}
   348  
   349  	hexWasm := make([]byte, len(wasm)*2)
   350  	hex.Encode(hexWasm, wasm)
   351  
   352  	jsonArgs := fmt.Sprintf(`
   353  		{
   354  			"account": "%s",
   355  			"vmtype": 0,
   356  			"vmversion": 0,
   357  			"code": "%s"
   358  		}
   359  		`,
   360  		account,
   361  		string(hexWasm))
   362  
   363  	permissions := fmt.Sprintf(`
   364  	{
   365  		"%s": "active"
   366  	}
   367  	`, account)
   368  
   369  	setCodeArgs := interfaces.NewActionArguments()
   370  	setCodeArgs.JSONArgs_ = &jsonArgs
   371  	setCodeAction := &interfaces.Action{
   372  		Account:     "eosio",
   373  		Action:      "setcode",
   374  		Arguments:   setCodeArgs,
   375  		Permissions: permissions,
   376  	}
   377  	actions := make([]*interfaces.Action, 0, 2)
   378  	actions = append(actions, setCodeAction)
   379  
   380  	if abiFile != "" {
   381  		abi, err := os.ReadFile(abiFile)
   382  		if err != nil {
   383  			return err
   384  		}
   385  		rawAbi, _ := p.PackAbi(string(abi))
   386  		hexRawAbi := make([]byte, len(rawAbi)*2)
   387  		hex.Encode(hexRawAbi, rawAbi)
   388  		jsonArgs := fmt.Sprintf(
   389  			`
   390  			{
   391  				"account": "%s",
   392  				"abi": "%s"
   393  			 }
   394  			`,
   395  			account,
   396  			string(hexRawAbi),
   397  		)
   398  		setAbiArgs := interfaces.NewActionArguments()
   399  		setAbiArgs.JSONArgs_ = &jsonArgs
   400  		setAbiAction := &interfaces.Action{
   401  			Account:     "eosio",
   402  			Action:      "setabi",
   403  			Arguments:   setAbiArgs,
   404  			Permissions: permissions,
   405  		}
   406  		actions = append(actions, setAbiAction)
   407  	}
   408  
   409  	_, err = p.PushActions(actions)
   410  	if err != nil {
   411  		return err
   412  	}
   413  	return nil
   414  }
   415  
   416  func (p *ChainTester) produceBlock(next_block_skip_seconds int64) error {
   417  	var _args41 interfaces.IPCChainTesterProduceBlockArgs
   418  	_args41.ID = p.id
   419  	_args41.NextBlockSkipSeconds = next_block_skip_seconds
   420  	var _result43 interfaces.IPCChainTesterProduceBlockResult
   421  	var _meta42 thrift.ResponseMeta
   422  
   423  	var _err error
   424  	_meta42, _err = p.Call(defaultCtx, "produce_block", &_args41, &_result43)
   425  	p.IPCChainTesterClient.SetLastResponseMeta_(_meta42)
   426  	if _err != nil {
   427  		return _err
   428  	}
   429  	return nil
   430  }
   431  
   432  func (p *ChainTester) ProduceBlock(next_block_skip_time ...int64) error {
   433  	if len(next_block_skip_time) == 0 {
   434  		return p.produceBlock(0)
   435  	}
   436  
   437  	if len(next_block_skip_time) != 1 {
   438  		panic("invalid arguments")
   439  	}
   440  	return p.produceBlock(next_block_skip_time[0])
   441  }
   442  
   443  func (p *ChainTester) GetInfo() (*JsonValue, error) {
   444  	ret, err := p.IPCChainTesterClient.GetInfo(defaultCtx, p.id)
   445  	value := &JsonValue{}
   446  	err = json.Unmarshal([]byte(ret), value)
   447  	if err != nil {
   448  		return nil, fmt.Errorf("%v", string(ret))
   449  	}
   450  	return value, nil
   451  }
   452  
   453  func (p *ChainTester) CreateKey(keyType ...string) (*JsonValue, error) {
   454  	value := &JsonValue{}
   455  	if len(keyType) == 0 {
   456  		ret, err := p.IPCChainTesterClient.CreateKey(defaultCtx, "K1")
   457  		err = json.Unmarshal([]byte(ret), value)
   458  		if err != nil {
   459  			return nil, fmt.Errorf("%v", string(ret))
   460  		}
   461  	} else {
   462  		if len(keyType) != 1 {
   463  			panic("Invalid keyType")
   464  		}
   465  		ret, err := p.IPCChainTesterClient.CreateKey(defaultCtx, keyType[0])
   466  		err = json.Unmarshal([]byte(ret), value)
   467  		if err != nil {
   468  			return nil, fmt.Errorf("%v", string(ret))
   469  		}
   470  	}
   471  
   472  	return value, nil
   473  }
   474  
   475  func (p *ChainTester) GetAccount(account string) (*JsonValue, error) {
   476  	ret, err := p.IPCChainTesterClient.GetAccount(defaultCtx, p.id, account)
   477  	value := &JsonValue{}
   478  	err = json.Unmarshal([]byte(ret), value)
   479  	if err != nil {
   480  		return nil, fmt.Errorf("%v", string(ret))
   481  	}
   482  	return value, nil
   483  }
   484  
   485  func (p *ChainTester) CreateAccount(creator string, account string, owner_key string, active_key string, ram_bytes int64, res ...int64) (*JsonValue, error) {
   486  	stake_net := int64(0)
   487  	stake_cpu := int64(0)
   488  
   489  	if len(res) != 0 {
   490  		if len(res) != 2 {
   491  			panic("invalid argugments")
   492  		}
   493  		stake_net = res[0]
   494  		stake_cpu = res[1]
   495  	}
   496  
   497  	ret, err := p.IPCChainTesterClient.CreateAccount(defaultCtx, p.id, creator, account, owner_key, active_key, ram_bytes, stake_net, stake_cpu)
   498  	value := &JsonValue{}
   499  	err = json.Unmarshal([]byte(ret), value)
   500  	if err != nil {
   501  		return nil, fmt.Errorf("%v", string(ret))
   502  	}
   503  	return value, nil
   504  }
   505  
   506  func (p *ChainTester) GetTableRows(_json bool, code string, scope string, table string, lower_bound string, upper_bound string, limit int64) (*JsonValue, error) {
   507  	return p.GetTableRowsEx(
   508  		_json,
   509  		code,
   510  		scope,
   511  		table,
   512  		lower_bound,
   513  		upper_bound,
   514  		limit,
   515  		"",
   516  		"",
   517  		"",
   518  		false,
   519  		false)
   520  }
   521  
   522  func (p *ChainTester) GetTableRowsEx(_json bool, code string, scope string, table string, lower_bound string, upper_bound string, limit int64, key_type string, index_position string, encode_type string, reverse bool, show_payer bool) (*JsonValue, error) {
   523  	ret, err := p.IPCChainTesterClient.GetTableRows(defaultCtx,
   524  		p.id,
   525  		_json,
   526  		code,
   527  		scope,
   528  		table,
   529  		lower_bound,
   530  		upper_bound,
   531  		limit,
   532  		key_type,
   533  		index_position,
   534  		encode_type,
   535  		reverse,
   536  		show_payer)
   537  	value := &JsonValue{}
   538  	err = json.Unmarshal([]byte(ret), value)
   539  	if err != nil {
   540  		return nil, fmt.Errorf("%v", string(ret))
   541  	}
   542  	return value, nil
   543  }
   544  
   545  func (tester *ChainTester) GetBalance(account string, extras ...string) uint64 {
   546  	tokenAccount := "eosio.token"
   547  	symbol := "EOS"
   548  
   549  	if len(extras) == 1 {
   550  		tokenAccount = extras[0]
   551  	}
   552  	if len(extras) == 2 {
   553  		symbol = extras[1]
   554  	}
   555  
   556  	rows, err := tester.GetTableRows(false, tokenAccount, account, "accounts", symbol, "", 1)
   557  	if err != nil {
   558  		panic(err)
   559  	}
   560  	hexString, err := rows.GetString("rows", 0)
   561  	if err != nil {
   562  		panic(err)
   563  	}
   564  
   565  	rawBalance, err := hex.DecodeString(hexString)
   566  	if err != nil {
   567  		panic(err)
   568  	}
   569  	return binary.LittleEndian.Uint64(rawBalance[:8])
   570  }
   571  
   572  func (p *ChainTester) EnableDebugContract(contract string, enable bool) error {
   573  	err := p.IPCChainTesterClient.EnableDebugContract(defaultCtx, p.id, contract, enable)
   574  	return err
   575  }
   576  
   577  func (p *ChainTester) PackAbi(abi string) ([]byte, error) {
   578  	return p.IPCChainTesterClient.PackAbi(defaultCtx, abi)
   579  }
   580  
   581  func (p *ChainTester) FreeChain() (int32, error) {
   582  	delete(g_ChainTesterApplyMap, p.id)
   583  	return p.IPCChainTesterClient.FreeChain(defaultCtx, p.id)
   584  }
   585  
   586  func handleClient(client *ChainTester) (err error) {
   587  	args := `
   588  	{
   589  		"name": "go"
   590  	}
   591  	`
   592  	permissions := `
   593  	{
   594  		"hello": "active"
   595  	}
   596  	`
   597  
   598  	// id, err := client.NewChain_(defaultCtx)
   599  	// if err != nil {
   600  	// 	return err
   601  	// }
   602  	client.PushAction("hello", "sayhello", args, permissions)
   603  	return nil
   604  }