Scatter is similar to MetaMask in that it allows you sign transactions securely locally on your machine. Scatter goes a couple of steps further by adding the following functionality over and above MetaMask. Scatter:
- provides improved privacy by not just giving away information from your machine as you visit various applications
- includes a reputation system called RIDL which allows a user to give a +1 or a -1 to a blockchain application
- includes desktop version, browser plug-in version and also mobile version
The following instructions were performed in order to test CyberMiles with Scatter
There are instructions on how to use a single node local node (I have listed the link below). In reality the single local node is super fast to set up and deals with CORS by default. If you use this method, you can skip to step 3.
https://travis.readthedocs.io/en/latest/getting-started.html
Alternatively, I have also used Docker to connect to the actual Testnet for a Scatter Test Case. The following URL has instructions on getting the official CyberMiles Testnet node up and running using Docker.
https://travis.readthedocs.io/en/latest/connect-testnet.html#docker
Here are some details on how I did this.
sudo usermod -a -G docker $USER
docker pull ywonline/travis
cd ~
sudo rm -rf $HOME/.travis
git clone https://github.com/CyberMiles/testnet.git
cd testnet/travis
git pull
Open the config.toml file and change the moniker value from "local" to something new, also make sure the chain id is 19 (this value is found at the very bottom of the config.toml file). 19 is the chain id of the CyberMiles Testnet.
vi init/config/config.toml
Copy the config to the right area
cd ~
cd testnet/travis
cp -r init $HOME/.travis
Start Docker instance
docker run --publish-all=true --name travis -v $HOME/.travis:/travis -p 26657:26657 -p 8545:8545 -t ywonline/travis node start --home /travis
Get Testnet ip
docker inspect -f '{{ .NetworkSettings.IPAddress }}' travis
Connect to Testnet command line tool
docker run --rm -it ywonline/travis attach http://172.17.0.2:8545
The following command will create a new account
personal.newAccount()
0x8e91cd2b624882a46380bcea561fa5aa2d768755
Run this again so that we have two accounts to play with
personal.newAccount()
0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a
Check to see of the testnet is still syncing, you will not see any testnet tokens from the faucet until your node is synced.
cmt.syncing
Once you have synced the Tesnet (running cmt.syncing returns false for catching_up), visit the Travis Testnet faucet to grab some free Testnet tokens.
> cmt.syncing
{
catching_up: false,
latest_app_hash: "B376053146D5A12C8ADAE71261811D2AC3668C07",
latest_block_hash: "297BD6DF3BE6C47CB4C1E952545DAF8DD334112C",
latest_block_height: 105537,
latest_block_time: "2018-09-05T00:08:51.831965507Z"
}
The Travis Testnet faucet URL is as follows
http://travis-faucet.cybermiles.io/
The faucet will give you a transaction hash, you can confirm that the transaction was successful by using the following command
cmt.getTransaction("0xInsertTheTransactionHashThatTheFaucetProvided")
It will return something like this for each grab which you performed
{
blockHash: "0xfc633d0d761f2b37ca60e485ea0bd857c7884d2ddfd4a8ea1df32a8397071e65",
blockNumber: 105544,
from: "0x7eff122b94897ea5b0e2a9abf47b86337fafebdc",
gas: 90000,
gasPrice: 2000000000,
hash: "0xb45750cd13d2d6ad63f0c3a4242a68260caeb3f98799f22c88c240d82f7ce371",
input: "0x",
nonce: 303,
r: "0x9d9165b9b60f31b646d6e83a573c9a7655792628322db499b2eabc1a51bcbee0",
s: "0x23e2b697bc7eecdee937f6921344db40c87acfc0e2ec4ab0d80fdfc990eabf2d",
to: "0x6d758d5d69af474c6d18b67f252ee39772960967",
transactionIndex: 0,
v: "0x49",
value: 1e+21
}
It is now time to check the balances of the two accounts which we created above. First we will unlock each account.
personal.unlockAccount("0x8e91cd2b624882a46380bcea561fa5aa2d768755","PutPasswordHere")
true
personal.unlockAccount("0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a","PutPasswordHere")
true
Then we will check each balance
web3.fromWei(cmt.getBalance("0x8e91cd2b624882a46380bcea561fa5aa2d768755"), 'cmt');
1000
Notice how we are converting the denomination of token to a whole cmt unit (as apposed to using the smaller default denomination of wei.
web3.fromWei(cmt.getBalance("0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a"), 'cmt');
1000
The following command will create a new account
personal.newAccount()
0x8e91cd2b624882a46380bcea561fa5aa2d768755
Run this again so that we have two accounts to play with
personal.newAccount()
0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a
You can unlock the coinbase account on your single local node by running the following command. This is a deliberate account and password which has been hardwired into the system for local node testing and so forth.
personal.unlockAccount('0x7eff122b94897ea5b0e2a9abf47b86337fafebdc', '1234')
Once you have unlocked the coinbase account, go ahead and fund the two new accounts which you have just created.
Now that we have two accounts on the CyberMiles Testnet, let us perform a test transfer from one account to the other.
First, let's quickly create a variable to hold an amount of cmt (the following command converts 100 cmt to wie so that it can be accepted by the transaction).
amount = web3.toWei(100, 'cmt')
Now let's perform the transaction and pass in the amount variable which we just created. Notice how we also set the gas to zero. This is a CyberMiles specific feature; allows users to send legitimate transactions on the network at no cost.
cmt.sendTransaction({from:"0x8e91cd2b624882a46380bcea561fa5aa2d768755", to:"0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a", value:amount, gasPrice:0})
The above will return a transaction hash, like this, "0x3bd0839327d0477fbe9d9a52190db8a73dc9d85d1d86bdc31ab382f83962466f". Again, we can go and check this transaction on the blockchain using the following command syntax.
cmt.getTransaction("0xInsertTheTransactionHashThatTheFaucetProvided")
For example, the following command returned the result below it.
cmt.getTransaction("0x3bd0839327d0477fbe9d9a52190db8a73dc9d85d1d86bdc31ab382f83962466f")
{
blockHash: "0x7871c3e853de78504facdb19a891be921951161866df9db75fca56d8f1740b63",
blockNumber: 106385,
from: "0x8e91cd2b624882a46380bcea561fa5aa2d768755",
gas: 90000,
gasPrice: 0,
hash: "0x9881e8205f6114ae2a2a3ca038e655fd5883fd1a57729a6f7b58a07c3a0ade1d",
input: "0x",
nonce: 0,
r: "0xa229e67a76dc204267a47782907d7e84f8de3990153dd1fb93c4184e6448908c",
s: "0x547d185a780ef7f788bbdb9a908d0009f718baf831b5fc1a0878f5f070f49fda",
to: "0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a",
transactionIndex: 2,
v: "0x49",
value: 100000000000000000000
}
NOTICE: Notice how the value is in wie i.e. 100000000000000000000 wei is equal to 100 cmt.
If we check both account balances again, we will see that the 100 tokens have been transfered successfully. web3.fromWei(cmt.getBalance("0x8e91cd2b624882a46380bcea561fa5aa2d768755"), 'cmt'); 900 web3.fromWei(cmt.getBalance("0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a"), 'cmt'); 1100
This is where it gets interesting. In Ethereum, as part of the standard functionality, you are unable to export private keys. This is for safety reasons. There are other ways to retrieve private keys from the keystore file (offline) but these tools are beyond the scope of this document.
Whilst Scatter allows you to put in your own private keys (for EOS and Ethereum). Scatter can also generate an Ethereum key pair for you. From this point onwards you are responsible for the private/public key pair (which you can download by clicking "copy" button in the Scatter software).
This is the public key which Scatter Desktop created for me
Public Key: 0xb2da22ab2404a2b008105217293d3db54b0f9a2c
At present, this is a non existant Ethereum key pair as you can see from the Etherscan link below
https://etherscan.io/address/0xb2da22ab2404a2b008105217293d3db54b0f9a2c
At present the CyberMiles Testnet which I am working on via the Docker container has the following account state. The address of 0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a has 1100 cmt tokens and the address of 0x8e91cd2b624882a46380bcea561fa5aa2d768755 has 900 cmt tokens.
Let's try and transfer 100 cmt tokens from our original address (which is currently holding 1100 cmt) to our newly generated address (which was created by allowing Scatter to generate a private/public key pair on our behalf).
First we unlock our account
personal.unlockAccount("0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a", "PutPasswordHere")
true
Then we create an amount variable (100 cmt) for our next transaction
amount = web3.toWei(100, 'cmt')
"100000000000000000000"
Then we transfer the funds
cmt.sendTransaction({from:"0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a", to:"0xb2da22ab2404a2b008105217293d3db54b0f9a2c", value:amount, gasPrice:0})
"0xb4e1bd41dff2001da2e70ffbeb84aca926e6e892e0f56aeade939e05a2f41ca3"
And finally, we check the transaction hash, which was returned by the sendTransaction function
cmt.getTransaction("0xb4e1bd41dff2001da2e70ffbeb84aca926e6e892e0f56aeade939e05a2f41ca3")
{
blockHash: "0xeff76bf20a11b17591f592ed3244131d2e5ef907b8f035b54cf5e0ccffa445c9",
blockNumber: 107167,
from: "0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a",
gas: 90000,
gasPrice: 0,
hash: "0xb4e1bd41dff2001da2e70ffbeb84aca926e6e892e0f56aeade939e05a2f41ca3",
input: "0x",
nonce: 0,
r: "0xe1663950f0c8f2fcea026782aacd26c0cf57b6ea7ebf6986bf0c0c2f4f383a8d",
s: "0x7baf4399a39373e744d1b71f41c27ec7b9543713d88278123acdf1eba1e79130",
to: "0xb2da22ab2404a2b008105217293d3db54b0f9a2c",
transactionIndex: 1,
v: "0x49",
value: 100000000000000000000
}
If we check the account balance of the from account, we will see that the 100 cmt has been moved on. There is only 1000 cmt in that account now. This is correct!
web3.fromWei(cmt.getBalance("0x3e06c3f127aaa3d93bb22b13fd59dc5257a52d5a"), 'cmt')
1000
Believe it or not, we can even check the Scatter generated account on this testnet. We do not need the private keys to get the account balance of a public key on this blockchain. The following command, correctly returns the result of 100 cmt.
web3.fromWei(cmt.getBalance("0xb2da22ab2404a2b008105217293d3db54b0f9a2c"), 'cmt')
100
This confirms that the address space of Ethereum and CyberMiles are compatible with these addresses.
The following code is a the most simplistic of web applications. It is a prototype for research and development into Scatter / CyberMiles interoperability, written using only HTML and inline Javascript. This is for ease of use and understanding.
I created a folder for the project. I then downloaded scatter-js (from https://github.com/GetScatter/scatter-js/tree/2.5.1) and web3.js from (https://github.com/ethereum/web3.js/) and stored them both inside a folder called modules. Here is a simple diagram. You will see the actual relative paths which I used in the head->script->src of the HTML file below.
/home/html
| |
modules theHTMLFile.html
| |
scatter web3
| |
dist dist
| |
scatter.js web3.js
Here I am installing the simplest of server containers. The idea here is to keep things as simple as possible; using the http-server functionality of node (similar to using a python simple server etc.) to host the single HTML/Javascript page. This minimalistic node usage is just to satisfy the Javascript execution of the single HTML/Javascript page which I created. Of course I learned that opening the HTML/Javascript page from the file system (file:///home/html/theHTMLFile.html) does not satisfy the application's full and proper execution and Javascript context. I typed the following in order to install the server container.
npm i -g http-server
I then created an HTML file (as shown below) and ran the following command (from within the directory where the HTML file is kept) in order to serve the HTML page on http://127.0.0.1:8080/theHTMLFile.html
http-server .
The following file is a starting point to get Scatter connecting in the browser.
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="modules/scatter-js-2.5.1/dist/scatter.min.js"></script>
<script type="text/javascript" src="modules/web3.js/dist/web3.min.js"></script>
<script>
console.log("Setting up blockchain configuration variables");
const network = {
blockchain: "cmt",
host: "127.0.0.1",
port: 8545,
protocol: "http",
chainId: "19"
}
console.log(network);
scatter.connect("cmt").then(function (connected) {
if (!connected) return false;
console.log("Scatter is now connected");
});
function testIdentity() {
scatter.getIdentity({
personal: ['firstname', 'lastname'],
}).then(function(error, identity){
if(!identity) console.log(error);
});
}
function getBlock4() {
const protocol = 'http' || 'ws';
console.log(protocol);
const web3 = scatter.eth(network, Web3, protocol);
web3.eth.getBlock(4, function (error, result) {
if (!error)
console.log(JSON.stringify(result));
else
console.error(error);
})
}
</script>
</head>
<body>
<h1>Scatter / CyberMiles integration</h1>
<p>1. This button connects your Scatter account with the app</p>
<button type="button" id="testIdentity" onclick='testIdentity()'>1. Test Identity</button>
<p>2. This button queries the CMT blockchain and retrieves the 4th block - just because we can</p>
<button type="button" id="cmtGetBlock" onclick='getBlock4()'>Step 2. Get 4th block</button>
</body>
</html>
You can type code, such as the following, into the Chrome browser's console, once the page has loaded. The following code queries the blockchain and returns the account balance for 0xb2da22ab2404a2b008105217293d3db54b0f9a2c
const protocol = 'http' || 'ws';
console.log(protocol);
const web3 = this.scatter.eth(network, Web3, protocol);
web3.eth.getBalance("0xb2da22ab2404a2b008105217293d3db54b0f9a2c", function(error, result){
if(!error)
console.log(JSON.stringify(result));
else
console.log(error);
})
Obviously, clicking the button called "Get 4th block" (which is on the single page HTML/JS page) also queries the blockchain and returns the data from block number 4.
There are https://get-scatter.com/docs/dev/requirable-fields[required fields] which come from your user's Scatter. The Identity Requests and Signature Requests can provide information to your application.
At present, there are two issues which prevent the transfering of the funds (as shown in the transferFunds function). These are as follows.
Fixed this by adding --rpccorsdomain="*" to the bottom of the ~/.travis/config/config.toml file
Running the transferFunds function returns the following error, in relation to security restrictions of cross origin resource sharig (CORS).
Failed to load http://myTravisTestNet:8545/: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8080' is therefore not allowed access.
Ethereum allows external applications to interact with Geth and the Ethereum network, as apposed to manually having to go through the Geth console. Some extra setup is required in relation to the --rpccorsdomain flag as per the information directly below this paragraph.
There is official Ethereum documentation which relates to the rpccorsdomain setting
Function can be re-written using the following Ethereum reference https://github.com/ethereum/wiki/wiki/JavaScript-API#using-callbacks This has been resolved for all functions except the transferFunds function, this is the next task
Running the transferFunds function used to return the following error
Web3ProviderEngine does not support synchronous requests.
This was an issue in relation to how the web3.eth.sendTransaction command is written inside the application (HTML/JS page).
The following synchronous syntax will not work.
web3.eth.getBalance("0xb2da22ab2404a2b008105217293d3db54b0f9a2c")
Instead this asynchronous syntax will work
web3.eth.getBalance("0xb2da22ab2404a2b008105217293d3db54b0f9a2c", function(error, result){
if(!error)
console.log(JSON.stringify(result));
else
console.log(error);
})