cleaned up error handling

This commit is contained in:
Julien Cassis 2020-11-26 20:06:13 -05:00
parent b893a1c31a
commit 026969ce35
20 changed files with 162 additions and 109 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
export SLNC_GLOBAL_INSECURE_VAULT_PASSPHRASE=secure

View File

@ -42,9 +42,6 @@ func getClient() *rpc.Client {
}
api.SetHeader(headerArray[0], headerArray[1])
}
api.Debug = viper.GetBool("global-debug")
return api
}

View File

@ -38,9 +38,10 @@ var getAccountCmd = &cobra.Command{
}
acct := resp.Value
data, err := json.MarshalIndent(acct, "", " ")
errorCheck("json marshal", err)
var data []byte
if data, err = json.MarshalIndent(acct, "", " "); err != nil {
return fmt.Errorf("unable to marshall account information: %w", err)
}
fmt.Println(string(data))

View File

@ -16,7 +16,6 @@ package cmd
import (
"context"
"errors"
"fmt"
"github.com/spf13/cobra"
@ -36,7 +35,7 @@ var getBalanceCmd = &cobra.Command{
}
if resp.Value == 0 {
errorCheck("not found", errors.New("account not found"))
return fmt.Errorf("account not found")
}
fmt.Println(resp.Value, "lamports")

View File

@ -27,12 +27,14 @@ var getConfirmedBlockCmd = &cobra.Command{
Use: "confirmed-block {block_num}",
Short: "Retrieve a confirmed block, with all of its transactions",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, args []string) (err error) {
client := getClient()
ctx := context.Background()
slot, err := strconv.ParseInt(args[0], 10, 64)
errorCheck("parsing slot number in first argument", err)
var slot int64
if slot, err = strconv.ParseInt(args[0], 10, 64); err != nil {
return fmt.Errorf("unable to parse provided slot number %q: %w", args[0], err)
}
resp, err := client.GetConfirmedBlock(ctx, uint64(slot), "")
if err != nil {
@ -42,16 +44,6 @@ var getConfirmedBlockCmd = &cobra.Command{
cnt, _ := json.MarshalIndent(resp, "", " ")
fmt.Println(string(cnt))
// for idx, trx := range resp.Transactions {
// unpacked, err := solana.TransactionFromData(trx.Transaction)
// if err != nil {
// return err
// }
// cnt, _ := json.MarshalIndent(unpacked, "", " ")
// fmt.Println(idx, string(cnt))
// }
return nil
},
}

View File

@ -17,7 +17,6 @@ package cmd
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/dfuse-io/solana-go"
@ -39,7 +38,7 @@ var getProgramAccountsCmd = &cobra.Command{
}
if resp == nil {
errorCheck("not found", errors.New("program account not found"))
return fmt.Errorf("program account not found")
}
for _, keyedAcct := range resp {

View File

@ -15,8 +15,6 @@
package cmd
import (
"context"
"errors"
"fmt"
"log"
"os"
@ -35,7 +33,7 @@ var getSPLTokenCmd = &cobra.Command{
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
client := getClient()
ctx := context.Background()
ctx := cmd.Context()
resp, err := client.GetProgramAccounts(
ctx,
@ -53,7 +51,7 @@ var getSPLTokenCmd = &cobra.Command{
}
if resp == nil {
errorCheck("not found", errors.New("program account not found"))
return fmt.Errorf("program account not found")
}
for _, keyedAcct := range resp {

View File

@ -39,15 +39,18 @@ var getTransactionsCmd = &cobra.Command{
address := args[0]
pubKey, err := solana.PublicKeyFromBase58(address)
errorCheck("public key", err)
if err != nil {
return fmt.Errorf("invalid account address %q: %w", address, err)
}
csList, err := client.GetConfirmedSignaturesForAddress2(ctx, pubKey, &rpc.GetConfirmedSignaturesForAddress2Opts{
Limit: 1,
Before: "",
Until: "",
})
errorCheck("getting confirm transaction:", err)
if err != nil {
return fmt.Errorf("unable to retrieve confirmed transaction signatures for account: %w", err)
}
for _, cs := range csList {
fmt.Println("-----------------------------------------------------------------------------------------------")
@ -60,19 +63,22 @@ var getTransactionsCmd = &cobra.Command{
fmt.Println(cs.Memo)
ct, err := client.GetConfirmedTransaction(ctx, cs.Signature)
errorCheck("confirm transaction", err)
if err != nil {
return fmt.Errorf("unable to get confirmed transaction with signature %q: %w", cs.Signature, err)
}
if ct.Meta.Err != nil {
fmt.Println("ERROR:", ct.Meta.Err)
// for k, _ := range ct.Meta.Err
return fmt.Errorf("unable to get confirmed transaction with signature %q: %s", cs.Signature, ct.Meta.Err)
}
fmt.Print("\nInstructions:\n-------------\n\n")
for _, i := range ct.Transaction.Message.Instructions {
//Missing Initial account instruction ??????
id, err := ct.Transaction.ResolveProgramIDIndex(i.ProgramIDIndex)
errorCheck("resolving programID", err)
if err != nil {
return fmt.Errorf("unable to resolve program ID: %w", err)
}
decoder := solana.InstructionDecoderRegistry[id.String()]
if decoder == nil {
fmt.Println("raw instruction:")
@ -88,9 +94,14 @@ var getTransactionsCmd = &cobra.Command{
}
decoded, err := decoder(ct.Transaction.AccountMetaList(), &i)
errorCheck("bin decode", err)
if err != nil {
return fmt.Errorf("unable to decode instruction: %w", err)
}
err = text.NewEncoder(os.Stdout).Encode(decoded, nil)
errorCheck("textEncoding", err)
if err != nil {
return fmt.Errorf("unable to text encoder instruction: %w", err)
}
}
text.EncoderColorCyan.Print("\n\nEnd of transaction\n\n")
}

View File

@ -15,7 +15,6 @@
package cmd
import (
"fmt"
"os"
"strings"
@ -32,19 +31,9 @@ func init() {
logging.Register("github.com/dfuse-io/solana-go/cmd/slnc/cmd", &zlog)
}
func SetupLogger(debug bool) {
fmt.Println("setting logger with debug:", debug)
if debug {
zlog, err := zap.NewDevelopment()
fmt.Println("setting logger1")
if err == nil {
fmt.Println("setting logger2")
logging.Set(zlog)
}
// Hijack standard Golang `log` and redirect it to our common logger
zap.RedirectStdLogAt(zlog, zap.DebugLevel)
}
func SetupLogger() {
commonLogger := createLogger("cli", 1, zap.FatalLevel)
logging.Set(commonLogger)
// Fine-grain customization
//

View File

@ -26,14 +26,16 @@ import (
var requestCmd = &cobra.Command{
Use: "request-airdrop {address} {lamport}",
Short: "request lamport airdrop for an account",
Short: "Request lamport airdrop for an account",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
client := getClient()
address, err := solana.PublicKeyFromBase58(args[0])
errorCheck("invalid token address", err)
if err != nil {
return fmt.Errorf("invalid account address %q: %w", args[0], err)
}
lamport, err := strconv.Atoi(args[1])
if err != nil {
@ -41,9 +43,11 @@ var requestCmd = &cobra.Command{
}
airDrop, err := client.RequestAirdrop(context.Background(), &address, uint64(lamport), rpc.CommitmentMax)
errorCheck("air drop", err)
fmt.Println("air drop hash:", airDrop)
if err != nil {
return fmt.Errorf("airdrop request failed: %w", err)
}
fmt.Println("Air drop succeeded, transaction hash:", airDrop)
return nil
},
}

View File

@ -46,19 +46,17 @@ func Execute() {
func init() {
cobra.OnInitialize(initConfig)
RootCmd.PersistentFlags().BoolP("debug", "", false, "Enables verbose API debug messages")
// Not implemnted
//RootCmd.PersistentFlags().BoolP("debug", "", false, "Enables verbose API debug messages")
RootCmd.PersistentFlags().StringP("vault-file", "", "./solana-vault.json", "Wallet file that contains encrypted key material")
RootCmd.PersistentFlags().StringP("rpc-url", "u", defaultRPCURL, "API endpoint of eos.io blockchain node")
RootCmd.PersistentFlags().StringSliceP("http-header", "H", []string{}, "HTTP header to add to JSON-RPC requests")
RootCmd.PersistentFlags().StringP("kms-gcp-keypath", "", "", "Path to the cryptoKeys within a keyRing on GCP")
// RootCmd.PersistentFlags().StringP("write-transaction", "", "", "Do not broadcast the transaction produced, but write it in json to the given filename instead.")
// RootCmd.PersistentFlags().StringP("offline-head-block", "", "", "Provide a recent block ID (long-form hex) for TaPoS. Use all --offline options to sign transactions offline.")
// RootCmd.PersistentFlags().StringP("offline-chain-id", "", "", "Chain ID to sign transaction with. Use all --offline- options to sign transactions offline.")
// RootCmd.PersistentFlags().StringSliceP("offline-sign-key", "", []string{}, "Public key to use to sign transaction. Must be in your vault or wallet. Use all --offline- options to sign transactions offline.")
// RootCmd.PersistentFlags().BoolP("skip-sign", "", false, "Do not sign the transaction. Use with --write-transaction.")
SetupLogger(viper.GetBool("global-debug"))
RootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
SetupLogger()
return nil
}
}
func initConfig() {
@ -66,7 +64,6 @@ func initConfig() {
viper.AutomaticEnv()
replacer := strings.NewReplacer("-", "_")
viper.SetEnvKeyReplacer(replacer)
recurseViperCommands(RootCmd, nil)
}

View File

@ -35,7 +35,9 @@ var tokenRegistryGetCmd = &cobra.Command{
address := args[0]
pubKey, err := solana.PublicKeyFromBase58(address)
errorCheck("public key", err)
if err != nil {
return fmt.Errorf("invalid mint address %q: %w", address, err)
}
t, err := tokenregistry.GetTokenRegistryEntry(cmd.Context(), client, pubKey)
if err != nil {
@ -47,7 +49,10 @@ var tokenRegistryGetCmd = &cobra.Command{
}
err = text.NewEncoder(os.Stdout).Encode(t, nil)
errorCheck("textEncoding", err)
if err != nil {
return fmt.Errorf("unable to text encode token registry entry: %w", err)
}
return nil
},
}

View File

@ -18,6 +18,8 @@ import (
"context"
"fmt"
"github.com/dfuse-io/solana-go/rpc"
"github.com/spf13/viper"
"github.com/dfuse-io/solana-go"
@ -27,22 +29,31 @@ import (
)
var tokenRegistryRegisterCmd = &cobra.Command{
Use: "register {token} {name} {symbol} {logo}",
Use: "register {token-address} {name} {symbol} {logo}",
Short: "register meta data for a token",
Args: cobra.ExactArgs(4),
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(cmd *cobra.Command, args []string) (err error) {
vault := mustGetWallet()
client := getClient()
tokenAddress, err := solana.PublicKeyFromBase58(args[0])
errorCheck("invalid token address", err)
var tokenAddress solana.PublicKey
if tokenAddress, err = solana.PublicKeyFromBase58(args[0]); err != nil {
return fmt.Errorf("invalid token address %q: %w", args[0], err)
}
logo, err := tokenregistry.LogoFromString(args[1])
errorCheck("invalid logo", err)
name, err := tokenregistry.NameFromString(args[2])
errorCheck("invalid name", err)
symbol, err := tokenregistry.SymbolFromString(args[3])
errorCheck("invalid symbol", err)
var logo tokenregistry.Logo
var name tokenregistry.Name
var symbol tokenregistry.Symbol
if logo, err = tokenregistry.LogoFromString(args[1]); err != nil {
return fmt.Errorf("invalid logo %q: %w", args[1], err)
}
if name, err = tokenregistry.NameFromString(args[2]); err != nil {
return fmt.Errorf("invalid name %q: %w", args[2], err)
}
if symbol, err = tokenregistry.SymbolFromString(args[3]); err != nil {
return fmt.Errorf("invalid symbol %q: %w", args[3], err)
}
pkeyStr := viper.GetString("token-registry-register-cmd-registrar")
if pkeyStr == "" {
@ -50,7 +61,9 @@ var tokenRegistryRegisterCmd = &cobra.Command{
}
registrarPubKey, err := solana.PublicKeyFromBase58(pkeyStr)
errorCheck(fmt.Sprintf("invalid registrar key %q", pkeyStr), err)
if err != nil {
return fmt.Errorf("invalid registrar key %q: %w", pkeyStr, err)
}
found := false
for _, privateKey := range vault.KeyBag {
@ -62,22 +75,29 @@ var tokenRegistryRegisterCmd = &cobra.Command{
return fmt.Errorf("registrar key must be present in the vault to register a token")
}
fmt.Println(registrarPubKey.String())
blockHashResult, err := client.GetRecentBlockhash(context.Background(), rpc.CommitmentMax)
if err != nil {
return fmt.Errorf("unable retrieve recent block hash: %w", err)
}
tokenMetaAccount := solana.NewAccount()
lamport, err := client.GetMinimumBalanceForRentExemption(context.Background(), tokenregistry.TOKEN_META_SIZE)
errorCheck("get minimum balance for rent exemption ", err)
if err != nil {
return fmt.Errorf("unable to retrieve lapoint rent: %w", err)
}
tokenRegistryProgramID := tokenregistry.ProgramID()
createAccountInstruction := system.NewCreateAccountInstruction(uint64(lamport), tokenregistry.TOKEN_META_SIZE, tokenRegistryProgramID, registrarPubKey, tokenMetaAccount.PublicKey())
registerTokenInstruction := tokenregistry.NewRegisterTokenInstruction(logo, name, symbol, tokenMetaAccount.PublicKey(), registrarPubKey, tokenAddress)
trx, err := solana.TransactionWithInstructions([]solana.TransactionInstruction{createAccountInstruction, registerTokenInstruction}, &solana.Options{
trx, err := solana.TransactionWithInstructions([]solana.TransactionInstruction{createAccountInstruction, registerTokenInstruction}, blockHashResult.Value.Blockhash, &solana.Options{
Payer: registrarPubKey,
})
errorCheck("unable to craft transaction", err)
if err != nil {
return fmt.Errorf("unable to craft transaction: %w", err)
}
_, err = trx.Sign(func(key solana.PublicKey) *solana.PrivateKey {
for _, k := range vault.KeyBag {
@ -87,12 +107,18 @@ var tokenRegistryRegisterCmd = &cobra.Command{
}
return nil
})
errorCheck("unable to sign transaction", err)
if err != nil {
return fmt.Errorf("unable to sign transaction: %w", err)
}
trxHash, err := client.SendTransaction(cmd.Context(), trx)
if err != nil {
return fmt.Errorf("unable to send transaction: %w", err)
}
fmt.Println("sent transaction hash:", trxHash, " error:", err)
fmt.Printf("Token Register successfully, with transaction hash: %s\n", trxHash)
fmt.Printf(" Mint Address Registerd: %s\n", tokenAddress.String())
fmt.Printf(" Token Registry Meta Address: %s\n", tokenMetaAccount.PublicKey().String())
return nil
},
}

View File

@ -34,18 +34,26 @@ var vaultAddCmd = &cobra.Command{
fmt.Println("Loading existing vault from file:", walletFile)
v, err := vault.NewVaultFromWalletFile(walletFile)
errorCheck("loading vault from file", err)
if err != nil {
fmt.Errorf("unable to load vault file: %w", err)
}
boxer, err := vault.SecretBoxerForType(v.SecretBoxWrap, viper.GetString("global-kms-gcp-keypath"))
errorCheck("missing parameters", err)
if err != nil {
fmt.Errorf("unable to intiate boxer: %w", err)
}
err = v.Open(boxer)
errorCheck("opening vault", err)
if err != nil {
fmt.Errorf("unable to open vault: %w", err)
}
v.PrintPublicKeys()
privateKeys, err := capturePrivateKeys()
errorCheck("entering private keys", err)
if err != nil {
fmt.Errorf("failed to enter private keys: %w", err)
}
var newKeys []solana.PublicKey
for _, privateKey := range privateKeys {
@ -54,10 +62,14 @@ var vaultAddCmd = &cobra.Command{
}
err = v.Seal(boxer)
errorCheck("sealing vault", err)
if err != nil {
fmt.Errorf("failed to seal vault: %w", err)
}
err = v.WriteToFile(walletFile)
errorCheck("writing vault file", err)
if err != nil {
fmt.Errorf("failed to write vault file: %w", err)
}
vaultWrittenReport(walletFile, newKeys, len(v.KeyBag))
},

View File

@ -46,7 +46,7 @@ You can create a Google Cloud Platform KMS-wrapped vault with:
cmd vault create --keys=2 --vault-type=kms-gcp --kms-gcp-keypath projects/.../locations/.../keyRings/.../cryptoKeys/name
You can then use this vault for the different cmd operations.`,
Run: func(cmd *cobra.Command, args []string) {
RunE: func(cmd *cobra.Command, args []string) (err error) {
walletFile := viper.GetString("global-vault-file")
if _, err := os.Stat(walletFile); err == nil {
@ -59,7 +59,7 @@ You can then use this vault for the different cmd operations.`,
kmsGCPKeypath := viper.GetString("global-kms-gcp-keypath")
if wrapType == "kms-gcp" && kmsGCPKeypath == "" {
errorCheck("missing parameter", fmt.Errorf("--kms-gcp-keypath is required with --vault-type=kms-gcp"))
return fmt.Errorf("missing parameter: --kms-gcp-keypath is required with --vault-type=kms-gcp")
}
v := vault.NewVault()
@ -70,7 +70,9 @@ You can then use this vault for the different cmd operations.`,
doImport := viper.GetBool("vault-create-cmd-import")
if doImport {
privateKeys, err := capturePrivateKeys()
errorCheck("entering private key", err)
if err != nil {
return fmt.Errorf("failed enterign private key: %w", err)
}
for _, privateKey := range privateKeys {
v.AddPrivateKey(privateKey)
@ -83,12 +85,14 @@ You can then use this vault for the different cmd operations.`,
numKeys := viper.GetInt("vault-create-cmd-keys")
if numKeys == 0 {
errorCheck("specify either --keys or --import", fmt.Errorf("create a vault with 0 keys?"))
return fmt.Errorf("specify either --keys or --import: create a vault with 0 keys?")
}
for i := 0; i < numKeys; i++ {
pubKey, err := v.NewKeyPair()
errorCheck("creating new keypair", err)
if err != nil {
return fmt.Errorf("unable ot create new keypair: %w", err)
}
newKeys = append(newKeys, pubKey)
}
@ -109,7 +113,9 @@ You can then use this vault for the different cmd operations.`,
boxer = vault.NewPassphraseBoxer(envVal)
} else {
password, err := cli.GetEncryptPassphrase()
errorCheck("password input", err)
if err != nil {
return fmt.Errorf("failed to get password input: %w", err)
}
boxer = vault.NewPassphraseBoxer(password)
}
@ -119,10 +125,16 @@ You can then use this vault for the different cmd operations.`,
os.Exit(1)
}
errorCheck("sealing vault", v.Seal(boxer))
errorCheck("writing wallet file", v.WriteToFile(walletFile))
if err = v.Seal(boxer); err != nil {
return fmt.Errorf("failed to seal the vault: %w", err)
}
if err = v.WriteToFile(walletFile); err != nil {
return fmt.Errorf("failed to write vault file: %w", err)
}
vaultWrittenReport(walletFile, newKeys, len(v.KeyBag))
return nil
},
}

View File

@ -64,7 +64,7 @@ func NewCreateAccountInstruction(lamports uint64, space uint64, owner, from, to
Owner: owner,
Accounts: &CreateAccountAccounts{
From: &solana.AccountMeta{PublicKey: from, IsSigner: true, IsWritable: true},
New: &solana.AccountMeta{PublicKey: to, IsSigner: true, IsWritable: true},
New: &solana.AccountMeta{PublicKey: to, IsSigner: false, IsWritable: true},
},
},
},

View File

@ -33,8 +33,6 @@ type Client struct {
rpcURL string
rpcClient jsonrpc.RPCClient
headers http.Header
Debug bool
}
func NewClient(rpcURL string) *Client {

View File

@ -57,7 +57,6 @@ func TestClient_GetConfirmedSignaturesForAddress2(t *testing.T) {
func TestClient_GetConfirmedTransaction(t *testing.T) {
zlog, _ = zap.NewDevelopment()
c := NewClient("http://api.mainnet-beta.solana.com:80/rpc")
c.Debug = true
signature := "53hoZ98EsCMA6L63GWM65M3Bd3WqA4LxD8bcJkbKoKWhbJFqX9M1WZ4fSjt8bYyZn21NwNnV2A25zirBni9Qk6LR"
trx, err := c.GetConfirmedTransaction(context.Background(), signature)
require.NoError(t, err)
@ -79,7 +78,6 @@ func TestClient_GetConfirmedTransaction(t *testing.T) {
func TestClient_getMinimumBalanceForRentExemption(t *testing.T) {
zlog, _ = zap.NewDevelopment()
c := NewClient("http://api.mainnet-beta.solana.com:80/rpc")
c.Debug = true
lamport, err := c.GetMinimumBalanceForRentExemption(context.Background(), 100)
require.NoError(t, err)
require.Equal(t, 1586880, lamport)

View File

@ -20,7 +20,7 @@ type Options struct {
Payer PublicKey
}
func TransactionWithInstructions(instructions []TransactionInstruction, opt *Options) (*Transaction, error) {
func TransactionWithInstructions(instructions []TransactionInstruction, blockHash PublicKey, opt *Options) (*Transaction, error) {
if len(instructions) == 0 {
return nil, fmt.Errorf("requires at-least one instruction to create a transaction")
}
@ -106,12 +106,16 @@ func TransactionWithInstructions(instructions []TransactionInstruction, opt *Opt
}
message := Message{
AccountKeys: nil,
RecentBlockhash: PublicKey{},
Instructions: nil,
RecentBlockhash: blockHash,
}
accountKeyIndex := map[string]uint8{}
for idx, acc := range finalAccounts {
zlog.Debug("transaction account",
zap.Int("account_index", idx),
zap.Stringer("account_pub_key", acc.PublicKey),
)
message.AccountKeys = append(message.AccountKeys, acc.PublicKey)
accountKeyIndex[acc.PublicKey.String()] = uint8(idx)
if acc.IsSigner {
@ -126,6 +130,11 @@ func TransactionWithInstructions(instructions []TransactionInstruction, opt *Opt
message.Header.NumReadonlyUnsignedAccounts++
}
}
zlog.Debug("message header compiled",
zap.Uint8("num_required_signatures", message.Header.NumRequiredSignatures),
zap.Uint8("num_readonly_signed_accounts", message.Header.NumReadonlySignedAccounts),
zap.Uint8("num_readonly_unsigned_accounts", message.Header.NumReadonlyUnsignedAccounts),
)
for trxIdx, instruction := range instructions {
accounts = instruction.Accounts()

View File

@ -47,7 +47,10 @@ func TestTransactionWithInstructions(t *testing.T) {
},
}
trx, err := TransactionWithInstructions(instructions, nil)
blockhash, err := PublicKeyFromBase58("A9QnpgfhCkmiBSjgBuWk76Wo3HxzxvDopUq9x6UUMmjn")
require.NoError(t, err)
trx, err := TransactionWithInstructions(instructions, blockhash, nil)
require.NoError(t, err)
assert.Equal(t, trx.Message.Header, MessageHeader{
@ -56,6 +59,8 @@ func TestTransactionWithInstructions(t *testing.T) {
NumReadonlyUnsignedAccounts: 3,
})
assert.Equal(t, trx.Message.RecentBlockhash, blockhash)
assert.Equal(t, trx.Message.AccountKeys, []PublicKey{
MustPublicKeyFromBase58("A9QnpgfhCkmiBSjgBuWk76Wo3HxzxvDopUq9x6UUMmjn"),
MustPublicKeyFromBase58("9hFtYBYmBJCVguRYs9pBTWKYAFoKfjYR7zBPpEkVsmD"),