1// SPDX-License-Identifier: ISC 2// Copyright (c) 2014-2020 Bitmark Inc. 3// Use of this source code is governed by an ISC 4// license that can be found in the LICENSE file. 5 6package main 7 8import ( 9 "encoding/hex" 10 "fmt" 11 "os" 12 "strconv" 13 "strings" 14 15 "github.com/urfave/cli" 16 17 "github.com/bitmark-inc/bitmarkd/account" 18 "github.com/bitmark-inc/bitmarkd/command/bitmark-cli/configuration" 19 "github.com/bitmark-inc/bitmarkd/currency" 20 "github.com/bitmark-inc/bitmarkd/fault" 21) 22 23// identity is required, but not check the config file 24func checkName(name string) (string, error) { 25 if "" == name { 26 return "", fault.IdentityNameIsRequired 27 } 28 29 // account names cannot be identities to prevent confusion 30 _, err := account.AccountFromBase58(name) 31 if nil == err { 32 return "", fault.InvalidIdentityName 33 } 34 35 return name, nil 36} 37 38// check for non-blank file name 39func checkFileName(fileName string) (string, error) { 40 if "" == fileName { 41 return "", fault.FileNameIsRequired 42 } 43 44 return fileName, nil 45} 46 47// connect is required. 48func checkConnect(connect string) (string, error) { 49 connect = strings.TrimSpace(connect) 50 if "" == connect { 51 return "", fault.ConnectIsRequired 52 } 53 54 // XXX: We should not need to []string{} variable s 55 //nolint:ignore SA4006 ignore this lint till somebody revisit this code 56 s := []string{} 57 if '[' == connect[0] { // IPv6 58 s = strings.Split(connect, "]:") 59 } else { // Ipv4 or host 60 s = strings.Split(connect, ":") 61 } 62 if 2 != len(s) { 63 return "", fault.ConnectRequiresPortNumberSuffix 64 } 65 66 port, err := strconv.Atoi(s[1]) 67 if nil != err || port < 1 || port > 65535 { 68 return "", fault.InvalidPortNumber 69 } 70 71 return connect, nil 72} 73 74// description is required 75func checkDescription(description string) (string, error) { 76 if "" == description { 77 return "", fault.DescriptionIsRequired 78 } 79 80 return description, nil 81} 82 83// asset fingerprint is required field 84func checkAssetFingerprint(fingerprint string) (string, error) { 85 if "" == fingerprint { 86 return "", fault.AssetFingerprintIsRequired 87 } 88 return fingerprint, nil 89} 90 91// asset metadata is required field 92func checkAssetMetadata(meta string) (string, error) { 93 if "" == meta { 94 return "", fault.AssetMetadataIsRequired 95 } 96 meta, err := strconv.Unquote(`"` + meta + `"`) 97 if nil != err { 98 return "", err 99 } 100 if 1 == len(strings.Split(meta, "\u0000"))%2 { 101 return "", fault.AssetMetadataMustBeMap 102 } 103 return meta, nil 104} 105 106// txid is required field ensure 32 hex bytes 107func checkTxId(txId string) (string, error) { 108 if 64 != len(txId) { 109 return "", fault.TransactionIdIsRequired 110 } 111 _, err := hex.DecodeString(txId) 112 if nil != err { 113 return "", err 114 115 } 116 return txId, nil 117} 118 119// transfer tx is required field 120func checkTransferTx(txId string) (string, error) { 121 if "" == txId { 122 return "", fault.TransactionHexDataIsRequired 123 } 124 125 return txId, nil 126} 127 128// make sure a seed can be decoded 129// strip the "SEED:" prefix if given 130func checkSeed(seed string, new bool, testnet bool) (string, error) { 131 132 if new && "" == seed { 133 var err error 134 seed, err = account.NewBase58EncodedSeedV2(testnet) 135 if nil != err { 136 return "", err 137 } 138 } 139 seed = strings.TrimPrefix(seed, "SEED:") 140 141 // failed to get a seed 142 if "" == seed { 143 return "", fault.IncompatibleOptions 144 } 145 146 // ensure can decode 147 _, err := account.PrivateKeyFromBase58Seed(seed) 148 if nil != err { 149 return "", err 150 } 151 return seed, nil 152} 153 154// get decrypted identity - prompts for password or uses agent 155// only use owner to sign things 156func checkOwnerWithPasswordPrompt(name string, config *configuration.Configuration, c *cli.Context) (string, *configuration.Private, error) { 157 if "" == name { 158 name = config.DefaultIdentity 159 } 160 161 var err error 162 163 // get global password items 164 agent := c.GlobalString("use-agent") 165 clearCache := c.GlobalBool("zero-agent-cache") 166 password := c.GlobalString("password") 167 168 // check owner password 169 if "" != agent { 170 password, err = passwordFromAgent(name, "Password for bitmark-cli", agent, clearCache) 171 if nil != err { 172 return "", nil, err 173 } 174 } else if "" == password { 175 password, err = promptPassword(name) 176 if nil != err { 177 return "", nil, err 178 } 179 180 } 181 owner, err := config.Private(password, name) 182 if nil != err { 183 return "", nil, err 184 } 185 return name, owner, nil 186} 187 188// recipient is required field convert to an account 189// used for any non-signing account process (e.g. provenance listing) 190func checkRecipient(c *cli.Context, name string, config *configuration.Configuration) (string, *account.Account, error) { 191 recipient := c.String(name) 192 if "" == recipient { 193 return "", nil, fmt.Errorf("%s is required", name) 194 } 195 196 newOwner, err := config.Account(recipient) 197 if nil != err { 198 return "", nil, err 199 } 200 201 return recipient, newOwner, nil 202} 203 204// coin address is a required field 205func checkCoinAddress(c currency.Currency, address string, testnet bool) (string, error) { 206 if "" == address { 207 return "", fault.CurrencyAddressIsRequired 208 } 209 err := c.ValidateAddress(address, testnet) 210 return address, err 211} 212 213// signature is required field ensure 64 hex bytes 214func checkSignature(s string) ([]byte, error) { 215 if 128 != len(s) { 216 return nil, fault.TransactionIdIsRequired 217 } 218 h, err := hex.DecodeString(s) 219 if nil != err { 220 return nil, err 221 222 } 223 return h, nil 224} 225 226// check if file exists 227func checkFileExists(name string) (bool, error) { 228 s, err := os.Stat(name) 229 if nil != err { 230 return false, err 231 } 232 return s.IsDir(), nil 233} 234