github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/litrpc/walletcmds.go (about) 1 package litrpc 2 3 import ( 4 "fmt" 5 6 "github.com/mit-dci/lit/bech32" 7 "github.com/mit-dci/lit/consts" 8 "github.com/mit-dci/lit/lnutil" 9 "github.com/mit-dci/lit/logging" 10 "github.com/mit-dci/lit/portxo" 11 "github.com/mit-dci/lit/wire" 12 ) 13 14 type TxidsReply struct { 15 Txids []string 16 } 17 type StatusReply struct { 18 Status string 19 } 20 21 type NoArgs struct { 22 // nothin 23 } 24 25 type CoinArgs struct { 26 CoinType uint32 27 } 28 29 // ------------------------- balance 30 // BalReply is the reply when the user asks about their balance. 31 type CoinBalReply struct { 32 CoinType uint32 33 SyncHeight int32 // height this wallet is synced to 34 ChanTotal int64 // total balance in channels 35 TxoTotal int64 // all utxos 36 MatureWitty int64 // confirmed, spendable and witness 37 FeeRate int64 // fee per byte 38 } 39 40 type BalanceReply struct { 41 Balances []CoinBalReply 42 } 43 44 func (r *LitRPC) Balance(args *NoArgs, reply *BalanceReply) error { 45 46 var allTxos portxo.TxoSliceByAmt 47 48 // get all channels 49 qcs, err := r.Node.GetAllQchans() 50 if err != nil { 51 return err 52 } 53 54 for cointype, wal := range r.Node.SubWallet { 55 // will add the balance for this wallet to the full reply 56 var cbr CoinBalReply 57 58 cbr.CoinType = cointype 59 // get wallet height 60 cbr.SyncHeight = wal.CurrentHeight() 61 // also current fee rate 62 cbr.FeeRate = wal.Fee() 63 64 allTxos, err = wal.UtxoDump() 65 if err != nil { 66 return err 67 } 68 69 // ask sub-wallet for balance 70 cbr.TxoTotal = allTxos.Sum() 71 cbr.MatureWitty = allTxos.SumWitness(cbr.SyncHeight) 72 73 // iterate through channels to figure out how much we have 74 for _, q := range qcs { 75 if q.Coin() == cointype && !q.CloseData.Closed { 76 cbr.ChanTotal += q.State.MyAmt 77 } 78 } 79 80 // I thought slices were pointery enough that I could put this line 81 // near the top. Guess not. 82 reply.Balances = append(reply.Balances, cbr) 83 } 84 return nil 85 } 86 87 type TxoInfo struct { 88 OutPoint string 89 Amt int64 90 Height int32 91 Delay int32 92 CoinType string 93 Witty bool 94 95 KeyPath string 96 } 97 type TxoListReply struct { 98 Txos []TxoInfo 99 } 100 101 // TxoList sends back a list of all non-channel utxos 102 func (r *LitRPC) TxoList(args *NoArgs, reply *TxoListReply) error { 103 104 for _, wal := range r.Node.SubWallet { 105 106 walTxos, err := wal.UtxoDump() 107 if err != nil { 108 return err 109 } 110 111 syncHeight := wal.CurrentHeight() 112 113 theseTxos := make([]TxoInfo, len(walTxos)) 114 for i, u := range walTxos { 115 theseTxos[i].OutPoint = u.Op.String() 116 theseTxos[i].Amt = u.Value 117 theseTxos[i].Height = u.Height 118 theseTxos[i].CoinType = wal.Params().Name 119 // show delay before utxo can be spent 120 if u.Seq != 0 { 121 theseTxos[i].Delay = u.Height + int32(u.Seq) - syncHeight 122 } 123 theseTxos[i].Witty = u.Mode&portxo.FlagTxoWitness != 0 124 theseTxos[i].KeyPath = u.KeyGen.String() 125 } 126 127 reply.Txos = append(reply.Txos, theseTxos...) 128 } 129 return nil 130 } 131 132 // ------------------------- send 133 type SendArgs struct { 134 DestAddrs []string 135 Amts []int64 136 } 137 138 func (r *LitRPC) Send(args SendArgs, reply *TxidsReply) error { 139 var err error 140 141 nOutputs := len(args.DestAddrs) 142 if nOutputs < 1 { 143 return fmt.Errorf("No destination address specified") 144 } 145 if nOutputs != len(args.Amts) { 146 return fmt.Errorf("%d addresses but %d amounts specified", 147 nOutputs, len(args.Amts)) 148 } 149 // get cointype for first address. 150 coinType := CoinTypeFromAdr(args.DestAddrs[0]) 151 // make sure we support that coin type 152 wal, ok := r.Node.SubWallet[coinType] 153 if !ok { 154 return fmt.Errorf("no connnected wallet for address %s type %d", 155 args.DestAddrs[0], coinType) 156 } 157 // All addresses must have the same cointype as they all 158 // must to be in the same tx. 159 for _, a := range args.DestAddrs { 160 if CoinTypeFromAdr(a) != coinType { 161 return fmt.Errorf("Coin type mismatch for address %s, %s", 162 a, args.DestAddrs[0]) 163 } 164 } 165 166 txOuts := make([]*wire.TxOut, nOutputs) 167 for i, s := range args.DestAddrs { 168 if args.Amts[i] < consts.MinSendAmt { 169 return fmt.Errorf("Amt %d less than minimum send amount %d", args.Amts[i], consts.MinSendAmt) 170 } 171 172 outScript, err := AdrStringToOutscript(s) 173 if err != nil { 174 return err 175 } 176 177 txOuts[i] = wire.NewTxOut(args.Amts[i], outScript) 178 } 179 180 // we don't care if it's witness or not 181 ops, err := wal.MaybeSend(txOuts, false) 182 if err != nil { 183 return err 184 } 185 186 err = wal.ReallySend(&ops[0].Hash) 187 if err != nil { 188 return err 189 } 190 191 reply.Txids = append(reply.Txids, ops[0].Hash.String()) 192 return nil 193 } 194 195 // ------------------------- sweep 196 type SweepArgs struct { 197 DestAdr string 198 NumTx uint32 199 Drop bool 200 } 201 202 func (r *LitRPC) Sweep(args SweepArgs, reply *TxidsReply) error { 203 // get cointype for first address. 204 coinType := CoinTypeFromAdr(args.DestAdr) 205 // make sure we support that coin type 206 wal, ok := r.Node.SubWallet[coinType] 207 if !ok { 208 return fmt.Errorf("no connnected wallet for address %s type %d", 209 args.DestAdr, coinType) 210 } 211 212 outScript, err := AdrStringToOutscript(args.DestAdr) 213 if err != nil { 214 return err 215 } 216 217 logging.Infof("numtx: %d\n", args.NumTx) 218 if args.NumTx < 1 { 219 return fmt.Errorf("can't send %d txs", args.NumTx) 220 } 221 222 txids, err := wal.Sweep(outScript, args.NumTx) 223 if err != nil { 224 return err 225 } 226 227 for _, txid := range txids { 228 reply.Txids = append(reply.Txids, txid.String()) 229 } 230 231 return nil 232 } 233 234 // ------------------------- fanout 235 type FanArgs struct { 236 DestAdr string 237 NumOutputs uint32 238 AmtPerOutput int64 239 } 240 241 func (r *LitRPC) Fanout(args FanArgs, reply *TxidsReply) error { 242 if args.NumOutputs < 1 { 243 return fmt.Errorf("Must have at least 1 output") 244 } 245 if args.AmtPerOutput < 5000 { 246 return fmt.Errorf("Minimum 5000 per output") 247 } 248 249 // get cointype for first address. 250 coinType := CoinTypeFromAdr(args.DestAdr) 251 // make sure we support that coin type 252 wal, ok := r.Node.SubWallet[coinType] 253 if !ok { 254 return fmt.Errorf("no connnected wallet for address %s type %d", 255 args.DestAdr, coinType) 256 } 257 258 outScript, err := AdrStringToOutscript(args.DestAdr) 259 if err != nil { 260 return err 261 } 262 263 txos := make([]*wire.TxOut, args.NumOutputs) 264 265 for i, _ := range txos { 266 txos[i] = new(wire.TxOut) 267 txos[i].Value = args.AmtPerOutput + int64(i) 268 txos[i].PkScript = outScript 269 } 270 271 // don't care if inputs are witty or not 272 ops, err := wal.MaybeSend(txos, false) 273 if err != nil { 274 return err 275 } 276 err = wal.ReallySend(&ops[0].Hash) 277 if err != nil { 278 return err 279 } 280 281 reply.Txids = append(reply.Txids, ops[0].String()) 282 return nil 283 } 284 285 // set fee 286 type SetFeeArgs struct { 287 Fee int64 288 CoinType uint32 289 } 290 291 // get fee 292 type FeeArgs struct { 293 CoinType uint32 294 } 295 type FeeReply struct { 296 CurrentFee int64 297 } 298 299 // SetFee allows you to set a fee rate for a wallet. 300 func (r *LitRPC) SetFee(args *SetFeeArgs, reply *FeeReply) error { 301 // if cointype is 0, use the node's default coin 302 if args.CoinType == 0 { 303 args.CoinType = r.Node.DefaultCoin 304 } 305 if args.Fee < 0 { 306 return fmt.Errorf("Invalid value for SetFee: %d", args.Fee) 307 } 308 // make sure we support that coin type 309 wal, ok := r.Node.SubWallet[args.CoinType] 310 if !ok { 311 return fmt.Errorf("no connnected wallet for coin type %d", args.CoinType) 312 } 313 reply.CurrentFee = wal.SetFee(args.Fee) 314 return nil 315 } 316 317 // Fee gets the fee rate for a wallet. 318 func (r *LitRPC) GetFee(args *FeeArgs, reply *FeeReply) error { 319 // if cointype is 0, use the node's default coin 320 if args.CoinType == 0 { 321 args.CoinType = r.Node.DefaultCoin 322 } 323 // make sure we support that coin type 324 wal, ok := r.Node.SubWallet[args.CoinType] 325 if !ok { 326 return fmt.Errorf("no connnected wallet for coin type %d", args.CoinType) 327 } 328 reply.CurrentFee = wal.Fee() 329 return nil 330 } 331 332 // ------------------------- address 333 type AddressArgs struct { 334 NumToMake uint32 335 CoinType uint32 336 } 337 338 // TODO Make this contain an array of structures not a structure of arrays. 339 type AddressReply struct { 340 CoinTypes []uint32 341 WitAddresses []string 342 LegacyAddresses []string 343 } 344 345 func (r *LitRPC) Address(args *AddressArgs, reply *AddressReply) error { 346 var allAdr [][20]byte 347 var ctypesPerAdr []uint32 348 349 // if cointype is 0, use the node's default coin 350 if args.CoinType == 0 { 351 args.CoinType = r.Node.DefaultCoin 352 } 353 354 // If you tell it to make 0 new addresses, it sends a list of all the old ones 355 // (from every wallet) 356 if args.NumToMake == 0 { 357 // this gets 20 byte addresses; need to convert them to bech32 / base58 358 // iterate through every wallet 359 for cointype, wal := range r.Node.SubWallet { 360 walAdr, err := wal.AdrDump() 361 if err != nil { 362 return err 363 } 364 365 for _, _ = range walAdr { 366 ctypesPerAdr = append(ctypesPerAdr, cointype) 367 } 368 allAdr = append(allAdr, walAdr...) 369 } 370 } else { 371 // if you have non-zero NumToMake, then cointype matters 372 wal, ok := r.Node.SubWallet[args.CoinType] 373 if !ok { 374 return fmt.Errorf("No wallet of cointype %d linked", args.CoinType) 375 } 376 377 // call NewAdr a bunch of times 378 remaining := args.NumToMake 379 for remaining > 0 { 380 adr, err := wal.NewAdr() 381 if err != nil { 382 return err 383 } 384 allAdr = append(allAdr, adr) 385 ctypesPerAdr = append(ctypesPerAdr, args.CoinType) 386 remaining-- 387 } 388 } 389 390 reply.CoinTypes = make([]uint32, len(allAdr)) 391 reply.WitAddresses = make([]string, len(allAdr)) 392 reply.LegacyAddresses = make([]string, len(allAdr)) 393 394 for i, a := range allAdr { 395 396 // Store the cointype 397 reply.CoinTypes[i] = ctypesPerAdr[i] 398 399 // convert 20 byte array to old address 400 param := r.Node.SubWallet[ctypesPerAdr[i]].Params() 401 402 oldadr := lnutil.OldAddressFromPKH(a, param.PubKeyHashAddrID) 403 reply.LegacyAddresses[i] = oldadr 404 405 // convert 20-byte PKH to a bech32 segwit v0 address 406 bech32adr, err := bech32.SegWitV0Encode(param.Bech32Prefix, a[:]) 407 408 if err != nil { 409 return err 410 } 411 reply.WitAddresses[i] = bech32adr 412 } 413 414 return nil 415 } 416 417 // More human-readable replies 418 func (r *LitRPC) GetAddresses(args *NoArgs, reply *AddressReply) error { 419 420 // return index 421 ri := 0 422 423 cts := make([]uint32, 0) 424 was := make([]string, 0) 425 las := make([]string, 0) 426 427 for cointype, wal := range r.Node.SubWallet { 428 429 walAdr, err := wal.AdrDump() 430 if err != nil { 431 panic("this should never happen, I don't think") 432 } 433 434 for _, pubkey := range walAdr { 435 436 param := r.Node.SubWallet[cointype].Params() 437 cts = append(cts, cointype) 438 439 b32, err := bech32.SegWitV0Encode(param.Bech32Prefix, pubkey[:]) 440 if err != nil { 441 panic("error encoding bech32 address") 442 } 443 444 was = append(was, b32) 445 las = append(las, lnutil.OldAddressFromPKH(pubkey, param.PubKeyHashAddrID)) 446 447 ri++ 448 } 449 450 } 451 452 reply.CoinTypes = cts 453 reply.WitAddresses = was 454 reply.LegacyAddresses = las 455 456 return nil 457 } 458 459 //func oldAddressPubKeyHash(pkHash []byte, netID byte) (string, error) { 460 // // Check for a valid pubkey hash length. 461 // if len(pkHash) != ripemd160.Size { 462 // return "", errors.New("pkHash must be 20 bytes") 463 // } 464 // return base58.CheckEncode(pkHash, netID), nil 465 //} 466 467 type ClaimHTLCArgs struct { 468 R [16]byte 469 } 470 471 func (r *LitRPC) ClaimHTLC(args *ClaimHTLCArgs, reply *TxidsReply) error { 472 txids, err := r.Node.ClaimHTLC(args.R) 473 if err != nil { 474 return err 475 } 476 477 reply.Txids = make([]string, 0) 478 for _, txid := range txids { 479 reply.Txids = append(reply.Txids, fmt.Sprintf("%x", txid)) 480 } 481 482 return nil 483 }