github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/cryptocurrency.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package engine 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 ) 13 14 type CryptocurrencyEngine struct { 15 libkb.Contextified 16 arg keybase1.RegisterAddressArg 17 res keybase1.RegisterAddressRes 18 } 19 20 func NewCryptocurrencyEngine(g *libkb.GlobalContext, arg keybase1.RegisterAddressArg) *CryptocurrencyEngine { 21 if arg.SigVersion == nil || libkb.SigVersion(*arg.SigVersion) == libkb.KeybaseNullSigVersion { 22 tmp := keybase1.SigVersion(libkb.GetDefaultSigVersion(g)) 23 arg.SigVersion = &tmp 24 } 25 return &CryptocurrencyEngine{ 26 Contextified: libkb.NewContextified(g), 27 arg: arg, 28 } 29 } 30 31 func (e *CryptocurrencyEngine) Name() string { 32 return "Cryptocurrency" 33 } 34 35 func (e *CryptocurrencyEngine) Prereqs() Prereqs { 36 return Prereqs{ 37 Device: true, 38 } 39 } 40 41 func (e *CryptocurrencyEngine) RequiredUIs() []libkb.UIKind { 42 return []libkb.UIKind{ 43 libkb.LogUIKind, 44 libkb.SecretUIKind, 45 } 46 } 47 48 func (e *CryptocurrencyEngine) SubConsumers() []libkb.UIConsumer { 49 return []libkb.UIConsumer{} 50 } 51 52 func normalizeAddress(address string) string { 53 if len(address) <= 3 { 54 return address 55 } 56 switch strings.ToLower(address)[0:3] { 57 case "bc1", "zs1": 58 // bech32 addresses require that cases not be mixed 59 // (an error which will be caught downstream in validation), 60 // but the spec is otherwise case-insensitive. so if it's 61 // passed in as uppercase, then we downcase it right away to 62 // ensure that everything we do with the address (e.g. sign it into 63 // a sigchain link) will be consistent and checksum correctly. 64 if strings.ToUpper(address) == address { 65 return strings.ToLower(address) 66 } 67 } 68 return address 69 } 70 71 func (e *CryptocurrencyEngine) Run(m libkb.MetaContext) (err error) { 72 m.G().LocalSigchainGuard().Set(m.Ctx(), "CryptocurrencyEngine") 73 defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "CryptocurrencyEngine") 74 75 defer m.Trace("CryptocurrencyEngine", &err)() 76 77 var typ libkb.CryptocurrencyType 78 e.arg.Address = normalizeAddress(e.arg.Address) 79 typ, _, err = libkb.CryptocurrencyParseAndCheck(e.arg.Address) 80 if err != nil { 81 return libkb.InvalidAddressError{Msg: err.Error()} 82 } 83 84 family := typ.ToCryptocurrencyFamily() 85 if len(e.arg.WantedFamily) > 0 && e.arg.WantedFamily != string(family) { 86 return libkb.InvalidAddressError{Msg: fmt.Sprintf("wanted coin type %q, but got %q", e.arg.WantedFamily, family)} 87 } 88 89 me, err := libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m)) 90 if err != nil { 91 return err 92 } 93 94 cryptocurrencyLink := me.IDTable().ActiveCryptocurrency(typ.ToCryptocurrencyFamily()) 95 if cryptocurrencyLink != nil && !e.arg.Force { 96 return libkb.ExistsError{Msg: string(family)} 97 } 98 var sigIDToRevoke keybase1.SigID 99 var lease *libkb.Lease 100 var merkleRoot *libkb.MerkleRoot 101 if cryptocurrencyLink != nil { 102 sigIDToRevoke = cryptocurrencyLink.GetSigID() 103 lease, merkleRoot, err = libkb.RequestDowngradeLeaseBySigIDs(m.Ctx(), m.G(), []keybase1.SigID{sigIDToRevoke}) 104 if err != nil { 105 return err 106 } 107 } 108 109 ska := libkb.SecretKeyArg{ 110 Me: me, 111 KeyType: libkb.DeviceSigningKeyType, 112 } 113 sigKey, err := m.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska, "to register a cryptocurrency address")) 114 if err != nil { 115 return err 116 } 117 if err = sigKey.CheckSecretKey(); err != nil { 118 return err 119 } 120 sigVersion := libkb.SigVersion(*e.arg.SigVersion) 121 claim, err := me.CryptocurrencySig(m, sigKey, e.arg.Address, typ, sigIDToRevoke, merkleRoot, sigVersion) 122 if err != nil { 123 return err 124 } 125 126 sigInner, err := claim.J.Marshal() 127 if err != nil { 128 return err 129 } 130 131 sig, _, linkID, err := libkb.MakeSig( 132 m, 133 sigKey, 134 libkb.LinkTypeCryptocurrency, 135 sigInner, 136 libkb.SigHasRevokes(len(sigIDToRevoke) > 0), 137 keybase1.SeqType_PUBLIC, 138 libkb.SigIgnoreIfUnsupported(false), 139 me, 140 sigVersion, 141 ) 142 143 if err != nil { 144 return err 145 } 146 147 kid := sigKey.GetKID() 148 args := libkb.HTTPArgs{ 149 "sig": libkb.S{Val: sig}, 150 "signing_kid": libkb.S{Val: kid.String()}, 151 "is_remote_proof": libkb.B{Val: false}, 152 "type": libkb.S{Val: "cryptocurrency"}, 153 } 154 if lease != nil { 155 args["downgrade_lease_id"] = libkb.S{Val: string(lease.LeaseID)} 156 } 157 158 if sigVersion == libkb.KeybaseSignatureV2 { 159 args["sig_inner"] = libkb.S{Val: string(sigInner)} 160 } 161 162 _, err = m.G().API.Post(m, libkb.APIArg{ 163 Endpoint: "sig/post", 164 SessionType: libkb.APISessionTypeREQUIRED, 165 Args: args, 166 }) 167 if err != nil { 168 return err 169 } 170 171 err = libkb.MerkleCheckPostedUserSig(m, me.GetUID(), claim.Seqno, linkID) 172 if err != nil { 173 return err 174 } 175 176 e.res.Family = string(family) 177 e.res.Type = typ.String() 178 179 return nil 180 } 181 182 func (e *CryptocurrencyEngine) Result() keybase1.RegisterAddressRes { 183 return e.res 184 }