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 }