├── .gitignore ├── bitcoin-script ├── CHANGELOG.md ├── test │ ├── helper.rb │ └── test_ops.rb ├── Manifest.txt ├── lib │ ├── bitcoin-script.rb │ └── bitcoin-script │ │ └── version.rb ├── .gitignore ├── Rakefile └── README.md ├── i └── trolly-ponzi.png ├── stackmachine_anyone.rb ├── stackmachine_add.rb ├── hash160.rb ├── ripemd160.rb ├── stackmachine_puzzle.rb ├── pay-to-pubkey.rb ├── pubkey.rb ├── stack.rb ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | #### for testing scripts / sandbox 2 | /test/ 3 | -------------------------------------------------------------------------------- /bitcoin-script/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.0.1 / 2019-04-13 2 | 3 | * Everything is new. First release 4 | -------------------------------------------------------------------------------- /i/trolly-ponzi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openblockchains/programming-bitcoin-script/HEAD/i/trolly-ponzi.png -------------------------------------------------------------------------------- /bitcoin-script/test/helper.rb: -------------------------------------------------------------------------------- 1 | ## minitest setup 2 | 3 | require 'minitest/autorun' 4 | 5 | 6 | ## our own code 7 | 8 | require 'bitcoin-script' 9 | -------------------------------------------------------------------------------- /bitcoin-script/Manifest.txt: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | Manifest.txt 3 | README.md 4 | Rakefile 5 | lib/bitcoin-script.rb 6 | lib/bitcoin-script/version.rb 7 | test/helper.rb 8 | test/test_ops.rb 9 | -------------------------------------------------------------------------------- /bitcoin-script/lib/bitcoin-script.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'pp' 4 | 5 | ## our own code 6 | require 'bitcoin-script/version' # note: let version always go first 7 | 8 | 9 | 10 | puts BitcoinScript.banner ## say hello 11 | -------------------------------------------------------------------------------- /bitcoin-script/test/test_ops.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | ### 4 | # to run use 5 | # ruby -I ./lib -I ./test test/test_ops.rb 6 | 7 | 8 | require 'helper' 9 | 10 | 11 | class TestOps < MiniTest::Test 12 | 13 | end # class TestOps 14 | -------------------------------------------------------------------------------- /stackmachine_anyone.rb: -------------------------------------------------------------------------------- 1 | ## A simple stack machine 2 | 3 | require 'pp' 4 | 5 | def op_true( stack ) 6 | stack.push( 1 ) 7 | end 8 | 9 | ## Let's run! 10 | 11 | pp stack = [] #=> [] 12 | ## I) ScriptSig (input/unlock) part 13 | pp op_true( stack ) #=> [1] 14 | 15 | ## II) ScriptPubKey (output/lock) part 16 | ## 17 | -------------------------------------------------------------------------------- /stackmachine_add.rb: -------------------------------------------------------------------------------- 1 | ## A simple stack machine 2 | 3 | require 'pp' 4 | 5 | def op_add( stack ) 6 | left = stack.pop 7 | right = stack.pop 8 | stack.push( left + right ) 9 | end 10 | 11 | def op_2( stack ) 12 | stack.push( 2 ) 13 | end 14 | 15 | ## Let's run! 16 | 17 | stack = [] 18 | pp op_2( stack ) #=> stack = [2] 19 | pp op_2( stack ) #=> stack = [2,2] 20 | pp op_add( stack ) #=> stack = [4] 21 | -------------------------------------------------------------------------------- /bitcoin-script/lib/bitcoin-script/version.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | 4 | 5 | module BitcoinScript 6 | 7 | MAJOR = 0 8 | MINOR = 0 9 | PATCH = 2 10 | VERSION = [MAJOR,MINOR,PATCH].join('.') 11 | 12 | def self.version 13 | VERSION 14 | end 15 | 16 | def self.banner 17 | "bitcoin-script/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" 18 | end 19 | 20 | def self.root 21 | "#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}" 22 | end 23 | 24 | end # module BitcoinScript 25 | -------------------------------------------------------------------------------- /hash160.rb: -------------------------------------------------------------------------------- 1 | # compute Hash160(pubkey) 2 | 3 | require 'pp' 4 | require 'digest' # Hash (Digest) Functions 5 | 6 | def hash160( pubkey ) 7 | binary = [pubkey].pack( "H*" ) # Convert to binary first before hashing 8 | sha256 = Digest::SHA256.digest( binary ) 9 | ripemd160 = Digest::RMD160.digest( sha256 ) 10 | ripemd160.unpack( "H*" )[0] # Convert back to hex 11 | end 12 | 13 | pubkey = "02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737" 14 | pp hash160( pubkey ) #=> "93ce48570b55c42c2af816aeaba06cfee1224fae" 15 | -------------------------------------------------------------------------------- /ripemd160.rb: -------------------------------------------------------------------------------- 1 | ## RIPEMD (RIPE Message Digest) 2 | ## 3 | ## see https://en.wikipedia.org/wiki/RIPEMD 4 | 5 | require 'pp' 6 | require 'digest' # Hash (Digest) Functions 7 | 8 | 9 | def ripemd160( message ) 10 | Digest::RMD160.hexdigest( message ) 11 | end 12 | 13 | pp ripemd160( "The quick brown fox jumps over the lazy dog" ) 14 | #=> "37f332f68db77bd9d7edd4969571ad671cf9dd3b" 15 | 16 | pp ripemd160( "The quick brown fox jumps over the lazy cog" ) 17 | #=> "132072df690933835eb8b6ad0b77e7b6f14acad7" 18 | 19 | # The hash of a zero-length string is: 20 | pp ripemd160( "" ) 21 | #=> "9c1185a5c5e9fc54612808977ee8f548b2258d31" 22 | -------------------------------------------------------------------------------- /bitcoin-script/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /test/tmp/ 9 | /test/version_tmp/ 10 | /tmp/ 11 | 12 | ## Specific to RubyMotion: 13 | .dat* 14 | .repl_history 15 | build/ 16 | 17 | ## Documentation cache and generated files: 18 | /.yardoc/ 19 | /_yardoc/ 20 | /doc/ 21 | /rdoc/ 22 | 23 | ## Environment normalisation: 24 | /.bundle/ 25 | /vendor/bundle 26 | /lib/bundler/man/ 27 | 28 | # for a library or gem, you might want to ignore these files since the code is 29 | # intended to run in multiple environments; otherwise, check them in: 30 | # Gemfile.lock 31 | # .ruby-version 32 | # .ruby-gemset 33 | 34 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 35 | .rvmrc 36 | -------------------------------------------------------------------------------- /stackmachine_puzzle.rb: -------------------------------------------------------------------------------- 1 | ## A simple stack machine 2 | 3 | require 'pp' 4 | 5 | 6 | def op_add( stack ) 7 | left = stack.pop 8 | right = stack.pop 9 | stack.push( left + right ) 10 | end 11 | 12 | def op_2( stack ) 13 | stack.push( 2 ) 14 | end 15 | 16 | def op_4( stack ) 17 | stack.push( 4 ) 18 | end 19 | 20 | def op_equal( stack ) 21 | left = stack.pop 22 | right = stack.pop 23 | stack.push( left == right ? 1 : 0 ) 24 | end 25 | 26 | ## Let's run! 27 | 28 | stack = [] 29 | ## I) ScriptSig (input/unlock) part 30 | ## FIX!!! - add your "unlock" stack operation / operations here 31 | 32 | 33 | ## II) ScriptPubKey (output/lock) part 34 | pp op_2( stack ) #=> stack = [?, 2] 35 | pp op_add( stack ) #=> stack = [4] 36 | pp op_4( stack ) #=> stack = [4,4] 37 | pp op_equal( stack ) #=> stack = [1] 38 | -------------------------------------------------------------------------------- /pay-to-pubkey.rb: -------------------------------------------------------------------------------- 1 | ## p2pk - Pay-to-pubkey bitcoin (standard) script 2 | 3 | require 'pp' 4 | 5 | ## Bitcoin crypto helper 6 | 7 | class Bitcoin 8 | def self.checksig( sig, pubkey ) 9 | ## "crypto" magic here 10 | ## for testing always return false for now 11 | false 12 | end 13 | end 14 | 15 | 16 | ## A simple stack machine 17 | 18 | def op_checksig( stack ) 19 | pubkey = stack.pop 20 | sig = stack.pop 21 | if Bitcoin.checksig( sig, pubkey ) 22 | stack.push( 1 ) 23 | else 24 | stack.push( 0 ) 25 | end 26 | end 27 | 28 | ## Let's run! 29 | 30 | stack = [] 31 | ## I) ScriptSig (input/unlock) part 32 | pp stack.push( "" ) #=> stack = [""] 33 | 34 | ## II) ScriptPubKey (output/lock) part 35 | pp stack.push( " stack = ["", "" ] 36 | pp op_checksig( stack ) #=> stack = [0] 37 | -------------------------------------------------------------------------------- /bitcoin-script/Rakefile: -------------------------------------------------------------------------------- 1 | require 'hoe' 2 | require './lib/bitcoin-script/version.rb' 3 | 4 | Hoe.spec 'bitcoin-script' do 5 | 6 | self.version = BitcoinScript::VERSION 7 | 8 | self.summary = "bitcoin-script - bitcoin script test simulator / stack machine - code 'n' run your own bitcoin (crypto) contract transaction scripts" 9 | self.description = summary 10 | 11 | self.urls = ['https://github.com/openblockchains/programming-bitcoin-script'] 12 | 13 | self.author = 'Gerald Bauer' 14 | self.email = 'wwwmake@googlegroups.com' 15 | 16 | # switch extension to .markdown for gihub formatting 17 | self.readme_file = 'README.md' 18 | self.history_file = 'CHANGELOG.md' 19 | 20 | self.extra_deps = [ 21 | ] 22 | 23 | self.licenses = ['Public Domain'] 24 | 25 | self.spec_extras = { 26 | required_ruby_version: '>= 2.2.2' 27 | } 28 | 29 | end 30 | -------------------------------------------------------------------------------- /bitcoin-script/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Bitcoin Script Test Simulator / Stack Machine - Code 'n' Run Your Own Bitcoin (Crypto) Contract Transaction Scripts 3 | 4 | bitcoin-script gem / library - bitcoin script test simulator / stack machine - code 'n' run your own bitcoin (crypto) contract transaction scripts 5 | 6 | * home :: [github.com/openblockchains/programming-bitcoin-script](https://github.com/openblockchains/programming-bitcoin-script) 7 | * bugs :: [github.com/openblockchains/programming-bitcoin-script/issues](https://github.com/openblockchains/programming-bitcoin-script/issues) 8 | * gem :: [rubygems.org/gems/bitcoin-script](https://rubygems.org/gems/bitcoin-script) 9 | * rdoc :: [rubydoc.info/gems/bitcoin-script](http://rubydoc.info/gems/bitcoin-script) 10 | 11 | 12 | ## Usage 13 | 14 | To be done 15 | 16 | 17 | 18 | ## License 19 | 20 | ![](https://publicdomainworks.github.io/buttons/zero88x31.png) 21 | 22 | The `bitcoin-script` scripts are dedicated to the public domain. 23 | Use it as you please with no restrictions whatsoever. 24 | 25 | 26 | ## Questions? Comments? 27 | 28 | Send them along to the [wwwmake forum](http://groups.google.com/group/wwwmake). 29 | Thanks! 30 | -------------------------------------------------------------------------------- /pubkey.rb: -------------------------------------------------------------------------------- 1 | require 'pp' 2 | require 'ecdsa' # Use an elliptic curve library 3 | 4 | # This private key is just an example. It should be much more secure! 5 | privatekey = 1234 6 | 7 | # Elliptic curve multiplication 8 | group = ECDSA::Group::Secp256k1 # Select the curve used in Bitcoin 9 | pp point = group.generator.multiply_by_scalar( privatekey ) # Multiply by integer (not hex) 10 | #=> 13 | 14 | # Uncompressed format (with prefix 04) 15 | # Convert to 64 hexstring characters (32 bytes) in length 16 | prefix = '04' 17 | pp pubkey = prefix + "%064x" % point.x + "%064x" % point.y 18 | #=> "04e37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f26d2ee9a82d4158f164ae653e9c6fa7f982ed8c94347fc05c2d068ff1d38b304c" 19 | 20 | # Compressed format (with prefix - even = 02, odd = 03) 21 | # Instead of using both x and y coordinates, 22 | # just use the x-coordinate and whether y is even/odd 23 | prefix = point.y % 2 == 0 ? '02' : '03' 24 | pp pubkey = prefix + "%064x" % point.x 25 | #=> "02e37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f2" 26 | -------------------------------------------------------------------------------- /stack.rb: -------------------------------------------------------------------------------- 1 | ## A stack - last-in first out (LIFO) data structure 2 | 3 | require 'pp' ## pp = pretty print 4 | 5 | pp stack = [] #=> [] 6 | pp stack.empty? #=> true 7 | 8 | pp stack.push( 1 ) #=> [1] 9 | pp stack.empty? #=> false 10 | pp stack.push( 2 ) #=> [1, 2] 11 | pp stack.push( 3 ) #=> [1, 2, 3] 12 | pp stack.push( "" ) #=> [1, 2, 3, ""] 13 | pp stack.push( "") #=> [1, 2, 3, "", ""] 14 | 15 | pp stack.pop #=> "" 16 | pp stack #=> [1, 2, 3, ""] 17 | pp stack.pop #=> "" 18 | pp stack #=> [1, 2, 3] 19 | 20 | pp stack.push( 4 ) #=> [1, 2, 3, 4] 21 | pp stack.push( 5 ) #=> [1, 2, 3, 4, 5] 22 | 23 | pp stack.pop #=> 5 24 | pp stack #=> [1, 2, 3, 4] 25 | pp stack.pop #=> 4 26 | pp stack #=> [1, 2, 3] 27 | pp stack.pop #=> 3 28 | pp stack #=> [1, 2] 29 | pp stack.pop #=> 2 30 | pp stack #=> [1] 31 | pp stack.empty? #=> false 32 | pp stack.pop #=> 1 33 | pp stack #=> [] 34 | pp stack.empty? #=> true 35 | pp stack.pop #=> nil 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | Bitcoin uses a scripting system for transactions. 44 | > Forth-like, Script is simple, stack-based, and processed from left to right. 45 | > It is intentionally not Turing-complete, with no loops. 46 | > 47 | > (Source: [Script @ Bitcoin Wiki](https://en.bitcoin.it/wiki/Script)) 48 | 49 | 50 | First impression. Adding 2+2 in Bitcoin Script starting from zero / scratch: 51 | 52 | ``` ruby 53 | ## A simple stack machine 54 | def op_add( stack ) 55 | left = stack.pop 56 | right = stack.pop 57 | stack.push( left + right ) 58 | end 59 | 60 | def op_2( stack ) 61 | stack.push( 2 ) 62 | end 63 | 64 | ## Let's run! 65 | 66 | stack = [] 67 | op_2( stack ) #=> stack = [2] 68 | op_2( stack ) #=> stack = [2,2] 69 | op_add( stack ) #=> stack = [4] 70 | ``` 71 | 72 | (Source: [`stackmachine_add.rb`](stackmachine_add.rb)) 73 | 74 | 75 | 76 | Yes, that's all the magic! You have built your own stack machine with 77 | two operations / ops, that is, `op_add` and `op_2`. 78 | 79 | The `op_2` operation pushes the number `2` onto the stack. 80 | The `op_add` operation pops the top two numbers from the stack 81 | and pushes the result onto the stack. 82 | 83 | 84 | Aside - What's a Stack? Push 'n' Pop 85 | 86 | A stack is a last-in first-out (LIFO) data structure. Use `push` 87 | to add an element to the top of the stack and use `pop` 88 | to remove the top element from the stack. 89 | Example: 90 | 91 | ``` ruby 92 | stack = [] #=> [] 93 | stack.empty? #=> true 94 | 95 | stack.push( 1 ) #=> [1] 96 | stack.empty? #=> false 97 | stack.push( 2 ) #=> [1, 2] 98 | stack.push( 3 ) #=> [1, 2, 3] 99 | stack.push( "" ) #=> [1, 2, 3, ""] 100 | stack.push( "") #=> [1, 2, 3, "", ""] 101 | 102 | stack.pop #=> "" 103 | stack #=> [1, 2, 3, ""] 104 | stack.pop #=> "" 105 | stack #=> [1, 2, 3] 106 | 107 | stack.push( 4 ) #=> [1, 2, 3, 4] 108 | stack.push( 5 ) #=> [1, 2, 3, 4, 5] 109 | 110 | stack.pop #=> 5 111 | stack #=> [1, 2, 3, 4] 112 | stack.pop #=> 4 113 | stack #=> [1, 2, 3] 114 | stack.pop #=> 3 115 | stack #=> [1, 2] 116 | stack.pop #=> 2 117 | stack #=> [1] 118 | stack.empty? #=> false 119 | stack.pop #=> 1 120 | stack #=> [] 121 | stack.empty? #=> true 122 | stack.pop #=> nil 123 | ``` 124 | 125 | (Source: [`stack.rb`](stack.rb)) 126 | 127 | 128 | 129 | Unlock+Lock / Input+Output / ScriptSig+ScriptPubKey 130 | 131 | In "real world" bitcoin the script has two parts / halves in two transactions 132 | that get combined. 133 | The "lock" or "output" or "ScriptPubKey" script 134 | that locks the "unspent transaction output (UTXO)", 135 | and the "unlock" or "input" or "ScriptSig" script that unlocks 136 | the bitcoins. 137 | 138 | 139 | Anyone Can Spend (Unlock) the Outputs (Bitcoins) 140 | 141 | The bitcoins are yours if the bitcoins haven't been spent yet - 142 | see blockchain and how it solves the double-spending problem :-) - 143 | AND if the script returns with true, that is, `1` is on top of the stack. 144 | 145 | ``` ruby 146 | ## A simple stack machine 147 | def op_true( stack ) 148 | stack.push( 1 ) 149 | end 150 | 151 | ## Let's run! 152 | 153 | stack = [] 154 | ## I) ScriptSig (input/unlock) part 155 | op_true( stack ) #=> stack = [1] 156 | 157 | ## II) ScriptPubKey (output/lock) part 158 | ## 159 | ``` 160 | 161 | (Source: [`stackmachine_anyone.rb`](stackmachine_anyone.rb)) 162 | 163 | 164 | Bingo! Yes, that's all the magic! 165 | The `op_true` operation pushes the number `1`, that is, `true` onto the stack. 166 | 167 | The "official" bitcoin script notation reads: 168 | 169 | ``` 170 | ScriptSig (input): OP_TRUE 171 | ScriptPubKey: (empty) 172 | ``` 173 | 174 | Now let's split the adding `2+2` script into a two part puzzle, 175 | that is, `?+2=4` 176 | or into `ScriptSig` and `ScriptPubKey`. 177 | If you know the answer you can "unlock" the bounty, 178 | that is, the bitcoins are yours! 179 | Here's the challenge: 180 | 181 | ``` ruby 182 | ## A simple stack machine 183 | def op_add( stack ) 184 | left = stack.pop 185 | right = stack.pop 186 | stack.push( left + right ) 187 | end 188 | 189 | def op_2( stack ) 190 | stack.push( 2 ) 191 | end 192 | 193 | def op_4( stack ) 194 | stack.push( 4 ) 195 | end 196 | 197 | def op_equal( stack ) 198 | left = stack.pop 199 | right = stack.pop 200 | stack.push( left == right ? 1 : 0 ) 201 | end 202 | 203 | ## Let's run! 204 | 205 | stack = [] 206 | ## I) ScriptSig (input/unlock) part 207 | ## FIX!!! - add your "unlock" stack operation / operations here 208 | 209 | ## II) ScriptPubKey (output/lock) part 210 | op_2( stack ) #=> stack = [?, 2] 211 | op_add( stack ) #=> stack = [4] 212 | op_4( stack ) #=> stack = [4,4] 213 | op_equal( stack ) #=> stack = [1] 214 | ``` 215 | 216 | (Source: [`stackmachine_puzzle.rb`](stackmachine_puzzle.rb)) 217 | 218 | 219 | 220 | 221 | The "official" bitcoin script notation reads: 222 | 223 | ``` 224 | ScriptSig (input): ? 225 | ScriptPubKey: OP_2 OP_ADD OP_4 OP_EQUAL 226 | ``` 227 | 228 | 229 | If you check all Bitcoin script operations - 230 | the following ops should no longer be a mystery: 231 | 232 | Constants 233 | 234 | |Word | Opcode |Hex | Input | Output | Description| 235 | |-----|--------|----|-------|--------|------------| 236 | | OP_0, OP_FALSE | 0 | 0x00 | Nothing. | (empty value) | An empty array of bytes is pushed onto the stack. (This is not a no-op: an item is added to the stack.) | 237 | | OP_1, OP_TRUE | 81 | 0x51 | Nothing. | 1 | The number 1 is pushed onto the stack. | 238 | | OP_2-OP_16 | 82-96 | 0x52-0x60 | Nothing. | 2-16 | The number in the word name (2-16) is pushed onto the stack. | 239 | 240 | Bitwise logic 241 | 242 | |Word | Opcode |Hex | Input | Output | Description| 243 | |-----|--------|----|-------|--------|------------| 244 | | OP_EQUAL | 135 | 0x87 | x1 x2 | True / false | Returns 1 if the inputs are exactly equal, 0 otherwise. | 245 | 246 | Arithmetic 247 | 248 | |Word | Opcode |Hex | Input | Output | Description| 249 | |-----|--------|----|-------|--------|------------| 250 | | OP_ADD | 147 | 0x93 | a b | out | a is added to b. | 251 | | OP_MUL | 149 | 0x95 | a b | out | a is multiplied by b. **disabled.** | 252 | | OP_DIV | 150 | 0x96 | a b | out | a is divided by b. **disabled.** | 253 | 254 | 255 | 256 | Trivia Corner: Did you know? The `OP_MUL` for multiplications (e.g. `2*2`) 257 | has been banned, that is, disabled! Why? 258 | Because of security concerns, that is, fear of stack overflows. 259 | What about `OP_DIV` for divisions (e.g. `4/2`)? Don't ask! 260 | Ask who's protecting you from stack underflows? 261 | So what's left for programming - not much really other than checking 262 | signatures and timelocks :-). 263 | 264 | 265 | 266 | ## Standard Scripts 267 | 268 | You don't have to start from zero / scratch. 269 | Bitcoin has many standard script templates. 270 | The most important include: 271 | 272 | 273 | | Short Name | Long Name | 274 | |------------|------------| 275 | | p2pk | Pay-to-pubkey | 276 | | p2pkh | Pay-to-pubkey-hash | 277 | | p2sh | Pay-to-script-hash | 278 | 279 | Standard Scripts with SegWit (Segregated Witness) 280 | 281 | | Short Name | Long Name | 282 | |------------|------------| 283 | | p2wpkh | Pay-to-witness-pubkey-hash | 284 | | p2wsh | Pay-to-witness-script-hash | 285 | 286 | 287 | 288 | ## p2pk - Pay-to-pubkey 289 | 290 | Pay-to-pubkey (p2pk) is the simplest standard script 291 | and was used in the early days 292 | including by Satoshi Nakamoto (the pseudonymous Bitcoin founder). 293 | 294 | Bitcoin Trivia: 295 | 296 | > As initially the sole and subsequently the predominant miner, 297 | > Nakamoto was awarded bitcoin at genesis and for 10 days afterwards. 298 | > Except for test transactions these remain unspent since mid January 2009. 299 | > The public bitcoin transaction log shows that Nakamoto's known addresses contain 300 | > roughly one million bitcoins. At bitcoin's peak in December 2017, 301 | > this was worth over US$19 billion, 302 | > making Nakamoto possibly the 44th richest person in the world at the time. 303 | > 304 | > (Source: [Satoshi Nakamoto @ Wikipedia](https://en.wikipedia.org/wiki/Satoshi_Nakamoto)) 305 | 306 | 307 | The one million bitcoins are yours if the pay-to-pubkey (p2pk) script 308 | returns with true, that is, `1` is on top of the stack. 309 | The only input you need to unlock the the fortune is the signature. Are you Satoshi? 310 | Let's try: 311 | 312 | 313 | ``` ruby 314 | ## Bitcoin crypto helper 315 | 316 | class Bitcoin 317 | def self.checksig( sig, pubkey ) 318 | ## "crypto" magic here 319 | ## for testing always return false for now; sorry 320 | false 321 | end 322 | end 323 | 324 | 325 | ## A simple stack machine 326 | 327 | def op_checksig( stack ) 328 | pubkey = stack.pop 329 | sig = stack.pop 330 | if Bitcoin.checksig( sig, pubkey ) 331 | stack.push( 1 ) 332 | else 333 | stack.push( 0 ) 334 | end 335 | end 336 | 337 | ## Let's run! 338 | 339 | stack = [] 340 | ## I) ScriptSig (input/unlock) part 341 | stack.push( "" ) #=> stack = [""] 342 | 343 | ## II) ScriptPubKey (output/lock) part 344 | stack.push( " stack = ["", "" ] 345 | op_checksig( stack ) #=> stack = [0] 346 | ``` 347 | 348 | (Source: [`pay-to-pubkey.rb`](pay-to-pubkey.rb)) 349 | 350 | Bingo! Yes, that's all the magic! 351 | The `op_checksig` operation pops two elements from 352 | the stack, that is, the public key (pubkey) 353 | and the signature (sig) and 354 | if the elliptic curve crypto validates the signature (from the input/unlock transaction) 355 | using the public key (from the the output/lock transaction) 356 | then the fortune is yours! If not 357 | the number `0`, that is, `false` gets pushed onto the stack 358 | and you're out of luck. Sorry. 359 | 360 | The "official" bitcoin script notation reads: 361 | 362 | ``` 363 | ScriptSig (input): 364 | ScriptPubKey: OP_CHECKSIG 365 | ``` 366 | 367 | Note: Can you guess where the input / unlock part got its ScriptSig name 368 | and where the output / lock part got its ScriptPubKey name? 369 | Yes, from the pay-to-pubkey script. 370 | 371 | 372 | 373 | Aside - Ivy - Higher-Level Bitcoin Script Language 374 | 375 | What's Ivy? 376 | 377 | From the project's readme: 378 | 379 | > Ivy is a higher-level language that allows you to write (crypto) contracts 380 | > for the Bitcoin protocol. Ivy can compile to opcodes for Bitcoin’s stack machine, 381 | > Bitcoin Script, and can be used to create SegWit-compatible Bitcoin addresses... 382 | > 383 | > You can try out Ivy using the [Ivy Playground for Bitcoin](https://ivy-lang.org/bitcoin), 384 | > which allows you to create test contracts and try spending them, 385 | > all in a sandboxed environment. 386 | > 387 | > (Source: [Ivy Language Documentation](https://docs.ivy-lang.org/bitcoin/)) 388 | 389 | 390 | Let's look at the pay-to-pubkey script in Ivy: 391 | 392 | ``` 393 | contract LockWithPublicKey(publicKey: PublicKey, val: Value) { 394 | clause spend(sig: Signature) { 395 | verify checkSig(publicKey, sig) 396 | unlock val 397 | } 398 | } 399 | ``` 400 | 401 | And - surprise, surprise - the higher-level script compiles to 402 | 403 | ``` 404 | OP_CHECKSIG 405 | ``` 406 | 407 | 408 | Elliptic Curve Cryptography 409 | 410 | So what does a "real world" public key (pubkey) look like? 411 | In the early days Satoshi Nakamoto 412 | used the uncompressed SEC (Standards for Efficient Cryptography) format 413 | for the public key that results 414 | in 65 raw bytes. 415 | Bitcoin uses elliptic curve 416 | cryptography and the public key is a point (x,y) on 417 | the curve where the x and y coordinates are each 256-bit (32 byte) numbers. 418 | 419 | In the uncompressed format, place the x and y coordinate next to each other, 420 | then prefix with `04` to indicate that it is an uncompressed public key: 421 | 422 | ``` 423 | prefix (1 byte) : 04 424 | x-coordinate (32 bytes) : fe53c78e36b86aae8082484a4007b706d5678cabb92d178fc95020d4d8dc41ef 425 | y-coordinate (32 bytes) : 44cfbb8dfa7a593c7910a5b6f94d079061a7766cbeed73e24ee4f654f1e51904 426 | => 427 | 04fe53c78e36b86aae8082484a4007b706d5678cabb92d178fc95020d4d8dc41ef44cfbb8dfa7a593c7910a5b6f94d079061a7766cbeed73e24ee4f654f1e51904 428 | ``` 429 | 430 | And in the compressed form because the elliptic curve is symmetrical 431 | along its x-axis, the trick is that each x-coordinate will 432 | only ever have one of two possible y coordinates: 433 | 434 | - If y is even, it corresponds to one of the points. 435 | - If y is odd, it corresponds to the other. 436 | 437 | Thus, in the compressed public key format place the x coordinate 438 | along with a prefix (`02` or `03`) 439 | that tells whether the y is even (`02`) or odd (`03`). 440 | 441 | ``` 442 | prefix (1 byte) : 03 443 | x-coordinate (32 bytes) : df51984d6b8b8b1cc693e239491f77a36c9e9dfe4a486e9972a18e03610a0d22 444 | => 445 | 03df51984d6b8b8b1cc693e239491f77a36c9e9dfe4a486e9972a18e03610a0d22 446 | ```` 447 | 448 | 449 | Let's create a public key from the private key 450 | 451 | Note: Let's use the 3rd party [Elliptic Curve Digital Signature Algorithm (ECDSA) 452 | library / gem](https://rubygems.org/gems/ecdsa) by David Grayson. 453 | 454 | ``` ruby 455 | require 'pp' 456 | require 'ecdsa' # Use an elliptic curve library 457 | 458 | # This private key is just an example. It should be much more secure! 459 | privatekey = 1234 460 | 461 | # Elliptic curve multiplication 462 | group = ECDSA::Group::Secp256k1 # Select the curve used in Bitcoin 463 | point = group.generator.multiply_by_scalar( privatekey ) # Multiply by integer (not hex) 464 | #=> 467 | 468 | # Uncompressed format (with prefix 04) 469 | # Convert to 64 hexstring characters (32 bytes) in length 470 | prefix = '04' 471 | pubkey = prefix + "%064x" % point.x + "%064x" % point.y 472 | #=> "04e37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f26d2ee9a82d4158f164ae653e9c6fa7f982ed8c94347fc05c2d068ff1d38b304c" 473 | 474 | # Compressed format (with prefix - 02 = even / 03 = odd) 475 | # Instead of using both x and y coordinates, 476 | # just use the x-coordinate and whether y is even/odd 477 | prefix = point.y % 2 == 0 ? '02' : '03' 478 | pubkey = prefix + "%064x" % point.x 479 | #=> "02e37648435c60dcd181b3d41d50857ba5b5abebe279429aa76558f6653f1658f2" 480 | ``` 481 | 482 | (Source: [`pubkey.rb`](pubkey.rb)) 483 | 484 | 485 | 486 | 487 | 488 | 489 | ## p2pkh - Pay-to-pubkey-hash 490 | 491 | 492 | ... 493 | 494 | 495 | 496 | Aside - What's Hash160? 497 | 498 | It's a hash function to hash and shorten public keys. Public keys 499 | if uncompressed shorten from 65 bytes to 20 bytes 500 | (or if compressed from 33 bytes). Example: 501 | 502 | ``` 503 | pubkey = 02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737 504 | hash160(pubkey) = 93ce48570b55c42c2af816aeaba06cfee1224fae 505 | ```` 506 | 507 | To compute the Hash160 run the public key through the SHA256 and RIPEMD160 hash functions. 508 | Example: 509 | 510 | ``` ruby 511 | require 'digest' # Hash (Digest) Functions 512 | 513 | def hash160( pubkey ) 514 | binary = [pubkey].pack( "H*" ) # Convert to binary first before hashing 515 | sha256 = Digest::SHA256.digest( binary ) 516 | ripemd160 = Digest::RMD160.digest( sha256 ) 517 | ripemd160.unpack( "H*" )[0] # Convert back to hex 518 | end 519 | 520 | pubkey = "02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737" 521 | hash160( pubkey ) 522 | #=> "93ce48570b55c42c2af816aeaba06cfee1224fae" 523 | ``` 524 | 525 | (Source: [`hash160.rb`](hash160.rb)) 526 | 527 | 528 | Security Trivia I: Why use SHA256 and RIPEMD160? 529 | 530 | RIPEMD160 gets used because it results in 531 | a short 160 bit (20 byte) digest BUT is not the strongest hash function on it's own, 532 | thus, SHA256 gets used for more strength. Best of both world. 533 | 534 | Security Trivia II: What's RIPEMD160? 535 | 536 | RACE¹ Integrity Primitives Evaluation Message Digest 160-bit 537 | 538 | ¹: Research and development in Advanced Communications technologies in Europe 539 | 540 | ``` ruby 541 | def ripemd160( message ) 542 | Digest::RMD160.hexdigest( message ) 543 | end 544 | 545 | ripemd160( "The quick brown fox jumps over the lazy dog" ) 546 | #=> "37f332f68db77bd9d7edd4969571ad671cf9dd3b" 547 | 548 | ripemd160( "The quick brown fox jumps over the lazy cog" ) 549 | #=> "132072df690933835eb8b6ad0b77e7b6f14acad7" 550 | 551 | # The hash of a zero-length string is: 552 | ripemd160( "" ) 553 | #=> "9c1185a5c5e9fc54612808977ee8f548b2258d31" 554 | ``` 555 | 556 | (Source: [RIPEMD @ Wikipedia](https://en.wikipedia.org/wiki/RIPEMD)) 557 | 558 | 559 | 560 | ... 561 | 562 | 563 | 564 | The "official" bitcoin script notation reads: 565 | 566 | ``` 567 | ScriptSig (input): 568 | ScriptPubKey: OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG 569 | ``` 570 | 571 | And the Ivy higher-level version reads: 572 | 573 | ``` 574 | contract LockWithPublicKeyHash(pubKeyHash: Hash160(PublicKey), val: Value) { 575 | clause spend(pubKey: PublicKey, sig: Signature) { 576 | verify hash160(pubKey) == pubKeyHash 577 | verify checkSig(pubKey, sig) 578 | unlock val 579 | } 580 | } 581 | ``` 582 | 583 | that compiles to 584 | 585 | ``` 586 | OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG 587 | ``` 588 | 589 | 590 | 591 | To be continued ... 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | ## Appendix 601 | 602 | Aside - Simplicity - A New Bitcoin Contract Language? 603 | 604 | > Simplicity is a blockchain programming language 605 | > designed as an alternative to Bitcoin script. 606 | > 607 | > (Source: [Simplicity README](https://github.com/ElementsProject/simplicity)) 608 | 609 | 610 | > Why Simplicity? 611 | > 612 | > Bitcoin's Script language is generally limited to combinations 613 | > of digital signature checks, timelocks, and hashlocks. 614 | > While impressive protocols (such as the Lightning Network) 615 | > have been built on these primitives, 616 | > Bitcoin's Script language lacks the expressiveness needed 617 | > for more complex contract scripts. 618 | > 619 | > (Source: [Simplicity: High-Assurance Bitcoin Contract Scripting](https://blockstream.com/2018/11/28/en-simplicity-github/) by Russell O'Connor, Andrew Poelstra, Blockstream Research, November 2018) 620 | 621 | 622 | > Simplicity: A New Language for Blockchains (Whitepaper Abstract) 623 | > 624 | > Simplicity is a typed, combinator-based, functional language without 625 | > loops and recursion, designed to be used for crypto-currencies 626 | > and blockchain applications. It aims to improve upon existing crypto-currency languages, 627 | > such as Bitcoin's Script, Ethereum's Solidity or Michelson's Liquidity, 628 | > while avoiding some 629 | > of the problems they face. Simplicity comes with formal denotational 630 | > semantics defined in Coq, a popular, general purpose software proof assistant. 631 | > Simplicity also includes operational semantics that are defined 632 | > with an abstract machine that we call the Bit Machine. 633 | > The Bit Machine is used as a tool for measuring the computational space and time 634 | > resources needed to evaluate Simplicity programs. Owing to its Turing 635 | > incompleteness, Simplicity is amenable to static analysis that can be used 636 | > to derive upper bounds on the computational resources needed, prior to 637 | > execution. While Turing incomplete, Simplicity can express any finitary 638 | > function, which we believe is enough to build useful contracts for 639 | > blockchain applications. 640 | > 641 | > (Source: [Simplicity Whitepaper (PDF)](https://blockstream.com/simplicity.pdf) by Russell O'Connor, Blockstream, December 2017) 642 | 643 | 644 | 645 | 646 | ## Resources 647 | 648 | Articles 649 | 650 | - [Bitcoin Script @ Bitcoin Wiki](https://en.bitcoin.it/wiki/Script) 651 | - [Script - A mini programming language @ Learn Me a Bitcoin](http://learnmeabitcoin.com/glossary/script) by Greg Walker 652 | - [Opcodes @ Bitcoin Developer Reference](https://bitcoin.org/en/developer-reference#opcodes) 653 | 654 | Books / Series 655 | 656 | - [A developer-oriented series about Bitcoin](https://davidederosa.com/basic-blockchain-programming/) by Davide De Rosa 657 | - [The Bitcoin Script language (pt. 1)](https://davidederosa.com/basic-blockchain-programming/bitcoin-script-language-part-one/) 658 | - [The Bitcoin Script language (pt. 2)](https://davidederosa.com/basic-blockchain-programming/bitcoin-script-language-part-two/) 659 | - [Standard scripts](https://davidederosa.com/basic-blockchain-programming/standard-scripts/) 660 | 661 | 662 | 663 | - [Programming Bitcoin from Scratch](https://github.com/jimmysong/programmingbitcoin) by Jimmy Song 664 | - [Chapter 6 - Script](https://github.com/jimmysong/programmingbitcoin/blob/master/ch06.asciidoc) - How Script Works • Example Operations • Parsing the Script Fields • Combining the Script Fields • Standard Scripts • p2pk • Problems with p2pk • Solving the Problems with p2pkh • Scripts Can Be Arbitrarily Constructed • Conclusion 665 | - [Chapter 8 - Pay-to-Script Hash](https://github.com/jimmysong/programmingbitcoin/blob/master/ch08.asciidoc) - Bare Multisig • Coding OP_CHECKMULTISIG • Problems with Bare Multisig • Pay-to-Script-Hash (p2sh) • Coding p2sh • Conclusion 666 | - [Chapter 13 - Segregated Witness](https://github.com/jimmysong/programmingbitcoin/blob/master/ch13.asciidoc) - Pay-to-Witness-Pubkey-Hash (p2wpkh) • p2wpkh Transactions • p2sh-p2wpkh • Coding p2wpkh and p2sh-p2wpkh • Pay-to-Witness-Script-Hash (p2wsh) • p2sh-p2wsh • Coding p2wsh and p2sh-p2wsh • Other Improvements • Conclusion 667 | 668 | 669 | Talk Notes 670 | 671 | - [Contracts, Contracts, Contracts - Code Your Own (Crypto Blockchain) Contracts w/ Ruby (sruby), Universum & Co](https://github.com/geraldb/talks/blob/master/contracts.md) 672 | - Genesis - Bitcoin Script 673 | - Ivy - Higher-Level Bitcoin Script 674 | - History Corner - Bitcoin - The World's Worst Database for Everything? - Bitcoin Maximalism in Action 675 | - Turing Complete and the Halting Problem 676 | - Fees, Fees, Fees - $$$ - There's No Free Lunch 677 | 678 | Code 679 | 680 | - [Learn Me a Bitcoin - Simple code snippets to help you understand how Bitcoin works](https://github.com/in3rsha/learnmeabitcoin-code) by Greg Walker 681 | 682 | 683 | 684 | ## License 685 | 686 | ![](https://publicdomainworks.github.io/buttons/zero88x31.png) 687 | 688 | The Programming Bitcoin Script Step-by-Step book / guide 689 | is dedicated to the public domain. 690 | Use it as you please with no restrictions whatsoever. 691 | 692 | --------------------------------------------------------------------------------