REVISED:
There are some complaints about the existing CHECKSIG procedures:
1. Choice of SIGHASH types are restricted. It is either all or one or none, but cannot sign some of the outputs. (
https://bitcointalk.org/index.php?topic=212555.0)
2. It wastes a lot of blockchain space to sign outputs of the same script, because it requires one sig per input
3. In order to calculate the fee, one needs to see the full transactions which could be a problem for lightweight hardware wallet (
https://bitcointalk.org/index.php?topic=181734.0)
4. It has some unexpected behaviors:
https://bitcointalk.org/index.php?topic=260595.0To solve these problems, we need to revolutionize the way of checking signatures. Here I propose the OP_CHECKSIG2
The format of signature is completely different for OP_CHECKSIG2. In human language, the signature will look like:
I agree to release the fund of <tx-hash1>:<vout1>:<value1>, <tx-hash2>:<vout2>:<value2> , <tx-hash3>:<vout3>:<value3>...........
to <outputscript-a>:<value-a>, <outputscript-b>:<value-b>, <outputscript-c>:<value-c>........
given that <tx-hash4>:<vout4>:<value4>, <tx-hash5>:<vout5>:<value5> , <tx-hash6>:<vout6>:<value6>...... are part of this transaction
This structure will supersede all existing sighash types (all/none/single/anyonecanpay), and do a lot more
------------------------------
Some definitions:
Input index: order of an input in a final transaction. The first input is index 0
Output index: order of an output in a final transaction. The first output is index 0
OutPoint: <hash of previous tx|output index of previous tx>. A 36 bytes identifier of previous outputs, describe at
https://en.bitcoin.it/wiki/Protocol_specification#txInputID: An unique identifier for an input, in a format of <value of the input|OutPoint> (36+8=44bytes)
OutputID: <value of the output|pk_script length|pk_script>, same format as TxOut describe at
https://en.bitcoin.it/wiki/Protocol_specification#txSome new OP codes:
OP_EVAL2:
- Pop the second-to-top stake (a)
- Calculate HASH160(a)
- Pop the top stake (b)
- If HASH160(a) is not equal to b, the script fails
- Deserialize a, and run it as script
OP_CHECKSIG2: it pops the top 3 stakes <sig> <hash> <public key>, and check the validity of <sig> against <hash> with <public key>. If it is, 1 is returned, 0 otherwise.
OP_PUSHINPUT: it pops the top stake. From the top stake, it pops the least significant byte. If the least significant byte is:
- 0x00:
- The script fails if the remaining string is not empty
- Returns InputID of the input
- 0x01:
- The script fails if the remaining string is not empty
- Returns InputID of all inputs in a concatenated form by the order of input index, with Input 0 at the beginning.
- Appends a 0xff at the end of the concatenated string
- 0x02:
- Enumerate the number of inputs, divide by 8, round up to the next integer if not an integer.
- The script fails if the length (in bytes) of the remaining string is larger than the integer.
- Read the remaining string as a bitmap. Each bit refers the an input, with the least significant bit refers to input 0.
- If there is any positive bit for an non-existing input, the script fails.
- For every positive bit, returns the InputID of the corresponding input by the order of input index.
- Concatenate the results, with the InputID of the lowest index at the beginning.
- 0x03:
- Enumerate the number of inputs, take log256, round up to the next integer if not an integer.
- The script fails if the length (in bytes) of the remaining string is not equal to 2 times the integer.
- Split the remaining string in half. Call the more significant halve x and the less significant halve y.
- If x>y, the script fails.
- If y is greater than the largest input index, the script fails.
- Return the InputID of all inputs with index from x to y (inclusive) in a concatenated form by the order of input index, the InputID of the lowest index at the beginning.
OP_PUSHOUTPUT: it pops the top stake. From the top stake, it pops the least significant byte. If the least significant byte is:
- 0x00:
- The script fails if the remaining string is not empty
- Returns an empty string
- 0x01, 0x02, 0x03: Same as OP_PUSHINPUT, just replace inputs with outputs.
OP_PUSHSCRIPTSIG:
- Pops the top stake, an interger (z >= 0)
- The script fails if z is greater than the highest input index
- Return the ScriptSig of the input z
To make this a soft-fork, OP_EVAL2 and OP_PUSHSCRIPTSIG will use some of the unused OP_NOP (e.g. OP_NOP3 and OP_NOP4); The other new codes will use currently unspecified codes, and they will fail the script if not called by OP_EVAL2
For example, in a standard transaction, the ScriptSig would look like (in a bitmap mode):
<sig> <0x1802> <0x0C02>
<serialized OP_PUSHINPUT OP_SWAP OP_PUSHOUTPUT OP_CAT OP_HASH256 <public key> OP_CHECKSIG2>
scriptPubKey would be:
<hash of serialized OP_PUSHINPUT OP_SWAP OP_PUSHOUTPUT OP_CAT OP_HASH256 <public key> OP_CHECKSIG2>
OP_EVAL2
Running the script
- 1. stake = <sig> <0x1802> <0x0C02> <serialized OP_PUSHINPUT OP_SWAP OP_PUSHOUTPUT OP_CAT OP_HASH256 <public key> OP_CHECKSIG2> <hash of serialized OP_PUSHINPUT OP_SWAP OP_PUSHOUTPUT OP_CAT OP_HASH256 <public key> OP_CHECKSIG2>
- 2. OP_EVAL2, stake = <sig> <0x1802> <0x0C02>
- 3. OP_PUSHINPUT, stake = <sig> <0x1802> <concatenated InputIDs for inputs 2 and 3>
- 4. OP_SWAP, stake = <sig> <concatenated InputIDs> <0x1802>
- 5. OP_PUSHOUTPUT, stake = <sig> <concatenated InputIDs> <concatenated OutputIDs for outputs for outputs 3 and 4>
- 6. OP_CAT, stake = <sig> <concatenated InputIDs and OutputIDs>
- 7. OP_HASH256, stake = <sig> <hash of concatenated InputIDs and OutputIDs>
- 8. stake = <sig> <hash of concatenated InputIDs and OutputIDs> <public key>
- 9. OP_CHECKSIG2, stake = <1>. Script ends.
-----------------------
To prepare a signature:
- List the InputID of all required inputs (including inputs to be signed by the key pair, and any required inputs owned by other key pair)
- Concatenate the InputID, by the desired order
- Append an 0xff at the end if the exact inputs are required (no more, no less)
- List the OutputID of all required outputs
- Concatenate the OutputID, by the desired order
- Append an 0xff at the end if the exact outputs are required (no more, no less)
- Concatenate the InputIDs and OutputIDs, take double SHA256, sign with private key
-----------------------
To prepare a transaction:
- Arrange the order of inputs and outputs to make sure they are coherent with all signatures
- ScriptSig will be <signature> <command for OP_PUSHOUTPUT> <command for OP_PUSHINPUT> <serialized script>
- If a key pair is signing for 2 or more outputs, the ScriptSig for the rest of inputs is simply <input index> OP_PUSHSCRIPTSIG
------------------------
Example of a full transaction:
There are 4 key pairs: Alice, Bob, Carl, David
5 inputs, the InputIDs are: Alice-1, Alice-2, Bob-1, Carl-1, David-1
5 outputs the OutputIDs are: A, B, C, D, E
As long as outputs A, B, C are paid, Alice is willing to sign and don't care where other BTC comes from and where the rest of BTC goes
As long as outputs D, E are paid, and Carl-1 is involved, Bob is willing to sign and don't care where other BTC comes from and where the rest of BTC goes
Carl will sign if Bob-1 is involved. He doesn't care where other BTC comes from and where the BTC goes
David will sign if all 5 inputs and 5 outputs are involved. No more, no less.
Alice will construct this message: "Alice-1|Alice-2|A|B|C", take double SHA256, sign with her key (Alice-sig)*
Bob will construct this message: "Bob-1|Carl-1|D|E", take double SHA256, sign with his key (Bob-sig)
Carl will construct this message: "Bob-1|Carl-1", take double SHA256, sign with his key (Carl-sig)
David will construct this message: "Alice-1|Alice-2|Bob-1|Carl-1|David-1|0xff|A|B|C|D|E|0xff", take double SHA256, sign with his key (Carl-sig)
The raw, unsigned transaction is arranged this way:
Input index 0: Alice-1
Input index 1: Alice-2
Input index 2: Bob-1
Input index 3: Carl-1
Input index 4: David-1
Output index 0: A
Output index 1: B
Output index 2: C
Output index 3: D
Output index 4: E
The ScriptSig for input 0: <Alice-sig><0x0702><0x0302><serialized script>
The ScriptSig for input 1: OP_0 OP_PUSHSCRIPTSIG
The ScriptSig for input 2: <Bob-sig><0x1802><0x0C02><serialized script>
The ScriptSig for input 3: <Carl-sig><0x00><0x0C02><serialized script>
The ScriptSig for input 4: <David-sig><0x01><0x01><serialized script>
The tx is completed. Broadcast.
-------------------------------------
Evaluating inputs with OP_PUSHSCRIPTSIG:
OP_0 OP_PUSHSCRIPTSIG
<sig> <0x1802> <0x0C02>
<serialized OP_PUSHINPUT OP_SWAP OP_PUSHOUTPUT OP_CAT OP_HASH256 <public key> OP_CHECKSIG2>
scriptPubKey would be:
<hash of serialized OP_PUSHINPUT OP_SWAP OP_PUSHOUTPUT OP_CAT OP_HASH256 <public key> OP_CHECKSIG2>
OP_EVAL2
Running the script
- 1. stake = <0>
- 2. OP_PUSHSCRIPTSIG; stake = <sig> <0x0702> <0x0302> <serialized OP_PUSHINPUT OP_SWAP OP_PUSHOUTPUT OP_CAT OP_HASH256 <public key> OP_CHECKSIG2>
- 3. stake = <sig> <0x0702> <0x0302> <serialized OP_PUSHINPUT OP_SWAP OP_PUSHOUTPUT OP_CAT OP_HASH256 <public key> OP_CHECKSIG2><hash of serialized OP_PUSHINPUT OP_SWAP OP_PUSHOUTPUT OP_CAT OP_HASH256 <public key> OP_CHECKSIG2>
- 4. OP_EVAL2, stake = <sig> <0x0702> <0x0302>
- 5. The rest is same as above
If properly optimised, these steps are actually not needed, saving many hashing and ECDSA operations