From 34491cb6714364e340037aef9f9342fac247ed10 Mon Sep 17 00:00:00 2001
From: ross-weir <29697678+ross-weir@users.noreply.github.com>
Date: Fri, 16 Dec 2022 21:53:51 +1100
Subject: [PATCH 1/5] draft eip41 stealth address spec
---
README.md | 3 +-
eip-0041.md | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 96 insertions(+), 1 deletion(-)
create mode 100644 eip-0041.md
diff --git a/README.md b/README.md
index b8907a9d..4602bd17 100644
--- a/README.md
+++ b/README.md
@@ -21,4 +21,5 @@ Please check out existing EIPs, such as [EIP-1](eip-0001.md), to understand the
| [EIP-0025](eip-0025.md) | Payment Request URI |
| [EIP-0027](eip-0027.md) | Emission Retargeting Soft-Fork |
| [EIP-0031](eip-0031.md) | Babel Fees |
-| [EIP-0039](eip-0039.md) | Monotonic box creation height rule |
\ No newline at end of file
+| [EIP-0039](eip-0039.md) | Monotonic box creation height rule |
+| [EIP-0041](eip-0041.md) | Stealth address standard |
diff --git a/eip-0041.md b/eip-0041.md
new file mode 100644
index 00000000..31250599
--- /dev/null
+++ b/eip-0041.md
@@ -0,0 +1,94 @@
+# EIP-41 Stealth Address Standard
+
+* Author: ross-weir
+* Status: Draft
+* Created: 16-Dec-2022
+* Last edited: 16-Dec-2022
+* License: CC0
+* Forking: not needed
+
+## Motivation
+
+This specification defines a standardized way of implementing and interacting with stealth addresses on the Ergo blockchain.
+
+Stealth addresses enable recipients of a payment to remain anonymous when receiving funds thus providing financial privacy should an actor desire it.
+
+## Scenario
+
+An actor, `Receiver`, wishes to receive a stealth payment so they generate a public address and share it.
+
+An actor, `Sender`, wishes to make a stealth payment to `Receiver` so they create a box protected by a "one-time-secret" generated from the `Receiver`s public address. Due to the method of generation this box will be spendable by `Receiver` and cannot be linked to the `Receiver`s public address they shared.
+
+## Stealth address specification
+
+The implemention suggested in this EIP was posted by `scalahub` in a thread on `ergoforum.org` [[1]](#1) and is outlined below.
+
+**Script protecting stealth boxes:**
+
+```scala
+{
+ // ===== Contract Information ===== //
+ // Name: EIP-0041 Stealth address contract
+ // Version: 1.0.0
+ val gr = SELF.R4[GroupElement].get
+ val gy = SELF.R5[GroupElement].get
+ val ur = SELF.R6[GroupElement].get
+ val uy = SELF.R7[GroupElement].get
+
+ proveDHTuple(gr,gy,ur,uy)
+}
+```
+
+**Script ErgoTree:**
+
+```
+1000cee4c6a70407e4c6a70507e4c6a70607e4c6a70707
+```
+
+**Generation of stealth box registers [[5]](#5):**
+
+```typescript
+const g = new EC("secp256k1").g; // group element generator
+const u = receiverPublicAddress;
+const r = BigInt(rand(32));
+const y = BigInt(rand(32));
+const gr = g.mul(r); // gr = g^r = R4
+const gy = g.mul(y); // gy = g^y = R5
+const ur = u.mul(r); // ur = u^r = R6
+const uy = u.mul(y); // uy = u^y = R7
+```
+
+**Box register declarations:**
+
+- Register `R4`
+ - Type: `SConstant[SGroupElement]`
+ - Value: gr = g^r
+- Register `R5`
+ - Type: `SConstant[SGroupElement]`
+ - Value: gy = g^y
+- Register `R6`
+ - Type: `SConstant[SGroupElement]`
+ - Value: ur = u^r
+- Register `R7`
+ - Type: `SConstant[SGroupElement]`
+ - Value: ur = u^r
+
+> 📝 As discussed in the `ergoforum` discussion [[1]](#1) this register declaration is larger in size than that originally proposed by `kushti` but possesses the useful property that it could look like a legitimate use-case.
+
+## `Receiver`s public address and wallets
+
+Leaning on deterministically generated wallet addresses [[6]](#6) enables stealth boxes to be retreived following a fresh wallet restore but raises a problem. When a user shares such a public address and wishes to receive stealth payments how do wallets determine they are requesting a stealth payment - the address will appear to be a regular P2PK address.
+
+Ideally when `Sender` is creating a stealth transaction the UX flow should remain the same as a regular transaction in supporting wallets, the fact that the `Receiver` is requesting a stealth payment should be opaque to the `Sender`. To achieve this we could encode a "payment type" in the public address so wallets can detect the `Receiver` is requesting a specific type of payment and construct the transaction accordingly, similar to network and address type prefixes but only for supporting wallets.
+
+
+[... TODO ...]
+
+## References
+
+- [1] [Stealth Address Contract (ergoforum)](https://www.ergoforum.org/t/stealth-address-contract/255)
+- [2] [ErgoScript by example](https://github.com/ergoplatform/ergoscript-by-example/blob/main/stealthAddress.md)
+- [3] [Stealth-doc (ERGOHACK III aragogi)](https://github.com/aragogi/Stealth-doc)
+- [4] [Ethereum (EIP-5564)](https://eips.ethereum.org/EIPS/eip-5564#:~:text=A%20Stealth%20address%20is%20generated,compute%20the%20matching%20private%20key.)
+- [5] [TypeScript stealth address example](https://github.com/ross-weir/ergo-stealth-address-example/blob/main/index.ts)
+- [6] [Ergo (EIP-0003)](eip-0003.md)
From c7872ec6834ead8218f6dee1c19f81106b3a6a36 Mon Sep 17 00:00:00 2001
From: ross-weir <29697678+ross-weir@users.noreply.github.com>
Date: Sat, 17 Dec 2022 18:36:26 +1100
Subject: [PATCH 2/5] update eip41
---
eip-0041.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 58 insertions(+), 4 deletions(-)
diff --git a/eip-0041.md b/eip-0041.md
index 31250599..bf070815 100644
--- a/eip-0041.md
+++ b/eip-0041.md
@@ -1,9 +1,9 @@
# EIP-41 Stealth Address Standard
* Author: ross-weir
-* Status: Draft
+* Status: Proposed
* Created: 16-Dec-2022
-* Last edited: 16-Dec-2022
+* Last edited: 17-Dec-2022
* License: CC0
* Forking: not needed
@@ -79,10 +79,64 @@ const uy = u.mul(y); // uy = u^y = R7
Leaning on deterministically generated wallet addresses [[6]](#6) enables stealth boxes to be retreived following a fresh wallet restore but raises a problem. When a user shares such a public address and wishes to receive stealth payments how do wallets determine they are requesting a stealth payment - the address will appear to be a regular P2PK address.
-Ideally when `Sender` is creating a stealth transaction the UX flow should remain the same as a regular transaction in supporting wallets, the fact that the `Receiver` is requesting a stealth payment should be opaque to the `Sender`. To achieve this we could encode a "payment type" in the public address so wallets can detect the `Receiver` is requesting a specific type of payment and construct the transaction accordingly, similar to network and address type prefixes but only for supporting wallets.
+Requiring `Sender` to manually perform special actions when sending a stealth payment in their wallet introduces the possibility of (in addition to other issues):
+1. `Sender` uses a regular payment exposing a transaction that `Receiver` wanted to remain private
+2. Introduces friction when making payments, possiblily reducing conversions
+3. Less user friendly, `Sender` as a non-technical user might not know about stealth payments
-[... TODO ...]
+Ideally when `Sender` is creating a stealth transaction in their wallet the UX flow should remain the same as a regular transaction, the fact that the `Receiver` is requesting a stealth payment should be opaque to the `Sender` when making the payment. To achieve this we could encode a "payment type" in the public address so wallets can detect the `Receiver` is requesting a specific type of payment and construct the transaction accordingly, similar to network and address type prefixes but only for supporting wallets.
+
+This could extend in the future to more non-interactive custom payment types, not just stealth payments.
+
+**Encoding**
+
+Encoding of the public key to include custom payment requests could be implemented as follows:
+
+| Field | Type | Description |
+|-------------------|----------|--------------------------------------------------------------------------|
+| Magic Byte Prefix | byte | Magic byte indiciating `Receiver` is requesting a custom transaction |
+| Custom Tx Type | byte | Enum defining the type of custom transaction `Receiver` is requesting |
+| Network Address | byte[] | Standard network encoded p2pk address |
+
+**Constructing the public address for a `Receiver` requesting a stealth payment**
+
+```ts
+enum CustomTxType {
+ Stealth = 0x0,
+ // More in future?
+}
+
+const magicBytePrefix = 0xCE // arbitrarily chosen for now
+const customTxType = CustomTxType.Stealth
+const publicShareableAddress = [magicBytePrefix, customTxType] + normalP2PKNetworkAddress
+
+// publicShareableAddress is shared to social media, etc to receive payments
+```
+
+**Possible wallet implementation**
+
+As mentioned previously, when `Sender` is making a payment to `Receiver` ideally their user journey would remain the same with the wallet handling the custom transaction type.
+
+`Sender` inputs `Receiver`s public address encoded as above and a wallet might handle it in the following manner:
+
+```ts
+enum CustomTxType {
+ Stealth = 0x0
+}
+
+const magicBytePrefix = 0xCE
+const receiverPublicAddress = new UInt8([/** receivers pub key **/])
+
+if (receiverPublicAddress[0] === magicBytePrefix) {
+ const customTxType = receiverPublicAddress[1] as CustomTxType
+ const networkEncodedPubKey = receiverPublicAddress.slice(2)
+
+ handleCustomTx(customTxType, networkEncodedPubKey)
+} else {
+ handleNormalTx(receiverPublicAddress) // handle making payment as normal
+}
+```
## References
From 8de1ee3e86a432f53ee28fd2dc1494553864c3ba Mon Sep 17 00:00:00 2001
From: ross-weir <29697678+ross-weir@users.noreply.github.com>
Date: Sat, 17 Dec 2022 18:52:27 +1100
Subject: [PATCH 3/5] fix r7 typo
---
eip-0041.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/eip-0041.md b/eip-0041.md
index bf070815..35394b91 100644
--- a/eip-0041.md
+++ b/eip-0041.md
@@ -71,7 +71,7 @@ const uy = u.mul(y); // uy = u^y = R7
- Value: ur = u^r
- Register `R7`
- Type: `SConstant[SGroupElement]`
- - Value: ur = u^r
+ - Value: uy = u^y
> 📝 As discussed in the `ergoforum` discussion [[1]](#1) this register declaration is larger in size than that originally proposed by `kushti` but possesses the useful property that it could look like a legitimate use-case.
From 33c18a4e3bd904f16318247cc67c5521f5c51d7b Mon Sep 17 00:00:00 2001
From: ross-weir <29697678+ross-weir@users.noreply.github.com>
Date: Mon, 19 Dec 2022 14:59:33 +1100
Subject: [PATCH 4/5] address feedback
---
eip-0041.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/eip-0041.md b/eip-0041.md
index 35394b91..05c8a742 100644
--- a/eip-0041.md
+++ b/eip-0041.md
@@ -49,7 +49,7 @@ The implemention suggested in this EIP was posted by `scalahub` in a thread on `
```typescript
const g = new EC("secp256k1").g; // group element generator
-const u = receiverPublicAddress;
+const u = receiverPublicKey;
const r = BigInt(rand(32));
const y = BigInt(rand(32));
const gr = g.mul(r); // gr = g^r = R4
From d21280977f2c21dc733632c48c98d0f614bc6123 Mon Sep 17 00:00:00 2001
From: ross-weir <29697678+ross-weir@users.noreply.github.com>
Date: Wed, 21 Dec 2022 14:10:15 +1100
Subject: [PATCH 5/5] address feedback
---
eip-0041.md | 77 +++++++++++++++++++++++------------------------------
1 file changed, 34 insertions(+), 43 deletions(-)
diff --git a/eip-0041.md b/eip-0041.md
index 05c8a742..9744130c 100644
--- a/eip-0041.md
+++ b/eip-0041.md
@@ -21,7 +21,7 @@ An actor, `Sender`, wishes to make a stealth payment to `Receiver` so they creat
## Stealth address specification
-The implemention suggested in this EIP was posted by `scalahub` in a thread on `ergoforum.org` [[1]](#1) and is outlined below.
+The implementation suggested in this EIP was posted by `scalahub` in a thread on `ergoforum.org` [[1]](#1) and is outlined below.
**Script protecting stealth boxes:**
@@ -75,69 +75,60 @@ const uy = u.mul(y); // uy = u^y = R7
> 📝 As discussed in the `ergoforum` discussion [[1]](#1) this register declaration is larger in size than that originally proposed by `kushti` but possesses the useful property that it could look like a legitimate use-case.
-## `Receiver`s public address and wallets
+## Public key generation
-Leaning on deterministically generated wallet addresses [[6]](#6) enables stealth boxes to be retreived following a fresh wallet restore but raises a problem. When a user shares such a public address and wishes to receive stealth payments how do wallets determine they are requesting a stealth payment - the address will appear to be a regular P2PK address.
+Public keys used for `Receiver` stealth addresses should be generated following `EIP-3` [[6]](#6) so they are deterministic and payments made to `Receiver` can be retrieved after a fresh wallet restore.
-Requiring `Sender` to manually perform special actions when sending a stealth payment in their wallet introduces the possibility of (in addition to other issues):
+## Scanning for spendable stealth boxes
-1. `Sender` uses a regular payment exposing a transaction that `Receiver` wanted to remain private
-2. Introduces friction when making payments, possiblily reducing conversions
-3. Less user friendly, `Sender` as a non-technical user might not know about stealth payments
+All stealth boxes created following this standard will be located at the encoded address `2pxnLshTr2aL5t3QFd6ScSEHC5Jk6EpnMRzGb3`. In order to discover spendable stealth boxes a wallet would need to check the following for each box at this address:
-Ideally when `Sender` is creating a stealth transaction in their wallet the UX flow should remain the same as a regular transaction, the fact that the `Receiver` is requesting a stealth payment should be opaque to the `Sender` when making the payment. To achieve this we could encode a "payment type" in the public address so wallets can detect the `Receiver` is requesting a specific type of payment and construct the transaction accordingly, similar to network and address type prefixes but only for supporting wallets.
+```ts
+const gr = stealthBox.R4;
+const gy = stealthBox.R5;
+const ur = stealthBox.R6;
+const uy = stealthBox.R7;
+const x = receiverSecretKey; // the secret key corresponding to the public key shared by `Receiver`
+const isSpendable = ur.eq(gr.mul(x)) && uy.eq(gy.mul(x));
+```
-This could extend in the future to more non-interactive custom payment types, not just stealth payments.
+> ⚠️ All stealth boxes being located at `2pxnLshTr2aL5t3QFd6ScSEHC5Jk6EpnMRzGb3` is a potential scalability issue related to this EIP. Wallets/indexing services will need to ensure stealth box scanning implementations are efficient.
-**Encoding**
+## Stealth address encoding
-Encoding of the public key to include custom payment requests could be implemented as follows:
+To provide users of Ergo a seamless experience when interacting with stealth addresses there needs to be a way for wallets/applications to detect a `Receiver` is requesting a stealth payment. If a `Receiver` shares a standard `P2PK` address, for example, there is currently no way for a wallet to know that the user wants to receive a stealth payment.
-| Field | Type | Description |
-|-------------------|----------|--------------------------------------------------------------------------|
-| Magic Byte Prefix | byte | Magic byte indiciating `Receiver` is requesting a custom transaction |
-| Custom Tx Type | byte | Enum defining the type of custom transaction `Receiver` is requesting |
-| Network Address | byte[] | Standard network encoded p2pk address |
+To address this issue we propose adding a new `AddressType`.
-**Constructing the public address for a `Receiver` requesting a stealth payment**
+Currently Ergo uses:
```ts
-enum CustomTxType {
- Stealth = 0x0,
- // More in future?
+enum AddressType {
+ P2PK = 1,
+ P2SH = 2,
+ P2S = 3
}
-
-const magicBytePrefix = 0xCE // arbitrarily chosen for now
-const customTxType = CustomTxType.Stealth
-const publicShareableAddress = [magicBytePrefix, customTxType] + normalP2PKNetworkAddress
-
-// publicShareableAddress is shared to social media, etc to receive payments
```
-**Possible wallet implementation**
-
-As mentioned previously, when `Sender` is making a payment to `Receiver` ideally their user journey would remain the same with the wallet handling the custom transaction type.
-
-`Sender` inputs `Receiver`s public address encoded as above and a wallet might handle it in the following manner:
+We propose adding a new address type of `AddressType.P2SA`:
```ts
-enum CustomTxType {
- Stealth = 0x0
+enum AddressType {
+ P2PK = 1,
+ P2SH = 2,
+ P2S = 3,
+ P2SA = 4
}
+```
-const magicBytePrefix = 0xCE
-const receiverPublicAddress = new UInt8([/** receivers pub key **/])
-
-if (receiverPublicAddress[0] === magicBytePrefix) {
- const customTxType = receiverPublicAddress[1] as CustomTxType
- const networkEncodedPubKey = receiverPublicAddress.slice(2)
+This will allow `Sender`s wallet to detect that a `Receiver` is requesting a stealth payment like so:
- handleCustomTx(customTxType, networkEncodedPubKey)
-} else {
- handleNormalTx(receiverPublicAddress) // handle making payment as normal
-}
+```ts
+const isStealthAddress = headByte & 0xf == AddressType.P2SA
```
+And the `Sender`s wallet can construct the stealth transaction accordingly.
+
## References
- [1] [Stealth Address Contract (ergoforum)](https://www.ergoforum.org/t/stealth-address-contract/255)