Bitcoin Cash Pope's Sermon on Easter
During the Paschal Vigil we've been hearing twelve prophecies, the fourth one being the story of Israelites crossing the Red Sea.
[...] And the Lord said to Moses: Stretch forth thy hand over the sea, that the waters may come again upon the Egyptians, upon their chariots and horsemen. And when Moses had stretched forth his hand towards the sea, it returned at the first break of day to the former place: and as the Egyptians were fleeing away the waters came upon them, and the Lord shut them up in the middle of the waves. [...] But the children of Israel marched through the midst of the sea upon dry land, and the waters were to them as a wall on the right hand and the left: and the Lord delivered Israel on that day out of the hand of the Egyptians. And they saw the Egyptians dead upon the sea shore, and the mighty hand of the Lord had used against them: and the people feared the Lord, and they believed the Lord, and Moses His servant. Then Moses and the children of Israel sung this canticle to the Lord, and said:
*Let us sing to the Lord, for He is gloriously honored: the horse and the rider He hath thrown into the sea: He has become my Helper and Protector unto salvation.* [...]
Israelites rejoiced their liberation and the death of Egyptians. But what about angels that were seeing this? Midrash, a Jewish traditional interpretation, tells us:
The ministering angels wanted to sing their song**,** for the angels would sing songs to each other, as it states: “And they called out to each other and said”, but the Holy One, Blessed be He, said: **The work of My hands,** **the Egyptians, are drowning at sea, and you** **wish to say songs?** This indicates that God does not rejoice over the downfall of the wicked.
Jewish tradition understands something that people crossing the sea didn't. This is clearly shown in subsequent chapters of the Exodus book. Israelites have quit Egypt but did Egypt quit Israelites? They have been liberated externally but they have remained slaves mentally. The entire generation had to pass away for the nation to walk away from the desert, when it's a trip that should have taken about several weeks (actually, they reached Kadesh Barnea that quick, but were too afraid to move forward and fight, just as you would expect from a slave).
What can Bitcoin Cash community learn from this story?
After Genesis block, a lot happened in the Bitcoin community and it somehow ended up enslaved in Blockstream hands, small blocks propaganda, maximalism, SoV and other bullshit. We've had our own Exodus led by our own Moses. And now we're wandering in the desert despite there's a roadmap. People even similarly complain about Moses being a bad leader in between rejoicing Egyptians failures. https://blockchair.com/bitcoin-cash/block/478559 https://twitter.com/deadalnix https://www.bitcoincash.org/roadmap.html
Just look how many r/btc topics are about Egyptians. Lightning bad, Liquid bad, r/bitcoin censorship, u/nullc bad, Blockstream bad. How many topics are recycled years-old stories. If we've left Egypt why are we still mentally in there?
What is freedom? It's not just an ability to think or do what we want. Thoughts can be compulsive, so do actions. Is it freedom to hate something or is it a compulsion?
This is the wisdom of the midrash. Angels were rebuked for wanting to join a celebration that was supposed to be rejoicing freedom but it was a dance of mental slaves rejoicing the harm of former masters.
**Freedom is a power of doing what is ought to be done. Wish your enemies good luck and move forward not looking back at them.**
We're also wandering through the desert longer than it's needed to in a strictly technical sense. We are still tied to the Core codebase full of design decisions taken around 1MB blocks assumption. We're running in place backporting bugfixes from Core.
Freedom means responsibility. It means self-ownership. **Without** **taking code ownership** **we'll be still wandering in the desert** enslaved technically by Core codebase which provides us safety but not freedom. Just like Israelites over and over referring back to how safe it was in Egypt. https://read.cash/@Bitcoin_ABC/what-is-bitcoin-abcs-take-code-ownership-project-79354f13
When the bad leader Moses wasn't delivering, people rebelled. Many times. Always with a poor outcome.
**The Bitcoin Cash Pope herby condemns the infights between "Team ABC" and "Team BCHN" and even more so the tribalism emerging around them. Solutions are preferred over accusations.** Start with understanding (not presuming but understanding) reasons behind each position. Both teams have brilliant talents and it's a pity they escalated hatred between each other. The initial IFP proposal sparked alternatives like Flipstarter or ABC Fundraiser campaign, all having their own, unique sets of tradeoffs. https://read.cash/@flipstarter https://fund.bitcoinabc.org/
Easter is the solemnity of hope. Let us hope the Bitcoin Cash community learn freedom faster than Israelites.
With papal blessing,
Tendo Pein
BCH Covenants with Spedn
*This is an updated and refreshed version of the article originally publihed* *here**.* https://honest.cash/v2/pein_sama/spending-constraints-with-op_checkdatasig-172
The Great Schism in Christianity happened over a single word: *filioque.* What a silly thing, you might say - to fight over a word. But...
*What is the good of words if they aren't important enough to quarrel over? Why do we choose one word more than another if there isn't any difference between them? If you called a woman a chimpanzee instead of an angel, wouldn't there be a quarrel about a word? If you're not going to argue about words, what are you going to argue about? Are you going to convey your meaning to me by moving your ears? The Church and the heresies always used to fight about words, because they are the only thing worth fighting about. [****G.K. Chesterton****]*
Similarily, the Hashwar in Bitcoin Cash happened over a single opcode - a word in the Bitcoin Virtual Machine vocabluary. Was it worth fighting over? Let's explore some of the new capabilities that `OP_CHECKDATASIG` enables.
One of the limitations of Bitcoin Script was that you could only specify if one can spend the coin but there was no way of constraining how. In this article, I’ll demonstrate that this is possible now. For better readability, all code will be expressed in Spedn, a high-level language for Bitcoin Cash smart contracts. http://spedn.readthedocs.io/
Simple Things
We'll start with a simple contract (a transaction output definition) in that is basically an ordinary Pay to Public Key Hash:
contract Constraint(Ripemd160 pkh) {
challenge spend(PubKey pk, Sig sig) {
verify hash160(pk) == pkh;
verify checkSig(sig, pk);
}
}
So in plain English, we check whether a public key (`pk`) provided in input's `scriptSig` field matches the hash (`pkh`) specified in this output and then, that a signature (`sig`) also provided in in `scriptSig` matches the key (`pk`) and the serialized transaction body. Nothing fancy, this is how most of the transactions in Bitcoin Cash are constructed.
Fancy Things
Now the fancy thing. The only thre differences between `OP_CHECKSIG` *(*`checkSig` *function in Spedn) and* `OP_CHECKDATASIG` (`checkDataSig` in Spedn) is that the former gets the signature preimage (a message to be signed) implicitly, the preimage is hashed twice with SHA256 rather than once and the signature contains additional `sighash` flag. So it is possible for `checkDataSig` to mimic plain old `checkSig`, we just have to provide the same serialized tx body in the `scriptSig`, hash it and strip the `sighash` flag with `toDataSig` function. It might look like this:
contract Constraint(Ripemd160 pkh) {
challenge spend(PubKey pk, Sig sig, [byte] preimage) {
verify hash160(pk) == pkh;
verify checkSig(sig, pk);
verify checkDataSig(toDataSig(sig), sha256(preimage), pk);
}
}
Here, we've just ensured that the preimage provided in `scriptSig` is exactly the same as the one used to perform `checkSig` operation.
But, what's the point? Well, our contract has just become aware of the content of the transaction spending it. And because of that, we can now introspect it. And, for example, check whether the tx outputs meet our conditions.
Fancier Things
According to the spec, here are the components of the preimage: https://www.bitcoincash.org/spec/replay-protected-sighash.html#digest-algorithm
nVersion of the transaction (4-byte little endian)
hashPrevouts (32-byte hash)
hashSequence (32-byte hash)
outpoint (32-byte hash + 4-byte little endian)
scriptCode of the input (serialized as scripts inside CTxOuts)
value of the output spent by this input (8-byte little endian)
nSequence of the input (4-byte little endian)
hashOutputs (32-byte hash)
nLocktime of the transaction (4-byte little endian)
sighash type of the signature (4-byte little endian)
If we want to inspect the outputs, here we have (8). We can cut it out with `OP_SPLIT` applied twice. Because the size of (5) is not fixed, we'll have to count bytes from the end. For that, we can measure the preimage size with `OP_SIZE`.
contract Constraint(Ripemd160 pkh) {
challenge spend(PubKey pk, Sig sig,
[byte] preimage) {
verify hash160(pk) == pkh;
verify checkSig(sig, pk);
verify checkDataSig(toDataSig(sig), sha256(preimage), pk);
(_, [byte;40] tail) = preimage @ size(preimage) - 40;
([byte;32] hashOutputs, _) = tail @ 32;
}
}
Unfortunately, it's only a hash, we can't see what outputs produced it. But we can repeat the trick that we have already done once with the preimage as a whole - we can require the `hashOutputs`'s preimage to be put in scriptSig and check if it matches the hash. For the sake of this demonstration, we'll assume the transaction spending this contract will use sighash type set to Single which mean the `hashOutputs` will be made from a single output corresponding to the input spending the contract. The output serialization for the `hashOutputs` is concatenated 8-bytes little endian amount in satoshis and `scriptPubKey` (script).
contract Constraint(Ripemd160 pkh) {
challenge spend(PubKey pk, Sig sig,
[byte] preimage, [byte] script, int amount) {
verify hash160(pk) == pkh;
verify checkSig(sig, pk);
verify checkDataSig(toDataSig(sig), sha256(preimage), pk);
(_, [byte;40] tail) = preimage @ size(preimage) - 40;
([byte;32] hashOutputs, _) = tail @ 32;
verify hash256(num2bin(amount, 8) . script) == Sha256(hashOutputs);
}
}
So we do the same in Script. We convert a number to an 8-bytes long little endian form with num2bin, concatenate it with script, hash it and compare the result with hashOutputs. Because Spedn is strongly typed, we also had to cast hashOutputs to the matching type explicitly.
The Fanciest
All the above have led us to the point where the contract knows what is the amount and script this contract will be spent to. So now we could impose some constraints on that. For example:
contract Constraint(Ripemd160 pkh, int minimum) {
challenge spend(PubKey pk, Sig sig,
[byte] preimage, [byte] script, int amount) {
verify hash160(pk) == pkh;
verify checkSig(sig, pk);
verify checkDataSig(toDataSig(sig), sha256(preimage), pk);
(_, [byte;40] tail) = preimage @ size(preimage) - 40;
([byte;32] hashOutputs, _) = tail @ 32;
verify hash256(num2bin(amount, 8) . script) == Sha256(hashOutputs);
verify amount >= minimum;
}
}
With this, we can impose the particular output of the transaction to have some minimal value. Maybe not the most useful thing but very simple and therefore good for the demonstration purpose. What else can be done? Here are some projects that have been developed since the original publication of this article:
Last Will plugin for Electron Cash will help you to prepare for your death so thay your bitcoins will be reliably handed over to your inheritors. https://github.com/KarolTrzeszczkowski/Electron-Cash-Last-Will-Plugin
Mecenas plugin for Electron Cash, named after Gaius Maecenas, is a non-custodial solution for recurring payments. Think - decentralized Patreon. https://github.com/KarolTrzeszczkowski/Mecenas-recurring-payment-EC-plugin https://en.wikipedia.org/wiki/Gaius_Maecenas
ChashCahnnels - a similar solution that lets users pre-approve future transactions of a specified amount, valued in any currency. The exchange rate is provided by an oracle - another functionality that `OP_CHECKDATASIG` enables. https://blog.bitjson.com/cashchannels-recurring-payments-for-bitcoin-cash-3b274fbfa6e2
Hamingja - non tradable, loyalty SLP tokens. You can spend them only in the shop that issued them. https://slpvh.github.io/hamingja/
Fancy Box
BITBOX with Spedn SDK is a great toolkit that will help you to create a working solution utilizing covenants in just one noon. https://developer.bitcoin.com/bitbox/docs/getting-started https://spedn.readthedocs.io/en/latest/bitbox.html
To compile a contract, use something like this:
const compiler = new Spedn();
const { Covenant } = await compiler.compileCode(`
contract Covenant(Ripemd160 alice) {
challenge spend(Sig sig, PubKey pubKey, [byte] preimage) {
verify hash160(pubKey) == alice;
verify checkSig(sig, pubKey);
verify checkDataSig(toDataSig(sig), sha256(preimage), pubKey);
// and your custom logic here
}
}
`);
compiler.dispose();
To find coins locked in this contract use:
const alice = bitbox.HDNode.derivePath(wallet, "m/44'/145'/0'/0/0");
const covenant = new Covenant({
alice: alice.getIdentifier()
});
const coins = await covenant.findCoins("mainnet");
And to spend them use this:
const txid = await new TxBuilder("mainnet")
.from(coins, (input, context) =>
input.spend({
sig: context.sign(alice.keyPair, SigHash.SIGHASH_ALL),
pubKey: alice.getPublicKeyBuffer(),
preimage: context.preimage(SigHash.SIGHASH_ALL)
})
)
.to("bitcoincash:qrc2jhalczuka8q3dvk0g8mnkqx79wxp9gvvqvg7qt", 500000)
.to(covenant.getAddress("mainnet")) // change address
.broadcast();
As you can see, it's trivial to provide a signature or a preimage of the transaction you're crafting.
*Now it's time to #buidl.*
[sponsors]