From 1edc52dddfb1ac6952e6be5057b9a7fdfbe75b21 Mon Sep 17 00:00:00 2001 From: genjix Date: Tue, 13 Dec 2011 17:24:13 +0000 Subject: [PATCH 1/6] Updated status. --- bip-0013.md | 2 +- bip-0014.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bip-0013.md b/bip-0013.md index 1b08085f..d0e3c150 100644 --- a/bip-0013.md +++ b/bip-0013.md @@ -2,7 +2,7 @@ BIP: 13 Title: Address Format for OP_EVAL Author: Gavin Andresen - Status: Pre-Draft + Status: Draft Type: Standards Track Created: 18-10-2011 diff --git a/bip-0014.md b/bip-0014.md index 888e980c..5944ea7a 100644 --- a/bip-0014.md +++ b/bip-0014.md @@ -3,7 +3,7 @@ Title: BIP Protocol Version and User Agent Author: Amir Taaki Patrick Strateman - Status: Draft + Status: Accepted Type: Standards Track Created: 10-11-2011 Post-History: 02-11-2011 From 314cc17cf578d2cebec74a33ab692f425e8617e9 Mon Sep 17 00:00:00 2001 From: genjix Date: Tue, 13 Dec 2011 17:24:22 +0000 Subject: [PATCH 2/6] deleted BIP 10 - never updated published or discussed --- bip-0010.md | 189 ---------------------------------------------------- 1 file changed, 189 deletions(-) delete mode 100644 bip-0010.md diff --git a/bip-0010.md b/bip-0010.md deleted file mode 100644 index c794fdb2..00000000 --- a/bip-0010.md +++ /dev/null @@ -1,189 +0,0 @@ -## BIP 0010 - Proposal for Standardized, Multi-Signature Transaction Execution - - -Author: Alan Reiner -Contact: etotheipi@gmail.com -Status: Draft -Orig Date: 28 Oct, 2011 - - -### Abstract: - -A multi-signature transaction is one where a certain number of Bitcoins are "encumbered" with more than one recipient address. The subsequent transaction that spends these coins will require each party involved (or some subset, depending on the script), to see the final, proposed transaction, and sign it with their private key. This necessarily requires collaboration between all parties -- to propose a distribution of encumbered funds, collect signatures from all necessary participants, and then broadcast the completed transaction. - -This BIP describes a protocol to standardize the representation of proposal transactions and the subsequent collection of signatures to execute multi-signature transactions. The goal is to encourage a standard that guarantees interoperability of all programs that implement it. - - -### Motivation: - -The enabling of multi-signature transactions in Bitcoin will introduce a great deal of extra functionality to the users of the network, but also a great deal of extra complexity. Executing a multi-signature tx will be a multi-step process, and will potentially get worse with multiple clients, each implementing this process differently. By providing an efficient, standardized technique, we can improve the chance that developers will adopt compatible protocols and not bifurcate the user-base based on client selection. - - -### Specification: - -This BIP proposes the following process, with terms in quotes referring to recommended terminology that should be encouraged across all implementations. - -1. One party will initiate this process by creating a "Distribution Proposal", which could be abbreviated DP, or TxDP -2. Transaction preparation -- the user creating the TxDP will create the transaction as they would like to see it spent (obviously without the signatures). Then they will go through each input and replace its script with the script of the txout that the input is spending. The reason for is so that receiving parties can sign with their private key *without* needing access to the blockchain. -3. This TxDP will be serialized (see below), which will include a tag identifying the TxDP in the serialization, as well as in the filename, if it is saved to file. -4. The TxDP will have an "DP ID" which is the hash of the TxDP in Base58 -- the reason for this is to make sure it is not confused with the actual the transaction ID that it will have after it is broadcast (the transaction ID cannot be determined until after all signatures are collected). The final Tx ID can be referred to as its "Broadcast ID", in order to distinguish it from the pre-signed ID. -5. The TxDP will have an unordered list of sig-pubkey pairs which represent collected signatures. If you receive a TxDP missing only your signature, you can broadcast it as soon as you sign it. -6. Identical TxDP objects with different signatures can be easily combined -7. For cases where the TxDP might be put into a file to be sent via email, it should use .txdp or .btcdp suffix - - -Anyone adopting BIP 0010 for multi-sig transactions will use the following format: - - ("_TXDIST_") (magicBytes) (base58Txid) (varIntTxSize) - (preparedTxSerializedHex) - ("_TX_SIGS_") (#sigsIncludedVarInt) - ("_SIG_") (BTCAddress8char) (Sig0InputIndex) (varIntScriptSz) - (SigPubKeyPairHex) - ("_SIG_") (BTCAddress8char) (Sig1InputIndex) (varIntScriptSz) - (SigPubKeyPairHex) - ("_SIG_") (BTCAddress8char) (Sig2InputIndex) (varIntScriptSz) - (SigPubKeyPairHex) - -A multi-signature proposal that has 3 signatures on it could be stored in a file "Tx_QrtZ3K42n.txdp" and it would look something like: - - -----BEGIN-TXDP----- - _TXDIST_f9beb4d9_QrtZ3K42n_fda5 - 204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e642062 - 61696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104 - 678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb6 - 49f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f - ac00000000f9beb4d9d7000000010000006fe28c0ab6f1b372c1a6a246ae63f7 - 4f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14 - 677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299010100 - fe328f9a3920119cbd3f1311f830039832abb3baf284625151f328f9a3920 - _TXSIGS_03 - _SIG_1Gffm3Kj3_02_7e - fa8d9127149200f568383a089c68d619000000000098205bbbe680e1fee14677 - 44bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649 - _SIG_1Mr983F2s_00_7e - 99271840918f81ab18c1144bbbe680e1fee14677ba1a3c3fa8d9127149200f56 - 8383a089c68d619000000000098205bbbe680e1fee146774bbbe680e1f - _SIG_1QRTt83p8_00_7f - ffff00db606e857233e0e61bc6649ffff00db606efa8d9127149200f568383a0 - 89c68d619000000000098205bbbe680e1fee146770e1fee14677ba1a3c35 - ------END-TXDP------ - -In this transaction, there are 3 signatures already included, two for input 0, and one for input 2 (implying that that input 0 requires at least two signatures, and input 2 requires at least 1 -- the necessary number of signatures can be inferred from the TxOut scripts included in the TXDIST body). Bear in mind, most multi-signature TxDPs will only have a single input requiring multiple signatures. But there's no reason for this specification to be restricted to that case. - -The style of communication is taken directly from PGP/GPG, which typically uses blocks of ASCII like this to communicate encrypted messages and signatures. This serialization is compact, and will be interpretted the same in all character encodings. It can be copied inline into an email, or saved in a text file. The advantage over the analogous PGP encoding is that there are some human readable elements to it, for users that wish to examine the TxDP packet more closely. - -A party receiving this TxDP can simply add their signature to the end of the list, and incremenet the 0003 to 0004 on the _TXSIGS_ line. If that is the last signature required, they can broadcast it themselves. Any software that implements this standard should be able to combine multiple TxDPs into a single TxDP. However, even without the programmatic support, a user could manually combine them by copying the appropriate _TXSIGS_ lines between serializations, though it should not be the recommended method for combining TxDPs. - - -### Reference implementation - -The following python pseudo-code provides an example of how this serialization can be performed, and how to sign it - - - # Requires the multi-sig tx to be spent, and a list of recipients and values - def createTxDistProposal(multiSigTxOut, RecipientList, ValueList): - - # Do some sanity checks on the input data - assert(len(RecipientList) == len(ValueList)) - - totalDist = sum(valueList) - txFee = multiSigTxOut.value - totalDist - assert(txFee < 0) - if(txFee < minRecFee) - warn('Tx fee (%f) is lower than recommended (%f)' % (txFee,minRecFee)) - - - # Create empty tx - txdp = PyTx() - txdp.version = 1 - txdp.lockTime = 0 - - # Create empty tx, create only one input - txdp = PyTx() - txdp.inputs = [ PyTxOut() ] - txdp.inputs[0].prevTxOutHash = multiSigTxOut.parentHash - txdp.inputs[0].prevTxOutIndex = multiSigTxOut.parentIndex - txdp.inputs[0].binaryScript = multiSigTxOut.script - txdp.inputs[0].sequence = 0xffffffff - - # Create standard outputs - txdp.outputs = [] - for addr,val in zip(RecipientList, ValueList): - newTxOut = createStdTxOut(addr, val) - txdp.outputs.append(newTxOut) - - - # Serialize the transaction and create a DPID - txdpBinary = txdp.serialize() - txdpSize = len(txdpBinary) - dpidHash = sha256(sha256(txdpBinary)) - dpidID = binary_to_base58(dpidHash)[:8] - - # Start creating the ASCII message - txdpStr = '-----BEGIN-TXDP-----' - txdpStr += '_TXDIST_%s_%s_%s' % (magicBytes, dpidID, txdpSize) + '\n' - txdpHex = binary_to_hex(txdpBinary) - for byte in range(0,txdpSize,80): - txdpStr += txdpHex[byte:byte+80] + '\n' - txdpStr = '_TXSIGS_00' + '\n' - txdpStr = '-----END-TXDP-----' - - return txdpStr - - -Then a TxDP can be signed by - - - # To sign a txDP, we zero out all the inputs that aren't ours, add hashcode, then sign - def signTxDistProposal(txdpStr, inputToSign, myAddr): - - txdpLines = txdpStr.split('\n') - readDp = False - txHex = '' - output = '' - - # We copy the TxDP exactly as we read it, except for the TXSIGS line that - # will require incremeting. We stop just before the END-TXDP line so we - # can append our signature to the end of the TXSIGS list - for line in txdpLines: - if 'END-TXDP' in line: - break - - if readDp: - txHex += line.strip() - - # Read TXDP, starting next line - if line.startswith('_TXDIST_'): - readDp = True - - # Copy the line exactly as it's read, unless it's TXSIGS line - if line.startswith('_TXSIGS_'): - readDp = False - nSigs = readVarInt(line.split('_')[-1].strip()) - output += '_TXSIGS_' + writeVarIntHex(nSigs+1) + '\n' - else: - output += line - - - # All inputs have the appropriate TxOut script already included - # For signing (SIGHASH_ALL) we need to blank out the ones not being signed - txToSign = PyTx().unserialize(hex_to_binary(txHex)) - for i in range(len(txToSign.inputs)): - if not i==inputToSign: - txToSign[i] = '' - - SIGHASH_ALL = 1 - hashcode = int_to_binary(SIGHASH_ALL, widthBytes=4, endOut=LITTLEENDIAN) - binaryToSign = sha256(sha256(txToSign.serialize() + hashcode)) - binaryToSign = switchEndian(binaryToSign) # hash needs to be BigEndian - sig = myAddr.privKey.generateDERSignature(binaryToSign) - - txinScript = createStdTxInScript(sig, myAddr.pubKey) - txinScriptHex = binary_to_hex(txinScript) - inputNum = binary_to_hex(writeVarInt(inputToSign)) - scriptSz = binary_to_hex(writeVarInt(len(txinScript)) - output += '_SIG_%s_%s_%s\n' % (myAddr.base58str()[:8], inputNum, scriptSz) - for byte in range(0,len(txinScriptHex), 80): - output += txinScriptHex[byte:byte+80] + '\n' - output += '-----END-TXDP-----' - return output From 5e56dc94ca9a007d95f7c5d8625667ccd0870250 Mon Sep 17 00:00:00 2001 From: genjix Date: Tue, 13 Dec 2011 17:27:59 +0000 Subject: [PATCH 3/6] Synchronised BIPs wiki Wiki changes. --- bip-0011.md | 2 ++ bip-0012.md | 1 + bip-0013.md | 8 ++++---- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/bip-0011.md b/bip-0011.md index e5257ba2..2a5b3476 100644 --- a/bip-0011.md +++ b/bip-0011.md @@ -35,6 +35,8 @@ OP_CHECKMULTISIG transactions are redeemed using a standard scriptSig: (OP_0 is required because of a bug in OP_CHECKMULTISIG; it pops one too many items off the execution stack, so a dummy value must be placed on the stack). +The current Satoshi bitcoin client does not relay or mine transactions with scriptSigs larger than 200 bytes; to accomodate 3-signature transactions, this will be increased to 500 bytes. + ==Rationale== OP_CHECKMULTISIG is already an enabled opcode, and is the most straightforward way to support several important use cases. diff --git a/bip-0012.md b/bip-0012.md index 2ef52633..bddccc20 100644 --- a/bip-0012.md +++ b/bip-0012.md @@ -82,3 +82,4 @@ https://bitcointalk.org/index.php?topic=46538 M-of-N Multisignature Transactions BIP 11 + diff --git a/bip-0013.md b/bip-0013.md index d0e3c150..373972a4 100644 --- a/bip-0013.md +++ b/bip-0013.md @@ -8,9 +8,9 @@ ==Abstract== -This BIP describes a new type of bitcoin address to support arbitrarily complex transactions. Complexity in this context is defined as what information is needed by the recipient to respend the received coins, in contrast to needing a single ECDSA private key as in current implementations of Bitcoin. +This BIP describes a new type of Bitcoin address to support arbitrarily complex transactions. Complexity in this context is defined as what information is needed by the recipient to respend the received coins, in contrast to needing a single ECDSA private key as in current implementations of Bitcoin. -In essence, an address encoded under this proposal represents the encoded hash of a script, rather than the encoded hash of an ECDSA [[public key]]. +In essence, an address encoded under this proposal represents the encoded hash of a [[script]], rather than the encoded hash of an ECDSA public key. ==Motivation== @@ -18,11 +18,11 @@ Enable "end-to-end" secure wallets and payments to fund escrow transactions or o ==Specification== -The new bitcoin address type is constructed in the same manner as existing bitcoin addresses: +The new bitcoin address type is constructed in the same manner as existing bitcoin addresses (see [[Base58Check encoding]]): base58-encode: [one-byte version][20-byte hash][4-byte checksum] -Version byte is 1 for a main-network address, 112 for a testnet address. +Version byte is 2 for a main-network address, 109 for a testnet address. The 20-byte hash is the hash of the script that will be used to redeem the coins. And the 4-byte checksum is the first four bytes of the SHA256 hash of the version and hash. From 271b2a4c7f3ae35e1f84a03b39e147d88d3822c8 Mon Sep 17 00:00:00 2001 From: genjix Date: Tue, 13 Dec 2011 17:28:52 +0000 Subject: [PATCH 4/6] BIP 0015 - still Work In Progress and under discussion (Draft) --- bip-0015.md | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 bip-0015.md diff --git a/bip-0015.md b/bip-0015.md new file mode 100644 index 00000000..2a042bab --- /dev/null +++ b/bip-0015.md @@ -0,0 +1,325 @@ +
+  BIP: 15
+  Title: BIP Aliases
+  Author: Amir Taaki 
+  Status: Draft
+  Type: Standards Track
+  Created: 10-12-2011
+
+ +Using vanilla bitcoin, to send funds to a destination, an address in the form 1Hd44nkJfNAcPJeZyrGC5sKJS1TzgmCTjjZ is needed. The problem with using addresses is they are not easy to remember. An analogy can be thought if one were required to enter the IP address of their favourite websites if domain names did not exist. + +This document aims to layout through careful argument, a bitcoin alias system. This is a big modification to the protocol that is not easily changed in the future and has big ramifications. There is impetus in getting it correct the first time. Aliases have to be robust and secure. + +== Schemes == + +Here are a few different proposals and the properties of each system. + +=== FirstBits === + +FirstBits is a proposal for using the blockchain as an address book. + +When bitcoins are sent to an address, that address becomes recorded in the blockchain. It is therefore known that this address exists or did exist by simply seeing that there was a payment to that address. FirstBits is a method to have a memorable alias. One first converts the address to lower-case, then takes the first few unique characters. This is your FirstBits alias. + +As an example, brmlab hackerspace in Prague has an address for purchasing food or drink, or making donations: + + 1BRMLAB7nryYgFGrG8x9SYaokb8r2ZwAsX + +Their FirstBits alias becomes: + + 1brmlab + +It is enough information to be given the FirstBits alias ''1brmlab''. When someone wishes to make a purchase, without FirstBits, they either have to type out their address laboriously by hand, scan their QR code (which requires a mobile handset that this author does not own) or find their address on the internet to copy and paste into the client to send bitcoins. FirstBits alleviates this impracticality by providing an easy method to make payments. + +Together with Vanitygen (vanity generator), it becomes possible to create memorable unique named addresses. Addresses that are meaningful, rather than an odd assemblage of letters and numbers but add context to the destination. + +However FirstBits has its own problems. One is that the possible aliases one is able to generate is limited by the available computing power available. It may not be feasible to generate a complete or precise alias that is wanted- only approximates may be possible. It is also computationally resource intensive which means a large expenditure of power for generating unique aliases in the future, and may not scale up to the level of individuals at home or participants with hand-held devices in an environment of ubiquitous computing. + +FirstBits scales extremely poorly as the network grows. Each indexer or lookup node needs to keep track of every bitcoin address ever in existence and provide a fast lookup from the aliases to those addresses. As the network grows linearly, the number of addresses should grow exponentially (assuming a networked effect of (n-1)*(n-2)/2) rapidly making this scheme unfeasible. + +Light clients of the partial merkle root types become dependent on a trusted third party for their alias lookups. The cost of storing every bitcoin address is too high considering their typical use-case on low-resource devices. This factor more than the others, means this scheme is sub-optimal and must be rejected. + +=== DNS TXT Records === + +DNS allows TXT records to be created containing arbitrary data. In a bitcoin alias system, a custom format mutually agreed upon by a BIP standard would be used to store mappings to bitcoin addresses from domain names. How such a format would look is out of the scope of this document. + +An issue is that it requires people who wish to create such mappings to be familiar with configuring DNS records, and be able to run the necessary toolsets to insert the correct data. Although not a huge concern, it is a usability issue. + +Security wise, DNS is unsafe and insecure by design. It is possible to spoof records by being on the same network as another host. A number of revisions to mitigate the issue under the guise of DNSSEC have been in the works since the 1990s and are still being rolled out. + +As of Dec 2011, DNSSEC is still not yet a defacto standard on the internet. Should a participant in the bitcoin network wish to use DNS TXT records, they would in addition to having to configure DNS, be able to setup DNSSEC. This may not be feasible, especially where some registrars provide access to DNS through a web interface only. + +=== Server Service === + +Aside from using DNS TXT records, another possibility is using the domain name system to lookup hosts and then contact a service running on a predefined port to get the bitcoin address. + +# User wishes to send to foo@bar.net +# Client uses DNS to find the IP address of bar.net: 123.123.123.123 +# Client connects to port 123.123.123.123:4567 and requests the bitcoin address for the user ''foo'' +# Server responds with the address or error code and terminates the connection. +# Client sends the funds to the address + +The service would be responsible for providing the mechanisms for changing and storing the mappings on their service. A front-end web interface could be provided to users wishing to use the service and customise their accounts on the server. + +This approach has the positive aspect of providing the best flexibility for the implementer to store the records however they wish in a database or plaintext file, and then serve them up quickly using a small server side daemon typically written in C. This approach is highly scalable. + +However this approach also suffers the problem of being reliant on DNS and hence also being vulnerable to spoofing. Hence DNSSEC is also required. This approach is slightly better than the DNS TXT records though since it makes inserting new users and modifying aliases very easy which allows people to run these server services more cheaply. + +=== HTTPS Web Service === + +HTTPS provides an additional layer of security by encrypting the connection, providing much needed privacy for users. Together with using Certificate Authorities, it fixes the issue with using DNSSEC since an error would be thrown up were someone to try to spoof a domain name on the local network. + +When trying to send to: + + genjix@foo.org + +The request is broken into the handle (genjix) and domain (foo.org) at the last occurrence of the @. The client then constructs a request that will query for the address. + + https://foo.org/bitcoin-alias/?handle=genjix + +bitcoin-alias has been chosen as the query suffix because it allows this system to co-exist easily within another web root without the fear of name clashes. + +The query will then return an address which is used to make the payment. + + 1Hd44nkJfNAcPJeZyrGC5sKJS1TzgmCTjjZ + +The details of whether a unique address is returned per query, whether an address is fetched from a pre-existing pool of addresses, and so on is an implementation detail unique to every server. How alias to address mappings are setup is dependent on the site which could have a web-interface and be providing a free service to users or be a private customised service serving pre-existing addresses. This is left up to sysop policy, and deliberately not defined here. + +A web service is trivial to setup and the cost is low. There are many free out of the box providers on the net that allows anyone with the most basic knowledge of web technologies to create their own website. By providing users with a package, anybody can quickly set themselves up with a bitcoin alias. It could be something as simple as a PHP script that the user edits with their custom settings and uploads themselves to their website. + +It also scales reasonably- anybody wishing to run a naming service can attach a backend with a variety of database technologies then provide a web frontend for users to customise and create their own aliases. + +A naive implementation is provided below as an example. + + +// resolv.h +#ifndef NOMRESOLV_H__ +#define NOMRESOLV_H__ + +#include +#include "curl/curl.h" + +using std::string; + +/* + +This class resolves against a server to lookup addresses. +To not conflict with the bitcoin addresses, we refer here to people's handles. +A handle is of the form: + + genjix@foo.org + +Most characters are valid for the username + password (and handled accordingly), but the domain follows usual web standards. It is possible to affix a path if needed, + + genjix@bar.com/path/to/ + +*/ + +class NameResolutionService +{ +public: + NameResolutionService(); + ~NameResolutionService(); + + // Three main methods map to RPC actions. + string FetchAddress(const string& strHandle, string& strAddy); + +private: + // A POST block + class PostVariables + { + public: + PostVariables(); + ~PostVariables(); + // Add a new key, value pair + bool Add(const string& strKey, const string& strVal); + curl_httppost* operator()() const; + private: + // CURL stores POST blocks as linked lists. + curl_httppost *pBegin, *pEnd; + }; + + // Explodes user@domain => user, domain + static void ExplodeHandle(const string& strHandle, string& strNickname, string& strDomain); + // Perform the HTTP request. Returns true on success. + bool Perform(); + + // CURL error message + char pErrorBuffer[CURL_ERROR_SIZE]; + // CURL response + string strBuffer; + // CURL handle + CURL *curl; +}; + +#endif + + + +// resolv.cpp +#include "resolv.h" + +#include + +#include "access.h" + +// callback used to write response from the server +static int writer(char *pData, size_t nSize, size_t nNmemb, std::string *pBuffer) +{ + int nResult = 0; + if (pBuffer != NULL) + { + pBuffer->append(pData, nSize * nNmemb); + // How much did we write? + nResult = nSize * nNmemb; + } + return nResult; +} + +NameResolutionService::NameResolutionService() +{ + // Initialise CURL with our various options. + curl = curl_easy_init(); + // This goes first in case of any problems below. We get an error message. + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, pErrorBuffer); + // fail when server sends >= 404 + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); + curl_easy_setopt(curl, CURLOPT_HEADER, 0); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_302); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer); + curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); + // server response goes in strBuffer + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &strBuffer); + pErrorBuffer[0] = '\0'; +} +NameResolutionService::~NameResolutionService() +{ + curl_easy_cleanup(curl); +} + +void NameResolutionService::ExplodeHandle(const string& strHandle, string& strNickname, string& strDomain) +{ + // split address at @ furthrest to the right + size_t nPosAtsym = strHandle.rfind('@'); + strNickname = strHandle.substr(0, nPosAtsym); + strDomain = strHandle.substr(nPosAtsym + 1, strHandle.size()); +} +bool NameResolutionService::Perform() +{ + // Called after everything has been setup. This actually does the request. + CURLcode result = curl_easy_perform(curl); + return (result == CURLE_OK); +} + +string NameResolutionService::FetchAddress(const string& strHandle, string& strAddy) +{ + // GET is defined for 'getting' data, so we use GET for the low risk fetching of people's addresses + if (!curl) + // For some reason CURL didn't start... + return pErrorBuffer; + // Expand the handle + string strNickname, strDomain; + ExplodeHandle(strHandle, strNickname, strDomain); + // url encode the nickname for get request + const char* pszEncodedNick = curl_easy_escape(curl, strNickname.c_str(), strNickname.size()); + if (!pszEncodedNick) + return "Unable to encode nickname."; + // construct url for GET request + string strRequestUrl = strDomain + "/bitcoin-alias/?handle=" + pszEncodedNick; + // Pass URL to CURL + curl_easy_setopt(curl, CURLOPT_URL, strRequestUrl.c_str()); + if (!Perform()) + return pErrorBuffer; + // Server should respond with a JSON that has the address in. + strAddy = strBuffer; + return ""; // no error +} + +NameResolutionService::PostVariables::PostVariables() +{ + // pBegin/pEnd *must* be null before calling curl_formadd + pBegin = NULL; + pEnd = NULL; +} +NameResolutionService::PostVariables::~PostVariables() +{ + curl_formfree(pBegin); +} +bool NameResolutionService::PostVariables::Add(const string& strKey, const string& strVal) +{ + // Copy strings to this block. Return true on success. + return curl_formadd(&pBegin, &pEnd, CURLFORM_COPYNAME, strKey.c_str(), CURLFORM_COPYCONTENTS, strVal.c_str(), CURLFORM_END) == CURL_FORMADD_OK; +} + +curl_httppost* NameResolutionService::PostVariables::operator()() const +{ + return pBegin; +} + + + +// rpc.cpp +... + +const Object CheckMaybeThrow(const string& strJsonIn) +{ + // Parse input JSON + Value valRequest; + if (!read_string(strJsonIn, valRequest) || valRequest.type() != obj_type) + throw JSONRPCError(-32700, "Parse error"); + const Object& request = valRequest.get_obj(); + // Now check for a key called "error" + const Value& error = find_value(request, "error"); + // It's an error JSON! so propagate the error. + if (error.type() != null_type) + throw JSONRPCError(-4, error.get_str()); + // Return JSON object + return request; +} + +const string CollectAddress(const string& strIn) +{ + // If the handle does not have an @ in it, then it's a normal base58 bitcoin address + if (strIn.find('@') == (size_t)-1) + return strIn; + + // Open the lookup service + NameResolutionService ns; + // We established that the input string is not a BTC address, so we use it as a handle now. + string strHandle = strIn, strAddy; + string strError = ns.FetchAddress(strHandle, strAddy); + if (!strError.empty()) + throw JSONRPCError(-4, strError); + + const Object& request(CheckMaybeThrow(strAddy)); + // Get the BTC address from the JSON + const Value& address = find_value(request, "address"); + if (address.type() != str_type) + throw JSONRPCError(-32600, "Server responded with malformed reply."); + return address.get_str(); +} + +// Named this way to prevent possible conflicts. +Value rpc_send(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error( + "send \n" + " is a real and is rounded to the nearest 0.01"); + + // Intelligent function which looks up address given handle, or returns address + string strAddy = CollectAddress(params[0].get_str()); + int64 nAmount = AmountFromValue(params[1]); + // Do the send + CWalletTx wtx; + string strError = SendMoneyToBitcoinAddress(strAddy, nAmount, wtx); + if (!strError.empty()) + throw JSONRPCError(-4, strError); + return wtx.GetHash().GetHex(); +} + +... + + From 12627baeea94a377bb94612beeec9b9cc4bd978e Mon Sep 17 00:00:00 2001 From: genjix Date: Tue, 13 Dec 2011 17:35:53 +0000 Subject: [PATCH 5/6] BIP 0011, BIP 0012: Draft -> Accepted --- bip-0011.md | 2 +- bip-0012.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bip-0011.md b/bip-0011.md index 2a5b3476..3eba9337 100644 --- a/bip-0011.md +++ b/bip-0011.md @@ -2,7 +2,7 @@ BIP: 11 Title: M-of-N Standard Transactions Author: Gavin Andresen - Status: Draft + Status: Accepted Type: Standards Track Created: 18-10-2011 Post-History: 02-10-2011 diff --git a/bip-0012.md b/bip-0012.md index bddccc20..e339d3f1 100644 --- a/bip-0012.md +++ b/bip-0012.md @@ -2,7 +2,7 @@ BIP: 12 Title: OP_EVAL Author: Gavin Andresen - Status: Draft + Status: Accepted Type: Standards Track Created: 18-10-2011 From d0c2d53a56bb48cd75ec8280dc296019a3dd5e6c Mon Sep 17 00:00:00 2001 From: genjix Date: Tue, 20 Dec 2011 17:57:51 +0000 Subject: [PATCH 6/6] My mistake. Apologies Alan Reiner (etotheipi) Revert "deleted BIP 10 - never updated published or discussed" This reverts commit 314cc17cf578d2cebec74a33ab692f425e8617e9. --- bip-0010.md | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 bip-0010.md diff --git a/bip-0010.md b/bip-0010.md new file mode 100644 index 00000000..c794fdb2 --- /dev/null +++ b/bip-0010.md @@ -0,0 +1,189 @@ +## BIP 0010 - Proposal for Standardized, Multi-Signature Transaction Execution + + +Author: Alan Reiner +Contact: etotheipi@gmail.com +Status: Draft +Orig Date: 28 Oct, 2011 + + +### Abstract: + +A multi-signature transaction is one where a certain number of Bitcoins are "encumbered" with more than one recipient address. The subsequent transaction that spends these coins will require each party involved (or some subset, depending on the script), to see the final, proposed transaction, and sign it with their private key. This necessarily requires collaboration between all parties -- to propose a distribution of encumbered funds, collect signatures from all necessary participants, and then broadcast the completed transaction. + +This BIP describes a protocol to standardize the representation of proposal transactions and the subsequent collection of signatures to execute multi-signature transactions. The goal is to encourage a standard that guarantees interoperability of all programs that implement it. + + +### Motivation: + +The enabling of multi-signature transactions in Bitcoin will introduce a great deal of extra functionality to the users of the network, but also a great deal of extra complexity. Executing a multi-signature tx will be a multi-step process, and will potentially get worse with multiple clients, each implementing this process differently. By providing an efficient, standardized technique, we can improve the chance that developers will adopt compatible protocols and not bifurcate the user-base based on client selection. + + +### Specification: + +This BIP proposes the following process, with terms in quotes referring to recommended terminology that should be encouraged across all implementations. + +1. One party will initiate this process by creating a "Distribution Proposal", which could be abbreviated DP, or TxDP +2. Transaction preparation -- the user creating the TxDP will create the transaction as they would like to see it spent (obviously without the signatures). Then they will go through each input and replace its script with the script of the txout that the input is spending. The reason for is so that receiving parties can sign with their private key *without* needing access to the blockchain. +3. This TxDP will be serialized (see below), which will include a tag identifying the TxDP in the serialization, as well as in the filename, if it is saved to file. +4. The TxDP will have an "DP ID" which is the hash of the TxDP in Base58 -- the reason for this is to make sure it is not confused with the actual the transaction ID that it will have after it is broadcast (the transaction ID cannot be determined until after all signatures are collected). The final Tx ID can be referred to as its "Broadcast ID", in order to distinguish it from the pre-signed ID. +5. The TxDP will have an unordered list of sig-pubkey pairs which represent collected signatures. If you receive a TxDP missing only your signature, you can broadcast it as soon as you sign it. +6. Identical TxDP objects with different signatures can be easily combined +7. For cases where the TxDP might be put into a file to be sent via email, it should use .txdp or .btcdp suffix + + +Anyone adopting BIP 0010 for multi-sig transactions will use the following format: + + ("_TXDIST_") (magicBytes) (base58Txid) (varIntTxSize) + (preparedTxSerializedHex) + ("_TX_SIGS_") (#sigsIncludedVarInt) + ("_SIG_") (BTCAddress8char) (Sig0InputIndex) (varIntScriptSz) + (SigPubKeyPairHex) + ("_SIG_") (BTCAddress8char) (Sig1InputIndex) (varIntScriptSz) + (SigPubKeyPairHex) + ("_SIG_") (BTCAddress8char) (Sig2InputIndex) (varIntScriptSz) + (SigPubKeyPairHex) + +A multi-signature proposal that has 3 signatures on it could be stored in a file "Tx_QrtZ3K42n.txdp" and it would look something like: + + -----BEGIN-TXDP----- + _TXDIST_f9beb4d9_QrtZ3K42n_fda5 + 204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e642062 + 61696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104 + 678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb6 + 49f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f + ac00000000f9beb4d9d7000000010000006fe28c0ab6f1b372c1a6a246ae63f7 + 4f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14 + 677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299010100 + fe328f9a3920119cbd3f1311f830039832abb3baf284625151f328f9a3920 + _TXSIGS_03 + _SIG_1Gffm3Kj3_02_7e + fa8d9127149200f568383a089c68d619000000000098205bbbe680e1fee14677 + 44bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649 + _SIG_1Mr983F2s_00_7e + 99271840918f81ab18c1144bbbe680e1fee14677ba1a3c3fa8d9127149200f56 + 8383a089c68d619000000000098205bbbe680e1fee146774bbbe680e1f + _SIG_1QRTt83p8_00_7f + ffff00db606e857233e0e61bc6649ffff00db606efa8d9127149200f568383a0 + 89c68d619000000000098205bbbe680e1fee146770e1fee14677ba1a3c35 + ------END-TXDP------ + +In this transaction, there are 3 signatures already included, two for input 0, and one for input 2 (implying that that input 0 requires at least two signatures, and input 2 requires at least 1 -- the necessary number of signatures can be inferred from the TxOut scripts included in the TXDIST body). Bear in mind, most multi-signature TxDPs will only have a single input requiring multiple signatures. But there's no reason for this specification to be restricted to that case. + +The style of communication is taken directly from PGP/GPG, which typically uses blocks of ASCII like this to communicate encrypted messages and signatures. This serialization is compact, and will be interpretted the same in all character encodings. It can be copied inline into an email, or saved in a text file. The advantage over the analogous PGP encoding is that there are some human readable elements to it, for users that wish to examine the TxDP packet more closely. + +A party receiving this TxDP can simply add their signature to the end of the list, and incremenet the 0003 to 0004 on the _TXSIGS_ line. If that is the last signature required, they can broadcast it themselves. Any software that implements this standard should be able to combine multiple TxDPs into a single TxDP. However, even without the programmatic support, a user could manually combine them by copying the appropriate _TXSIGS_ lines between serializations, though it should not be the recommended method for combining TxDPs. + + +### Reference implementation + +The following python pseudo-code provides an example of how this serialization can be performed, and how to sign it + + + # Requires the multi-sig tx to be spent, and a list of recipients and values + def createTxDistProposal(multiSigTxOut, RecipientList, ValueList): + + # Do some sanity checks on the input data + assert(len(RecipientList) == len(ValueList)) + + totalDist = sum(valueList) + txFee = multiSigTxOut.value - totalDist + assert(txFee < 0) + if(txFee < minRecFee) + warn('Tx fee (%f) is lower than recommended (%f)' % (txFee,minRecFee)) + + + # Create empty tx + txdp = PyTx() + txdp.version = 1 + txdp.lockTime = 0 + + # Create empty tx, create only one input + txdp = PyTx() + txdp.inputs = [ PyTxOut() ] + txdp.inputs[0].prevTxOutHash = multiSigTxOut.parentHash + txdp.inputs[0].prevTxOutIndex = multiSigTxOut.parentIndex + txdp.inputs[0].binaryScript = multiSigTxOut.script + txdp.inputs[0].sequence = 0xffffffff + + # Create standard outputs + txdp.outputs = [] + for addr,val in zip(RecipientList, ValueList): + newTxOut = createStdTxOut(addr, val) + txdp.outputs.append(newTxOut) + + + # Serialize the transaction and create a DPID + txdpBinary = txdp.serialize() + txdpSize = len(txdpBinary) + dpidHash = sha256(sha256(txdpBinary)) + dpidID = binary_to_base58(dpidHash)[:8] + + # Start creating the ASCII message + txdpStr = '-----BEGIN-TXDP-----' + txdpStr += '_TXDIST_%s_%s_%s' % (magicBytes, dpidID, txdpSize) + '\n' + txdpHex = binary_to_hex(txdpBinary) + for byte in range(0,txdpSize,80): + txdpStr += txdpHex[byte:byte+80] + '\n' + txdpStr = '_TXSIGS_00' + '\n' + txdpStr = '-----END-TXDP-----' + + return txdpStr + + +Then a TxDP can be signed by + + + # To sign a txDP, we zero out all the inputs that aren't ours, add hashcode, then sign + def signTxDistProposal(txdpStr, inputToSign, myAddr): + + txdpLines = txdpStr.split('\n') + readDp = False + txHex = '' + output = '' + + # We copy the TxDP exactly as we read it, except for the TXSIGS line that + # will require incremeting. We stop just before the END-TXDP line so we + # can append our signature to the end of the TXSIGS list + for line in txdpLines: + if 'END-TXDP' in line: + break + + if readDp: + txHex += line.strip() + + # Read TXDP, starting next line + if line.startswith('_TXDIST_'): + readDp = True + + # Copy the line exactly as it's read, unless it's TXSIGS line + if line.startswith('_TXSIGS_'): + readDp = False + nSigs = readVarInt(line.split('_')[-1].strip()) + output += '_TXSIGS_' + writeVarIntHex(nSigs+1) + '\n' + else: + output += line + + + # All inputs have the appropriate TxOut script already included + # For signing (SIGHASH_ALL) we need to blank out the ones not being signed + txToSign = PyTx().unserialize(hex_to_binary(txHex)) + for i in range(len(txToSign.inputs)): + if not i==inputToSign: + txToSign[i] = '' + + SIGHASH_ALL = 1 + hashcode = int_to_binary(SIGHASH_ALL, widthBytes=4, endOut=LITTLEENDIAN) + binaryToSign = sha256(sha256(txToSign.serialize() + hashcode)) + binaryToSign = switchEndian(binaryToSign) # hash needs to be BigEndian + sig = myAddr.privKey.generateDERSignature(binaryToSign) + + txinScript = createStdTxInScript(sig, myAddr.pubKey) + txinScriptHex = binary_to_hex(txinScript) + inputNum = binary_to_hex(writeVarInt(inputToSign)) + scriptSz = binary_to_hex(writeVarInt(len(txinScript)) + output += '_SIG_%s_%s_%s\n' % (myAddr.base58str()[:8], inputNum, scriptSz) + for byte in range(0,len(txinScriptHex), 80): + output += txinScriptHex[byte:byte+80] + '\n' + output += '-----END-TXDP-----' + return output