@keep-network/tbtc-v2 0.1.1-dev.35 → 0.1.1-dev.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/TBTC.json +3 -3
- package/artifacts/TBTCToken.json +3 -3
- package/artifacts/VendingMachine.json +10 -10
- package/artifacts/solcInputs/{922339b8aca537314dc3d35162317588.json → 4718d6e944ad9d1fc247efda870cf51a.json} +1 -1
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
- package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
- package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
- package/build/contracts/bridge/Bridge.sol/Bridge.json +2 -2
- package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
- package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +1 -1
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
- package/build/contracts/bridge/Frauds.sol/Frauds.dbg.json +1 -1
- package/build/contracts/bridge/Frauds.sol/Frauds.json +2 -2
- package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
- package/build/contracts/bridge/Sweep.sol/Sweep.dbg.json +1 -1
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
- package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
- package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
- package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
- package/contracts/bridge/Bridge.sol +164 -75
- package/package.json +1 -1
|
@@ -101,6 +101,22 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
101
101
|
uint64 changeValue;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/// @notice Represents temporary information needed during the processing of
|
|
105
|
+
/// the redemption Bitcoin transaction outputs. This structure is an
|
|
106
|
+
/// internal one and should not be exported outside of the redemption
|
|
107
|
+
/// transaction processing code.
|
|
108
|
+
/// @dev Allows to mitigate "stack too deep" errors on EVM.
|
|
109
|
+
struct RedemptionTxOutputsProcessingInfo {
|
|
110
|
+
// The first output starting index in the transaction.
|
|
111
|
+
uint256 outputStartingIndex;
|
|
112
|
+
// The number of outputs in the transaction.
|
|
113
|
+
uint256 outputsCount;
|
|
114
|
+
// P2PKH script for the wallet. Needed to determine the change output.
|
|
115
|
+
bytes32 walletP2PKHScriptKeccak;
|
|
116
|
+
// P2WPKH script for the wallet. Needed to determine the change output.
|
|
117
|
+
bytes32 walletP2WPKHScriptKeccak;
|
|
118
|
+
}
|
|
119
|
+
|
|
104
120
|
BridgeState.Storage internal self;
|
|
105
121
|
|
|
106
122
|
/// TODO: Make it governable.
|
|
@@ -1195,6 +1211,39 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1195
1211
|
abi.encodePacked(hex"160014", walletPubKeyHash)
|
|
1196
1212
|
);
|
|
1197
1213
|
|
|
1214
|
+
return
|
|
1215
|
+
processRedemptionTxOutputs(
|
|
1216
|
+
redemptionTxOutputVector,
|
|
1217
|
+
walletPubKeyHash,
|
|
1218
|
+
RedemptionTxOutputsProcessingInfo(
|
|
1219
|
+
outputStartingIndex,
|
|
1220
|
+
outputsCount,
|
|
1221
|
+
walletP2PKHScriptKeccak,
|
|
1222
|
+
walletP2WPKHScriptKeccak
|
|
1223
|
+
)
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/// @notice Processes all outputs from the redemption transaction. Tries to
|
|
1228
|
+
/// identify output as a change output, pending redemption request
|
|
1229
|
+
// or reported redemption. Reverts if one of the outputs cannot be
|
|
1230
|
+
/// recognized properly. Marks each request as processed by removing
|
|
1231
|
+
/// them from `pendingRedemptions` mapping.
|
|
1232
|
+
/// @param redemptionTxOutputVector Bitcoin redemption transaction output
|
|
1233
|
+
/// vector. This function assumes vector's structure is valid so it
|
|
1234
|
+
/// must be validated using e.g. `BTCUtils.validateVout` function
|
|
1235
|
+
/// before it is passed here
|
|
1236
|
+
/// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin
|
|
1237
|
+
// HASH160 over the compressed ECDSA public key) of the wallet which
|
|
1238
|
+
/// performed the redemption transaction.
|
|
1239
|
+
/// @param processInfo RedemptionTxOutputsProcessingInfo identifying output
|
|
1240
|
+
/// starting index, the number of outputs and possible wallet change
|
|
1241
|
+
/// P2PKH and P2WPKH scripts.
|
|
1242
|
+
function processRedemptionTxOutputs(
|
|
1243
|
+
bytes memory redemptionTxOutputVector,
|
|
1244
|
+
bytes20 walletPubKeyHash,
|
|
1245
|
+
RedemptionTxOutputsProcessingInfo memory processInfo
|
|
1246
|
+
) internal returns (RedemptionTxOutputsInfo memory resultInfo) {
|
|
1198
1247
|
// Helper variable that counts the number of processed redemption
|
|
1199
1248
|
// outputs. Redemptions can be either pending or reported as timed out.
|
|
1200
1249
|
// TODO: Revisit the approach with redemptions count according to
|
|
@@ -1202,14 +1251,14 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1202
1251
|
uint256 processedRedemptionsCount = 0;
|
|
1203
1252
|
|
|
1204
1253
|
// Outputs processing loop.
|
|
1205
|
-
for (uint256 i = 0; i < outputsCount; i++) {
|
|
1254
|
+
for (uint256 i = 0; i < processInfo.outputsCount; i++) {
|
|
1206
1255
|
// TODO: Check if we can optimize gas costs by adding
|
|
1207
1256
|
// `extractValueAt` and `extractHashAt` in `bitcoin-spv-sol`
|
|
1208
1257
|
// in order to avoid allocating bytes in memory.
|
|
1209
1258
|
uint256 outputLength = redemptionTxOutputVector
|
|
1210
|
-
.determineOutputLengthAt(outputStartingIndex);
|
|
1259
|
+
.determineOutputLengthAt(processInfo.outputStartingIndex);
|
|
1211
1260
|
bytes memory output = redemptionTxOutputVector.slice(
|
|
1212
|
-
outputStartingIndex,
|
|
1261
|
+
processInfo.outputStartingIndex,
|
|
1213
1262
|
outputLength
|
|
1214
1263
|
);
|
|
1215
1264
|
|
|
@@ -1221,88 +1270,36 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1221
1270
|
bytes memory outputScript = output.slice(8, output.length - 8);
|
|
1222
1271
|
|
|
1223
1272
|
if (
|
|
1224
|
-
|
|
1225
|
-
(keccak256(outputScript) ==
|
|
1226
|
-
|
|
1273
|
+
resultInfo.changeValue == 0 &&
|
|
1274
|
+
(keccak256(outputScript) ==
|
|
1275
|
+
processInfo.walletP2PKHScriptKeccak ||
|
|
1276
|
+
keccak256(outputScript) ==
|
|
1277
|
+
processInfo.walletP2WPKHScriptKeccak) &&
|
|
1227
1278
|
outputValue > 0
|
|
1228
1279
|
) {
|
|
1229
1280
|
// If we entered here, that means the change output with a
|
|
1230
1281
|
// proper non-zero value was found.
|
|
1231
|
-
|
|
1232
|
-
|
|
1282
|
+
resultInfo.changeIndex = uint32(i);
|
|
1283
|
+
resultInfo.changeValue = outputValue;
|
|
1233
1284
|
} else {
|
|
1234
1285
|
// If we entered here, that the means the given output is
|
|
1235
|
-
// supposed to represent a redemption.
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
)
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
// as a pending redemption request.
|
|
1244
|
-
RedemptionRequest storage request = pendingRedemptions[
|
|
1245
|
-
redemptionKey
|
|
1246
|
-
];
|
|
1247
|
-
// Compute the request's redeemable amount as the requested
|
|
1248
|
-
// amount reduced by the treasury fee. The request's
|
|
1249
|
-
// minimal amount is then the redeemable amount reduced by
|
|
1250
|
-
// the maximum transaction fee.
|
|
1251
|
-
uint64 redeemableAmount = request.requestedAmount -
|
|
1252
|
-
request.treasuryFee;
|
|
1253
|
-
// Output value must fit between the request's redeemable
|
|
1254
|
-
// and minimal amounts to be deemed valid.
|
|
1255
|
-
require(
|
|
1256
|
-
redeemableAmount - request.txMaxFee <= outputValue &&
|
|
1257
|
-
outputValue <= redeemableAmount,
|
|
1258
|
-
"Output value is not within the acceptable range of the pending request"
|
|
1259
|
-
);
|
|
1260
|
-
// Add the redeemable amount to the total burnable value
|
|
1261
|
-
// the Bridge will use to decrease its balance in the Bank.
|
|
1262
|
-
info.totalBurnableValue += redeemableAmount;
|
|
1263
|
-
// Add the request's treasury fee to the total treasury fee
|
|
1264
|
-
// value the Bridge will transfer to the treasury.
|
|
1265
|
-
info.totalTreasuryFee += request.treasuryFee;
|
|
1266
|
-
// Request was properly handled so remove its redemption
|
|
1267
|
-
// key from the mapping to make it reusable for further
|
|
1268
|
-
// requests.
|
|
1269
|
-
delete pendingRedemptions[redemptionKey];
|
|
1270
|
-
|
|
1271
|
-
processedRedemptionsCount++;
|
|
1272
|
-
} else {
|
|
1273
|
-
// If we entered here, the output is not a redemption
|
|
1274
|
-
// request but there is still a chance the given output is
|
|
1275
|
-
// related to a reported timed out redemption request.
|
|
1276
|
-
// If so, check if the output value matches the request
|
|
1277
|
-
// amount to confirm this is an overdue request fulfillment
|
|
1278
|
-
// then bypass this output and process the subsequent
|
|
1279
|
-
// ones. That also means the wallet was already punished
|
|
1280
|
-
// for the inactivity. Otherwise, just revert.
|
|
1281
|
-
RedemptionRequest storage request = timedOutRedemptions[
|
|
1282
|
-
redemptionKey
|
|
1283
|
-
];
|
|
1284
|
-
|
|
1285
|
-
require(
|
|
1286
|
-
request.requestedAt != 0,
|
|
1287
|
-
"Output is a non-requested redemption"
|
|
1286
|
+
// supposed to represent a redemption.
|
|
1287
|
+
(
|
|
1288
|
+
uint64 burnableValue,
|
|
1289
|
+
uint64 treasuryFee
|
|
1290
|
+
) = processNonChangeRedemptionTxOutput(
|
|
1291
|
+
walletPubKeyHash,
|
|
1292
|
+
outputScript,
|
|
1293
|
+
outputValue
|
|
1288
1294
|
);
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
require(
|
|
1294
|
-
redeemableAmount - request.txMaxFee <= outputValue &&
|
|
1295
|
-
outputValue <= redeemableAmount,
|
|
1296
|
-
"Output value is not within the acceptable range of the timed out request"
|
|
1297
|
-
);
|
|
1298
|
-
|
|
1299
|
-
processedRedemptionsCount++;
|
|
1300
|
-
}
|
|
1295
|
+
resultInfo.totalBurnableValue += burnableValue;
|
|
1296
|
+
resultInfo.totalTreasuryFee += treasuryFee;
|
|
1297
|
+
processedRedemptionsCount++;
|
|
1301
1298
|
}
|
|
1302
1299
|
|
|
1303
1300
|
// Make the `outputStartingIndex` pointing to the next output by
|
|
1304
1301
|
// increasing it by current output's length.
|
|
1305
|
-
outputStartingIndex += outputLength;
|
|
1302
|
+
processInfo.outputStartingIndex += outputLength;
|
|
1306
1303
|
}
|
|
1307
1304
|
|
|
1308
1305
|
// Protect against the cases when there is only a single change output
|
|
@@ -1312,8 +1309,100 @@ contract Bridge is Ownable, EcdsaWalletOwner {
|
|
|
1312
1309
|
processedRedemptionsCount > 0,
|
|
1313
1310
|
"Redemption transaction must process at least one redemption"
|
|
1314
1311
|
);
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/// @notice Processes a single redemption transaction output. Tries to
|
|
1315
|
+
/// identify output as a pending redemption request or reported
|
|
1316
|
+
/// redemption timeout. Output script passed to this function must
|
|
1317
|
+
/// not be the change output. Such output needs to be identified
|
|
1318
|
+
/// separately before calling this function.
|
|
1319
|
+
/// Reverts if output is neither requested pending redemption nor
|
|
1320
|
+
/// requested and reported timed-out redemption.
|
|
1321
|
+
/// This function also marks each pending request as processed by
|
|
1322
|
+
/// removing it from `pendingRedemptions` mapping.
|
|
1323
|
+
/// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin
|
|
1324
|
+
// HASH160 over the compressed ECDSA public key) of the wallet which
|
|
1325
|
+
/// performed the redemption transaction.
|
|
1326
|
+
/// @param outputScript Non-change output script to be processed
|
|
1327
|
+
/// @param outputValue Value of the output being processed
|
|
1328
|
+
/// @return burnableValue The value burnable as a result of processing this
|
|
1329
|
+
/// single redemption output. This value needs to be summed up with
|
|
1330
|
+
/// burnable values of all other outputs to evaluate total burnable
|
|
1331
|
+
/// value for the entire redemption transaction. This value is 0
|
|
1332
|
+
/// for a timed-out redemption request.
|
|
1333
|
+
/// @return treasuryFee The treasury fee from this single redemption output.
|
|
1334
|
+
/// This value needs to be summed up with treasury fees of all other
|
|
1335
|
+
/// outputs to evaluate the total treasury fee for the entire
|
|
1336
|
+
/// redemption transaction. This value is 0 for a timed-out
|
|
1337
|
+
/// redemption request.
|
|
1338
|
+
function processNonChangeRedemptionTxOutput(
|
|
1339
|
+
bytes20 walletPubKeyHash,
|
|
1340
|
+
bytes memory outputScript,
|
|
1341
|
+
uint64 outputValue
|
|
1342
|
+
) internal returns (uint64 burnableValue, uint64 treasuryFee) {
|
|
1343
|
+
// This function should be called only if the given output is
|
|
1344
|
+
// supposed to represent a redemption. Build the redemption key
|
|
1345
|
+
// to perform that check.
|
|
1346
|
+
uint256 redemptionKey = uint256(
|
|
1347
|
+
keccak256(abi.encodePacked(walletPubKeyHash, outputScript))
|
|
1348
|
+
);
|
|
1315
1349
|
|
|
1316
|
-
|
|
1350
|
+
if (pendingRedemptions[redemptionKey].requestedAt != 0) {
|
|
1351
|
+
// If we entered here, that means the output was identified
|
|
1352
|
+
// as a pending redemption request.
|
|
1353
|
+
RedemptionRequest storage request = pendingRedemptions[
|
|
1354
|
+
redemptionKey
|
|
1355
|
+
];
|
|
1356
|
+
// Compute the request's redeemable amount as the requested
|
|
1357
|
+
// amount reduced by the treasury fee. The request's
|
|
1358
|
+
// minimal amount is then the redeemable amount reduced by
|
|
1359
|
+
// the maximum transaction fee.
|
|
1360
|
+
uint64 redeemableAmount = request.requestedAmount -
|
|
1361
|
+
request.treasuryFee;
|
|
1362
|
+
// Output value must fit between the request's redeemable
|
|
1363
|
+
// and minimal amounts to be deemed valid.
|
|
1364
|
+
require(
|
|
1365
|
+
redeemableAmount - request.txMaxFee <= outputValue &&
|
|
1366
|
+
outputValue <= redeemableAmount,
|
|
1367
|
+
"Output value is not within the acceptable range of the pending request"
|
|
1368
|
+
);
|
|
1369
|
+
// Add the redeemable amount to the total burnable value
|
|
1370
|
+
// the Bridge will use to decrease its balance in the Bank.
|
|
1371
|
+
burnableValue = redeemableAmount;
|
|
1372
|
+
// Add the request's treasury fee to the total treasury fee
|
|
1373
|
+
// value the Bridge will transfer to the treasury.
|
|
1374
|
+
treasuryFee = request.treasuryFee;
|
|
1375
|
+
// Request was properly handled so remove its redemption
|
|
1376
|
+
// key from the mapping to make it reusable for further
|
|
1377
|
+
// requests.
|
|
1378
|
+
delete pendingRedemptions[redemptionKey];
|
|
1379
|
+
} else {
|
|
1380
|
+
// If we entered here, the output is not a redemption
|
|
1381
|
+
// request but there is still a chance the given output is
|
|
1382
|
+
// related to a reported timed out redemption request.
|
|
1383
|
+
// If so, check if the output value matches the request
|
|
1384
|
+
// amount to confirm this is an overdue request fulfillment
|
|
1385
|
+
// then bypass this output and process the subsequent
|
|
1386
|
+
// ones. That also means the wallet was already punished
|
|
1387
|
+
// for the inactivity. Otherwise, just revert.
|
|
1388
|
+
RedemptionRequest storage request = timedOutRedemptions[
|
|
1389
|
+
redemptionKey
|
|
1390
|
+
];
|
|
1391
|
+
|
|
1392
|
+
require(
|
|
1393
|
+
request.requestedAt != 0,
|
|
1394
|
+
"Output is a non-requested redemption"
|
|
1395
|
+
);
|
|
1396
|
+
|
|
1397
|
+
uint64 redeemableAmount = request.requestedAmount -
|
|
1398
|
+
request.treasuryFee;
|
|
1399
|
+
|
|
1400
|
+
require(
|
|
1401
|
+
redeemableAmount - request.txMaxFee <= outputValue &&
|
|
1402
|
+
outputValue <= redeemableAmount,
|
|
1403
|
+
"Output value is not within the acceptable range of the timed out request"
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1317
1406
|
}
|
|
1318
1407
|
|
|
1319
1408
|
/// @notice Notifies that there is a pending redemption request associated
|