import getWeb3 from '../utils/web3'; import DeviceManager, { getDefaultAccount } from '../DeviceManager'; import elliptic from 'elliptic'; import ethWallet from 'ethereumjs-wallet'; import { sha3, addHexPrefix, setLengthLeft } from 'ethereumjs-util'; import { merkleRoot } from 'merkle-tree-solidity'; import React, { Component } from 'react'; import './RegisterDevice.css'; import { Steps, Button, Input, Card, Spin, Alert, Divider, Form, Icon, Dropdown, Menu, message, notification } from 'antd'; const Step = Steps.Step; const { Meta } = Card; const EC = elliptic.ec; const FormItem = Form.Item; const steps = [{ title: 'Identifier', }, { title: 'Metadata', }, { title: 'Firmware', }, { title: 'Confirm', }]; const openNotificationWithIcon = (type, message, description) => { notification[type]({ message, description }); }; class RegisterDevice extends Component { constructor(props) { super(props); this.state = this.getInitialState(); } getInitialState() { return { loading: false, current: 0, identifier: '', metadataHash: '', firmwareHash: '', showIdentifierInfo: false, publicKey: '', privateKey: '', address: '', metadata: [{ value: '' }], firmware: '', curve: '', deviceId: '', }; } reset() { this.setState(this.getInitialState()); } async componentWillMount() { try { let results = await getWeb3; this.setState({ web3: results.web3, }); } catch (error) { console.log(error); message.error(error.message); } } async watchForChanges(txHash) { let instance = await DeviceManager; let deviceCreatedEvent = instance.DeviceCreated() deviceCreatedEvent.watch((error, result) => { if (!error) { if (result.transactionHash === txHash) { openNotificationWithIcon('success', 'Transaction mined', 'Your device has been registered.'); this.state.deviceCreatedEvent.stopWatching(); this.setState({ loading: false, deviceId: result.args.deviceId.toNumber() }) this.next(); } } else { console.error(error); } }); this.setState({ deviceCreatedEvent }) } next() { const { current, identifier/*, metadataHash, firmwareHash*/ } = this.state; if ((current === 0) && (identifier === null || identifier === '')) { message.error('Invalid identifier: can\'t be empty'); //} else if ((current === 1) && (metadataHash === null || metadataHash === '')) { // message.error('Invalid metadata hash.'); //} else if ((current === 2) && (firmwareHash === null || firmwareHash === '')) { // message.error('Invalid firmware hash.'); } else { this.setState(prevState => ({ current: prevState.current + 1 })); } } prev() { const current = this.state.current - 1; this.setState({ current }); } gotoStep(i) { this.setState({ current: i }); } handleChange(e) { this.setState({ [e.target.name]: e.target.value, }); if (this.state.current === 0) { this.setState({ showIdentifierInfo: false }); } if (this.state.current === 0 && e.target.name === 'identifier') { this.setState({ showIdentifierInfo: false, publicKey: '', privateKey: '', address: '', curve: '' }); } if (this.state.current === 1 && e.target.name === 'metadataHash') { this.setState({ metadata: [{ value: '' }] }); } if (this.state.current === 2 && e.target.name === 'firmwareHash') { this.setState({ firmware: '' }); } } generateEthWallet() { console.log(`Generating new Ethereum wallet`); const newWallet = ethWallet.generate(); let publicKey = newWallet.getPublicKey().toString('hex'); let privateKey = newWallet.getPrivateKey().toString('hex'); let address = newWallet.getAddressString(); console.log(`Private key: ${privateKey}`); console.log(`Public key: ${publicKey}`); console.log(`Address: ${address}`); this.setState({ identifier: address, showIdentifierInfo: true, address, publicKey, privateKey, curve: 'secp256k1' }) } generateEcKeyPair(curve) { let ec = new EC(curve); console.log(`Generating new ${curve} key pair`); let keyPair = ec.genKeyPair(); let publicKey = keyPair.getPublic(true, 'hex'); let privateKey = keyPair.getPrivate('hex'); console.log(`Private key: ${privateKey}`); console.log(`Public key compressed: ${publicKey}`); console.log(`Public key uncompressed: ${keyPair.getPublic().encode('hex')}`); this.setState({ identifier: publicKey, showIdentifierInfo: true, address: '', publicKey, privateKey, curve }) } calculateMetadataHash() { let elements = this.state.metadata.map(el => sha3(el.value)); console.log(`Generating Merkle root hash`); let metadataRootSha3 = merkleRoot(elements); console.log(`Merkle root hash ${metadataRootSha3.toString('hex')}`); this.setState({ metadataHash: metadataRootSha3.toString('hex') }) } calculateFirmwareHash() { let firmwareHash = sha3(this.state.firmware); this.setState({ firmwareHash: firmwareHash.toString('hex') }) } removeMetadataField(k) { const { metadata } = this.state; metadata.splice(k, 1); this.setState({ metadata }) } addMetadataField() { const { metadata } = this.state; metadata.push({ value: '' }); this.setState({ metadata }); } handleMetadataChange(e, index) { const { metadata } = this.state; metadata[index].value = e.target.value; this.setState({ metadata }); } downloadConfiguration() { const { identifier, metadataHash, firmwareHash, metadata, firmware, address, publicKey, privateKey, curve, deviceId } = this.state; const configuration = { identifier, metadataHash, firmwareHash, }; if (metadata.length > 0 && metadata[0].value !== '' && metadataHash !== '') { configuration.metadata = metadata.map(el => el.value); } if (firmware !== '' && firmwareHash !== '') { configuration.firmware = firmware; } if (address !== '') { configuration.address = address; } if (publicKey !== '') { configuration.publicKey = publicKey; } if (privateKey !== '') { configuration.privateKey = privateKey; } if (curve !== '') { configuration.curve = curve; } if (deviceId !== '') { configuration.deviceId = deviceId; } let configurationJson = JSON.stringify(configuration); let element = document.createElement("a"); let file = new Blob([configurationJson], { type: 'text/json' }); element.href = URL.createObjectURL(file); element.download = `device_${deviceId}.json`; element.click(); } onCurveSelect({ key }) { this.generateEcKeyPair(key); }; getContentForStep(step) { const { identifier, metadataHash, firmwareHash, metadata, firmware } = this.state; // Identifier if (step === 0) { const curves = ['p224', 'curve25519']; const ecMenu = ( this.onCurveSelect(e)}> {curves.map(curve => {curve})} ); return (

Unique device identifier is a public key or a fingerprint of RSA/ECC public key. It can also be an Ethereum address (recommended).

this.handleChange(e)} />

{this.state.showIdentifierInfo ?

: null}
); } // Metadata hash if (step === 1) { return (

Metadash hash is Merkle root hash of device information or just a hash of any data.

this.handleChange(e)} />

If you already don't have one, you can use inputs below to generate SHA-3 (Keccak) hash. With multiple fields, Merkle tree will be used.


{metadata.map((key, index) => { return ( this.handleMetadataChange(e, index)} /> {metadata.length > 1 ? ( this.removeMetadataField(index)} /> ) : null} ) }) }
); } // Firmware hash if (step === 2) { return (

Firmware hash is a hash of actual firmware hash. Actual firmware hash is not supposed to be stored.

this.handleChange(e)} />

You can use input to generate SHA-3 (Keccak) hash of any data.


this.handleChange(e)} />

); } // Overview/confirm if (step === 3) { return (
Identifier {identifier} this.gotoStep(0)} />
} bordered={false}> Metadata hash {metadataHash.length > 0 ? metadataHash : 'empty'} this.gotoStep(1)} />} description={
Firmware hash {firmwareHash.length > 0 ? firmwareHash : 'empty'} this.gotoStep(2)} />
} /> ); } // Configuration if (step === 4) { return (


Click below to download device configuration.


); } } async createDevice() { const { identifier, metadataHash, firmwareHash, address } = this.state; try { let instance = await DeviceManager; let identifierToSave = identifier; if (address !== '') { let addressToPad = address; if (address.startsWith('0x')) { addressToPad = addressToPad.substring(2); } identifierToSave = setLengthLeft(Buffer.from(addressToPad, 'hex'), 32).toString('hex'); } let result = await instance.createDevice(addHexPrefix(identifierToSave), addHexPrefix(metadataHash), addHexPrefix(firmwareHash), { from: getDefaultAccount() }); await this.watchForChanges(result.tx); openNotificationWithIcon('info', 'Transaction sent', 'Once mined, your device will be registered.'); this.setState({ loading: true }); } catch (error) { console.log(error); message.error(error.message); } } render() { const { current } = this.state; return (
{steps.map(item => )}
{this.getContentForStep(current)}
{ current < steps.length - 1 && } { current === steps.length - 1 && } { current > 0 && current !== 4 && ( ) } { current === 4 && ( ) }
); } } export default RegisterDevice;