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 share 7 8import ( 9 "golang.org/x/time/rate" 10 11 "github.com/bitmark-inc/bitmarkd/account" 12 "github.com/bitmark-inc/bitmarkd/fault" 13 "github.com/bitmark-inc/bitmarkd/merkle" 14 "github.com/bitmark-inc/bitmarkd/messagebus" 15 "github.com/bitmark-inc/bitmarkd/mode" 16 "github.com/bitmark-inc/bitmarkd/pay" 17 "github.com/bitmark-inc/bitmarkd/reservoir" 18 "github.com/bitmark-inc/bitmarkd/rpc/owner" 19 "github.com/bitmark-inc/bitmarkd/rpc/ratelimit" 20 "github.com/bitmark-inc/bitmarkd/transactionrecord" 21 "github.com/bitmark-inc/logger" 22) 23 24// Share 25// -------- 26 27const ( 28 rateLimitShare = 200 29 rateBurstShare = 100 30) 31 32// Share - type for RPC 33type Share struct { 34 Log *logger.L 35 Limiter *rate.Limiter 36 IsNormalMode func(mode.Mode) bool 37 Rsvr reservoir.Reservoir 38} 39 40func New(log *logger.L, isNormalMode func(mode.Mode) bool, rsvr reservoir.Reservoir) *Share { 41 return &Share{ 42 Log: log, 43 Limiter: rate.NewLimiter(rateLimitShare, rateBurstShare), 44 IsNormalMode: isNormalMode, 45 Rsvr: rsvr, 46 } 47} 48 49// Create a share with initial balance 50// ----------------------------------- 51 52// CreateReply - results from creating a share 53type CreateReply struct { 54 TxId merkle.Digest `json:"txId"` 55 ShareId merkle.Digest `json:"shareId"` 56 PayId pay.PayId `json:"payId"` 57 Payments map[string]transactionrecord.PaymentAlternative `json:"payments"` 58} 59 60// Create - create fractional bitmark 61func (share *Share) Create(bmfr *transactionrecord.BitmarkShare, reply *CreateReply) error { 62 63 if err := ratelimit.Limit(share.Limiter); nil != err { 64 return err 65 } 66 67 log := share.Log 68 69 log.Infof("Share.Create: %+v", bmfr) 70 71 if !share.IsNormalMode(mode.Normal) { 72 return fault.NotAvailableDuringSynchronise 73 } 74 75 // save transfer/check for duplicate 76 stored, duplicate, err := share.Rsvr.StoreTransfer(bmfr) 77 if nil != err { 78 return err 79 } 80 81 payId := stored.Id 82 txId := stored.TxId 83 shareId := stored.IssueTxId 84 packed := stored.Packed 85 86 log.Debugf("id: %v", txId) 87 log.Debugf("share id: %v", shareId) 88 89 reply.TxId = txId 90 reply.ShareId = shareId 91 reply.PayId = payId 92 reply.Payments = make(map[string]transactionrecord.PaymentAlternative) 93 94 for _, payment := range stored.Payments { 95 c := payment[0].Currency.String() 96 reply.Payments[c] = payment 97 } 98 99 // announce transaction block to other peers 100 if !duplicate { 101 messagebus.Bus.Broadcast.Send("transfer", packed) 102 } 103 104 return nil 105} 106 107// Get share balance 108// -------------------- 109 110// BalanceArguments - arguments for RPC 111type BalanceArguments struct { 112 Owner *account.Account `json:"owner"` // base58 113 ShareId merkle.Digest `json:"shareId"` 114 Count int `json:"count"` // number of records 115} 116 117// BalanceReply - balances of shares belonging to an account 118type BalanceReply struct { 119 Balances []reservoir.BalanceInfo `json:"balances"` 120} 121 122// Balance - list balances for an account 123func (share *Share) Balance(arguments *BalanceArguments, reply *BalanceReply) error { 124 125 if err := ratelimit.Limit(share.Limiter); nil != err { 126 return err 127 } 128 129 log := share.Log 130 131 log.Infof("Share.Balance: %+v", arguments) 132 133 if nil == arguments || nil == arguments.Owner { 134 return fault.InvalidItem 135 } 136 137 count := arguments.Count 138 if count <= 0 { 139 return fault.InvalidCount 140 } 141 if count > owner.MaximumBitmarksCount { 142 count = owner.MaximumBitmarksCount 143 } 144 145 if !share.IsNormalMode(mode.Normal) { 146 return fault.NotAvailableDuringSynchronise 147 } 148 149 if arguments.Owner.IsTesting() != mode.IsTesting() { 150 return fault.WrongNetworkForPublicKey 151 } 152 153 result, err := share.Rsvr.ShareBalance(arguments.Owner, arguments.ShareId, arguments.Count) 154 if nil != err { 155 return err 156 } 157 158 reply.Balances = result 159 160 return nil 161} 162 163// Grant some shares 164// ----------------- 165 166// GrantReply - result of granting some shares to another account 167type GrantReply struct { 168 Remaining uint64 `json:"remaining"` 169 TxId merkle.Digest `json:"txId"` 170 PayId pay.PayId `json:"payId"` 171 Payments map[string]transactionrecord.PaymentAlternative `json:"payments"` 172} 173 174// Grant - grant a number of shares to another account 175func (share *Share) Grant(arguments *transactionrecord.ShareGrant, reply *GrantReply) error { 176 177 if err := ratelimit.Limit(share.Limiter); nil != err { 178 return err 179 } 180 181 log := share.Log 182 183 log.Infof("Share.Grant: %+v", arguments) 184 185 if nil == arguments || nil == arguments.Owner || nil == arguments.Recipient { 186 return fault.InvalidItem 187 } 188 189 if arguments.Quantity < 1 { 190 return fault.ShareQuantityTooSmall 191 } 192 193 if !share.IsNormalMode(mode.Normal) { 194 return fault.NotAvailableDuringSynchronise 195 } 196 197 if arguments.Owner.IsTesting() != mode.IsTesting() { 198 return fault.WrongNetworkForPublicKey 199 } 200 201 if arguments.Recipient.IsTesting() != mode.IsTesting() { 202 return fault.WrongNetworkForPublicKey 203 } 204 205 // save transfer/check for duplicate 206 stored, duplicate, err := share.Rsvr.StoreGrant(arguments) 207 if nil != err { 208 return err 209 } 210 211 // only first result needs to be considered 212 payId := stored.Id 213 txId := stored.TxId 214 packed := stored.Packed 215 216 log.Debugf("id: %v", txId) 217 reply.Remaining = stored.Remaining 218 reply.TxId = txId 219 reply.PayId = payId 220 reply.Payments = make(map[string]transactionrecord.PaymentAlternative) 221 222 for _, payment := range stored.Payments { 223 c := payment[0].Currency.String() 224 reply.Payments[c] = payment 225 } 226 227 // announce transaction block to other peers 228 if !duplicate { 229 messagebus.Bus.Broadcast.Send("transfer", packed) 230 } 231 232 return nil 233} 234 235// Swap some shares 236// ------------------- 237 238// SwapReply - result of a share swap 239type SwapReply struct { 240 RemainingOne uint64 `json:"remainingOne"` 241 RemainingTwo uint64 `json:"remainingTwo"` 242 TxId merkle.Digest `json:"txId"` 243 PayId pay.PayId `json:"payId"` 244 Payments map[string]transactionrecord.PaymentAlternative `json:"payments"` 245} 246 247// Swap - atomically swap shares between accounts 248func (share *Share) Swap(arguments *transactionrecord.ShareSwap, reply *SwapReply) error { 249 250 if err := ratelimit.Limit(share.Limiter); nil != err { 251 return err 252 } 253 254 log := share.Log 255 256 log.Infof("Share.Swap: %+v", arguments) 257 258 if nil == arguments || nil == arguments.OwnerOne || nil == arguments.OwnerTwo { 259 return fault.InvalidItem 260 } 261 262 if arguments.QuantityOne < 1 || arguments.QuantityTwo < 1 { 263 return fault.ShareQuantityTooSmall 264 } 265 266 if !share.IsNormalMode(mode.Normal) { 267 return fault.NotAvailableDuringSynchronise 268 } 269 270 if arguments.OwnerOne.IsTesting() != mode.IsTesting() { 271 return fault.WrongNetworkForPublicKey 272 } 273 274 if arguments.OwnerTwo.IsTesting() != mode.IsTesting() { 275 return fault.WrongNetworkForPublicKey 276 } 277 278 // save transfer/check for duplicate 279 stored, duplicate, err := share.Rsvr.StoreSwap(arguments) 280 if nil != err { 281 return err 282 } 283 284 // only first result needs to be considered 285 payId := stored.Id 286 txId := stored.TxId 287 packed := stored.Packed 288 289 log.Debugf("id: %v", txId) 290 reply.RemainingOne = stored.RemainingOne 291 reply.RemainingTwo = stored.RemainingTwo 292 reply.TxId = txId 293 reply.PayId = payId 294 reply.Payments = make(map[string]transactionrecord.PaymentAlternative) 295 296 for _, payment := range stored.Payments { 297 c := payment[0].Currency.String() 298 reply.Payments[c] = payment 299 } 300 301 // announce transaction block to other peers 302 if !duplicate { 303 messagebus.Bus.Broadcast.Send("transfer", packed) 304 } 305 306 return nil 307} 308