Skip to content
This repository has been archived by the owner on Jul 19, 2024. It is now read-only.

Corresponding to "regtest mode" in more detail #889

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion docker/nginx/counterwallet.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ upstream counterblock_t_api_server {
server ${COUNTERBLOCK_HOST_TESTNET}:${COUNTERBLOCK_PORT_TESTNET};
keepalive 30;
}
upstream counterblock_r_api_server {
server ${COUNTERBLOCK_HOST_REGTEST}:${COUNTERBLOCK_PORT_REGTEST};
keepalive 30;
}

#Content Security Policy (CSP) header
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://ssl.google-analytics.com https://query.yahooapis.com; img-src 'self' data: https://ssl.google-analytics.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://themes.googleusercontent.com https://fonts.gstatic.com; frame-src 'none'; object-src 'self'; connect-src 'self' wss://$host wss://*.counterwallet.io https://*.counterwallet.io https://api.rollbar.com https://bitcoinfees.earn.com https://apiv2.bitcoinaverage.com https://min-api.cryptocompare.com; report-uri /_report_csp/;";
Expand Down Expand Up @@ -81,6 +85,11 @@ server {
expires 1h;
alias /counterblock_data/asset_img.testnet/;
}
location /_r_asset_img/ {
access_log off;
expires 1h;
alias /counterblock_data/asset_img.regtest/;
}
location /src {
#For dev/testing (uses unminified resources)
open_file_cache off;
Expand Down Expand Up @@ -165,7 +174,40 @@ server {
include /etc/nginx/sites-enabled/counterblock_socketio.inc;
proxy_pass http://${COUNTERBLOCK_HOST_TESTNET}:${COUNTERBLOCK_PORT_TESTNET_CHAT}/socket.io;
}


#####
# REGTEST
# PROXY TO COUNTERBLOCK API REQUESTS (WSGI) - try to hit the cache in redis first
location ^~ /_r_api
{
#reject everything except GET, POST and OPTIONS
limit_except GET POST OPTIONS {
deny all;
}

include /etc/nginx/sites-enabled/counterblock_api_cache.inc;
set $redis_db "2";

# Send to app server if Redis could not answer the request
error_page 404 405 550 = @r_wsgi_api;
}
# PROXY TO COUNTERBLOCK API BACKEND (WSGI)
location @r_wsgi_api {
include /etc/nginx/sites-enabled/counterblock_api.inc;
rewrite ^/_r_api/?$ /api/? break;
proxy_pass http://counterblock_r_api_server;
}
# PROXY TO COUNTERBLOCK FEED BACKEND (socket.io)
location ^~ /_r_feed {
include /etc/nginx/sites-enabled/counterblock_socketio.inc;
proxy_pass http://${COUNTERBLOCK_HOST_REGTEST}:${COUNTERBLOCK_PORT_REGTEST_FEED}/socket.io;
}
# PROXY TO COUNTERBLOCK CHAT BACKEND (socket.io)
location ^~ /_r_chat {
include /etc/nginx/sites-enabled/counterblock_socketio.inc;
proxy_pass http://${COUNTERBLOCK_HOST_REGTEST}:${COUNTERBLOCK_PORT_REGTEST_CHAT}/socket.io;
}

#####
# CSP REPORTING (mainnet and testnet)
location ^~ /_report_csp/ {
Expand Down
6 changes: 5 additions & 1 deletion docker/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@ export REDIS_PORT=${REDIS_PORT:=6379}
export REDIS_DB=${REDIS_DB:=0}
export COUNTERBLOCK_HOST_MAINNET=${COUNTERBLOCK_HOST_MAINNET:="counterblock"}
export COUNTERBLOCK_HOST_TESTNET=${COUNTERBLOCK_HOST_TESTNET:="counterblock-testnet"}
export COUNTERBLOCK_HOST_REGTEST=${COUNTERBLOCK_HOST_REGTEST:="counterblock-regtest"}
export COUNTERBLOCK_PORT_MAINNET=${COUNTERBLOCK_PORT_MAINNET:=4100}
export COUNTERBLOCK_PORT_TESTNET=${COUNTERBLOCK_PORT_TESTNET:=14100}
export COUNTERBLOCK_PORT_REGTEST=${COUNTERBLOCK_PORT_REGTEST:=24100}
export COUNTERBLOCK_PORT_MAINNET_FEED=${COUNTERBLOCK_PORT_MAINNET_FEED:=4101}
export COUNTERBLOCK_PORT_TESTNET_FEED=${COUNTERBLOCK_PORT_TESTNET_FEED:=14101}
export COUNTERBLOCK_PORT_REGTEST_FEED=${COUNTERBLOCK_PORT_REGTEST_FEED:=24101}
export COUNTERBLOCK_PORT_MAINNET_CHAT=${COUNTERBLOCK_PORT_MAINNET_CHAT:=4102}
export COUNTERBLOCK_PORT_TESTNET_CHAT=${COUNTERBLOCK_PORT_TESTNET_CHAT:=14102}
export COUNTERBLOCK_PORT_REGTEST_CHAT=${COUNTERBLOCK_PORT_REGTEST_CHAT:=24102}

VARS='$REDIS_HOST:$REDIS_PORT:$REDIS_DB:$COUNTERBLOCK_HOST_MAINNET:$COUNTERBLOCK_HOST_TESTNET:$COUNTERBLOCK_PORT_MAINNET:$COUNTERBLOCK_PORT_TESTNET:$COUNTERBLOCK_PORT_MAINNET_FEED:$COUNTERBLOCK_PORT_TESTNET_FEED:$COUNTERBLOCK_PORT_MAINNET_CHAT:$COUNTERBLOCK_PORT_TESTNET_CHAT'
VARS='$REDIS_HOST:$REDIS_PORT:$REDIS_DB:$COUNTERBLOCK_HOST_MAINNET:$COUNTERBLOCK_HOST_TESTNET:$COUNTERBLOCK_HOST_REGTEST:$COUNTERBLOCK_PORT_MAINNET:$COUNTERBLOCK_PORT_TESTNET:$COUNTERBLOCK_PORT_REGTEST:$COUNTERBLOCK_PORT_MAINNET_FEED:$COUNTERBLOCK_PORT_TESTNET_FEED:$COUNTERBLOCK_PORT_REGTEST_FEED:$COUNTERBLOCK_PORT_MAINNET_CHAT:$COUNTERBLOCK_PORT_TESTNET_CHAT:$COUNTERBLOCK_PORT_REGTEST_CHAT'
envsubst "$VARS" < /counterwallet/docker/nginx/counterwallet.conf.template > /etc/nginx/sites-enabled/counterwallet.conf

# Launch utilizing the SIGTERM/SIGINT propagation pattern from
Expand Down
6 changes: 3 additions & 3 deletions src/css/misc.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ span.modeIndicator {
}


#noticeTestnet {
#noticeTestnet, #noticeRegtest {
font-size: 26px;
font-weight: bolder;
color: red;
Expand All @@ -91,15 +91,15 @@ span.modeIndicator {
}

@media only screen and (max-width : 860px) {
#noticeTestnet {
#noticeTestnet, #noticeRegtest {
font-size:13px;
min-width: 116px;
margin-left: -58px;
}
}

@media only screen and (max-width : 500px) {
#noticeTestnet {
#noticeTestnet, #noticeRegtest {
display:none;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ <h2 data-bind="locale: 'pending_actions'"></h2>
</div> <!-- logo-group -->

<span id="noticeTestnet" data-bind="visible: USE_TESTNET, locale: 'testnet_in_use'"></span>
<span id="noticeRegtest" data-bind="visible: USE_REGTEST">REGTEST</span>
<span id="noticeRegtest" data-bind="visible: USE_REGTEST, locale: 'regtest_in_use'"></span>

<div class="pull-right" id="headerBtsRight"> <!-- pulled right: nav area -->
<div id="logout" class="btn-header transparent pull-right"> <!-- logout button -->
Expand Down
3 changes: 2 additions & 1 deletion src/js/components/balances.js
Original file line number Diff line number Diff line change
Expand Up @@ -812,13 +812,14 @@ var SweepAssetInDropdownItemModel = function(asset, rawBalance, normalizedBalanc


var privateKeyValidator = function(required) {
var translationKeyIdx = USE_TESTNET ? 'not_valid_testnet_pk' : (USE_REGTEST ? 'not_valid_regtest_pk' : 'not_valid_pk');
return {
required: required,
validation: {
validator: function(val, self) {
return (new CWPrivateKey(val)).isValid();
},
message: USE_TESTNET ? i18n.t('not_valid_testnet_pk') : i18n.t('not_valid_pk'),
message: translationKeyIdx,
params: self
},
rateLimit: {timeout: 500, method: "notifyWhenChangesStop"}
Expand Down
6 changes: 4 additions & 2 deletions src/js/components/balances_assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -919,8 +919,10 @@ function ShowAssetInfoModalViewModel() {
if (!ext_info)
return; //asset has no extended info

if (ext_info['image'])
self.extImageURL((USE_TESTNET ? '/_t_asset_img/' : '/_asset_img/') + assetObj.ASSET + '.png');
if (ext_info['image']) {
var prefix = USE_TESTNET ? '_t' : (USE_REGTEST ? '_r' : '');
self.extImageURL('/' + prefix + '_asset_img/' + assetObj.ASSET + '.png');
}

self.extWebsite(ext_info['website']);
self.extDescription(ext_info['description']);
Expand Down
2 changes: 1 addition & 1 deletion src/js/components/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ function BalanceHistoryViewModel() {
//An address on a wallet
var self = this;
self.selectedAsset = ko.observable('');
self.availableAssets = !USE_TESTNET ? ko.observableArray([KEY_ASSET.XCP, KEY_ASSET.BTC]) : ko.observableArray([KEY_ASSET.XCP]);
self.availableAssets = !(USE_TESTNET || USE_REGTEST) ? ko.observableArray([KEY_ASSET.XCP, KEY_ASSET.BTC]) : ko.observableArray([KEY_ASSET.XCP]);
//^ don't load BTC as an asset on testnet, since we can't show the data (since blockchain doesn't support testnet)
self.graphData = null;
self.ASSET_LASTCHANGE = null;
Expand Down
3 changes: 2 additions & 1 deletion src/js/components/leaderboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ AssetLeaderboardViewModel.formulateExtendedAssetInfo = function(asset, hasImage,
var website = asset === KEY_ASSET.XCP ? KEY_ASSET_WEBSITE.XCP : KEY_ASSET_WEBSITE.BTC;
dispAsset += '<a href="' + website + '" target="_blank" rel="noopener noreferrer">' + asset + '</a>';
} else if (hasImage) {
dispAsset = '<img alt="" src="' + (USE_TESTNET ? '/_t_asset_img/' : '/_asset_img/') + asset + '.png" />&nbsp;';
var prefix = USE_TESTNET ? '_t' : (USE_REGTEST ? '_r' : '');
dispAsset = '<img alt="" src="' + '/' + prefix + '_asset_img/' + asset + '.png" />&nbsp;';
//dispAsset += website ? ('<a href="' + website + '" target="_blank" rel="noopener noreferrer">' + asset + '</a>') : asset;
dispAsset += asset; //keep it simple for now for avoid XSS
}
Expand Down
13 changes: 7 additions & 6 deletions src/js/components/logon.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function LogonViewModel() {
self.cryptedPassphrase = ko.observable(CRYPTED_PASSPHRASE);

self.USE_TESTNET = USE_TESTNET;
self.USE_REGTEST = USE_REGTEST;
self.IS_DEV = IS_DEV;

self.sanitizedEnteredPassphrase = ko.computed(function() { //cleans whitespace and gunk off the passphrase
Expand Down Expand Up @@ -132,16 +133,16 @@ function LogonViewModel() {
multiAPI("is_ready", {}, function(data, endpoint) {
assert(data['caught_up'], "Invalid is_ready result"); //otherwise we should have gotten a 525 error
assert(USE_TESTNET == data['testnet'], "USE_TESTNET is " + USE_TESTNET + " from URL-based detection, but the server API disagrees!");

$.jqlog.log("Backend is ready. Testnet status: " + USE_TESTNET + ". Last message feed index: " + data['last_message_index'] + ". CW last message seq: " + data['cw_last_message_seq']);
$.jqlog.log("Backend is ready. Testnet status: " + USE_TESTNET + ". Regtest status: " + USE_REGTEST + "Last message feed index: " + data['last_message_index'] + ". CW last message seq: " + data['cw_last_message_seq']);
assert(data['last_message_index'] > 0);

//User is logging in...
self.walletGenProgressVal(0); //reset so the progress bar hides again...
self.setExtraInfoOpacity(0);

//generate the wallet ID from a double SHA256 hash of the passphrase and the network (if testnet)
var hashBase = CryptoJS.SHA256(self.sanitizedEnteredPassphrase() + (USE_TESTNET ? '_testnet' : ''));
var network = USE_TESTNET ? '_testnet' : (USE_REGTEST ? '_regtest' : '');
var hashBase = CryptoJS.SHA256(self.sanitizedEnteredPassphrase() + network);
var hash = CryptoJS.SHA256(hashBase).toString(CryptoJS.enc.Base64);
//var hashBase = self.sanitizedEnteredPassphrase() + (USE_TESTNET ? '_testnet' : '');

Expand Down Expand Up @@ -170,7 +171,7 @@ function LogonViewModel() {
//Grab preferences
multiAPINewest("get_preferences", {
'wallet_id': WALLET.identifier(),
'network': (USE_TESTNET || USE_REGTEST) ? 'testnet' : 'mainnet',
'network': USE_TESTNET ? 'testnet' : (USE_REGTEST ? 'regtest' : 'mainnet'),
'for_login': true
}, 'last_updated', self.onReceivedPreferences);

Expand Down Expand Up @@ -218,7 +219,7 @@ function LogonViewModel() {
} else { //could not find user stored preferences
//No server had the preferences
$.jqlog.log("Stored preferences NOT found on server(s). Creating new...");
trackEvent("Login", "NewWallet", USE_TESTNET ? "Testnet" : "Mainnet");
trackEvent("Login", "NewWallet", USE_TESTNET ? "Testnet" : (USE_REGTEST ? "Regtest" : "Mainnet"));
WALLET.isNew(true);
//no stored preferences on any server(s) in the federation, go with the local storage preferences or default...
if (localPref && localPref['preferences']) {
Expand Down Expand Up @@ -439,7 +440,7 @@ function LogonViewModel() {

//record some metrics...
trackEvent("Login", "Wallet", "Size", PREFERENCES['num_addresses_used'] + PREFERENCES['num_segwit_addresses_used']);
trackEvent("Login", "Network", USE_TESTNET ? "Testnet" : "Mainnet");
trackEvent("Login", "Network", USE_TESTNET ? "Testnet" : (USE_REGTEST ? "Regtest" : "Mainnet"));
trackEvent("Login", "Country", USER_COUNTRY || 'UNKNOWN');
trackEvent("Login", "Language", PREFERENCES['selected_lang']);
trackEvent("Login", "Theme", PREFERENCES['selected_theme']);
Expand Down
1 change: 1 addition & 0 deletions src/js/components/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function CreateSupportCaseViewModel() {
'currentBlockID': WALLET.networkBlockHeight(),
'currentMsgID': MESSAGE_FEED.lastMessageIndexReceived(),
'useTestnet': USE_TESTNET,
'useRegtest': USE_REGTEST,
'devMode': IS_DEV,
'walletID': WALLET.identifier(),
'originURL': window.location.origin,
Expand Down
4 changes: 2 additions & 2 deletions src/js/components/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ function WalletViewModel() {
var verifyDestAddr = data['destination'] || data['transfer_destination'] || data['feed_address'] || data['destBtcPay'] || data['source'];
delete data['destBtcPay'];
if (action == "create_burn") {
verifyDestAddr = TESTNET_UNSPENDABLE;
verifyDestAddr = USE_TESTNET ? TESTNET_UNSPENDABLE : REGTEST_UNSPENDABLE;
} else if (action === 'create_dividend' && data['dividend_asset'] == KEY_ASSET.BTC) {
verifyDestAddr = data['_btc_dividend_dests'];
delete data['_btc_dividend_dests'];
Expand Down Expand Up @@ -971,7 +971,7 @@ function WalletViewModel() {
var params = {
'wallet_id': WALLET.identifier(),
'preferences': PREFERENCES,
'network': USE_TESTNET ? 'testnet' : 'mainnet',
'network': USE_TESTNET ? 'testnet' : (USE_REGTEST ? 'regtest' : 'mainnet'),
'referer': ORIG_REFERER
};
if (forLogin) {
Expand Down
8 changes: 4 additions & 4 deletions src/js/counterwallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//Set up logging (jqlog) and monkey patch jqlog with a debug function
$.jqlog.enabled(true);
$.jqlog.debug = function(object, options) {
if (IS_DEV || USE_TESTNET) //may change to just IS_DEV in the future
if (IS_DEV || USE_TESTNET || USE_REGTEST) //may change to just IS_DEV in the future
$.jqlog.info(object, options);
}

Expand All @@ -19,7 +19,7 @@ if (!window.location.origin) {

//if in dev or testnet mode (both of which are specified based on a URL querystring being present), IF a query string is
// provided clear the query string so that our hash-based AJAX navigation works after logging in...
if ((IS_DEV || USE_TESTNET) && location.search) {
if ((IS_DEV || USE_TESTNET || USE_REGTEST) && location.search) {
//history.replaceState is NOT supported on IE 9...ehh
assert($.layout.className !== 'trident9',
"Use of 'dev' or 'testnet' flags NOT supported on IE 9, due to lack of history.replaceState() support.");
Expand Down Expand Up @@ -66,7 +66,7 @@ function produceCWServerList() {
if (USE_TESTNET) {
return element + '/_t_api';
} else if (USE_REGTEST) {
return element + '/';
return element + '/_r_api';
} else {
return element + '/_api';
}
Expand Down Expand Up @@ -98,7 +98,7 @@ function initRollbar() {
accessToken: ROLLBAR_ACCESS_TOKEN,
captureUncaught: true,
payload: {
environment: USE_TESTNET ? "testnet" : "mainnet"
environment: USE_TESTNET ? "testnet" : (USE_REGTEST ? "regtest" : "mainnet")
}
};
try {
Expand Down
1 change: 1 addition & 0 deletions src/js/pages.init.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function initIndex() { //main page

//so that knockout is run on the DOM sections and global context is accessible...
ko.applyBindings({}, document.getElementById("noticeTestnet"));
ko.applyBindings({}, document.getElementById("noticeRegtest"));
ko.applyBindings({}, document.getElementById("noticeDevMode"));
ko.applyBindings({}, document.getElementById("donate"));
ko.applyBindings({}, document.getElementById("logo"));
Expand Down
6 changes: 4 additions & 2 deletions src/js/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ function cleanHtmlPrice(price) {

function feedImageUrl(image_name) {
var url = cwBaseURLs()[0];
url += USE_TESTNET ? '/_t_feed_img/' : '/_feed_img/';
var prefix = USE_TESTNET ? "_t" : ( USE_REGTEST ? "_r" : "");
url += '/' + prefix + '_feed_img/';
url += image_name + '.png';
return url;
}

function assetImageUrl(image_name) {
var url = cwBaseURLs()[0];
url += USE_TESTNET ? '/_t_asset_img/' : '/_asset_img/';
var prefix = USE_TESTNET ? "_t" : ( USE_REGTEST ? "_r" : "");
url += '/' + prefix + '_asset_img/';
url += image_name + '.png';
return url;
}
Expand Down
12 changes: 6 additions & 6 deletions src/js/util.knockout.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,23 +90,23 @@ var initDateTimePicker = function(locale) {
};


/*
/*
* Shared knockout Validation custom rules
*/
function createSharedKnockoutValidators() {

var addressKeyIdx = USE_TESTNET ? 'must_be_valid_testnet_address' : (USE_REGTEST ? 'must_be_valid_regtest_address' : 'must_be_valid_bitcoin_address');
ko.validation.rules['isValidBitcoinAddress'] = {
validator: function(val, self) {
return CWBitcore.isValidAddress(val) || CWBitcore.isValidMultisigAddress(val);
},
message: USE_TESTNET ? i18n.t('must_be_valid_testnet_address') : i18n.t('must_be_valid_bitcoin_address')
message: addressKeyIdx
};

ko.validation.rules['isValidMonosigAddress'] = {
validator: function(val, self) {
return CWBitcore.isValidAddress(val);
},
message: USE_TESTNET ? i18n.t('must_be_valid_testnet_address') : i18n.t('must_be_valid_bitcoin_address')
message: addressKeyIdx
};

ko.validation.rules['isValidBitcoinAddressIfSpecified'] = {
Expand All @@ -118,7 +118,7 @@ function createSharedKnockoutValidators() {
return false;
}
},
message: USE_TESTNET ? i18n.t('must_be_valid_testnet_address') : i18n.t('must_be_valid_bitcoin_address')
message: addressKeyIdx
};

ko.validation.rules['isValidMonosigAddressIfSpecified'] = {
Expand All @@ -130,7 +130,7 @@ function createSharedKnockoutValidators() {
return false;
}
},
message: USE_TESTNET ? i18n.t('must_be_valid_testnet_address') : i18n.t('must_be_valid_bitcoin_address')
message: addressKeyIdx
};

ko.validation.rules['isValidQtyForDivisibility'] = {
Expand Down
2 changes: 2 additions & 0 deletions src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"log_into_your_wallet": "Log Into Your Wallet",
"dev_mode_enabled": "Dev Mode Enabled",
"testnet_in_use": "Testnet In Use",
"regtest_in_use": "Regtest In Use",
"open_wallet": "Open Wallet",
"login": "Login",
"login_problems": "Problems logging in?",
Expand Down Expand Up @@ -176,6 +177,7 @@
"cannot_send_server_unavailable": "Cannot send <b class='notoAssetColor'>BTC</b> right now, as we cannot currently get in touch with the server to get your balance. Please try again later.",
"bal": "bal",
"not_valid_testnet_pk": "Not a valid TESTNET private key",
"not_valid_regtest_pk": "Not a valid REGTEST private key",
"not_valid_pk": "Not a valid private key",
"not_able_to_sweep": "Counterwallet cannot sweep all of the tokens you selected. Please send %s BTC to address %s and try again. OR use the following fields to pay fees with another address",
"asset_sent_to": "Sent <b class='notoQuantityColor'>%s</b> <b class='notoAssetColor'>%s</b> to <b class='notoAddrColor'>%s</b>",
Expand Down
2 changes: 1 addition & 1 deletion src/pages/balances.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ <h3 class="jarviswidget-ctrls">
<ul class="dropdown-menu" role="menu">
<li><a data-bind="visible: !$parent.IS_WATCH_ONLY, click: send, locale: 'send'" href="#"></a></li>
<li><a data-bind="visible: !isBTC() && !isXCP(), click: showInfo, locale: 'show_info'" href="#"></a></li>
<li data-bind="visible: !$parent.IS_WATCH_ONLY && USE_TESTNET && isBTC()"><a data-bind="click: testnetBurn, locale: 'burn_for_xcp'" href="#"></a></li>
<li data-bind="visible: !$parent.IS_WATCH_ONLY && (USE_TESTNET || USE_REGTEST) && isBTC()"><a data-bind="click: testnetBurn, locale: 'burn_for_xcp'" href="#"></a></li>
<li data-bind="visible: !$parent.IS_WATCH_ONLY && isMine() && !locked()"><a data-bind="click: issueAdditional, locale: 'issue_additional'" href="#"></a></li>
<li data-bind="visible: !$parent.IS_WATCH_ONLY && isMine()"><a data-bind="click: transfer, locale: 'transfer_ownership'" href="#"></a></li>
<li data-bind="visible: !$parent.IS_WATCH_ONLY && isMine() && !locked()"><a data-bind="click: lock, locale: 'lock_token_issuance'" href="#"></a></li>
Expand Down