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 rpccalls 7 8import ( 9 "encoding/hex" 10 11 "golang.org/x/crypto/ed25519" 12 13 "github.com/bitmark-inc/bitmarkd/account" 14 "github.com/bitmark-inc/bitmarkd/command/bitmark-cli/configuration" 15 "github.com/bitmark-inc/bitmarkd/fault" 16 "github.com/bitmark-inc/bitmarkd/merkle" 17 "github.com/bitmark-inc/bitmarkd/pay" 18 "github.com/bitmark-inc/bitmarkd/rpc/bitmark" 19 "github.com/bitmark-inc/bitmarkd/transactionrecord" 20) 21 22// TransferData - data for a transfer request 23type TransferData struct { 24 Owner *configuration.Private 25 NewOwner *account.Account 26 TxId string 27} 28 29// TransferCountersignData - countersign data request 30type TransferCountersignData struct { 31 Transfer string 32 NewOwner *configuration.Private 33} 34 35// TransferReply - JSON data to output after transfer completes 36type TransferReply struct { 37 TransferId merkle.Digest `json:"transferId"` 38 BitmarkId merkle.Digest `json:"bitmarkId"` 39 PayId pay.PayId `json:"payId"` 40 Payments map[string]transactionrecord.PaymentAlternative `json:"payments"` 41 Commands map[string]string `json:"commands,omitempty"` 42} 43 44// TransferSingleSignedReply - response to single signature 45type TransferSingleSignedReply struct { 46 Identity string `json:"identity"` 47 Transfer string `json:"transfer"` 48} 49 50// Transfer - perform a bitmark transfer 51func (client *Client) Transfer(transferConfig *TransferData) (*TransferReply, error) { 52 53 var link merkle.Digest 54 err := link.UnmarshalText([]byte(transferConfig.TxId)) 55 if nil != err { 56 return nil, err 57 } 58 59 transfer, err := makeTransferUnratified(client.testnet, link, transferConfig.Owner, transferConfig.NewOwner) 60 if nil != err { 61 return nil, err 62 } 63 if nil == transfer { 64 return nil, fault.MakeTransferFailed 65 } 66 67 client.printJson("Transfer Request", transfer) 68 69 var reply bitmark.TransferReply 70 err = client.client.Call("Bitmark.Transfer", transfer, &reply) 71 if err != nil { 72 return nil, err 73 } 74 75 tpid, err := reply.PayId.MarshalText() 76 if nil != err { 77 return nil, err 78 } 79 80 commands := make(map[string]string) 81 for _, payment := range reply.Payments { 82 currency := payment[0].Currency 83 commands[currency.String()] = paymentCommand(client.testnet, currency, string(tpid), payment) 84 } 85 86 client.printJson("Transfer Reply", reply) 87 88 // make response 89 response := TransferReply{ 90 TransferId: reply.TxId, 91 BitmarkId: reply.BitmarkId, 92 PayId: reply.PayId, 93 Payments: reply.Payments, 94 Commands: commands, 95 } 96 97 return &response, nil 98} 99 100// SingleSignedTransfer - perform a single signed transfer 101func (client *Client) SingleSignedTransfer(transferConfig *TransferData) (*TransferSingleSignedReply, error) { 102 103 var link merkle.Digest 104 err := link.UnmarshalText([]byte(transferConfig.TxId)) 105 if nil != err { 106 return nil, err 107 } 108 109 packed, transfer, err := makeTransferOneSignature(client.testnet, link, transferConfig.Owner, transferConfig.NewOwner) 110 if nil != err { 111 return nil, err 112 } 113 if nil == transfer { 114 return nil, fault.MakeTransferFailed 115 } 116 117 client.printJson("Transfer Request", transfer) 118 119 response := TransferSingleSignedReply{ 120 Identity: transfer.GetOwner().String(), 121 Transfer: hex.EncodeToString(packed), 122 } 123 124 return &response, nil 125} 126 127// CountersignTransfer - perform as countersigned transfer 128func (client *Client) CountersignTransfer(transfer *transactionrecord.BitmarkTransferCountersigned) (*TransferReply, error) { 129 130 client.printJson("Transfer Request", transfer) 131 132 var reply bitmark.TransferReply 133 err := client.client.Call("Bitmark.Transfer", transfer, &reply) 134 if nil != err { 135 return nil, err 136 } 137 138 tpid, err := reply.PayId.MarshalText() 139 if nil != err { 140 return nil, err 141 } 142 143 commands := make(map[string]string) 144 for _, payment := range reply.Payments { 145 currency := payment[0].Currency 146 commands[currency.String()] = paymentCommand(client.testnet, currency, string(tpid), payment) 147 } 148 149 client.printJson("Transfer Reply", reply) 150 151 // make response 152 response := TransferReply{ 153 TransferId: reply.TxId, 154 BitmarkId: reply.BitmarkId, 155 PayId: reply.PayId, 156 Payments: reply.Payments, 157 Commands: commands, 158 } 159 160 return &response, nil 161} 162 163func makeTransferUnratified(testnet bool, link merkle.Digest, owner *configuration.Private, newOwner *account.Account) (transactionrecord.BitmarkTransfer, error) { 164 165 r := transactionrecord.BitmarkTransferUnratified{ 166 Link: link, 167 Owner: newOwner, 168 Signature: nil, 169 } 170 171 ownerAccount := owner.PrivateKey.Account() 172 173 // pack without signature 174 packed, err := r.Pack(ownerAccount) 175 if nil == err { 176 return nil, fault.MakeTransferFailed 177 } else if fault.InvalidSignature != err { 178 return nil, err 179 } 180 181 // attach signature 182 signature := ed25519.Sign(owner.PrivateKey.PrivateKeyBytes(), packed) 183 r.Signature = signature[:] 184 185 // check that signature is correct by packing again 186 _, err = r.Pack(ownerAccount) 187 if nil != err { 188 return nil, err 189 } 190 return &r, nil 191} 192 193func makeTransferOneSignature(testnet bool, link merkle.Digest, owner *configuration.Private, newOwner *account.Account) ([]byte, transactionrecord.BitmarkTransfer, error) { 194 195 r := transactionrecord.BitmarkTransferCountersigned{ 196 Link: link, 197 Owner: newOwner, 198 Signature: nil, 199 Countersignature: nil, 200 } 201 202 ownerAccount := owner.PrivateKey.Account() 203 204 // pack without signature 205 packed, err := r.Pack(ownerAccount) 206 if nil == err { 207 return nil, nil, fault.MakeTransferFailed 208 } else if fault.InvalidSignature != err { 209 return nil, nil, err 210 } 211 212 // attach signature 213 signature := ed25519.Sign(owner.PrivateKey.PrivateKeyBytes(), packed) 214 r.Signature = signature[:] 215 216 // include first signature by packing again 217 packed, err = r.Pack(ownerAccount) 218 if nil == err { 219 return nil, nil, fault.MakeTransferFailed 220 } else if fault.InvalidSignature != err { 221 return nil, nil, err 222 } 223 return packed, &r, nil 224} 225