github.com/deso-protocol/core@v1.2.9/lib/block_view_bitcoin.go (about) 1 package lib 2 3 import ( 4 "fmt" 5 "github.com/btcsuite/btcd/btcec" 6 "github.com/btcsuite/btcd/chaincfg" 7 "github.com/btcsuite/btcd/txscript" 8 "github.com/btcsuite/btcd/wire" 9 "github.com/btcsuite/btcutil" 10 "github.com/pkg/errors" 11 "math" 12 "math/big" 13 ) 14 15 // The blockchain used to store the USD to BTC exchange rate in bav.USDCentsPerBitcoin, which was set by a 16 // UPDATE_BITCOIN_USD_EXCHANGE_RATE txn, but has since moved to the GlobalParamsEntry, which is set by a 17 // UPDATE_GLOBAL_PARAMS txn. 18 func (bav *UtxoView) GetCurrentUSDCentsPerBitcoin() uint64 { 19 usdCentsPerBitcoin := bav.USDCentsPerBitcoin 20 if bav.GlobalParamsEntry.USDCentsPerBitcoin != 0 { 21 usdCentsPerBitcoin = bav.GlobalParamsEntry.USDCentsPerBitcoin 22 } 23 return usdCentsPerBitcoin 24 } 25 26 func (bav *UtxoView) _existsBitcoinTxIDMapping(bitcoinBurnTxID *BlockHash) bool { 27 // If an entry exists in the in-memory map, return the value of that mapping. 28 mapValue, existsMapValue := bav.BitcoinBurnTxIDs[*bitcoinBurnTxID] 29 if existsMapValue { 30 return mapValue 31 } 32 33 // If we get here it means no value exists in our in-memory map. In this case, 34 // defer to the db. If a mapping exists in the db, return true. If not, return 35 // false. Either way, save the value to the in-memory view mapping got later. 36 dbHasMapping := DbExistsBitcoinBurnTxID(bav.Handle, bitcoinBurnTxID) 37 bav.BitcoinBurnTxIDs[*bitcoinBurnTxID] = dbHasMapping 38 return dbHasMapping 39 } 40 41 func (bav *UtxoView) _setBitcoinBurnTxIDMappings(bitcoinBurnTxID *BlockHash) { 42 bav.BitcoinBurnTxIDs[*bitcoinBurnTxID] = true 43 } 44 45 func (bav *UtxoView) _deleteBitcoinBurnTxIDMappings(bitcoinBurnTxID *BlockHash) { 46 bav.BitcoinBurnTxIDs[*bitcoinBurnTxID] = false 47 } 48 49 func ExtractBitcoinPublicKeyFromBitcoinTransactionInputs( 50 bitcoinTransaction *wire.MsgTx, btcdParams *chaincfg.Params) ( 51 _publicKey *btcec.PublicKey, _err error) { 52 53 for _, input := range bitcoinTransaction.TxIn { 54 // P2PKH follows the form: <sig len> <sig> <pubKeyLen> <pubKey> 55 if len(input.SignatureScript) == 0 { 56 continue 57 } 58 sigLen := input.SignatureScript[0] 59 pubKeyStart := sigLen + 2 60 pubKeyBytes := input.SignatureScript[pubKeyStart:] 61 addr, err := btcutil.NewAddressPubKey(pubKeyBytes, btcdParams) 62 if err != nil { 63 continue 64 } 65 66 // If we were able to successfully decode the bytes into a public key, return it. 67 if addr.PubKey() != nil { 68 return addr.PubKey(), nil 69 } 70 71 // If we get here it means we could not extract a public key from this 72 // particular input. This is OK as long as we can find a public key in 73 // one of the other inputs. 74 } 75 76 // If we get here it means we went through all the inputs and were not able to 77 // successfully decode a public key from the inputs. Error in this case. 78 return nil, fmt.Errorf("ExtractBitcoinPublicKeyFromBitcoinTransactionInputs: " + 79 "No valid public key found after scanning all input signature scripts") 80 } 81 82 func _computeBitcoinBurnOutput(bitcoinTransaction *wire.MsgTx, bitcoinBurnAddress string, 83 btcdParams *chaincfg.Params) (_burnedOutputSatoshis int64, _err error) { 84 85 totalBurnedOutput := int64(0) 86 for _, output := range bitcoinTransaction.TxOut { 87 class, addresses, _, err := txscript.ExtractPkScriptAddrs( 88 output.PkScript, btcdParams) 89 if err != nil { 90 // If we hit an error processing an output just let it slide. We only honor 91 // P2PKH transactions and even this we do on a best-effort basis. 92 // 93 // TODO: Run this over a few Bitcoin blocks to see what its errors look like 94 // so we can catch them here. 95 continue 96 } 97 // We only allow P2PK and P2PKH transactions to be counted as burns. Allowing 98 // anything else would require making this logic more sophisticated. Additionally, 99 // limiting the gamut of possible transactions protects us from weird attacks 100 // whereby someone could make us think that some Bitcoin was burned when really 101 // it's just some fancy script that fools us into thinking that. 102 if !(class == txscript.PubKeyTy || class == txscript.PubKeyHashTy) { 103 continue 104 } 105 // We only process outputs if they have a single address in them, which should 106 // be the case anyway given the classes we're limiting ourselves to above. 107 if len(addresses) != 1 { 108 continue 109 } 110 111 // At this point we're confident that we're dealing with a nice vanilla 112 // P2PK or P2PKH output that contains just one address that its making a 113 // simple payment to. 114 115 // Extract the address and add its output to the total if it happens to be 116 // equal to the burn address. 117 outputAddress := addresses[0] 118 if outputAddress.EncodeAddress() == bitcoinBurnAddress { 119 // Check for overflow just in case. 120 if output.Value < 0 || totalBurnedOutput > math.MaxInt64-output.Value { 121 return 0, fmt.Errorf("_computeBitcoinBurnOutput: output value %d would "+ 122 "overflow totalBurnedOutput %d; this should never happen", 123 output.Value, totalBurnedOutput) 124 } 125 totalBurnedOutput += output.Value 126 } 127 } 128 129 return totalBurnedOutput, nil 130 } 131 132 func (bav *UtxoView) _connectBitcoinExchange( 133 txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) ( 134 _totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) { 135 136 if bav.Params.DeflationBombBlockHeight != 0 && 137 uint64(blockHeight) >= bav.Params.DeflationBombBlockHeight { 138 139 return 0, 0, nil, RuleErrorDeflationBombForbidsMintingAnyMoreDeSo 140 } 141 142 // Check that the transaction has the right TxnType. 143 if txn.TxnMeta.GetTxnType() != TxnTypeBitcoinExchange { 144 return 0, 0, nil, fmt.Errorf("_connectBitcoinExchange: called with bad TxnType %s", 145 txn.TxnMeta.GetTxnType().String()) 146 } 147 txMetaa := txn.TxnMeta.(*BitcoinExchangeMetadata) 148 149 // Verify that the the transaction has: 150 // - no inputs 151 // - no outputs 152 // - no public key 153 // - no signature 154 // 155 // For BtcExchange transactions the only thing that should be set is the 156 // BitcoinExchange metadata. This is because we derive all of the other 157 // fields for this transaction from the underlying BitcoinTransaction in 158 // the metadata. Not doing this would potentially open up avenues for people 159 // to repackage Bitcoin burn transactions paying themselves rather than the person 160 // who originally burned the Bitcoin. 161 if len(txn.TxInputs) != 0 { 162 return 0, 0, nil, RuleErrorBitcoinExchangeShouldNotHaveInputs 163 } 164 if len(txn.TxOutputs) != 0 { 165 return 0, 0, nil, RuleErrorBitcoinExchangeShouldNotHaveOutputs 166 } 167 if len(txn.PublicKey) != 0 { 168 return 0, 0, nil, RuleErrorBitcoinExchangeShouldNotHavePublicKey 169 } 170 if txn.Signature != nil { 171 return 0, 0, nil, RuleErrorBitcoinExchangeShouldNotHaveSignature 172 } 173 174 // Check that the BitcoinTransactionHash has not been used in a BitcoinExchange 175 // transaction in the past. This ensures that all the Bitcoin that is burned can 176 // be converted to DeSo precisely one time. No need to worry about malleability 177 // because we also verify that the transaction was mined into a valid Bitcoin block 178 // with a lot of work on top of it, which means we can't be tricked by someone 179 // twiddling the transaction to give it a different hash (unless the Bitcoin chain 180 // is also tricked, in which case we have bigger problems). 181 bitcoinTxHash := (BlockHash)(txMetaa.BitcoinTransaction.TxHash()) 182 if bav._existsBitcoinTxIDMapping(&bitcoinTxHash) { 183 return 0, 0, nil, RuleErrorBitcoinExchangeDoubleSpendingBitcoinTransaction 184 } 185 186 if verifySignatures { 187 // We don't check for signatures and we don't do any checks to verify that 188 // the inputs of the BitcoinTransaction are actually entitled to spend their 189 // outputs. We get away with this because we check that the transaction 190 // was mined into a Bitcoin block with a lot of work on top of it, which 191 // would presumably be near-impossible if the Bitcoin transaction were invalid. 192 } 193 194 // Extract a public key from the BitcoinTransaction's inputs. Note that we only 195 // consider P2PKH inputs to be valid. If no P2PKH inputs are found then we consider 196 // the transaction as a whole to be invalid since we don't know who to credit the 197 // new DeSo to. If we find more than one P2PKH input, we consider the public key 198 // corresponding to the first of these inputs to be the one that will receive the 199 // DeSo that will be created. 200 publicKey, err := ExtractBitcoinPublicKeyFromBitcoinTransactionInputs( 201 txMetaa.BitcoinTransaction, bav.Params.BitcoinBtcdParams) 202 if err != nil { 203 return 0, 0, nil, RuleErrorBitcoinExchangeValidPublicKeyNotFoundInInputs 204 } 205 // At this point, we should have extracted a public key from the Bitcoin transaction 206 // that we expect to credit the newly-created DeSo to. 207 208 // The burn address cannot create this type of transaction. 209 addrFromPubKey, err := btcutil.NewAddressPubKey( 210 publicKey.SerializeCompressed(), bav.Params.BitcoinBtcdParams) 211 if err != nil { 212 return 0, 0, nil, fmt.Errorf("_connectBitcoinExchange: Error "+ 213 "converting public key to Bitcoin address: %v", err) 214 } 215 if addrFromPubKey.AddressPubKeyHash().EncodeAddress() == bav.Params.BitcoinBurnAddress { 216 return 0, 0, nil, RuleErrorBurnAddressCannotBurnBitcoin 217 } 218 219 // Go through the transaction's outputs and count up the satoshis that are being 220 // allocated to the burn address. If no Bitcoin is being sent to the burn address 221 // then we consider the transaction to be invalid. Watch out for overflow as we do 222 // this. 223 totalBurnOutput, err := _computeBitcoinBurnOutput( 224 txMetaa.BitcoinTransaction, bav.Params.BitcoinBurnAddress, 225 bav.Params.BitcoinBtcdParams) 226 if err != nil { 227 return 0, 0, nil, RuleErrorBitcoinExchangeProblemComputingBurnOutput 228 } 229 if totalBurnOutput <= 0 { 230 return 0, 0, nil, RuleErrorBitcoinExchangeTotalOutputLessThanOrEqualZero 231 } 232 233 // At this point we know how many satoshis were burned and we know the public key 234 // that should receive the DeSo we are going to create. 235 usdCentsPerBitcoin := bav.GetCurrentUSDCentsPerBitcoin() 236 // Compute the amount of DeSo that we should create as a result of this transaction. 237 nanosToCreate := CalcNanosToCreate(bav.NanosPurchased, uint64(totalBurnOutput), usdCentsPerBitcoin) 238 239 // Compute the amount of DeSo that the user will receive. Note 240 // that we allocate a small fee to the miner to incentivize her to include the 241 // transaction in a block. The fee for BitcoinExchange transactions is fixed because 242 // if it weren't then a miner could theoretically repackage the BitcoinTransaction 243 // into a new BitcoinExchange transaction that spends all of the newly-created DeSo as 244 // a fee. This way of doing it is a bit annoying because it means that for small 245 // BitcoinExchange transactions they might have to wait a long time and for large 246 // BitcoinExchange transactions they are highly likely to be overpaying. But it has 247 // the major benefit that all miners can autonomously scan the Bitcoin chain for 248 // burn transactions that they can turn into BitcoinExchange transactions, effectively 249 // making it so that the user doesn't have to manage the process of wrapping the 250 // Bitcoin burn into a BitcoinExchange transaction herself. 251 // 252 // We use bigints because we're paranoid about overflow. Realistically, though, 253 // it will never happen. 254 nanosToCreateBigint := big.NewInt(int64(nanosToCreate)) 255 bitcoinExchangeFeeBigint := big.NewInt( 256 int64(bav.Params.BitcoinExchangeFeeBasisPoints)) 257 // = nanosToCreate * bitcoinExchangeFeeBps 258 nanosTimesFeeBps := big.NewInt(0).Mul(nanosToCreateBigint, bitcoinExchangeFeeBigint) 259 // feeNanos = nanosToCreate * bitcoinExchangeFeeBps / 10000 260 feeNanosBigint := big.NewInt(0).Div(nanosTimesFeeBps, big.NewInt(10000)) 261 if feeNanosBigint.Cmp(big.NewInt(math.MaxInt64)) > 0 || 262 nanosToCreate < uint64(feeNanosBigint.Int64()) { 263 264 return 0, 0, nil, RuleErrorBitcoinExchangeFeeOverflow 265 } 266 feeNanos := feeNanosBigint.Uint64() 267 userNanos := nanosToCreate - feeNanos 268 269 // Now that we have all the information we need, save a UTXO allowing the user to 270 // spend the DeSo she's purchased in the future. 271 outputKey := UtxoKey{ 272 TxID: *txn.Hash(), 273 // We give all UTXOs that are created as a result of BitcoinExchange transactions 274 // an index of zero. There is generally only one UTXO created in a BitcoinExchange 275 // transaction so this field doesn't really matter. 276 Index: 0, 277 } 278 utxoEntry := UtxoEntry{ 279 AmountNanos: userNanos, 280 PublicKey: publicKey.SerializeCompressed(), 281 BlockHeight: blockHeight, 282 UtxoType: UtxoTypeBitcoinBurn, 283 UtxoKey: &outputKey, 284 // We leave the position unset and isSpent to false by default. 285 // The position will be set in the call to _addUtxo. 286 } 287 // If we have a problem adding this utxo return an error but don't 288 // mark this block as invalid since it's not a rule error and the block 289 // could therefore benefit from being processed in the future. 290 newUtxoOp, err := bav._addUtxo(&utxoEntry) 291 if err != nil { 292 return 0, 0, nil, errors.Wrapf(err, "_connectBitcoinExchange: Problem adding output utxo") 293 } 294 295 // Rosetta uses this UtxoOperation to provide INPUT amounts 296 var utxoOpsForTxn []*UtxoOperation 297 utxoOpsForTxn = append(utxoOpsForTxn, newUtxoOp) 298 299 // Increment NanosPurchased to reflect the total nanos we created with this 300 // transaction, which includes the fee paid to the miner. Save the previous 301 // value so it can be easily reverted. 302 prevNanosPurchased := bav.NanosPurchased 303 bav.NanosPurchased += nanosToCreate 304 305 // Add the Bitcoin TxID to our unique mappings 306 bav._setBitcoinBurnTxIDMappings(&bitcoinTxHash) 307 308 // Save a UtxoOperation of type OperationTypeBitcoinExchange that will allow 309 // us to easily revert NanosPurchased when we disconnect the transaction. 310 utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{ 311 Type: OperationTypeBitcoinExchange, 312 PrevNanosPurchased: prevNanosPurchased, 313 }) 314 315 // Note that the fee is implicitly equal to (nanosToCreate - userNanos) 316 return nanosToCreate, userNanos, utxoOpsForTxn, nil 317 } 318 319 func (bav *UtxoView) _connectUpdateBitcoinUSDExchangeRate( 320 txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) ( 321 _totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) { 322 323 // Check that the transaction has the right TxnType. 324 if txn.TxnMeta.GetTxnType() != TxnTypeUpdateBitcoinUSDExchangeRate { 325 return 0, 0, nil, fmt.Errorf("_connectUpdateBitcoinUSDExchangeRate: called with bad TxnType %s", 326 txn.TxnMeta.GetTxnType().String()) 327 } 328 txMeta := txn.TxnMeta.(*UpdateBitcoinUSDExchangeRateMetadataa) 329 330 // Validate that the exchange rate is not less than the floor as a sanity-check. 331 if txMeta.USDCentsPerBitcoin < MinUSDCentsPerBitcoin { 332 return 0, 0, nil, RuleErrorExchangeRateTooLow 333 } 334 if txMeta.USDCentsPerBitcoin > MaxUSDCentsPerBitcoin { 335 return 0, 0, nil, RuleErrorExchangeRateTooHigh 336 } 337 338 // Validate the public key. Only a paramUpdater is allowed to trigger this. 339 _, updaterIsParamUpdater := bav.Params.ParamUpdaterPublicKeys[MakePkMapKey(txn.PublicKey)] 340 if !updaterIsParamUpdater { 341 return 0, 0, nil, RuleErrorUserNotAuthorizedToUpdateExchangeRate 342 } 343 344 // Connect basic txn to get the total input and the total output without 345 // considering the transaction metadata. 346 totalInput, totalOutput, utxoOpsForTxn, err := bav._connectBasicTransfer( 347 txn, txHash, blockHeight, verifySignatures) 348 if err != nil { 349 return 0, 0, nil, errors.Wrapf(err, "_connectUpdateBitcoinUSDExchangeRate: ") 350 } 351 352 // Output must be non-zero 353 if totalOutput == 0 { 354 return 0, 0, nil, RuleErrorUserOutputMustBeNonzero 355 } 356 357 if verifySignatures { 358 // _connectBasicTransfer has already checked that the transaction is 359 // signed by the top-level public key, which is all we need. 360 } 361 362 // Update the exchange rate using the txn metadata. Save the previous value 363 // so it can be easily reverted. 364 prevUSDCentsPerBitcoin := bav.USDCentsPerBitcoin 365 bav.USDCentsPerBitcoin = txMeta.USDCentsPerBitcoin 366 367 // Save a UtxoOperation of type OperationTypeUpdateBitcoinUSDExchangeRate that will allow 368 // us to easily revert when we disconnect the transaction. 369 utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{ 370 Type: OperationTypeUpdateBitcoinUSDExchangeRate, 371 PrevUSDCentsPerBitcoin: prevUSDCentsPerBitcoin, 372 }) 373 374 return totalInput, totalOutput, utxoOpsForTxn, nil 375 } 376 377 func (bav *UtxoView) _disconnectBitcoinExchange( 378 operationType OperationType, currentTxn *MsgDeSoTxn, txnHash *BlockHash, 379 utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error { 380 381 // Check that the last operation has the required OperationType 382 operationIndex := len(utxoOpsForTxn) - 1 383 if len(utxoOpsForTxn) == 0 { 384 return fmt.Errorf("_disconnectBitcoinExchange: Trying to revert "+ 385 "%v but utxoOperations are missing", 386 OperationTypeBitcoinExchange) 387 } 388 if utxoOpsForTxn[operationIndex].Type != OperationTypeBitcoinExchange { 389 return fmt.Errorf("_disconnectBitcoinExchange: Trying to revert "+ 390 "%v but found type %v", 391 OperationTypeBitcoinExchange, utxoOpsForTxn[operationIndex].Type) 392 } 393 operationData := utxoOpsForTxn[operationIndex] 394 395 // Get the transaction metadata from the transaction now that we know it has 396 // OperationTypeBitcoinExchange. 397 txMeta := currentTxn.TxnMeta.(*BitcoinExchangeMetadata) 398 399 // Remove the BitcoinTransactionHash from our TxID mappings since we are 400 // unspending it. This makes it so that this hash can be processed again in 401 // the future in order to re-grant the public key the DeSo they are entitled 402 // to (though possibly more or less than the amount of DeSo they had before 403 // because they might execute at a different conversion price). 404 bitcoinTxHash := (BlockHash)(txMeta.BitcoinTransaction.TxHash()) 405 bav._deleteBitcoinBurnTxIDMappings(&bitcoinTxHash) 406 407 // Un-add the UTXO taht was created as a result of this transaction. It should 408 // be the one at the end of our UTXO list at this point. 409 // 410 // The UtxoKey is simply the transaction hash with index zero. 411 utxoKey := UtxoKey{ 412 TxID: *currentTxn.Hash(), 413 // We give all UTXOs that are created as a result of BitcoinExchange transactions 414 // an index of zero. There is generally only one UTXO created in a BitcoinExchange 415 // transaction so this field doesn't really matter. 416 Index: 0, 417 } 418 if err := bav._unAddUtxo(&utxoKey); err != nil { 419 return errors.Wrapf(err, "_disconnectBitcoinExchange: Problem unAdding utxo %v: ", utxoKey) 420 } 421 422 // Reset NanosPurchased to the value it was before granting this DeSo to this user. 423 // This previous value comes from the UtxoOperation data. 424 prevNanosPurchased := operationData.PrevNanosPurchased 425 bav.NanosPurchased = prevNanosPurchased 426 427 // At this point the BitcoinExchange transaction should be fully reverted. 428 return nil 429 } 430 431 func (bav *UtxoView) _disconnectUpdateBitcoinUSDExchangeRate( 432 operationType OperationType, currentTxn *MsgDeSoTxn, txnHash *BlockHash, 433 utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error { 434 435 // Check that the last operation has the required OperationType 436 operationIndex := len(utxoOpsForTxn) - 1 437 if len(utxoOpsForTxn) == 0 { 438 return fmt.Errorf("_disconnectUpdateBitcoinUSDExchangeRate: Trying to revert "+ 439 "%v but utxoOperations are missing", 440 OperationTypeUpdateBitcoinUSDExchangeRate) 441 } 442 if utxoOpsForTxn[operationIndex].Type != OperationTypeUpdateBitcoinUSDExchangeRate { 443 return fmt.Errorf("_disconnectUpdateBitcoinUSDExchangeRate: Trying to revert "+ 444 "%v but found type %v", 445 OperationTypeUpdateBitcoinUSDExchangeRate, utxoOpsForTxn[operationIndex].Type) 446 } 447 operationData := utxoOpsForTxn[operationIndex] 448 449 // Get the transaction metadata from the transaction now that we know it has 450 // OperationTypeUpdateBitcoinUSDExchangeRate. 451 txMeta := currentTxn.TxnMeta.(*UpdateBitcoinUSDExchangeRateMetadataa) 452 _ = txMeta 453 454 // Reset exchange rate to the value it was before granting this DeSo to this user. 455 // This previous value comes from the UtxoOperation data. 456 prevUSDCentsPerBitcoin := operationData.PrevUSDCentsPerBitcoin 457 bav.USDCentsPerBitcoin = prevUSDCentsPerBitcoin 458 459 // Now revert the basic transfer with the remaining operations. Cut off 460 // the UpdateBitcoinUSDExchangeRate operation at the end since we just reverted it. 461 return bav._disconnectBasicTransfer( 462 currentTxn, txnHash, utxoOpsForTxn[:operationIndex], blockHeight) 463 }