android and his quest for redemption

Publishing to the Decentralized Web with Android, Ethereum, and IPFS

What happens when you combine Android, the world’s most popular mobile operating system, with the newest, user-empowering, decentralized web technologies?

Ether Cyrcus, currently only running on the Ethereum “Rinkeby” testnet

ARTICLE ORGANIZATION

First I’d like to describe the TECHNICAL CHALLENGES of building the decentralized app (or dapp), so that other developers can learn from my process. For non developers, I recommend skipping down to the CONCLUSIONS section, where I’ll describe what this dapp building process reveals about the trajectory of blockchain technology and the future potential of the decentralized web.

TECHNICAL CHALLENGES

The most interesting advantage to creating a dapp on Android is that it is possible to include in the native Android app an Ethereum Light Client node, and an IPFS node. With those pieces in place, the strong guarantees of Ethereum, “applications that run exactly as programmed without any possibility of downtime, censorship, fraud, or third-party interference,” get extended all the way up through the user interface, in an application that can be accessed by anyone with an Android phone and an internet connection. This can be contrasted with building static web pages for dapps that rely on browsers to glue the interface to a remote trusted Ethereum node, or desktop apps that typically do not reach as wide an audience as Web or Native mobile apps. In the next three sections I’ll walk through three key technical challenges and the short, medium, and long term strategies for addressing them.

geth --rinkeby --datadir=~/.rinkeby --cache=2048 --bootnodes=enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303 --port 30305 --rpcapi admin --rpc --maxpeers 10 --lightserv 80 --lightpeers 1 console 2> ~/.rinkeby/Rinkeby.log
func listenForTrustedNodeRequest(w http.ResponseWriter, r *http.Request) {
enodeAddress := r.URL.Path
enodeAddress = strings.TrimPrefix(enodeAddress, "/")
enodeAddressWithSlash := enodeAddress[:6] + "/" + enodeAddress[6:]
command := "/usr/bin/curl -X POST --data '{\"jsonrpc\":\"2.0\",\"method\":\"admin_addTrustedPeer\",\"params\":[\""
command += enodeAddressWithSlash
command += "\"], \"id\":1}' localhost:8545 -H \"Content-Type: application/json\""
cmd := exec.Command("sh", "-c", command);
cmdOutput := &bytes.Buffer{}
cmd.Stdout = cmdOutput
err := cmd.Run()
if err != nil {
os.Stderr.WriteString(err.Error())
}
fmt.Println(string(cmdOutput.Bytes()))
fmt.Println(enodeAddressWithSlash)
w.Write([]byte(command))
}
func main() {
http.HandleFunc("/", listenForTrustedNodeRequest)
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}
private String sendAddTrustedPeerRequest() {
try {
URL myUrl = new URL(SERVER_IP_ADDRESS + "enode://" + mNode.getNodeInfo().getEnode().substring(8, 136) + "@" + MY_IP_ADDRESS + ":" + mNode.getNodeInfo().getListenerPort());
HttpURLConnection connection = (HttpURLConnection) myUrl.openConnection();
connection.connect();
InputStreamReader streamReader = new
InputStreamReader(connection.getInputStream());
BufferedReader reader = new BufferedReader(streamReader);
StringBuilder stringBuilder = new StringBuilder();
String inputLine;
while ((inputLine = reader.readLine()) != null) {
stringBuilder.append(inputLine);
}
reader.close();
streamReader.close();
String response = stringBuilder.toString();
return response;
} catch (Exception e) {
Log.e("AddTrustedPeer", e.getMessage());
return e.getMessage();
}
}
func main() {
//Create and Boot up the IPFS node
ipfs, err := makeIpfs(filepath.Join(os.Getenv("HOME"), ".ethercircus"))
if err != nil {
log.Crit("Failed to create IPFS node", "err", err)
}
defer ipfs.Close()
// Create an IPC based RPC connection to a remote node
conn, err := ethclient.Dial("~/.rinkeby/geth.ipc")
if err != nil {
log.Crit("Failed to connect to the Ethereum client: %v", err)
}
// Instantiate the contract and display its name
publicationRegister, err := gocontracts.NewPublicationRegister(common.HexToAddress("0x18BC6842b400b09D5CC06aBfB7522a12d8F23579"), conn)
if err != nil {
log.Crit("Failed to instantiate the PublicationRegister contract: %v", err)
}
var blockNumber uint64 = 2530622
ch := make(chan *gocontracts.PublicationRegisterStoreData)
opts := &bind.WatchOpts{}
opts.Start = &blockNumber
storeDataSub, err := publicationRegister.WatchStoreData(opts, ch)
if err != nil {
log.Crit("Failed WatchStoreData: %v", err)
}
go func() {
defer storeDataSub.Unsubscribe()
for {
//received an Ethereum Contract Event
var newEvent *gocontracts.PublicationRegisterStoreData = <-ch
fmt.Println(newEvent.Data)
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
defer cancel()
_, err := ipfs.Content(ctx, newEvent.Data)
if err == nil {
fmt.Println("Success!")
} else {
fmt.Println("Error ipfs.Content command: " + err.Error())
}
}()
}
}()
//Monitor for interrupts and terminate cleanly
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt)
defer signal.Stop(sigc)
<-sigc
}

CONCLUSIONS

The power of the Android platform allows us to take another step closer to a fully decentralized application stack. Trade-offs between decentralization, and near term viability can be made, but these trade-offs quickly make our dapps’ advantages over centralized architectures disappear.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store