Go up to the CCC HW page (md) | view one-page version
In this assignment you will be writing a series of Bitcoin scripts to enact transfers. You will be using a Bitcoin test network to do so. While regular Bitcoin uses the abbreviation BTC, we will use the abbreviation ‘tBTC’ (for test-BTC) for the Bitcoin on our test network.
There are four separate Bitcoin scripts that you will need to write. You will need to be familiar with the Bitcoin slide set, specifically the Bitcoin Script and Cross-Chain Transactions sections. You will also likely need to refer to the Bitcoin Script wiki page.
You will be submitting an edited version of scripts.py (src).
Any changes to this page will be put here for easy reference. Typo fixes and minor clarifications are not listed here. So far there aren’t any significant changes to report.
This assignment uses the python-bitcoinlib package (documentation is here, if you are interested, but you probably won’t need it). Thus, this assignment must be completed in Python. You can install the Python package via pip install python-bitcoinlib
(you may need to use pip3
on your system). Note that bitcoinlib, python-bitcoinlib, and bitcoin are all different libraries! We are specifically using python-bitcoinlib.
You also will have to install the requests
library: pip install requests
, as one of the provided files, below, uses that library.
We provide you with a few files to use:
This can be a tricky assignment, and there are a lot of ways to run into problems. We include a number of hints here to try to head that off – please read through all of these!
utxo_index
value in scripts.py. Note that the command-line parameter will override the utxo_index
value.VerifyScript()
method, which the provided code base calls for you before any attempted broadcast transaction. So if you see an error such as, verifyerror: "bitcoin.core.scripteval.VerifyOpFailedError: EvalScript: OP_EQUALVERIFY failed
, or similar, it means that the Bitcoin library was able to detect that your script would not work, and did not broadcast the transaction.create_CHECKSIG_signature()
function in the scripts.py file – use it! See the comments in that file for details as to how. We also describe its usage below (the end of the Python Library section).OP_2
opcode. In fact, OP_2
happens to have integer value 82, and the integer value 2 has a different meaning.We will add to this list as more errors (and their solutions) are reported to us.
If you have a M1 Mac, there are a few issues you should be aware of.
Installing OpenSSL via homebrew has caused errors in past semesters. These errors report a problem with the “libeay32” library. Here are some possible solutions that have helped in the past:
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/bitcoin
(that may differ on your machine), in core/key.py
, replace _ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library('ssl.35')
or ctypes.util.find_library('ssl')
or ctypes.util.find_library('libeay32')
with _ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library('ssl.35')
or ctypes.util.find_library('ssl'))
.We cannot vouch for any of these solutions; we just collected a bunch of Piazza responses from previous semesters.
As we do not want to have to buy, and likely lose, real BTC, we are going to use a Bitcoin test network. Because the coins we are going to be using are not “real” Bitcoins, we will use the abbreviation ‘tBTC’ (for testnet-BTC) instead of ‘BTC’. When using a test network, you get coins for free via a faucet – in the same way that a water faucet provides water once turned on, so does a testnet faucet provide free tBTC when requested.
You will need around 0.001 (10−3) tBTC for this assignment. You can obtain this all at once, or as needed throughout the assignment. You will likely have to use multiple faucets, or use the same faucet multiple times (there is a multiple-hour wait between requests to a given faucet) to obtain this amount.
python3 bitcoinctl.py genkey
, and note both the public and private keys. While these keys are not valid on the main Bitcoin test network – the have a different value for the version byte in the invoice address – you will need them throughout this assignment.
private_key_str
and invoice_address
fieldstxid_funding_list
; each one is just a separate string in that listpython3 bitcoinctl.py urls
. As you fill in more transaction hashes throughout this assignment, re-running this command will show an increasing list of URLs.txid_split
is the particular transaction hash that you are splitting, which should be in the txid_funding_list
list – you may have to run this multiple times to split multiple faucet funding UTXOs, each with a different transaction hash from the faucetsplit_amount_to_split
is how much is in the incoming UTXO; look this up on https://live.blockcypher.com to get the correct amountsplit_amount_after_split
is how much you want in each UTXO after the split – it should not be less than 0.0001 BTC (10−4), and 0.001 (10−3) – the default – is idealsplit_into_n
attempts to determine how many UTXOs to split it into, and you should not need to change itutxo_index
– the UTXO index is set via a command-line parameter, described belowpython3 bitcoinctl.py split <utxo>
to split your coins. The <utxo>
field is the integer UTXO index, which is indexed from 0. This uses the values in the splitting coins section of scripts.py that were just discussed.
txid_split_list
list – there are two ways to determine that hash:
hash
linepython3 bitcoinctl.py urls
to get the URL), and note the transaction hash of the split transaction – it should be the top transaction listed, and will have 9 or 10 different outputs. The transaction hash itself is also listed in the output from the split transaction – it’s the hash
field of the dictionary, and is about a half a dozen lines down. Record that transaction hash in scripts.py in txid_split_list
Be careful not to lose the information (keys and TXIDs) that you recorded above. To prevent abuse, the faucets only allows one request every so often (1 to 12 hours, depending on the faucet) for a given IP address or tBTC address. If you need more during that 12 hour window, or you are running into ‘exceeded limit’ issues, you can try requesting it through a different faucet or through your cell phone. If you put it on cellular (meaning disconnect from UVA’s network), it will report a different IP address to the faucet.
The python-bitcoinlib
library for Python handles much of the heavy lifting – conversion from one type to another, encryption, signing, verification, etc. If you were to enter actual keys that have real BTC then you could use this library to make real BTC transactions.
While the library can do many things, below is a quick summary of the relevant aspects that you will need to know for this assignment.
Creating Bitcoin scripts is really just putting everything into a list. All the opcodes are named the same as on the Bitcoin Script page. For example, here is the the provably unspendable transaction discussed in the lecture slides: [ OP_RETURN ]
. Other things that go into scripts – signatures and public key hashes – are also just included in such a list. Assuming you got the types correct, then the library will create the full script from such a list.
You will enter your private key into scripts.py as a string. To convert it to the private key format that the library uses, you pass it to CBitcoinSecret()
. The object returned has a .pub
field, which is the public key (the type of that public key is CPubKey
). And that public key can be converted into a Bitcoin address by calling P2PKHBitcoinAddress.from_pubkey(public_key)
. In fact, this is almost the exact code used by the bitcoinctl.py (src) file to generate keys (although for that it just used random data – via os.urandom(32)
– instead of a pre-defined public key string). Here is an example as to how to convert your keys:
my_private_key_str="..." # fill this in
from bitcoin.wallet import CBitcoinSecret, P2PKHBitcoinAddress
from bitcoin import SelectParams
SelectParams('testnet')
private_key = CBitcoinSecret(my_private_key_str)
public_key = private_key.pub
address = P2PKHBitcoinAddress.from_pubkey(public_key)
CPubKey
– the type of the .pub
field of the object returned by CBitcoinSecret()
Creating a signature is a bit more involved.
txin
: the transaction input from the transaction that created the UTXO that is being redeemedtxout
: the transaction output from the transaction that created the UTXO that is being redeemedtxin_scriptPubKey
: the pubKey script (aka output script) of the UTXO being redeemedprivate_key
: the secret key being used to sign this transactiontx = CMutableTransaction([txin], [txout])
:sighash = SignatureHash(CScript(txin_scriptPubKey), tx, 0, SIGHASH_ALL)
:sig = private_key.sign(sighash) + bytes([SIGHASH_ALL])
:create_CHECKSIG_signature()
, in scripts.py to perform these calls.The UTXO indices that you created when you split your tBTC are paid to a standard P2PKH transaction. Your task is to redeem them by writing the appropriate scripts (pubkey and sigscript) to redeem the coins from one of the UTXOs. It should be paid back to the designated return address – use the tbtc_return_address
variable, defined at the top of the scripts.py
file, as the receiver of this transaction.
To complete this transaction, you need to complete four things:
P2PKH_scriptSig(...)
function provides the sigscript needed to redeem the UTXO being spent. The UTXO that is being redeemed – one of the split UTXO indices from above – requires a P2PKH sigScript to redeem one of the indices.P2PKH_scriptPubKey(address)
function defines the pubKey script (aka output script). This was discussed in lecture in the P2PKH transaction slides. This script creates a new UTXO, payable to the tBTC return address, that is also a P2PKH script. The parameter is of type P2PKHBitcoinAddress
, which is what the P2PKHBitcoinAddress.from_pubkey(public_key)
call (shown above) returns; a variable this type can be put directly into a script.IMPORTANT NOTE: For the sigScript, the public key used in the P2PKH script must come from the private_key
parameter to the P2PKH_scriptSig()
(it’s private_key.pub
). If you use your global public key, it will work for this part, but it will fail for successive parts of this assignment.
When you have finished the script, you can run it via python3 bitcoinctl.py part1 <utxo>
, where <utxo>
is the particular UTXO index from the split transaction that you are using to fund this one; it will report an error if you get it wrong. The <utxo>
field is the integer UTXO index, which is indexed from 0. Some common errors at this point are:
If it works, you will see a JSON dictionary printed to the screen. Record the transaction hash of that transaction in txid_p2pkh
. The TXID is the ‘hash’ field in the dictionary that is printed to the screen when run. You can then run python3 bitcoinctl.py urls
to get the URL for the transaction that you just executed. It may take up to 10 minutes for it to be mined into the blockchain.
You should notice your wallet balance has decreased.
For this tBTC transaction, you are going to create an algebraic puzzle script – one that anybody can redeem as long as they complete the numerical puzzle.
You will first need to pick two 4-digit base-10 numbers (meaning between 1,000 and 10,000 in value) for the values of p and q in the equations below. You can take your UVA SIS ID and split the digits in half. These will be the solutions to the linear equations below. You will likely need to tweak these numbers in a moment. You will need to store those values into puzzle_txn_p
and puzzle_txn_q
in scripts.py.
The puzzle transaction will deal with the solution to the following two linear equations:
2x + y = p x + y = q
You can use an online linear question solver, such as this one, to find the solution. And make sure that the solutions are positive integer values! If not, then tweak one (or both) of your solutions (p and/or q) until you have integer solutions. You can also change which of the equations has the higher of p and q to see if that will help as well. Once you know those values, put them into puzzle_txn_p
and puzzle_txn_q
in scripts.py. You will also want to x and y solutions to these equations into puzzle_txn_x
and puzzle_txn_y
.
For this part, you will create a transaction to redeem one of the split UTXO indices that were created, above. The pubKey (output) script of that newly created transaction will be specified in the puzzle_scriptPubKey()
function in scripts.py. Note that because this output script does not depend on the receiver’s public key, that is not provided as a parameter to the function. Also note that the OP_MUL
opcode has been disabled on the Bitcoin networks, so you can’t use that. This pubKey script should verify that the two values specified by the redeemer fulfill those two equations. Once this is created, run python3 bitcoinctl.py part2a <utxo>
– remember to choose an unspent UTXO index first via the second command line parameter. That <utxo>
field is the integer UTXO index from the split transaction, which is indexed from 0. As above, record the transaction hash into the txid_puzzle_txn1
variable.
You will also need to create the sigScript that redeems this transaction. This should ONLY contain the two values x and y – their order is up to you, as long as it works with the script you created above. That script goes into puzzle_scriptSig()
.
IMPORTANT: Your your pubKey script should actually do the math to test that the two values (that will be provided in the sigScript) do, in fact, fulfill those linear equations. Just testing for equality to your pre-specified x and y is not the point of this portion of the assignment. This is something we explicitly check for when grading the assignment.
This also does not depend on any signatures, which is why there are no parameters to that function. Ensure that the previous transaction has been mined into the blockchain, which may take up to 10 minutes – if you have entered the previous transaction’s URL into the txid_puzzle_txn1
variable, you can get the URL of that transaction via python3 bitcoinctl.py urls
. When ready, you can send the redeeming trasnaction to the tBTC network via python3 bitcoinctl.py part2b <utxo>
(remember to choose an unspent UTXO index first). The <utxo>
field is the integer UTXO index, which is indexed from 0. As this UTXO is from the above transaction, and that transaction only created one UTXO index, the <utxo>
value should be 0.
Record the transaction hash into txid_puzzle_txn2
.
WARNING: There are occasionally people who redeeming all of our puzzle transactions (part 2a) on the Bitcoin test network – they are parsing the output script, computing the answers, and redeeming the transaction. Because this script does not have a signature, anybody can redeem it. If you keep getting oddball errors, and you have set your transaction hash and UTXO index correctly, check the transaction page itself to see if it’s already spent. When running part2b
it might also report that the UTXO has been spent. For the puzzle transactions, blockcyper.com just says “unknown script type”, and does not indicate if it’s spent or not, but https://blockstream.info does; search for the transaction from part 2a, and in the lower-right of that page where it lists the output, click on the “Details +” link – it will list the hash of the spending transaction. You should include that transaction hash in your scripts.py, even though you were not the entity that redeemed it. When grading it, we will look at (1) if the transaction from part 2a was broadcast, (2) whether it was redeemed (by you or somebody else), and (3) whether the two scripts verify with each other. Thus, it does not have to be your transaction that redeems part 2a in part 2b, but your part 2b script does have to verify with your part 2a script.
You will notice that the amount in each UTXO index from the split transaction is 0.001 tBTC. For the first half of this puzzle transaction, the amount transacted is slightly less (90% of that, or 0.0009). The difference – 0.0001 tBTC – is the transaction fee. Even though this is a test network, and no actual money is involved, your transaction will not be mined into the blockchain unless you have a sufficient transaction fee. For the second half of this, we need to lower the amount even further, so the amount transacted is 90% of 0.0009, or 0.00081; this lowering is done automatically by the code base provided. The difference here – 0.00009 tBTC – is the transaction fee. This automatic lowering of the transaction amount will recur elsewhere in this assignment.
You are going to create a multi-signature transaction, which must use the OP_CHECKMULTISIG opcode (or the OP_CHECKMULTISIGVERIFY opcode).
To set this up, you will need to create three more key pairs using python3 bitcoinctl.py keygen
. Save these in the variables for Alice, Bob, and Charlie in part 3 of the scripts.py
file. These addresses don’t need any tBTC – we just need the key pairs to perform digital signatures.
The scenario is this: you are taking on the role of a bank. Three siblings (Alice, Bob, and Charlie) have deposited money into an account, and it can be redeemed if two of the three – and also the bank! – agree to it. Formally, the transaction must be signed by the bank (i.e., you – via the keys in the my_private_key_str
variable) and any two of the three siblings (via their private keys).
This will actually require two transactions. The first redeems one of the split UTXOs and creates a multi-signature pubKey (output) script. The second redeems that multi-signature script and pays it to the tBTC return address.
P2PKH_scriptSig()
. So you don’t have to write this again. If your part 1 (P2PKH) worked, then this should work as well.multisig_scriptPubKey()
function.txid_multisig_txn1
variable.multisig_scriptSig()
function.P2PKH_scriptPubKey()
. So you don’t have to write this again. If you part 1 (P2PKH) worked, then this should work as well.txid_multisig_txn2
variable.You only have to write the pubKey (output) script of the first transaction, and the sigScript (input) script of the second transaction. The other two parts (sigScript of the first and pubKey script of the second) are taken from your code in part 1 (P2PKH) – so if that is not working, then this will not work either.
The first step is to create the transaction that sets up the multi-signature requirement in the pubKey script. This must use either the OP_CHECKMULTISIG
or OP_CHECKMULTISIGVERIFY
opcode! See the description of these opcodes in the lecture slides. The task, then, is to fill in the multisig_scriptPubKey()
function. Recall that the sigScript will be used from your code for part 1 (P2PKH). We recommend that you write this and the next script – the redeeming script – together, and trace its stack execution (on paper or similar). When you are ready to run it, run python3 bitcoinctl.py part3a <utxo>
. The <utxo>
field is the integer UTXO index, which is indexed from 0. Once successful, record the transaction hash in the txid_multisig_txn1
variable in scripts.py.
The second step is to create a transaction that will redeem it. You will have to wait until the previous transaction receives at least one confirmation before you can execute this part, which can take up to 10 minutes. This part requires that the txid_multisig_txn1
variable, from the first step above, is set properly, as that is the UTXO that is going to redeem. You will fill in your sigScript into multisig_scriptSig()
. Recall that the pubKey script for this transaction will be used from your answer for part 1 (P2PKH). When you are ready to run it, run python3 bitcoinctl.py part3b <utxo>
. The <utxo>
field is the integer UTXO index, which is indexed from 0; it is likely 0, since there is only one output from the UTXO from the transaction from part 3a. Once successful, record the transaction hash in the txid_multisig_txn2
variable in scripts.py.
IMPORTANT NOTE: For the OP_CHECKMULTISIG
(or OP_CHECKMULTISIGVERIFY
), it should have ONLY the keys/signatures of Alice, Bob, and Charlie; the bank signature should not be in there. Instead, the bank signature should be separate and verified with an OP_CHECKSIG
(or OP_CHECKSIGVERIFY
). The reason is that if everything is in the OP_CHECKMULTISIG
, then an empty set of signatures and keys will verify correctly. This is relevant because there is some user on the Bitcoin test network who is trying to redeem your UTXOs. In particular, it has been observed when the UTXO was only verified with a single OP_CHECKMULTISIG
.
In this part you will create the scripts for a cross-chain transaction. Typically this would be for two different cryptocurrencies. However, since we have only learned Bitcoin Script, we will use that for both parts. There are many cryptocurrencies that are forks of Bitcoin, and thus have the same scripting language, so the same program could work for any of them. A completely different cryptocurrency, with a different scripting language, would have an analogous script. However, to test this we will be using two different Bitcoin testing blockchains.
Below you will be obtaining a Bitcoin key pair and funds on a separate Bitcoin blockchain. There will be three script producing functions in scripts.py, although you only have to create one. The first one, atomicswap_scriptPubKey()
, will create the TXN 1 and TXN 3 from the slides; this is the one that you have to create. This script will be used for BOTH of these transactions (with different parameters, of course) on the two different blockchains by the provided code in bitcoinctl.py (src). The second function, atomcswap_scriptSig_redeem()
, will be when Alice or Bob knows the secret value and is redeeming the BTC; we provide this function for you in scripts.py. This is used in steps 5 and 6 on cross-chain atomic swap procedure slide. The third function, atomcswap_scriptSig_refund()
, will create the time-out redeeming script, which is TXN 2 and TXN 4 from the slides; we also provide this function for you in scripts.py. Again, this will be used on both blockchains by the provided code. There are some requirements for what has to be in these scripts, described below (in “Notes and hints”).
In addition to the lecture slides, you may want to refer to the Atomic swap article in the Bitcoin wiki.
So far we have been using tBTC on the Bitcoin Testnet. For this part we will also be using the BlockCypher Testnet – this is also a Bitcoin network for testing, and it operates just like the Bitcoin Testnet we’ve been using. Bitcoin on this other testnet will be abbreviated as BCY (for BlockCYpher testnet). Note that we have been using blockcypher.com to view all of our transactions, since that site can display transactions and invoice addresses on both of these Bitcoin test networks.
In this part, you (Alice) and Bob will be exchanging coins through a cross-chain transaction. You will need to be familiar with the cross-chain transaction section of the Bitcoin slide set. You are going to take on the role of Alice in the lecture slides.
As an overview, this is what is going to happen.
my_private_key_str
and my_invoice_address_str
in scripts.py). Bob will receive it in the account that was created for him above (bob_private_key_str
and bob_invoice_address_str
in scripts.py). This corresponds to part 1 of the cross-chain transaction – again, you are taking on the role of Alice. You will only be creating TXN1 from that slide; we are omitting TXN2.Note that you are only creating one function, called atomicswap_scriptPubKey()
. This is going to be used for both of the steps 1 (where you (Alice) send tBTC to Bob) and 2 (where Bob send BCY to you (Alice), above.
To set this up, we need to create Bitcoin keypairs for the BlockCypher testnet, and use a faucet to give us some coins. The process for creating keys and funding the accounts is different for the BCY test network. The BCY blockchain uses a different version byte in the invoice address, so we cannot re-use the invoice addresses generated above for the BCY blockchain.
Create an account at https://accounts.blockcypher.com/, which will allow you to get an API token. Your token will be a hex number such as 0123456789abcdef0123456789abcdef. Save this token somewhere safe! You are welcome to record it in scripts.py (blockcypher_api_token
is set aside for that), but that’s completely optional.
You will need to be able to run the curl
program from the command-line. Try running it without any parameters to see if it is installed. If not, you will have to install it – web searches for how to install curl will guide your way.
To create keys, you will need to run the following from the command line, putting your token in there instead of API_TOKEN
. You should do this twice, one for you and once for Bob.
curl -X POST 'https://api.blockcypher.com/v1/bcy/test/addrs?token=API_TOKEN'
Save those tokens in scripts.py; yours go into my_private_key_bcy_str
and my_invoice_address_bcy_str
. Note that the curl
command returns 4 values, but we only need to save two for each of the accounts (you are welcome to save the others, if you would like – just name the variables appropriately, or put them into comments). Also note that the format of the private key is different for this network – this one is hex encoded, whereas the one for the tBTC network was base-58 encoded. The provided assignment code properly handles this difference.
Run that curl
command again for Bob’s keys, and save them into bob_private_key_bcy_str
and bob_invoice_address_bcy_str
. We only need to save the private key and the address, but you are welcome to save the other two parts as well.
Only Bob needs BCY funds. You can fund his account via the following command, replacing both Bob’s address for BOB_BCY_ADDRESS
and your token for API_TOKEN
:
curl -d '{"address": "BOB_BCY_ADDRESS", "amount": 100000}' "https://api.blockcypher.com/v1/bcy/test/faucet?token=API_TOKEN"
The above command will return a transaction hash; save that in txid_bob_bcy_funding
. If you run python3 bitcoinctl.py urls
it will display the full URL that you can use to view that funding transaction.
We will need to split Bob’s funds into parts, just like we did in the setup, above. Make sure that you have Bob’s private key and invoice address set in scripts.py (in bob_private_key_bcy_str
and bob_invoice_address_bcy_str
), as well as the transaction hash that funded the wallet (in txid_bob_bcy_funding
). Lastly, look at the URL for that funding transaction (you can get that via python3 bitcoinctl.py urls
) and determine the UTXO index – that needs to be set in the second command line parameter. You will also need to change split_amount_to_split
and split_amount_after_split
(the latter can be 0.0001 (10−4 for this). Then run python3 bitcoinctl.py splitbcy <utxo>
– notice that the command is splitbcy
, not split
! The <utxo>
field is the integer UTXO index, which is indexed from 0. Record the transaction hash returned from that execution run in txid_bob_bcy_split
.
Whew! The setup for this part is all done! Now onto the scripting part….
Because we are swapping between two different Bitcoin test networks, the atomic swap code is the same – both are in Bitcoin script. TXN 1 (from here in the slides) and TXN 3 (from here in the slides) differ only by the public keys:
Your script code for this will go into the atomicswap_scriptPubKey()
function.
NOTE: the hash that is used in this part is RIPEMD-160, not SHA-256. So be sure to use the OP_HASH160
opcode to get the hash, and not the OP_SHA256
opcode.
To help you with this code, we provide the two redeeming functions:
atomcswap_scriptSig_redeem()
is when Alice or Bob, knowing the secret, wants to redeem the transaction. The OP_TRUE
is for use in an OP_IF
statement in the script you have to write.atomcswap_scriptSig_refund()
is used when the transaction times out – that’s when the sender can get a refund on his/her transaction; this has to be signed by both Alice and Bob. The particular script sent to redeem this would be TXN 2 (from here) or TXN 4 (from here). You do not have to implement TXN 2 or TXN 4. In practice, this would be time-locked in the future – it would include a timestamp and call OP_CHECKLOCKTIMEVERIFY
. Because the time can not be known when the assignment is written, and as it will vary for each student, that part is omitted from the script.The last thing to do before you write the atomicswap_scriptPubKey()
function is to determine what the secret is – pick a number between 17 million and 2 billion, and save that in atomic_swap_secret
. (It needs to be in that range to ensure it’s encoded as a 4-byte integer). Keep in mind that this secret is only known to Alice initially; Bob is only given the hash of the secret (the bitcoinctl.py file handles that part for you).
Once you have written the script in the atomicswap_scriptPubKey()
function, you can perform your cross-chain transaction. This involves four steps, which are outlined below. After you perform each step, enter the transaction hash into the variable as specified, and then get the URL via python3 bitcoinctl.py urls
. You can check that URL to ensure that it works properly. As with the previous transactions, you have to wait up to 10 minutes for at least one confirmation before you can redeem that UTXO.
atomicswap_scriptPubKey()
function. Be sure to set the correct UTXO before running this part! This is sending tBTC, so the UTXO index is from the very first split of tBTC. This is run via python3 bitcoinctl.py part4a <utxo>
. The <utxo>
field is the integer UTXO index, which is indexed from 0. Save the transaction hash for this in the txid_atomicswap_alice_send_tbtc
variable.atomicswap_scriptPubKey()
function. Be sure to set the correct UTXO before running this part! This is sending BCY, so it’s the split that was done earlier in this section. This is run via python3 bitcoinctl.py part4b <utxo>
. The <utxo>
field is the integer UTXO index, which is indexed from 0. Save the transaction hash for this in the txid_atomicswap_bob_send_bcy
variable.python3 bitcoinctl.py part4c <utxo>
. The <utxo>
field is the integer UTXO index, which is indexed from 0. As this is from TXN 2 above, the UTXO index is probably 0. Save the transaction hash into txid_atomicswap_alice_redeem_bcy
.python3 bitcoinctl.py part4d <utxo>
. The <utxo>
field is the integer UTXO index, which is indexed from 0. Save the transaction hash into txid_atomicswap_bob_redeem_tbtc
.atomicswap_scriptPubKey()
function will create TXN 1 and TXN 3 from the slides; because this has to check for two cases, it has to have an if/else structure
atomcswap_scriptSig_redeem()
, used in steps 5 and 6 on cross-chain atomic swap procedure slide, just provides the hash and signature; this is provided to you in scripts.py. You should design your function above to work with this as the redeeming script.atomcswap_scriptSig_refund()
, which is TXN 2 and TXN 2 from the slides, is also provided to you. You should design your function above to work with this as the refund script.Once you have completed this assignment, you should pay any unspent tBTC UTXOs back to the tBTC return address, which is in the the tbtc_return_address
variable in scripts.py. You can use the script from part 1 (P2PKH) for this, as it already pays the tBTC return address – just change the UTXO value and re-run it; repeat until all the UTXO indices are spent. If you have any other inputs – perhaps you used the faucet multiple times – just change the txid_split
variable (and the UTXO and the send_amount
), and then call python3 bitcoinctl.py part1 <utxo>
. The <utxo>
field is the integer UTXO index, which is indexed from 0. But be sure to change those values back!!!
You do not need to save the hashes from these transactions – we are going to verify it by checking the wallet’s balance.
When done, there should not be any unspent UTXOs remaining! We are going to test this by seeing if the amount of tBTC left in your wallet address is zero.
You do not need to do this for the extra BCY in your account(s).
NOTE: Make sure all the transactions are mined into the blockchain BEFORE you submit them. If you go to the URL for that particular transaction, as long as it has at least one confirmation, it is considered mined into the blockchain.
The only file you need to submit to Gradescope is scripts.py. There will be a few sanity checks made when you submit it. Those checks are:
userid
returns a non-zero length stringmy_private_key_str
, my_invoice_address_str
, and txid_initial
)txid_
) are of the expected length (64 characters)VerifyScript()
. This does NOT mean they work correctly! It just means that the VerifyScript()
function did not detect errors. For one example, the VerifyScript()
has no way of knowing the actual signature required on the blockchain. This is the same function that is run before broadcasting the transactions to the network. The full grading will check what is on the blockchain itself.my_invoice_address_str
) is zeroRate Limiter: The various aspects of this program are verified by checking via blockcyper.com’s API to obtain the wallet, transaction, balance, etc. As with most websites, there is a rate limiter, and if there are too many requests in too little a time period, then it will block requests to that IP address for some period. If everybody submits the assignment around the same time, this rate limiter will kick in, and the auto-grader will reports lots of errors. We will re-run the auto-grader at a later point to ensure that it is evaluated properly, but you will not see useful results when you submit your assignment. We have set up a proxy to help this issue (it caches previously made requests). However, if there are still too many requests, it will still run into the rate limiter. Unfortunately, there is nothing more we can do about this.
Autograder notes: As we know, a transaction is not considered valid until it is mined into the blockchain. It may be that your transaction has not yet been mined, which means it will report as not having happened. This means some of the visible tests when you submit your assignment could fail. As long as you submitted the transaction, you do not need to worry about it – we will re-run the auto-grader a day or two later to catch all these cases.