mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
signer: docs
This commit is contained in:
parent
3ade6cb242
commit
46bf5d9ebd
2 changed files with 152 additions and 52 deletions
|
|
@ -50,6 +50,53 @@ The general flow for signing a transaction using e.g. geth is as follows:
|
||||||
|
|
||||||
In this case, `geth` would be started with `--externalsigner=http://localhost:8550` and would relay requests to `eth.sendTransaction`.
|
In this case, `geth` would be started with `--externalsigner=http://localhost:8550` and would relay requests to `eth.sendTransaction`.
|
||||||
|
|
||||||
|
## TODOs
|
||||||
|
|
||||||
|
Some snags and todos
|
||||||
|
|
||||||
|
* The signer should take a startup param "--no-change", for UI:s that do not contain the capability
|
||||||
|
to perform changes to things, only approve/deny. Such a UI should be able to start the signer in
|
||||||
|
a more secure mode by telling it that it only wants approve/deny capabilities.
|
||||||
|
|
||||||
|
* It would be nice if the signer could collect new 4byte-id:s/method selectors, and have a
|
||||||
|
secondary database for those (`4byte_custom.json`). Users could then (optionally) submit their collections for
|
||||||
|
inclusion upstream.
|
||||||
|
|
||||||
|
* It should be possible to configure the signer to check if an account is indeed known to it, before
|
||||||
|
passing on to the UI. The reason it currently does not, is that it would make it possible to enumerate
|
||||||
|
accounts if it immediately returned "unknown account". Similarly, it should be possible to configure
|
||||||
|
the signer to auto-allow listing (certain) accounts, instead of asking every time.
|
||||||
|
|
||||||
|
* Upon startup, the signer should spit out some info to the caller (particularly important when executed in `stdio-ui`-mode),
|
||||||
|
invoking methods with the following info:
|
||||||
|
* Version info about the signer
|
||||||
|
* Address of API (http/ipc)
|
||||||
|
* This makes it posible for the UI to use the api for creating transactions
|
||||||
|
* List of known accounts
|
||||||
|
|
||||||
|
* The signer should pass the `Origin` header as call-info to the UI. As of right now, the way that info about the request is
|
||||||
|
put together is a bit of a hack into the http server. This could probably be greatly improved
|
||||||
|
|
||||||
|
* Geth relay
|
||||||
|
- Geth should be started in `geth --external_signer localhost:8550`.
|
||||||
|
|
||||||
|
* Geth checksum
|
||||||
|
- Currently, the Geth API:s use `common.Address` in the arguments to transaction submission (e.g `to` field). This
|
||||||
|
type is 20 `bytes`, and is incapable of carrying checksum information. The signer uses `common.MixedcaseAddress`, which
|
||||||
|
retains the original input.
|
||||||
|
- The Geth api should switch to use the same type, and relay `to`-account verbatim to the external api.
|
||||||
|
|
||||||
|
* Wallets / accounts. Add API methods for wallets.
|
||||||
|
|
||||||
|
* Storage
|
||||||
|
* An encrypted key-value storage should be implemented
|
||||||
|
* See [rules.md](rules.md) for more info about this.
|
||||||
|
|
||||||
|
* Another potential thing to introduce is pairing.
|
||||||
|
* To prevent spurious requests which users just accept, implement a way to "pair" the caller with the signer (external API).
|
||||||
|
* Thus geth/mist/cpp would cryptographically handshake and afterwards the caller would be allowed to make signing requests.
|
||||||
|
* This feature would make the addition of rules less dangerous.
|
||||||
|
|
||||||
## Communication
|
## Communication
|
||||||
|
|
||||||
### External API
|
### External API
|
||||||
|
|
@ -642,55 +689,3 @@ A UI should conform to the following rules.
|
||||||
along with the UI.
|
along with the UI.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## TODOs
|
|
||||||
|
|
||||||
Some snags and todos
|
|
||||||
|
|
||||||
* The audit-log perhaps leave some things to be desired. I have not found a perfect way to save an audit log of events.
|
|
||||||
|
|
||||||
* The signer should take a startup param "--no-change", for UI:s that do not contain the capability
|
|
||||||
to perform changes to things, only approve/deny. Such a UI should be able to start the signer in
|
|
||||||
a more secure mode by telling it that it only wants approve/deny capabilities.
|
|
||||||
|
|
||||||
* It would be nice if the signer could collect new 4byte-id:s/method selectors, and have a
|
|
||||||
secondary database for those (`4byte_custom.json`). Users could then (optionally) submit their collections for
|
|
||||||
inclusion upstream.
|
|
||||||
|
|
||||||
* It should be possible to configure the signer to check if an account is indeed known to it, before
|
|
||||||
passing on to the UI. The reason it currently does not, is that it would make it possible to enumerate
|
|
||||||
accounts if it immediately returned "unknown account". Similarly, it should be possible to configure
|
|
||||||
the signer to auto-allow listing (certain) accounts, instead of asking every time.
|
|
||||||
|
|
||||||
* Upon startup, the signer should spit out some info to the caller (particularly important when executed in `stdio-ui`-mode),
|
|
||||||
invoking methods with the following info:
|
|
||||||
* Version info about the signer
|
|
||||||
* Address of API (http/ipc)
|
|
||||||
* This makes it posible for the UI to use the api for creating transactions
|
|
||||||
* List of known accounts
|
|
||||||
|
|
||||||
* The signer should pass the `Origin` header as call-info to the UI. As of right now, the way that info about the request is
|
|
||||||
put together is a bit of a hack into the http server. This could probably be greatly improved
|
|
||||||
|
|
||||||
* Geth relay
|
|
||||||
- Geth should be started in `geth --external_signer localhost:8550`.
|
|
||||||
* Geth checksum
|
|
||||||
- Currently, the Geth API:s use `common.Address` in the arguments to transaction submission (e.g `to` field). This
|
|
||||||
type is 20 `bytes`, and is incapable of carrying checksum information. The signer uses `common.MixedcaseAddress`, which
|
|
||||||
retains the original input.
|
|
||||||
- The Geth api should switch to use the same type, and relay `to`-account verbatim to the external api.
|
|
||||||
|
|
||||||
* Wallets / accounts. Add API methods for wallets.
|
|
||||||
|
|
||||||
* Rules. In the future, it should be possible to specify rules, e.g. "Allow sending up to 1 eth per day to contract Y". Two problems:
|
|
||||||
* There needs to be a very good structure around rules. Either a full language (lua/js) or a limited but flexible syntax based on e.g. json/yaml. Kind of like a firewall ruleset.
|
|
||||||
* This implies that the signer would remember passwords, which is very problematic. However, a good UI implementation will want these things,
|
|
||||||
and it would be better to implement it once in the signer, than having UI:s develop their own remember-password logic.
|
|
||||||
* See [rules.md](rules.md) for more info about this.
|
|
||||||
|
|
||||||
* Another potential thing to introduce is pairing.
|
|
||||||
* To prevent spurious requests which users just accept, implement a way to "pair" the caller with the signer (external API).
|
|
||||||
* Thus geth/mist/cpp would cryptographically handshake and afterwards the caller would be allowed to make signing requests.
|
|
||||||
* This feature would make the addition of rules less dangerous.
|
|
||||||
|
|
@ -105,3 +105,108 @@ This would leave it up to the user to ensure that the `path/to/masterseed` is ha
|
||||||
imagine leveraging OS-level keychains where supported. The setup is however in general similar to how ssh-keys are stored in `.ssh/`.
|
imagine leveraging OS-level keychains where supported. The setup is however in general similar to how ssh-keys are stored in `.ssh/`.
|
||||||
|
|
||||||
|
|
||||||
|
# Implementation status
|
||||||
|
|
||||||
|
This is now implemented (with ephemeral non-encrypted storage for now, so not yet enabled).
|
||||||
|
|
||||||
|
## Example 1: ruleset for a rate-limited window
|
||||||
|
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
|
||||||
|
function big(str){
|
||||||
|
if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)}
|
||||||
|
return new BigNumber(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time window: 1 week
|
||||||
|
var window = 1000* 3600*24*7;
|
||||||
|
|
||||||
|
// Limit : 1 ether
|
||||||
|
var limit = new BigNumber("1e18");
|
||||||
|
|
||||||
|
function isLimitOk(transaction){
|
||||||
|
var value = big(transaction.value)
|
||||||
|
// Start of our window function
|
||||||
|
var windowstart = new Date().getTime() - window;
|
||||||
|
|
||||||
|
var txs = [];
|
||||||
|
var stored = storage.Get('txs');
|
||||||
|
|
||||||
|
if(stored != ""){
|
||||||
|
txs = JSON.parse(stored)
|
||||||
|
}
|
||||||
|
// First, remove all that have passed out of the time-window
|
||||||
|
var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart});
|
||||||
|
console.log(txs, newtxs.length);
|
||||||
|
|
||||||
|
// Secondly, aggregate the current sum
|
||||||
|
sum = new BigNumber(0)
|
||||||
|
|
||||||
|
sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum);
|
||||||
|
console.log("ApproveTx > Sum so far", sum);
|
||||||
|
console.log("ApproveTx > Requested", value.toNumber());
|
||||||
|
|
||||||
|
// Would we exceed weekly limit ?
|
||||||
|
return sum.plus(value).lt(limit)
|
||||||
|
|
||||||
|
}
|
||||||
|
function ApproveTx(jsonstr){
|
||||||
|
var r = JSON.parse(jsonstr)
|
||||||
|
if (isLimitOk(r.transaction)){
|
||||||
|
return "Approve"
|
||||||
|
}
|
||||||
|
return "Nope"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OnApprovedTx(str) is called when a transaction has been approved and signed. The parameter
|
||||||
|
* 'response_str' contains the return value that will be sent to the external caller.
|
||||||
|
* The return value from this method is ignore - the reason for having this callback is to allow the
|
||||||
|
* ruleset to keep track of approved transactions.
|
||||||
|
*
|
||||||
|
* When implementing rate-limited rules, this callback should be used.
|
||||||
|
* If a rule responds with neither 'Approve' nor 'Reject' - the tx goes to manual processing. If the user
|
||||||
|
* then accepts the transaction, this method will be called.
|
||||||
|
*
|
||||||
|
* TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx.
|
||||||
|
*/
|
||||||
|
function OnApprovedTx(response_str){
|
||||||
|
console.log("OnApprovedTx > called with data\n\t "+response_str)
|
||||||
|
var resp = JSON.parse(response_str)
|
||||||
|
var value = big(resp.tx.value)
|
||||||
|
var txs = []
|
||||||
|
// Load stored transactions
|
||||||
|
var stored = storage.Get('txs');
|
||||||
|
if(stored != ""){
|
||||||
|
txs = JSON.parse(stored)
|
||||||
|
}
|
||||||
|
// Add this to the storage
|
||||||
|
txs.push({tstamp: new Date().getTime(), value: value});
|
||||||
|
storage.Put("txs", JSON.stringify(txs));
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 2: allow destination
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
|
||||||
|
function ApproveTx(jsonstr){
|
||||||
|
r = JSON.parse(jsonstr)
|
||||||
|
if(r.transaction.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"}
|
||||||
|
if(r.transaction.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"}
|
||||||
|
// Otherwise goes to manual processing
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example 3: Allow listing
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
|
||||||
|
function ApproveListing(){
|
||||||
|
return "Approve"
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
Loading…
Reference in a new issue