View difference between Paste ID: bcs8XFNG and 2MHnW9FA
SHOW: | | - or go back to the newest paste.
1
# This is a proposal to eliminate the need for OP_RETURN to relay 
2
3
# First lets take an utxo that we own and try to send it to a stealth address.
4
5
# Info of utxo:
6
7
prev_hash = 'f3ebd0b3534518c23f439aac836ff207d6414b6530aecf1e0fff1d413774f994'
8
vout = '06' # The 7th output.
9
amount = 0.03170609 # in BTC
10
11
# The address I am using to send from in this example is 1FHxvwCfFz3B1VZSAQa71qWGyDbxdfamh4
12
13
14
# Normally, this is where we would generate an ephemeral pubkey.
15
# But instead, the sender, having entered the password for signature, reveals the private key
16
# to the above address.
17
18
privkey = Decrypt(encrypted_privkey) # lets assume privkey is in bigint format.
19
20
shared_secret = ECmultiply(privkey, scan_pubkey) # scan_pubkey of recipient
21
shared_secret_priv = sha256(shared_secret)
22
shared_secret_pub = ECmultiply(shared_secret_priv, SECP256k1_generator_point)
23
24
# We have in effect, used the senders key as the ephemeral key. Since the pubkey must be
25
# included in the ScriptSig, the recipient can know the shared secret without
26
# the tx looking obviously like a stealth transaction.
27
28
# Here is where I thought: Oh, but if I send more bitcoins from the same address to the
29
# same stealth address... the resulting send_to address will be the same! address reuse! OMG
30
31
# Now comes the solution. We take the hash of concatenated scan_pubkey, prev_hash, vout, and index
32
# index is only used in the case where the same input is used with the same stealth address in the
33
# same transaction. So if my input is fefe...fefe:3 and I have two outputs sending to Stealth Address Xyz...
34
# Then the first one uses '00' and the second uses '01' etc.
35
index = '00'
36
37
38
# This becomes the privkey for a 3rd point.
39
40
41
vout_priv = sha256((scan_pubkey + prev_hash + vout + index).decode('hex'))
42
vout_pub = ECmultiply(vout_priv, SECP256k1_generator_point)
43
44
45-
# Now we have 3 points: Scan_pubkey, shared_secret_pub, and vout_pub.
45+
# Now we have 3 points: spend_pubkey, shared_secret_pub, and vout_pub.
46
# Add all three together.
47
48-
address_point = ECaddition(ECaddition(scan_pubkey, shared_secret_pub), vout_pub)
48+
address_point = ECaddition(ECaddition(spend_pubkey, shared_secret_pub), vout_pub)
49
# As ECadd is communicative, order of addition doesnt matter
50
51
send_to_address = Base58CheckEncode(ripemd160(sha256(address_point)))
52
53
# create the output sending to the above address. Sending is finished.
54
55
56
# For the recipient:
57
# First, lets assume he knows what tx is to him from some outside source.
58
# (I will mention the search method later)
59
60
# He searches the ScriptSig of each input for any pubkeys, and tries them in order.
61
# (normal p2kh will have 1 pubkey per input)
62
63
# He notices the pubkey of the first input (which is prev_hash)
64
65
pubkey = '0309430d1bea1b90a59523f2892cd26084808394ce5cc7ea4b6ecbec0a63480c8c'
66
67
shared_secret = ECmultiply(scan_privkey, pubkey) # pubkey from ScriptSig x scan_privkey
68
shared_secret_priv = sha256(shared_secret)
69
70
71
# Then he looks at the vout index and prevhash used in the input.
72
73
vout_priv = sha256((scan_pubkey + prev_hash + vout + index).decode('hex'))
74
75
76
# Adds together the three privkeys to (hopefully) generate the address.
77
78-
send_to_address_priv = (scan_privkey + shared_secret_priv + vout_priv) % N # Mod order of the curve.
78+
send_to_address_priv = (spend_privkey + shared_secret_priv + vout_priv) % N # Mod order of the curve.
79
80
# Generate the address and check all outputs in the transaction to see if it is there.
81
send_to_address_pub = ECmultiply(send_to_address_priv, SECP256k1_generator_point)
82
send_to_address_test = Base58CheckEncode(ripemd160(sha256(send_to_address_pub)))
83
84
assert send_to_address_test == send_to_address
85
86
# If the addresses are equal, then you now have the private key,
87
# and the only information necessary was the txid.
88
89
90
91
"""
92
Lets talk about performance:
93
94
If we look at the procedures for send and receive with the old OP_RETURN
95
method (I left out simple things like adding bigints and modding the order of the curve etc.)
96
97
Old send   : ECmult > sha256 > ECmult > ECadd > sha256 > ripemd160
98
ECmult x 2
99
ECadd x 1
100
sha256 x 2
101
ripemd160 x 1
102
103
Old receive: ECmult > sha256 > ECmult > sha256 > ripemd160
104
ECmult x 2
105
sha256 x 2
106
ripemd160 x 1
107
108
109
Looking at the New method I explained above and comparing:
110
111
New send   : ECmult > sha256 > ECmult > sha256 > ECmult > ECadd > ECadd > sha256 > ripemd160
112
ECmult x 3
113
ECadd x 2
114
sha256 x 3
115
ripemd160 x 1
116
Compare: Increase of 1 of each (ECmult, ECadd, sha256)
117
118
New receive: ECmult > sha256 > sha256 > ECmult > sha256 > ripemd160
119
ECmult x 2
120
sha256 x 3
121
ripemd160 x 1
122
Compare: Increase of 1 sha256
123
124
The heavy lifting is on the receiving end (as you must now iterate through every single ScriptSig
125
and compare against all output addresses.) but luckily the receiving calculations only increased by one sha256.
126
127
To narrow down the txes you need to search... I was wondering if the input that was used to generate the address
128
could change the sequence (the one that is normally 0xffffffff at the end of each input) to hash to the prefix...
129
or even match the sequence msb bits to the prefix bits etc.
130
131
Caveats:
132
1. Can't send to stealth from Trezor using this method. (as you need to multiply your private key with the scan_pubkey)
133
   Unless Trezor allowed for performing ECmultiplication on the device, and Trezor returns the shared secret.
134
135
2. When thinking about coinjoin and other aspects where multiple people spending to multiple stealth addresses, if we consider coinjoin, we must check every input of every transaction against our info... which is costly. Doing things to mark inputs as stealth info (maybe changing the sequence value) could be considered... but for 100% stealth and 100% no way to discern stealth from normal... there seems to be no other way than to check every input.
136
137
However: that being said... it eliminates the requirement for the OP_RETURN table on Obelisk servers... and it's not much more strenuous than running a full node. (having to scan every input of every transaction etc. when blocks come in) but it will be more strenuous for SPV servers, as upon every block they will have to send every transaction to each user... it will also be hard to do on mobile devices (drain battery)... obviously the wallet file would store the most recently checked block so that it doesn't check from the beginning every time...
138
139
Perhaps a mode where the wallet doesn't actively search, but users need to input the txid or something that they receive from the sender might be good for mobile...
140
141
I would like to hear any comments / questions.
142
143
- dabura667
144
145
"""