Skip to content

Commit 64855c1

Browse files
authored
Merge pull request #67 from ten-protocol/tudor/add_visibility_cfg
Add visibility config to developer quickstart
2 parents 640db40 + f7e24c9 commit 64855c1

File tree

1 file changed

+111
-18
lines changed

1 file changed

+111
-18
lines changed

docs/introduction/developer-quickstart.md

Lines changed: 111 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ sidebar_position: 4
33
---
44

55
# Migrate your dApp to Ten
6-
Migrating to Ten is a straight-forward process that immediately unlocks private state.
7-
There are three main types of changes you need to make:
6+
Migrating to Ten is a straightforward process that immediately unlocks "Programmable Encryption".
87

9-
1. Change your hardhat deployment script so that you can use --network ten
8+
There are a couple of changes you need to make:
9+
10+
1. Change your hardhat deployment script so that you can use `--network ten`.
1011
2. Add logic to your view functions to protect data (if needed).
11-
3. Add a widget to your javascript UI to onboard Ten users.
12+
3. Configure event log visibility (if needed).
13+
4. Add a widget to your javascript UI to onboard Ten users.
1214

1315
## 1. Configuring Hardhat
1416

15-
To begin building on Ten, start by setting up a Hardhat project as usual.
17+
To begin building on Ten, you can start by setting up a Hardhat project as usual.
1618

1719
### 1.1 Installing the Ten Hardhat Plugin
1820

@@ -22,9 +24,9 @@ To integrate the Ten Network into your Hardhat project, install the ten-hardhat-
2224
npm install ten-hardhat-plugin
2325
```
2426

25-
You can extend the functionality of Hardhat by installing plugins. Install them using npm or Yarn & configure it in the next step.
27+
Note: Plugins can be installed using `npm` or `yarn`.
2628

27-
### 1.2 Configuring `hardhat.config.js`
29+
### 1.2 Configuring `hardhat.config.js` for the Ten Testnet
2830

2931
Open `hardhat.config.js` in your project's root directory and configure it in the following way:
3032

@@ -49,19 +51,20 @@ module.exports = {
4951

5052
export default config;
5153
```
52-
Now, start writing the smart contracts for your project.
54+
Now, you can start writing or migrating the smart contracts.
5355

5456
# 2. Writing Smart Contracts
5557

5658
Ten performs bytecode execution in the EVM identically to Ethereum, allowing developers to leverage their existing codebase and tools.
5759

58-
The main difference and advantage of Ten is that on Ten, during execution, private variables and the internal state of the contract are hidden from everyone, including node operators and the sequencer.
60+
The main difference is that, during execution, private variables and the internal state of the contract are hidden from everyone, including node operators and the sequencer.
61+
This is a major advantage that represents "Programmable Privacy".
5962

6063
:::info
6164
In Ten, the internal node database is encrypted, and the execution itself is also encrypted inside the TEE.
6265
:::
6366

64-
The calls to [getStorageAt](https://docs.alchemy.com/reference/eth-getstorageat) are disabled, so all data access will be performed through view functions which are under the control of the smart contract developer. Public variables are accessible to everyone because Solidity automatically generates a getter function for them.
67+
The calls to [getStorageAt](https://docs.alchemy.com/reference/eth-getstorageat) are disabled by default, so all data access will be performed through view functions which are under the control of the smart contract developer. Note that public variables are accessible to everyone because Solidity automatically generates a getter function for them.
6568

6669
We'll illustrate how this works by creating a simple data storage example. In this dApp, users can store a number and retrieve it later.
6770

@@ -114,7 +117,7 @@ In Ethereum, the `_storedValues` variable can also be accessed directly using th
114117

115118
## Step 3: Implementing Data Access Control
116119

117-
In this step, our aim is to restrict users to only access their own value. This feature can only be implemented in Ten because as mentioned above, `_storedValues` is not hidden in Ethereum.
120+
In this step, we aim to restrict users to only access their own value. This feature can only be implemented in Ten because as mentioned above, `_storedValues` is not hidden in Ethereum.
118121

119122
### Code:
120123

@@ -141,9 +144,9 @@ Since `getValue` is the only function which exposes the values, we can add a che
141144
In Ethereum, since all data is accessible anyway, there is no need to sign calls to view functions, so `tx.origin` can be spoofed.
142145
:::
143146

144-
In Ten, the platform ensures that calls to view functions are authenticated. Which means that behind the scenes, there is a signature of the `tx.origin` address.
147+
In Ten, the platform ensures that calls to view functions are authenticated, which means that behind the scenes, there is a "Viewing Key" signature of the `tx.origin` address.
145148

146-
## Step 4: Emitting Events
149+
## Step 4: Emitting Events - Default visibility
147150

148151
Events in Ethereum are crucial for UIs to react to smart contract state changes. In this step, we'll emit an event when a user stores a value. We'll also gauge the popularity of our contract by emitting an event when certain milestones are reached.
149152

@@ -175,15 +178,105 @@ contract StorageExample {
175178

176179
### Explanation:
177180

178-
On Ethereum, events are visible to anyone. For example, you can subscribe to the `DataChanged` event and receive notifications in real-time about the data of everyone else. In Ten, we wanted to do better than that.
181+
On Ethereum, events are visible to anyone. For example, you can subscribe to the `DataChanged` event and receive notifications in real time about the data of everyone else.
182+
183+
The programmable encryption of Ten allows you full control over visibility but also has sensible defaults.
184+
Event logs can be queried using `eth_getLogs` or subscribed to using the `logs` endpoint. Both these calls are authenticated, and the platform makes sure to return only visible logs.
185+
186+
In our case, the requirements are very simple and common sense:
179187

180188
- The `DataChanged` event is specific to an account, so it should only be received by that user.
181189
- `MilestoneReached`, on the other hand, is intended for everyone, as we want to show how popular our contract is.
182190

183-
The behavior you desire is to restrict the visibility of `DataChanged`, but not that of `MilestoneReached`. **This is exactly how it works by default!**
191+
The behaviour you desire is to restrict the visibility of `DataChanged`, but not that of `MilestoneReached`. **Which is exactly how it works by default!**
192+
193+
Default behaviour:
184194

185-
How it works:
186-
- `DataChanged` - has an address as a topic (an indexed field), which makes it relevant to that address.
187-
- `MilestoneReached` - has no address topic, so it is visible to everyone.
195+
- `DataChanged` - has an address as a topic (an indexed field), which instructs the platform that the event log is only visible to that address.
196+
- `MilestoneReached` - has no address topic which by default means it is visible to everyone.
188197

189198
All you have to do is emit events as usual, and the platform applies common-sense visibility rules.
199+
200+
201+
## Step 5: Emitting Events - Configuring visibility
202+
203+
Once you prepare your application for production, you will want explicit control over the event visibility.
204+
205+
### Code:
206+
207+
```solidity
208+
interface ContractTransparencyConfig {
209+
enum Field {
210+
TOPIC1, TOPIC2, TOPIC3,
211+
SENDER, // tx.origin - msg.sender
212+
EVERYONE // the event is public - visible to everyone
213+
}
214+
215+
enum ContractCfg {
216+
TRANSPARENT, // internal state via getStorageAt is accessible to everyone, all events are public
217+
PRIVATE // internal state is hidden, and events can be configured individually
218+
}
219+
220+
struct EventLogConfig {
221+
bytes32 eventSignature; // the event signature hash
222+
Field[] visibleTo; // list of fields denoting who can see the event when private
223+
}
224+
225+
struct VisibilityConfig {
226+
ContractCfg contractCfg;
227+
EventLogConfig[] eventLogConfigs;
228+
}
229+
230+
function visibilityRules() external pure returns (VisibilityConfig memory);
231+
}
232+
233+
contract StorageExample is ContractTransparencyConfig{
234+
mapping(address => uint256) private _storedValues;
235+
uint256 private totalCalls = 0;
236+
237+
event DataChanged(address indexed account, uint256 newValue);
238+
event MilestoneReached(uint256 noStoredValues);
239+
240+
function storeValue(uint256 value) public {
241+
_storedValues[tx.origin] = value;
242+
emit DataChanged(tx.origin, value);
243+
totalCalls++;
244+
if (totalCalls % 1000 == 0) {
245+
emit MilestoneReached(totalCalls);
246+
}
247+
}
248+
249+
function getValue(address account) public view returns (uint256) {
250+
require(tx.origin == account, "Not authorized!");
251+
return _storedValues[account];
252+
}
253+
254+
function visibilityRules() external pure override returns (VisibilityConfig memory) {
255+
EventLogConfig[] memory eventLogConfigs = new EventLogConfig[](2);
256+
257+
// the signagure of "event DataChanged(address indexed account, uint256 newValue);"
258+
bytes32 dataChangedEventSig = hex"0xec851d5c322f7f1dd5581f7432e9f6683a8709a4b1ca754ccb164742b82a7d2f";
259+
Field[] memory relevantTo = new Field[](2);
260+
relevantTo[0] = Field.TOPIC1;
261+
relevantTo[1] = Field.SENDER;
262+
eventLogConfigs[0] = EventLogConfig(dataChangedEventSig, relevantTo);
263+
264+
// the signagure of "event MilestoneReached(uint256 noStoredValues);"
265+
bytes32 milestoneReachedEventSig = hex"0xd41033274424d56dd572e7196fb4230cf4141d546b91fc00555cab8403965924";
266+
Field[] memory relevantTo = new Field[](1);
267+
relevantTo[0] = Field.EVERYONE;
268+
eventLogConfigs[1] = EventLogConfig(milestoneReachedEventSig, relevantTo);
269+
270+
return VisibilityConfig(ContractCfg.PRIVATE, eventLogConfigs);
271+
}
272+
}
273+
```
274+
275+
### Explanation:
276+
277+
By implementing the `ContractTransparencyConfig.visibilityRules` method you can configure the visibility concerns of the current contract.
278+
279+
A `ContractCfg.PUBLIC` contract behaves exactly like a contract deployed on Ethereum. The storage slots are exposed, and all contracts are public.
280+
281+
For private contracts, you can configure the visibility of each individual event type you're emitting by specifying the "fields" that can receive it.
282+
`Field.EVERYONE` means that this is a public event.

0 commit comments

Comments
 (0)