docs/clef: docs about clef/clique signing (#25121)
* docs/clef: docs about clef/clique signing * Update CliqueSigning.md Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
This commit is contained in:
parent
3b5a767ef7
commit
00d884f570
|
@ -0,0 +1,393 @@
|
||||||
|
---
|
||||||
|
title: Clique-signing with Clef
|
||||||
|
sort_key: C
|
||||||
|
---
|
||||||
|
|
||||||
|
The 'classic' way to sign PoA blocks is to use the "unlock"-feature of `geth`. This is a highly dangerous thing to do, because "unlock" is totally un-discriminatory. Meaning: if an account is unlocked and an attacker obtains access to the RPC api, the attacker can have anything signed by that account, without supplying a password.
|
||||||
|
|
||||||
|
The idea with `clef` was to remove the `unlock` capability, yet still provide sufficient usability to make it possible to automate some things while maintaining a high level of security. This post will show how to integrate `clef` as a sealer of clique-blocks.
|
||||||
|
|
||||||
|
## Part 0: Prepping a Clique network
|
||||||
|
|
||||||
|
Feel free to skip this section if you already have a Clique-network.
|
||||||
|
|
||||||
|
First of all, we'll set up a rudimentary testnet to have something to sign on. We create a new keystore (password `testtesttest`)
|
||||||
|
```
|
||||||
|
$ geth account new --datadir ./ddir
|
||||||
|
INFO [06-16|11:10:39.600] Maximum peer count ETH=50 LES=0 total=50
|
||||||
|
Your new account is locked with a password. Please give a password. Do not forget this password.
|
||||||
|
Password:
|
||||||
|
Repeat password:
|
||||||
|
|
||||||
|
Your new key was generated
|
||||||
|
|
||||||
|
Public address of the key: 0x9CD932F670F7eDe5dE86F756A6D02548e5899f47
|
||||||
|
Path of the secret key file: ddir/keystore/UTC--2022-06-16T09-10-48.578523828Z--9cd932f670f7ede5de86f756a6d02548e5899f47
|
||||||
|
|
||||||
|
- You can share your public address with anyone. Others need it to interact with you.
|
||||||
|
- You must NEVER share the secret key with anyone! The key controls access to your funds!
|
||||||
|
- You must BACKUP your key file! Without the key, it's impossible to access account funds!
|
||||||
|
- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
|
||||||
|
```
|
||||||
|
|
||||||
|
And create a genesis with that account as a sealer:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"chainId": 15,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"clique": {
|
||||||
|
"period": 30,
|
||||||
|
"epoch": 30000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"difficulty": "1",
|
||||||
|
"gasLimit": "8000000",
|
||||||
|
"extradata": "0x00000000000000000000000000000000000000000000000000000000000000009CD932F670F7eDe5dE86F756A6D02548e5899f470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"alloc": {
|
||||||
|
"0x9CD932F670F7eDe5dE86F756A6D02548e5899f47": {
|
||||||
|
"balance": "300000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
And init `geth`
|
||||||
|
```
|
||||||
|
$ geth --datadir ./ddir init genesis.json
|
||||||
|
...
|
||||||
|
INFO [06-16|11:14:54.123] Writing custom genesis block
|
||||||
|
INFO [06-16|11:14:54.125] Persisted trie from memory database nodes=1 size=153.00B time="64.715µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
|
||||||
|
INFO [06-16|11:14:54.125] Successfully wrote genesis state database=lightchaindata hash=187412..4deb98
|
||||||
|
```
|
||||||
|
|
||||||
|
At this point, we have a Clique network which we can start sealing on.
|
||||||
|
|
||||||
|
## Part 1: Prepping Clef
|
||||||
|
|
||||||
|
In order to make use of `clef` for signing, we need to do a couple of things.
|
||||||
|
|
||||||
|
1. Make sure that `clef` knows the password for the keystore.
|
||||||
|
2. Make sure that `clef` auto-approves clique signing requests.
|
||||||
|
|
||||||
|
These two things are independent of each other. First of all, however, we need to `init` clef (for this test I use the password `clefclefclef`)
|
||||||
|
|
||||||
|
```
|
||||||
|
$ clef --keystore ./ddir/keystore --configdir ./clef --chainid 15 --suppress-bootwarn init
|
||||||
|
|
||||||
|
The master seed of clef will be locked with a password.
|
||||||
|
Please specify a password. Do not forget this password!
|
||||||
|
Password:
|
||||||
|
Repeat password:
|
||||||
|
|
||||||
|
A master seed has been generated into clef/masterseed.json
|
||||||
|
|
||||||
|
This is required to be able to store credentials, such as:
|
||||||
|
* Passwords for keystores (used by rule engine)
|
||||||
|
* Storage for JavaScript auto-signing rules
|
||||||
|
* Hash of JavaScript rule-file
|
||||||
|
|
||||||
|
You should treat 'masterseed.json' with utmost secrecy and make a backup of it!
|
||||||
|
* The password is necessary but not enough, you need to back up the master seed too!
|
||||||
|
* The master seed does not contain your accounts, those need to be backed up separately!
|
||||||
|
```
|
||||||
|
|
||||||
|
After this operation, `clef` has it's own vault where it can store secrets and attestations, which we will utilize going forward.
|
||||||
|
|
||||||
|
### Storing passwords in `clef`
|
||||||
|
|
||||||
|
With that done, we can now make `clef` aware of the password. We invoke `setpw <address>` to store a password for a given address. `clef` asks for the password, and it also asks for the clef master-password, in order to update and store the new secrets inside clef vault.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ clef --keystore ./ddir/keystore --configdir ./clef --chainid 15 --suppress-bootwarn setpw 0x9CD932F670F7eDe5dE86F756A6D02548e5899f47
|
||||||
|
|
||||||
|
Please enter a password to store for this address:
|
||||||
|
Password:
|
||||||
|
Repeat password:
|
||||||
|
|
||||||
|
Decrypt master seed of clef
|
||||||
|
Password:
|
||||||
|
INFO [06-16|11:27:09.153] Credential store updated set=0x9CD932F670F7eDe5dE86F756A6D02548e5899f47
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
At this point, if we were to use clef as a sealer, we would be forced to manually click Approve for each block, but we would not be required to provide the password.
|
||||||
|
|
||||||
|
#### Testing stored password
|
||||||
|
|
||||||
|
Let's test using the stored password when sealing Clique-blocks. Start `clef` with
|
||||||
|
```
|
||||||
|
$ clef --keystore ./ddir/keystore --configdir ./clef --chainid 15 --suppress-bootwarn
|
||||||
|
```
|
||||||
|
And start `geth` with
|
||||||
|
```
|
||||||
|
$ geth --datadir ./ddir --signer ./clef/clef.ipc --mine
|
||||||
|
```
|
||||||
|
|
||||||
|
Geth will ask what accounts are present, to which we need to manually enter `y` to approve:
|
||||||
|
|
||||||
|
```
|
||||||
|
-------- List Account request--------------
|
||||||
|
A request has been made to list all accounts.
|
||||||
|
You can select which accounts the caller can see
|
||||||
|
[x] 0x9CD932F670F7eDe5dE86F756A6D02548e5899f47
|
||||||
|
URL: keystore:///home/user/tmp/clique_clef/ddir/keystore/UTC--2022-06-16T09-10-48.578523828Z--9cd932f670f7ede5de86f756a6d02548e5899f47
|
||||||
|
-------------------------------------------
|
||||||
|
Request context:
|
||||||
|
NA -> ipc -> NA
|
||||||
|
|
||||||
|
Additional HTTP header data, provided by the external caller:
|
||||||
|
User-Agent: ""
|
||||||
|
Origin: ""
|
||||||
|
Approve? [y/N]:
|
||||||
|
> y
|
||||||
|
DEBUG[06-16|11:36:42.499] Served account_list reqid=2 duration=3.213768195s
|
||||||
|
```
|
||||||
|
|
||||||
|
After this, `geth` will start asking `clef` to sign things:
|
||||||
|
|
||||||
|
```
|
||||||
|
-------- Sign data request--------------
|
||||||
|
Account: 0x9CD932F670F7eDe5dE86F756A6D02548e5899f47 [chksum ok]
|
||||||
|
messages:
|
||||||
|
Clique header [clique]: "clique header 1 [0x9b08fa3705e8b6e1b327d84f7936c21a3cb11810d9344dc4473f78f8da71e571]"
|
||||||
|
raw data:
|
||||||
|
"\xf9\x02\x14\xa0\x18t\x12:\x91f\xa2\x90U\b\xf9\xac\xc02i\xffs\x9f\xf4\xc9⮷!\x0f\x16\xaa?#M똠\x1d\xccM\xe8\xde\xc7]z\xab\x85\xb5g\xb6\xcc\xd4\x1a\xd3\x12E\x1b\x94\x8at\x13\xf0\xa1B\xfd@ԓG\x94\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0]1%\n\xfc\xee'\xd0e\xce\xc7t\xcc\\?\t4v\x8f\x06\xcb\xf8\xa0P5\xfeN\xea\x0ff\xfe\x9c\xa0V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!\xa0V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n[H\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!\xb9\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x01\x83z0\x83\x80\x84b\xaa\xf9\xaa\xa0\u0603\x01\n\x14\x84geth\x88go1.18.1\x85linux\x00\x00\x00\x00\x00\x00\x00\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x88\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||||
|
data hash: 0x9589ed81e959db6330b3d70e5f8e426fb683d03512f203009f7e41fc70662d03
|
||||||
|
-------------------------------------------
|
||||||
|
Request context:
|
||||||
|
NA -> ipc -> NA
|
||||||
|
|
||||||
|
Additional HTTP header data, provided by the external caller:
|
||||||
|
User-Agent: ""
|
||||||
|
Origin: ""
|
||||||
|
Approve? [y/N]:
|
||||||
|
> y
|
||||||
|
```
|
||||||
|
And indeed, after approving with `y`, we are not required to provide the password -- the signed block is returned to geth:
|
||||||
|
```
|
||||||
|
INFO [06-16|11:36:46.714] Successfully sealed new block number=1 sealhash=9589ed..662d03 hash=bd20b9..af8b87 elapsed=4.214s
|
||||||
|
```
|
||||||
|
This mode of operation is somewhat unusable, since we'd need to keep "Approving" each block to be sealed. So let's fix that too.
|
||||||
|
|
||||||
|
### Using rules to approve blocks
|
||||||
|
|
||||||
|
The basic idea with clef rules, is to let a piece of javascript take over the Approve/Deny decision. The javascript snippet has access to the same information as the manual operator.
|
||||||
|
|
||||||
|
Let's try with a simplistic first approach, which approves listing, and spits out the request data for `ApproveListing`
|
||||||
|
|
||||||
|
```js
|
||||||
|
function ApproveListing(){
|
||||||
|
return "Approve"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ApproveSignData(r){
|
||||||
|
console.log("In Approve Sign data")
|
||||||
|
console.log(JSON.stringify(r))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
In order to use a certain rule-file, we must first `attest` it. This is to prevent someone from modifying a ruleset-file on disk after creation.
|
||||||
|
```
|
||||||
|
$ clef --keystore ./ddir/keystore --configdir ./clef --chainid 15 --suppress-bootwarn attest `sha256sum rules.js | cut -f1`
|
||||||
|
Decrypt master seed of clef
|
||||||
|
Password:
|
||||||
|
INFO [06-16|13:49:00.298] Ruleset attestation updated sha256=54aae496c3f0eda063a62c73ee284ca9fae3f43b401da847ef30ea30e85e35d1
|
||||||
|
```
|
||||||
|
And then we can start clef, pointing out the `rules.js` file. OBS: if you later modify this file, you need to redo the `attest`-step.
|
||||||
|
```
|
||||||
|
$ clef --keystore ./ddir/keystore --configdir ./clef --chainid 15 --suppress-bootwarn --rules ./rules.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Once `geth` starts asking it to seal blocks, we will now see the data. And from that, we can decide on how to make a rule which allows signing clique headers but nothing else.
|
||||||
|
|
||||||
|
The actual data that gets passed to the js environment (and which our ruleset spit out to the console) looks like this:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"content_type": "application/x-clique-header",
|
||||||
|
"address": "0x9CD932F670F7eDe5dE86F756A6D02548e5899f47",
|
||||||
|
"raw_data": "+QIUoL0guY+66jZpzZh1wDX4Si/ycX4zD8FQqF/1Apy/r4uHoB3MTejex116q4W1Z7bM1BrTEkUblIp0E/ChQv1A1JNHlAAAAAAAAAAAAAAAAAAAAAAAAAAAoF0xJQr87ifQZc7HdMxcPwk0do8Gy/igUDX+TuoPZv6coFboHxcbzFWm/4NF5pLA+G5bSOAbmWytwAFiL7XjY7QhoFboHxcbzFWm/4NF5pLA+G5bSOAbmWytwAFiL7XjY7QhuQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICg3pPDoCEYqsY1qDYgwEKFIRnZXRoiGdvMS4xOC4xhWxpbnV4AAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAA==",
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"name": "Clique header",
|
||||||
|
"value": "clique header 2 [0xae525b65bc7f711bc136f502650039cd6959c3abc28fdf0ebfe2a5f85c92f3b6]",
|
||||||
|
"type": "clique"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"call_info": null,
|
||||||
|
"hash": "0x8ca6c78af7d5ae67ceb4a1e465a8b639b9fbdec4b78e4d19cd9b1232046fbbf4",
|
||||||
|
"meta": {
|
||||||
|
"remote": "NA",
|
||||||
|
"local": "NA",
|
||||||
|
"scheme": "ipc",
|
||||||
|
"User-Agent": "",
|
||||||
|
"Origin": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If we wanted our js to be extremely trustless/paranoid, we could (inside the javascript) take the `raw_data` and verify that it's the rlp structure for a clique header:
|
||||||
|
|
||||||
|
```
|
||||||
|
echo "+QIUoL0guY+66jZpzZh1wDX4Si/ycX4zD8FQqF/1Apy/r4uHoB3MTejex116q4W1Z7bM1BrTEkUblIp0E/ChQv1A1JNHlAAAAAAAAAAAAAAAAAAAAAAAAAAAoF0xJQr87ifQZc7HdMxcPwk0do8Gy/igUDX+TuoPZv6coFboHxcbzFWm/4NF5pLA+G5bSOAbmWytwAFiL7XjY7QhoFboHxcbzFWm/4NF5pLA+G5bSOAbmWytwAFiL7XjY7QhuQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICg3pPDoCEYqsY1qDYgwEKFIRnZXRoiGdvMS4xOC4xhWxpbnV4AAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAA==" | base64 -d | rlpdump
|
||||||
|
[
|
||||||
|
bd20b98fbaea3669cd9875c035f84a2ff2717e330fc150a85ff5029cbfaf8b87,
|
||||||
|
1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347,
|
||||||
|
0000000000000000000000000000000000000000,
|
||||||
|
5d31250afcee27d065cec774cc5c3f0934768f06cbf8a05035fe4eea0f66fe9c,
|
||||||
|
56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421,
|
||||||
|
56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421,
|
||||||
|
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,
|
||||||
|
02,
|
||||||
|
02,
|
||||||
|
7a4f0e,
|
||||||
|
"",
|
||||||
|
62ab18d6,
|
||||||
|
d883010a14846765746888676f312e31382e31856c696e757800000000000000,
|
||||||
|
0000000000000000000000000000000000000000000000000000000000000000,
|
||||||
|
0000000000000000,
|
||||||
|
]
|
||||||
|
```
|
||||||
|
However, we can also use the `messages`. They do not come from the external caller, but are generated from the `clef` internals: `clef` parsed the incoming request and verified the Clique wellformedness of the content. So we let's just check for such a message:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function OnSignerStartup(info){}
|
||||||
|
|
||||||
|
function ApproveListing(){
|
||||||
|
return "Approve"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ApproveSignData(r){
|
||||||
|
if (r.content_type == "application/x-clique-header"){
|
||||||
|
for(var i = 0; i < r.messages.length; i++){
|
||||||
|
var msg = r.messages[i]
|
||||||
|
if (msg.name=="Clique header" && msg.type == "clique"){
|
||||||
|
return "Approve"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "Reject"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Attest
|
||||||
|
```
|
||||||
|
$ clef --keystore ./ddir/keystore --configdir ./clef --chainid 15 --suppress-bootwarn attest `sha256sum rules.js | cut -f1`
|
||||||
|
Decrypt master seed of clef
|
||||||
|
Password:
|
||||||
|
INFO [06-16|14:18:53.476] Ruleset attestation updated sha256=7d5036d22d1cc66599e7050fb1877f4e48b89453678c38eea06e3525996c2379
|
||||||
|
```
|
||||||
|
Run clef
|
||||||
|
```
|
||||||
|
$ clef --keystore ./ddir/keystore --configdir ./clef --chainid 15 --suppress-bootwarn --rules ./rules.js
|
||||||
|
|
||||||
|
```
|
||||||
|
Run geth
|
||||||
|
```
|
||||||
|
$ geth --datadir ./ddir --signer ./clef/clef.ipc --mine
|
||||||
|
```
|
||||||
|
And you should now see `clef` happily signing blocks:
|
||||||
|
```
|
||||||
|
DEBUG[06-16|14:20:02.136] Served account_version reqid=1 duration="131.38µs"
|
||||||
|
INFO [06-16|14:20:02.289] Op approved
|
||||||
|
DEBUG[06-16|14:20:02.289] Served account_list reqid=2 duration=4.672441ms
|
||||||
|
INFO [06-16|14:20:02.303] Op approved
|
||||||
|
DEBUG[06-16|14:20:03.450] Served account_signData reqid=3 duration=1.152074109s
|
||||||
|
INFO [06-16|14:20:03.456] Op approved
|
||||||
|
DEBUG[06-16|14:20:04.267] Served account_signData reqid=4 duration=815.874746ms
|
||||||
|
INFO [06-16|14:20:32.823] Op approved
|
||||||
|
DEBUG[06-16|14:20:33.584] Served account_signData reqid=5 duration=766.840681ms
|
||||||
|
|
||||||
|
```
|
||||||
|
### Further refinements
|
||||||
|
|
||||||
|
|
||||||
|
If an attacker find the clef "external" interface (which would only happen if you start it with `http` enabled) , he
|
||||||
|
- cannot make it sign arbitrary transactions,
|
||||||
|
- cannot sign arbitrary data message,
|
||||||
|
|
||||||
|
However, he could still make it sign e.g. 1000 versions of a certain block height, making the chain very unstable.
|
||||||
|
|
||||||
|
It is possible for rule execution to be stateful -- storing data. In this case, one could for example store what block heights have been sealed, and thus reject sealing a particular block height twice. In other words, we can use these rules to build our own version of an Execution-Layer slashing-db.
|
||||||
|
|
||||||
|
We simply split the `clique header 2 [0xae525b65bc7f711bc136f502650039cd6959c3abc28fdf0ebfe2a5f85c92f3b6]` line, and store/check the number, using `storage.get` and `storage.put`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function OnSignerStartup(info){}
|
||||||
|
|
||||||
|
function ApproveListing(){
|
||||||
|
return "Approve"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ApproveSignData(r){
|
||||||
|
|
||||||
|
if (r.content_type != "application/x-clique-header"){
|
||||||
|
return "Reject"
|
||||||
|
}
|
||||||
|
for(var i = 0; i < r.messages.length; i++){
|
||||||
|
var msg = r.messages[i]
|
||||||
|
if (msg.name=="Clique header" && msg.type == "clique"){
|
||||||
|
var number = parseInt(msg.value.split(" ")[2])
|
||||||
|
var latest = storage.get("lastblock") || 0
|
||||||
|
console.log("number", number, "latest", latest)
|
||||||
|
if ( number > latest ){
|
||||||
|
storage.put("lastblock", number)
|
||||||
|
return "Approve"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "Reject"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Running with this ruleset:
|
||||||
|
```
|
||||||
|
JS:> number 45 latest 44
|
||||||
|
INFO [06-16|22:26:43.023] Op approved
|
||||||
|
DEBUG[06-16|22:26:44.305] Served account_signData reqid=3 duration=1.287465394s
|
||||||
|
JS:> number 46 latest 45
|
||||||
|
INFO [06-16|22:26:44.313] Op approved
|
||||||
|
DEBUG[06-16|22:26:45.317] Served account_signData reqid=4 duration=1.010612774s
|
||||||
|
```
|
||||||
|
This might be a bit over-the-top, security-wise, and may cause problems, if for some reason a clique-deadlock needs to be resolved by rolling back and continuing on a side-chain. It is mainly meant as a demonstration that rules can use javascript and statefulness to construct very intricate signing logic.
|
||||||
|
|
||||||
|
|
||||||
|
### TLDR quick-version
|
||||||
|
|
||||||
|
Creation and attestation is a one-off event:
|
||||||
|
```bash
|
||||||
|
## Create the rules-file
|
||||||
|
cat << END > rules.js
|
||||||
|
function OnSignerStartup(info){}
|
||||||
|
|
||||||
|
function ApproveListing(){
|
||||||
|
return "Approve"
|
||||||
|
}
|
||||||
|
|
||||||
|
function ApproveSignData(r){
|
||||||
|
if (r.content_type == "application/x-clique-header"){
|
||||||
|
for(var i = 0; i < r.messages.length; i++){
|
||||||
|
var msg = r.messages[i]
|
||||||
|
if (msg.name=="Clique header" && msg.type == "clique"){
|
||||||
|
return "Approve"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "Reject"
|
||||||
|
}
|
||||||
|
END
|
||||||
|
## Attest it, assumes clef master password is in `./clefpw`
|
||||||
|
clef --keystore ./ddir/keystore \
|
||||||
|
--configdir ./clef --chainid 15 \
|
||||||
|
--suppress-bootwarn --signersecret ./clefpw \
|
||||||
|
attest `sha256sum rules.js | cut -f1`
|
||||||
|
```
|
||||||
|
The normal startup command for `clef`:
|
||||||
|
```bash
|
||||||
|
clef --keystore ./ddir/keystore \
|
||||||
|
--configdir ./clef --chainid 15 \
|
||||||
|
--suppress-bootwarn --signersecret ./clefpw --rules ./rules.js
|
||||||
|
```
|
||||||
|
For `geth`, the only change is to provide `--signer <path to clef ipc>`.
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Advanced setup
|
title: Advanced setup
|
||||||
sort_key: B
|
sort_key: D
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Communication APIs
|
title: Communication APIs
|
||||||
sort_key: C
|
sort_key: E
|
||||||
---
|
---
|
||||||
|
|
||||||
### External API
|
### External API
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Communication data types
|
title: Communication data types
|
||||||
sort_key: C
|
sort_key: F
|
||||||
---
|
---
|
||||||
|
|
||||||
## UI Client interface
|
## UI Client interface
|
||||||
|
|
Loading…
Reference in New Issue