github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/deploy/def/client.go (about) 1 package def 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "reflect" 8 "strconv" 9 "time" 10 11 "github.com/hyperledger/burrow/encoding" 12 "github.com/tendermint/tendermint/p2p" 13 14 "github.com/hyperledger/burrow/acm" 15 "github.com/hyperledger/burrow/acm/acmstate" 16 "github.com/hyperledger/burrow/binary" 17 "github.com/hyperledger/burrow/crypto" 18 "github.com/hyperledger/burrow/execution/evm/abi" 19 "github.com/hyperledger/burrow/execution/exec" 20 "github.com/hyperledger/burrow/execution/names" 21 "github.com/hyperledger/burrow/execution/registry" 22 "github.com/hyperledger/burrow/genesis/spec" 23 "github.com/hyperledger/burrow/keys" 24 "github.com/hyperledger/burrow/logging" 25 "github.com/hyperledger/burrow/permission" 26 "github.com/hyperledger/burrow/rpc" 27 "github.com/hyperledger/burrow/rpc/rpcevents" 28 "github.com/hyperledger/burrow/rpc/rpcquery" 29 "github.com/hyperledger/burrow/rpc/rpctransact" 30 "github.com/hyperledger/burrow/txs" 31 "github.com/hyperledger/burrow/txs/payload" 32 hex "github.com/tmthrgd/go-hex" 33 ) 34 35 type Client struct { 36 MempoolSigning bool 37 ChainAddress string 38 KeysClientAddress string 39 // Memoised clients and info 40 chainID string 41 timeout time.Duration 42 transactClient rpctransact.TransactClient 43 queryClient rpcquery.QueryClient 44 executionEventsClient rpcevents.ExecutionEventsClient 45 keyClient keys.KeyClient 46 AllSpecs *abi.Spec 47 } 48 49 func NewClient(chain, keysClientAddress string, mempoolSigning bool, timeout time.Duration) *Client { 50 client := Client{ 51 ChainAddress: chain, 52 MempoolSigning: mempoolSigning, 53 KeysClientAddress: keysClientAddress, 54 timeout: timeout, 55 } 56 return &client 57 } 58 59 // Connect GRPC clients using ChainURL 60 func (c *Client) dial(logger *logging.Logger) error { 61 if c.transactClient == nil { 62 conn, err := encoding.GRPCDial(c.ChainAddress) 63 if err != nil { 64 return err 65 } 66 c.transactClient = rpctransact.NewTransactClient(conn) 67 c.queryClient = rpcquery.NewQueryClient(conn) 68 c.executionEventsClient = rpcevents.NewExecutionEventsClient(conn) 69 if c.KeysClientAddress == "" { 70 logger.InfoMsg("Using mempool signing since no keyClient set, pass --keys to sign locally or elsewhere") 71 c.MempoolSigning = true 72 c.keyClient, err = keys.NewRemoteKeyClient(c.ChainAddress, logger) 73 } else { 74 logger.InfoMsg("Using keys server", "server", c.KeysClientAddress) 75 c.keyClient, err = keys.NewRemoteKeyClient(c.KeysClientAddress, logger) 76 } 77 78 if err != nil { 79 return err 80 } 81 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 82 defer cancel() 83 84 stat, err := c.queryClient.Status(ctx, &rpcquery.StatusParam{}) 85 if err != nil { 86 return err 87 } 88 c.chainID = stat.ChainID 89 } 90 return nil 91 } 92 93 func (c *Client) Transact(logger *logging.Logger) (rpctransact.TransactClient, error) { 94 err := c.dial(logger) 95 if err != nil { 96 return nil, err 97 } 98 return c.transactClient, err 99 } 100 101 func (c *Client) Query(logger *logging.Logger) (rpcquery.QueryClient, error) { 102 err := c.dial(logger) 103 if err != nil { 104 return nil, err 105 } 106 return c.queryClient, nil 107 } 108 109 func (c *Client) Events(logger *logging.Logger) (rpcevents.ExecutionEventsClient, error) { 110 err := c.dial(logger) 111 if err != nil { 112 return nil, err 113 } 114 return c.executionEventsClient, nil 115 } 116 117 func (c *Client) Status(logger *logging.Logger) (*rpc.ResultStatus, error) { 118 err := c.dial(logger) 119 if err != nil { 120 return nil, err 121 } 122 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 123 defer cancel() 124 return c.queryClient.Status(ctx, &rpcquery.StatusParam{}) 125 } 126 127 func (c *Client) ParseAddress(key string, logger *logging.Logger) (crypto.Address, error) { 128 address, err := crypto.AddressFromHexString(key) 129 if err == nil { 130 return address, nil 131 } 132 err = c.dial(logger) 133 if err != nil { 134 return crypto.Address{}, err 135 } 136 return c.keyClient.GetAddressForKeyName(key) 137 } 138 139 func (c *Client) GetAccount(address crypto.Address) (*acm.Account, error) { 140 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 141 defer cancel() 142 return c.queryClient.GetAccount(ctx, &rpcquery.GetAccountParam{Address: address}) 143 } 144 145 func (c *Client) GetMetadataForAccount(address crypto.Address) (string, error) { 146 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 147 defer cancel() 148 metadata, err := c.queryClient.GetMetadata(ctx, &rpcquery.GetMetadataParam{Address: &address}) 149 if err != nil { 150 return "", err 151 } 152 153 return metadata.Metadata, nil 154 } 155 156 func (c *Client) GetMetadata(metahash acmstate.MetadataHash) (string, error) { 157 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 158 defer cancel() 159 var bs binary.HexBytes = metahash.Bytes() 160 metadata, err := c.queryClient.GetMetadata(ctx, &rpcquery.GetMetadataParam{MetadataHash: &bs}) 161 if err != nil { 162 return "", err 163 } 164 165 return metadata.Metadata, nil 166 } 167 168 func (c *Client) GetStorage(address crypto.Address, key binary.Word256) ([]byte, error) { 169 val, err := c.queryClient.GetStorage(context.Background(), &rpcquery.GetStorageParam{Address: address, Key: key}) 170 if err != nil { 171 return []byte{}, err 172 } 173 return val.Value, err 174 } 175 176 func (c *Client) GetName(name string, logger *logging.Logger) (*names.Entry, error) { 177 err := c.dial(logger) 178 if err != nil { 179 return nil, err 180 } 181 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 182 defer cancel() 183 return c.queryClient.GetName(ctx, &rpcquery.GetNameParam{Name: name}) 184 } 185 186 func (c *Client) GetValidatorSet(logger *logging.Logger) (*rpcquery.ValidatorSet, error) { 187 err := c.dial(logger) 188 if err != nil { 189 return nil, err 190 } 191 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 192 defer cancel() 193 return c.queryClient.GetValidatorSet(ctx, &rpcquery.GetValidatorSetParam{}) 194 } 195 196 func (c *Client) GetProposal(hash []byte, logger *logging.Logger) (*payload.Ballot, error) { 197 err := c.dial(logger) 198 if err != nil { 199 return nil, err 200 } 201 return c.queryClient.GetProposal(context.Background(), &rpcquery.GetProposalParam{Hash: hash}) 202 } 203 204 func (c *Client) ListProposals(proposed bool, logger *logging.Logger) ([]*rpcquery.ProposalResult, error) { 205 err := c.dial(logger) 206 if err != nil { 207 return nil, err 208 } 209 stream, err := c.queryClient.ListProposals(context.Background(), &rpcquery.ListProposalsParam{Proposed: proposed}) 210 if err != nil { 211 return nil, err 212 } 213 var ballots []*rpcquery.ProposalResult 214 ballot, err := stream.Recv() 215 for err == nil { 216 ballots = append(ballots, ballot) 217 ballot, err = stream.Recv() 218 } 219 if err == io.EOF { 220 return ballots, nil 221 } 222 return nil, err 223 } 224 225 func (c *Client) SignAndBroadcast(tx payload.Payload, logger *logging.Logger) (*exec.TxExecution, error) { 226 err := c.dial(logger) 227 if err != nil { 228 return nil, err 229 } 230 txEnv, err := c.SignTx(tx, logger) 231 if err != nil { 232 return nil, err 233 } 234 return unifyErrors(c.BroadcastEnvelope(txEnv, logger)) 235 } 236 237 func (c *Client) SignTx(tx payload.Payload, logger *logging.Logger) (*txs.Envelope, error) { 238 err := c.dial(logger) 239 if err != nil { 240 return nil, err 241 } 242 txEnv := txs.Enclose(c.chainID, tx) 243 if c.MempoolSigning { 244 logger.InfoMsg("Using mempool signing") 245 return txEnv, nil 246 } 247 inputs := tx.GetInputs() 248 signers := make([]acm.AddressableSigner, len(inputs)) 249 for i, input := range inputs { 250 signers[i], err = keys.AddressableSigner(c.keyClient, input.Address) 251 if err != nil { 252 return nil, err 253 } 254 } 255 err = txEnv.Sign(signers...) 256 if err != nil { 257 return nil, err 258 } 259 return txEnv, nil 260 } 261 262 // Creates a keypair using attached keys service 263 func (c *Client) CreateKey(keyName, curveTypeString string, logger *logging.Logger) (*crypto.PublicKey, error) { 264 err := c.dial(logger) 265 if err != nil { 266 return nil, err 267 } 268 if c.keyClient == nil { 269 return nil, fmt.Errorf("could not create key pair since no keys service is attached, " + 270 "pass --keys flag") 271 } 272 curveType := crypto.CurveTypeEd25519 273 if curveTypeString != "" { 274 curveType, err = crypto.CurveTypeFromString(curveTypeString) 275 if err != nil { 276 return nil, err 277 } 278 } 279 address, err := c.keyClient.Generate(keyName, curveType) 280 if err != nil { 281 return nil, err 282 } 283 return c.keyClient.PublicKey(address) 284 } 285 286 // Broadcast payload for remote signing 287 func (c *Client) Broadcast(tx payload.Payload, logger *logging.Logger) (*exec.TxExecution, error) { 288 err := c.dial(logger) 289 if err != nil { 290 return nil, err 291 } 292 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 293 defer cancel() 294 return unifyErrors(c.transactClient.BroadcastTxSync(ctx, &rpctransact.TxEnvelopeParam{Payload: tx.Any()})) 295 } 296 297 // Broadcast envelope - can be locally signed or remote signing will be attempted 298 func (c *Client) BroadcastEnvelope(txEnv *txs.Envelope, logger *logging.Logger) (*exec.TxExecution, error) { 299 err := c.dial(logger) 300 if err != nil { 301 return nil, err 302 } 303 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 304 defer cancel() 305 306 return unifyErrors(c.transactClient.BroadcastTxSync(ctx, &rpctransact.TxEnvelopeParam{Envelope: txEnv})) 307 } 308 309 func (c *Client) ParseUint64(amount string) (uint64, error) { 310 if amount == "" { 311 return 0, nil 312 } 313 return strconv.ParseUint(amount, 10, 64) 314 } 315 316 // Simulated call 317 318 type QueryArg struct { 319 Input string 320 Address string 321 Data string 322 } 323 324 func (c *Client) QueryContract(arg *QueryArg, logger *logging.Logger) (*exec.TxExecution, error) { 325 tx, err := c.Call(&CallArg{ 326 Input: arg.Input, 327 Address: arg.Address, 328 Data: arg.Data, 329 }, logger) 330 if err != nil { 331 return nil, err 332 } 333 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 334 defer cancel() 335 return unifyErrors(c.transactClient.CallTxSim(ctx, tx)) 336 } 337 338 // Transaction types 339 340 type GovArg struct { 341 Input string 342 Native string 343 Power string 344 Sequence string 345 Permissions []string 346 Roles []string 347 Address string 348 PublicKey string 349 } 350 351 func (c *Client) UpdateAccount(arg *GovArg, logger *logging.Logger) (*payload.GovTx, error) { 352 logger.InfoMsg("GovTx", "account", arg) 353 err := c.dial(logger) 354 if err != nil { 355 return nil, err 356 } 357 input, err := c.TxInput(arg.Input, arg.Native, arg.Sequence, true, logger) 358 if err != nil { 359 return nil, err 360 } 361 update := &spec.TemplateAccount{ 362 Permissions: arg.Permissions, 363 Roles: arg.Roles, 364 } 365 if arg.Address != "" { 366 addr, err := c.ParseAddress(arg.Address, logger) 367 if err != nil { 368 return nil, fmt.Errorf("could not parse address: %v", err) 369 } 370 update.Address = &addr 371 } 372 if arg.PublicKey != "" { 373 pubKey, err := PublicKeyFromString(arg.PublicKey) 374 if err != nil { 375 return nil, fmt.Errorf("could not parse publicKey: %v", err) 376 } 377 update.PublicKey = pubKey 378 } else { 379 // Attempt to get public key from connected key client 380 if arg.Address != "" { 381 // Try key client 382 pubKey, err := c.PublicKeyFromAddress(update.Address) 383 if err != nil { 384 logger.InfoMsg("did not get public key", "address", *update.Address) 385 } else { 386 update.PublicKey = pubKey 387 } 388 // We can still proceed with just address set 389 } else { 390 return nil, fmt.Errorf("neither target address or public key were provided") 391 } 392 } 393 394 _, err = permission.PermFlagFromStringList(arg.Permissions) 395 if err != nil { 396 return nil, fmt.Errorf("could not parse UpdateAccount permissions: %v", err) 397 } 398 399 if arg.Native != "" { 400 native, err := c.ParseUint64(arg.Native) 401 if err != nil { 402 return nil, fmt.Errorf("could not parse native token amount: %v", err) 403 } 404 update.Amounts = update.Balances().Native(native) 405 } 406 if arg.Power != "" { 407 power, err := c.ParseUint64(arg.Power) 408 if err != nil { 409 return nil, fmt.Errorf("could not parse native token amount: %v", err) 410 } 411 update.Amounts = update.Balances().Power(power) 412 } 413 tx := &payload.GovTx{ 414 Inputs: []*payload.TxInput{input}, 415 AccountUpdates: []*spec.TemplateAccount{update}, 416 } 417 return tx, nil 418 } 419 420 func (c *Client) PublicKeyFromAddress(address *crypto.Address) (*crypto.PublicKey, error) { 421 if c.keyClient != nil { 422 return nil, fmt.Errorf("key client is not set") 423 } 424 pubKey, err := c.keyClient.PublicKey(*address) 425 if err != nil { 426 return nil, fmt.Errorf("could not retrieve public key from keys server: %v", err) 427 } 428 return pubKey, nil 429 } 430 431 func PublicKeyFromString(publicKey string) (*crypto.PublicKey, error) { 432 bs, err := hex.DecodeString(publicKey) 433 if err != nil { 434 return nil, fmt.Errorf("could not parse public key string %s as hex: %v", publicKey, err) 435 } 436 switch len(bs) { 437 case crypto.PublicKeyLength(crypto.CurveTypeEd25519): 438 return crypto.PublicKeyFromBytes(bs, crypto.CurveTypeEd25519) 439 case crypto.PublicKeyLength(crypto.CurveTypeSecp256k1): 440 return crypto.PublicKeyFromBytes(bs, crypto.CurveTypeSecp256k1) 441 default: 442 return nil, fmt.Errorf("public key string %s has byte length %d which is not the size of either "+ 443 "ed25519 or uncompressed secp256k1 keys so cannot construct public key", publicKey, len(bs)) 444 } 445 } 446 447 type CallArg struct { 448 Input string 449 Amount string 450 Sequence string 451 Address string 452 Fee string 453 Gas string 454 Data string 455 WASM string 456 Metadata map[acmstate.CodeHash]string 457 } 458 459 func (c *Client) Call(arg *CallArg, logger *logging.Logger) (*payload.CallTx, error) { 460 logger.TraceMsg("CallTx", 461 "input", arg.Input, 462 "amount", arg.Amount, 463 "sequence", arg.Sequence, 464 "address", arg.Address, 465 "data", arg.Data, 466 "wasm", arg.WASM) 467 input, err := c.TxInput(arg.Input, arg.Amount, arg.Sequence, true, logger) 468 if err != nil { 469 return nil, err 470 } 471 var contractAddress *crypto.Address 472 if arg.Address != "" { 473 address, err := crypto.AddressFromHexString(arg.Address) 474 if err != nil { 475 return nil, err 476 } 477 contractAddress = &address 478 } 479 480 fee, err := c.ParseUint64(arg.Fee) 481 if err != nil { 482 return nil, err 483 } 484 gas, err := c.ParseUint64(arg.Gas) 485 if err != nil { 486 return nil, err 487 } 488 code, err := hex.DecodeString(arg.Data) 489 if err != nil { 490 return nil, err 491 } 492 493 wasm, err := hex.DecodeString(arg.WASM) 494 if err != nil { 495 return nil, err 496 } 497 498 metas := make([]*payload.ContractMeta, 0) 499 for codehash, metadata := range arg.Metadata { 500 metas = append(metas, &payload.ContractMeta{ 501 CodeHash: codehash.Bytes(), 502 Meta: metadata, 503 }) 504 } 505 506 tx := &payload.CallTx{ 507 Input: input, 508 Address: contractAddress, 509 Data: code, 510 WASM: wasm, 511 Fee: fee, 512 GasLimit: gas, 513 ContractMeta: metas, 514 } 515 516 return tx, nil 517 } 518 519 type SendArg struct { 520 Input string 521 Amount string 522 Sequence string 523 Output string 524 } 525 526 func (c *Client) Send(arg *SendArg, logger *logging.Logger) (*payload.SendTx, error) { 527 logger.InfoMsg("SendTx", "send", arg) 528 input, err := c.TxInput(arg.Input, arg.Amount, arg.Sequence, true, logger) 529 if err != nil { 530 return nil, err 531 } 532 outputAddress, err := c.ParseAddress(arg.Output, logger) 533 if err != nil { 534 return nil, err 535 } 536 tx := &payload.SendTx{ 537 Inputs: []*payload.TxInput{input}, 538 Outputs: []*payload.TxOutput{{ 539 Address: outputAddress, 540 Amount: input.Amount, 541 }}, 542 } 543 return tx, nil 544 } 545 546 type BondArg struct { 547 Input string 548 Amount string 549 Sequence string 550 } 551 552 func (c *Client) Bond(arg *BondArg, logger *logging.Logger) (*payload.BondTx, error) { 553 logger.InfoMsg("BondTx", "account", arg) 554 err := c.dial(logger) 555 if err != nil { 556 return nil, err 557 } 558 input, err := c.TxInput(arg.Input, arg.Amount, arg.Sequence, true, logger) 559 if err != nil { 560 return nil, err 561 } 562 return &payload.BondTx{ 563 Input: input, 564 }, nil 565 } 566 567 type UnbondArg struct { 568 Output string 569 Amount string 570 Sequence string 571 } 572 573 func (c *Client) Unbond(arg *UnbondArg, logger *logging.Logger) (*payload.UnbondTx, error) { 574 logger.InfoMsg("UnbondTx", "account", arg) 575 if err := c.dial(logger); err != nil { 576 return nil, err 577 } 578 input, err := c.TxInput(arg.Output, arg.Amount, arg.Sequence, true, logger) 579 if err != nil { 580 return nil, err 581 } 582 583 tx := payload.NewUnbondTx(input.Address, input.Amount) 584 tx.Input = input 585 586 return tx, nil 587 } 588 589 type NameArg struct { 590 Input string 591 Amount string 592 Sequence string 593 Name string 594 Data string 595 Fee string 596 } 597 598 func (c *Client) Name(arg *NameArg, logger *logging.Logger) (*payload.NameTx, error) { 599 logger.InfoMsg("NameTx", "name", arg) 600 input, err := c.TxInput(arg.Input, arg.Amount, arg.Sequence, true, logger) 601 if err != nil { 602 return nil, err 603 } 604 fee, err := c.ParseUint64(arg.Fee) 605 if err != nil { 606 return nil, err 607 } 608 tx := &payload.NameTx{ 609 Input: input, 610 Name: arg.Name, 611 Data: arg.Data, 612 Fee: fee, 613 } 614 return tx, nil 615 } 616 617 type PermArg struct { 618 Input string 619 Sequence string 620 Action string 621 Target string 622 Permission string 623 Value string 624 Role string 625 } 626 627 func (c *Client) Permissions(arg *PermArg, logger *logging.Logger) (*payload.PermsTx, error) { 628 logger.InfoMsg("PermsTx", "perm", arg) 629 input, err := c.TxInput(arg.Input, "", arg.Sequence, true, logger) 630 if err != nil { 631 return nil, err 632 } 633 action, err := permission.PermStringToFlag(arg.Action) 634 if err != nil { 635 return nil, err 636 } 637 permArgs := permission.PermArgs{ 638 Action: action, 639 } 640 if arg.Target != "" { 641 target, err := c.ParseAddress(arg.Target, logger) 642 if err != nil { 643 return nil, err 644 } 645 permArgs.Target = &target 646 } 647 if arg.Value != "" { 648 var value bool 649 switch arg.Value { 650 case "true": 651 value = true 652 case "false": 653 value = false 654 default: 655 return nil, fmt.Errorf("did not recognise value %s as boolean, use 'true' or 'false'", arg.Value) 656 } 657 permArgs.Value = &value 658 } 659 if arg.Permission != "" { 660 perm, err := permission.PermStringToFlag(arg.Permission) 661 if err != nil { 662 return nil, err 663 } 664 permArgs.Permission = &perm 665 } 666 667 if arg.Role != "" { 668 permArgs.Role = &arg.Role 669 } 670 671 tx := &payload.PermsTx{ 672 Input: input, 673 PermArgs: permArgs, 674 } 675 return tx, nil 676 } 677 678 type IdentifyArg struct { 679 Input string 680 NodeKey string 681 Moniker string 682 NetAddress string 683 Amount string 684 Sequence string 685 } 686 687 func (c *Client) Identify(arg *IdentifyArg, logger *logging.Logger) (*payload.IdentifyTx, error) { 688 logger.InfoMsg("IdentifyTx", "name", arg) 689 input, err := c.TxInput(arg.Input, arg.Amount, arg.Sequence, true, logger) 690 if err != nil { 691 return nil, err 692 } 693 694 address, err := crypto.AddressFromHexString(arg.Input) 695 if err != nil { 696 return nil, err 697 } 698 699 signer, err := keys.AddressableSigner(c.keyClient, address) 700 if err != nil { 701 return nil, err 702 } 703 704 nodeKey, err := p2p.LoadNodeKey(arg.NodeKey) 705 if err != nil { 706 return nil, err 707 } 708 709 id, err := crypto.AddressFromHexString(string(nodeKey.ID())) 710 if err != nil { 711 return nil, err 712 } 713 714 node := ®istry.NodeIdentity{ 715 Moniker: arg.Moniker, 716 NetworkAddress: arg.NetAddress, 717 TendermintNodeID: id, 718 ValidatorPublicKey: signer.GetPublicKey(), 719 } 720 721 return &payload.IdentifyTx{ 722 Inputs: []*payload.TxInput{input}, 723 Node: node, 724 }, nil 725 } 726 727 func (c *Client) TxInput(inputString, amountString, sequenceString string, allowMempoolSigning bool, logger *logging.Logger) (*payload.TxInput, error) { 728 var err error 729 var inputAddress crypto.Address 730 if inputString != "" { 731 inputAddress, err = c.ParseAddress(inputString, logger) 732 if err != nil { 733 return nil, fmt.Errorf("TxInput(): could not obtain input address from '%s': %v", inputString, err) 734 } 735 } 736 var amount uint64 737 if amountString != "" { 738 amount, err = c.ParseUint64(amountString) 739 if err != nil { 740 return nil, err 741 } 742 } 743 var sequence uint64 744 sequence, err = c.getSequence(sequenceString, inputAddress, c.MempoolSigning && allowMempoolSigning, logger) 745 if err != nil { 746 return nil, err 747 } 748 return &payload.TxInput{ 749 Address: inputAddress, 750 Amount: amount, 751 Sequence: sequence, 752 }, nil 753 } 754 755 func (c *Client) getSequence(sequence string, inputAddress crypto.Address, mempoolSigning bool, logger *logging.Logger) (uint64, error) { 756 err := c.dial(logger) 757 if err != nil { 758 return 0, err 759 } 760 if sequence == "" { 761 if mempoolSigning { 762 // Perform mempool signing 763 return 0, nil 764 } 765 // Get from chain 766 ctx, cancel := context.WithTimeout(context.Background(), c.timeout) 767 defer cancel() 768 acc, err := c.queryClient.GetAccount(ctx, &rpcquery.GetAccountParam{Address: inputAddress}) 769 if err != nil { 770 return 0, err 771 } 772 return acc.Sequence + 1, nil 773 } 774 return c.ParseUint64(sequence) 775 } 776 777 func argMap(value interface{}) map[string]interface{} { 778 rv := reflect.ValueOf(value) 779 if rv.Kind() == reflect.Ptr { 780 rv = rv.Elem() 781 } 782 rt := rv.Type() 783 fields := make(map[string]interface{}) 784 for i := 0; i < rv.NumField(); i++ { 785 if rv.Field(i).Kind() == reflect.String { 786 fields[rt.Field(i).Name] = rv.Field(i).String() 787 } 788 } 789 return fields 790 } 791 792 // In order to safely handle a TxExecution one must check the Exception field to account for committed transaction 793 // (therefore having no error) that may have exceptional executions (therefore not having the normal return values) 794 func unifyErrors(txe *exec.TxExecution, err error) (*exec.TxExecution, error) { 795 if err != nil { 796 return nil, err 797 } 798 return txe, txe.Exception.AsError() 799 }